jetbrick.template.JetTemplateImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jetbrick-template Show documentation
Show all versions of jetbrick-template Show documentation
Next generation template engine for Java
/**
* 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();
}
}