ru.histone.staticrender.StaticRender.java Source code

Java tutorial

Introduction

Here is the source code for ru.histone.staticrender.StaticRender.java

Source

/**
 *    Copyright 2013-2014 MegaFon
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */
package ru.histone.staticrender;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.yaml.snakeyaml.Yaml;
import ru.histone.Histone;
import ru.histone.HistoneBuilder;
import ru.histone.HistoneException;

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class StaticRender {
    private static final Logger log = LoggerFactory.getLogger(StaticRender.class);
    private static final String TEMPLATE_FILE_EXTENSION = "tpl";

    private static Histone histone;
    private static Yaml yaml;
    private static Map<String, ArrayNode> layouts = new HashMap();
    private static ObjectMapper jackson;

    public static void main(String... args) throws IOException {
        Path workDir = Paths.get(".").toRealPath();
        log.info("Working dir={}", workDir.toString());

        Path srcDir = workDir.resolve("src/site");
        Path dstDir = workDir.resolve("build/site");

        StaticRender app = new StaticRender();
        app.renderSite(srcDir, dstDir);
    }

    public StaticRender() {
        log.info("Starting StaticRender");
        HistoneBuilder builder = new HistoneBuilder();
        try {
            histone = builder.build();
        } catch (HistoneException e) {
            throw new RuntimeException("Error initializing Histone", e);
        }
        yaml = new Yaml();
        jackson = new ObjectMapper();
        log.info("StaticRender started");
    }

    public void renderSite(final Path srcDir, final Path dstDir) {
        log.info("Running StaticRender for srcDir={}, dstDir={}", srcDir.toString(), dstDir.toString());
        Path contentDir = srcDir.resolve("content/");
        final Path layoutDir = srcDir.resolve("layouts/");

        FileVisitor<Path> layoutVisitor = new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
                if (file.toString().endsWith("." + TEMPLATE_FILE_EXTENSION)) {
                    ArrayNode ast = null;
                    try {
                        ast = histone.parseTemplateToAST(new FileReader(file.toFile()));
                    } catch (HistoneException e) {
                        throw new RuntimeException("Error parsing histone template:" + e.getMessage(), e);
                    }

                    final String fileName = file.getFileName().toString();
                    String layoutId = fileName.substring(0,
                            fileName.length() - TEMPLATE_FILE_EXTENSION.length() - 1);
                    layouts.put(layoutId, ast);
                    if (log.isDebugEnabled()) {
                        log.debug("Layout found id='{}', file={}", layoutId, file);
                    } else {
                        log.info("Layout found id='{}'", layoutId);
                    }
                } else {
                    final Path relativeFileName = srcDir.resolve("layouts").relativize(Paths.get(file.toUri()));
                    final Path resolvedFile = dstDir.resolve(relativeFileName);
                    if (!resolvedFile.getParent().toFile().exists()) {
                        Files.createDirectories(resolvedFile.getParent());
                    }
                    Files.copy(Paths.get(file.toUri()), resolvedFile, StandardCopyOption.REPLACE_EXISTING,
                            LinkOption.NOFOLLOW_LINKS);
                }
                return FileVisitResult.CONTINUE;
            }
        };

        FileVisitor<Path> contentVisitor = new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {

                Scanner scanner = new Scanner(file, "UTF-8");
                scanner.useDelimiter("-----");

                String meta = null;
                StringBuilder content = new StringBuilder();

                if (!scanner.hasNext()) {
                    throw new RuntimeException("Wrong format #1:" + file.toString());
                }

                if (scanner.hasNext()) {
                    meta = scanner.next();
                }

                if (scanner.hasNext()) {
                    content.append(scanner.next());
                    scanner.useDelimiter("\n");
                }

                while (scanner.hasNext()) {
                    final String next = scanner.next();
                    content.append(next);

                    if (scanner.hasNext()) {
                        content.append("\n");
                    }
                }

                Map<String, String> metaYaml = (Map<String, String>) yaml.load(meta);

                String layoutId = metaYaml.get("layout");

                if (!layouts.containsKey(layoutId)) {
                    throw new RuntimeException(MessageFormat.format("No layout with id='{0}' found", layoutId));
                }

                final Path relativeFileName = srcDir.resolve("content").relativize(Paths.get(file.toUri()));
                final Path resolvedFile = dstDir.resolve(relativeFileName);
                if (!resolvedFile.getParent().toFile().exists()) {
                    Files.createDirectories(resolvedFile.getParent());
                }
                Writer output = new FileWriter(resolvedFile.toFile());
                ObjectNode context = jackson.createObjectNode();
                ObjectNode metaNode = jackson.createObjectNode();
                context.put("content", content.toString());
                context.put("meta", metaNode);
                for (String key : metaYaml.keySet()) {
                    if (!key.equalsIgnoreCase("content")) {
                        metaNode.put(key, metaYaml.get(key));
                    }
                }

                try {
                    histone.evaluateAST(layoutDir.toUri().toString(), layouts.get(layoutId), context, output);
                    output.flush();
                } catch (HistoneException e) {
                    throw new RuntimeException("Error evaluating content: " + e.getMessage(), e);
                } finally {
                    output.close();
                }

                return FileVisitResult.CONTINUE;
            }
        };

        try {
            Files.walkFileTree(layoutDir, layoutVisitor);
            Files.walkFileTree(contentDir, contentVisitor);
        } catch (Exception e) {
            throw new RuntimeException("Error during site render", e);
        }
    }
}