com.bunjlabs.fuga.templates.TemplateCompiler.java Source code

Java tutorial

Introduction

Here is the source code for com.bunjlabs.fuga.templates.TemplateCompiler.java

Source

/* 
 * 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 com.bunjlabs.fuga.templates;

import com.bunjlabs.fuga.resources.ResourceRepresenter;
import com.bunjlabs.fuga.templates.TemplateReader.Token;
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.script.Compilable;
import javax.script.CompiledScript;
import javax.script.ScriptEngine;
import javax.script.ScriptException;
import jdk.nashorn.api.scripting.NashornScriptEngineFactory;

public class TemplateCompiler {

    private static final Pattern CODE_PATTERN = Pattern.compile("(\\@([\\s\\S]*?)\\;)"
            + "|(\\@\\{([^\\{\\}\\n\\r]*?)\\})" + "|(\\<%([\\s\\S]*?)%\\>)" + "|(\\<#([^\\n\\r]*?)#\\>)");

    private final ResourceRepresenter resourceRepresenter;
    private final ScriptEngine engine;

    /**
     * Create new template compiler with resource manager
     * 
     * @param resourceRepresenter Resource manager
     */
    public TemplateCompiler(ResourceRepresenter resourceRepresenter) {
        this.resourceRepresenter = resourceRepresenter;
        this.engine = new NashornScriptEngineFactory().getScriptEngine("--print-no-newline");
    }

    /**
     * Compile template from the source
     * 
     * @param name path or/and name of template
     * @return Compiled template
     * @throws TemplateNotFoundException if template file not founded
     * @throws TemplateCompileException if compiling error is occured
     * @throws TemplateReaderException  if any error is occurred while rendering
     */
    public Template compile(String name)
            throws TemplateNotFoundException, TemplateCompileException, TemplateReaderException {
        InputStream is;
        try {
            is = resourceRepresenter.load(name.startsWith("/") ? name.substring(1) : name);
        } catch (FileNotFoundException ex) {
            throw new TemplateNotFoundException("Unable to load: " + name, ex);
        }

        BufferedReader reader = new BufferedReader(new InputStreamReader(is));

        return compile(new TemplateReader(reader));
    }

    private Template compile(TemplateReader input)
            throws TemplateNotFoundException, TemplateCompileException, TemplateReaderException {
        Template template = new Template();

        Token token;
        while ((token = input.next()).type != Token.EOS) {
            if (token.type == Token.EXTENDS) {
                String extendName = token.args[0];
                if (extendName.length() > 0) {
                    template.extend(compile(extendName));
                }
            } else if (token.type == Token.USE) {
                String useName = token.args[0];
                if (useName.length() > 0) {
                    template.use(compile(useName));
                }
            } else if (token.type == Token.BLOCK) {
                String blockName = token.args[0];
                String blockAppend = token.args[1];
                String blockSource = token.args[2];
                if (blockName.length() > 0 && blockSource.length() > 0) {
                    template.addBlock(blockName, compileBlock(blockSource), blockAppend.equals("append"));
                }
            } else if (token.type == Token.TAG) {
                String tagName = token.args[0];
                String tagSource = token.args[1];
                if (tagName.length() > 0 && tagSource.length() > 0) {
                    template.addTag(tagName, compileBlock(tagSource));
                }
            } else if (token.type == Token.CODE) {
                String blockSource = token.args[0];
                if (blockSource.length() > 0) {
                    template.addCodeBlock(compileCodeBlock(blockSource));
                }
            } else {
                throw new TemplateCompileException("Unexpected keyword: " + token.type);
            }
        }

        return template;
    }

    private ScriptBlock compileBlock(String input) throws TemplateCompileException {
        StringBuilder jsCode = new StringBuilder();

        while (input.length() > 0) {
            Matcher m = CODE_PATTERN.matcher(input);
            if (m.find()) {
                String text = input.substring(0, m.start());
                if (text.length() > 0) {
                    jsCode.append(String.format("print(\"%s\");", escapeSpaces(text)));
                }
                if (m.group(2) != null || m.group(4) != null) {
                    String code = m.group(2) != null ? m.group(2) : m.group(4);
                    jsCode.append(String.format("print(%s || '');", code.trim()));

                    input = input.substring(m.end());
                } else if (m.group(6) != null) {
                    String codeBlock = m.group(6).trim();

                    jsCode.append(codeBlock);
                    input = input.substring(m.end());

                } else if (m.group(8) != null) {
                    jsCode.append(String.format("print(%s || '');", m.group(8).trim()));
                    input = input.substring(m.end());
                }

            } else if (input.length() > 0) {
                jsCode.append(String.format("print(\"%s\");", escapeSpaces(input)));
                input = "";
            }
        }
        CompiledScript compiled;

        try {
            compiled = ((Compilable) engine).compile(jsCode.toString());
        } catch (ScriptException ex) {
            throw new TemplateCompileException(ex);
        }

        return new ScriptBlock(compiled);
    }

    private ScriptBlock compileCodeBlock(String input) throws TemplateCompileException {
        CompiledScript compiled;

        try {
            compiled = ((Compilable) engine).compile(input);
        } catch (ScriptException ex) {
            throw new TemplateCompileException(ex);
        }

        return new ScriptBlock(compiled);
    }

    private static String escapeSpaces(String input) {
        return input.replaceAll("\\\\", "\\\\\\\\").replaceAll("\\t", "\\\\t").replaceAll("\\n", "\\\\n")
                .replaceAll("\\r", "\\\\r").replaceAll("\\f", "\\\\f").replaceAll("\\'", "\\\\'")
                .replaceAll("\\\"", "\\\\\"");
    }
}