All Downloads are FREE. Search and download functionalities are using the official Maven repository.

jetbrick.template.JetTemplateImpl Maven / Gradle / Ivy

There is a newer version: 2.1.10
Show newest version
/**
 * Copyright 2013-2016 Guoqiang Chen, Shanghai, China. All rights reserved.
 *
 *   Author: Guoqiang Chen
 *    Email: [email protected]
 *   WebURL: https://github.com/subchen
 *
 * 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 jetbrick.template;

import java.io.OutputStream;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import jetbrick.io.resource.Resource;
import jetbrick.io.resource.ResourceNotFoundException;
import jetbrick.template.parser.*;
import jetbrick.template.parser.ast.AstDirectiveMacro;
import jetbrick.template.parser.ast.AstTemplate;
import jetbrick.template.resolver.macro.MacroResolver;
import jetbrick.template.runtime.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 代表一个模板文件.
 */
final class JetTemplateImpl implements JetTemplate {
    private static final Logger log = LoggerFactory.getLogger(JetTemplateImpl.class);

    private final JetEngine engine;
    private final Resource resource;
    private final JetConfig config;
    private final JetSecurityManager securityManager;

    private final boolean reloadable;
    private volatile long lastModified;

    private Source source;
    private AstTemplate astNode;
    private JetTemplateOption option;

    private final MacroResolver macroResolver;

    public JetTemplateImpl(JetEngine engine, Resource resource, boolean reloadable, JetSecurityManager securityManager) {
        this.engine = engine;
        this.resource = resource;
        this.securityManager = securityManager;
        this.config = engine.getConfig();
        this.reloadable = reloadable;
        this.lastModified = 0;
        this.macroResolver = new MacroResolver();
    }

    // 检测模板是否已更新/删除
    @Override
    public void reload() throws ResourceNotFoundException {
        if (reloadable == false && lastModified > 0) {
            return;
        }

        long ts = resource.lastModified();
        if (ts <= 0) {
            throw new ResourceNotFoundException(resource.getRelativePathName());
        }

        if (lastModified != ts) {
            synchronized (this) {
                // double check
                ts = resource.lastModified();
                if (lastModified != ts) {
                    // 重建 AST & Config
                    rebuildAstNodeAndConfig();

                    // 保存最后修改时间
                    lastModified = ts;
                }
            }
        }
    }

    // 重建 AST & Config
    private void rebuildAstNodeAndConfig() {
        // create source
        String filename = resource.getRelativePathName();
        char[] contents = resource.toCharArray(config.getInputEncoding());
        source = new Source(filename, contents);

        log.info("Loading template: {}", filename);

        // create ctx
        ParserContext ctx = new ParserContext(engine.getGlobalResolver(), engine.getGlobalContext());
        ctx.setStrict(config.isSyntaxStrict());
        ctx.setSafecall(config.isSyntaxSafecall());
        ctx.setTrimLeadingWhitespaces(config.isTrimLeadingWhitespaces());
        ctx.setTrimDirectiveWhitespaces(config.isTrimDirectiveWhitespaces());
        ctx.setTrimDirectiveComments(config.isTrimDirectiveComments());
        ctx.setTrimDirectiveCommentsPrefix(config.getTrimDirectiveCommentsPrefix());
        ctx.setTrimDirectiveCommentsSuffix(config.getTrimDirectiveCommentsSuffix());

        // 解析模板,然后生成 AST
        astNode = AstBuilder.create(source, ctx);

        // 重置配置
        option = new JetTemplateOption(ctx);

        // 重建 MacroResolver
        macroResolver.clear();

        // add declared macros
        List macros = ctx.getDeclaredMacros();
        if (macros != null && macros.size() > 0) {
            for (AstDirectiveMacro macro : macros) {
                macroResolver.register(new JetTemplateMacro(this, macro));
            }
        }
        // add included macros
        List files = ctx.getLoadMacroFiles();
        if (files != null && files.size() > 0) {
            for (String file : files) {
                JetTemplate t = engine.getTemplate(file);
                macroResolver.register(t);
            }
        }
    }

    @Override
    public void render(Map context, Writer out) {
        JetWriter writer = JetWriter.create(out, config.getOutputEncoding(), option.isTrimLeadingWhitespaces(), config.isIoSkiperrors());
        doInterpret(context, writer);
    }

    @Override
    public void render(Map context, OutputStream out) {
        JetWriter writer = JetWriter.create(out, config.getOutputEncoding(), option.isTrimLeadingWhitespaces(), config.isIoSkiperrors());
        doInterpret(context, writer);
    }

    private void doInterpret(Map context, JetWriter writer) {
        // 如果在扩展函数,方法,tag 等里面,再次调用 getTemplate().render(),需要保持当前环境,在运行完之后,进行现场恢复
        // see: https://github.com/subchen/jetbrick-template-2x/issues/6
        InterpretContextImpl last = (InterpretContextImpl) InterpretContext.current();
        try {
            InterpretContextImpl ctx = new InterpretContextImpl(engine, writer, context, securityManager);
            try {
                ctx.getTemplateStack().push(this);
                ctx.getValueStack().push(option.getSymbols(), null, true);

                astNode.execute(ctx);

                ctx.getValueStack().pop();
                ctx.getTemplateStack().pop();
            } catch (InterpretException e) {
                throw e.set(ctx.getSource());
            } finally {
                ctx.freeThreadLocal();
            }
        } finally {
            // 进行现场恢复
            if (last != null) {
                last.setThreadLocal();
            }
        }
    }

    @Override
    public JetTemplateMacro resolveMacro(String name, Class[] argumentTypes, boolean root) {
        JetTemplateMacro macro = macroResolver.resolve(name, argumentTypes, reloadable);
        if (macro == null && root) {
            macro = engine.getGlobalResolver().resolveMacro(name, argumentTypes, reloadable);
        }
        return macro;
    }

    @Override
    public String getName() {
        return resource.getRelativePathName();
    }

    @Override
    public Source getSource() {
        return source;
    }

    @Override
    public boolean isReloadable() {
        return reloadable;
    }

    @Override
    public long getLastModified() {
        return lastModified;
    }

    @Override
    public JetTemplateOption getOption() {
        return option;
    }

    @Override
    public AstTemplate getAstNode() {
        return astNode;
    }

    @Override
    public JetEngine getEngine() {
        return engine;
    }

    @Override
    public JetSecurityManager getSecurityManager() {
        return securityManager;
    }

    @Override
    public String toString() {
        return getName();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy