/**
* Copyright (c) 2011-2017, James Zhan 詹波 (jfinal@126.com).
*
* 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.jfinal.template;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import com.jfinal.kit.HashKit;
import com.jfinal.kit.StrKit;
import com.jfinal.template.stat.Parser;
import com.jfinal.template.stat.ast.Stat;
/**
* Engine
*
* Example:
* Engine.use().getTemplate(fileName).render(...);
* Engine.use().getTemplate(fileName).renderToString(...);
*/
public class Engine {
public static final String MAIN_ENGINE_NAME = "main";
private static Engine MAIN_ENGINE;
private static Map<String, Engine> engineMap = new HashMap<String, Engine>();
// Create main engine
static {
MAIN_ENGINE = new Engine(MAIN_ENGINE_NAME);
engineMap.put(MAIN_ENGINE_NAME, MAIN_ENGINE);
}
private String name;
private boolean devMode = false;
private EngineConfig config = new EngineConfig();
private Map<String, Template> templateCache = new HashMap<String, Template>();
/**
* Create engine without management of JFinal
*/
public Engine() {
this.name = "NO_NAME";
}
/**
* Create engine by engineName without management of JFinal
*/
public Engine(String engineName) {
this.name = engineName;
}
/**
* Using the main Engine
*/
public static Engine use() {
return MAIN_ENGINE;
}
/**
* Using the engine with engine name
*/
public static Engine use(String engineName) {
return engineMap.get(engineName);
}
/**
* Create engine with engine name managed by JFinal
*/
public synchronized static Engine create(String engineName) {
if (StrKit.isBlank(engineName)) {
throw new IllegalArgumentException("Engine name can not be blank");
}
engineName = engineName.trim();
if (engineMap.containsKey(engineName)) {
throw new IllegalArgumentException("Engine already exists : " + engineName);
}
Engine newEngine = new Engine(engineName);
engineMap.put(engineName, newEngine);
return newEngine;
}
/**
* Remove engine with engine name managed by JFinal
*/
public synchronized static Engine remove(String engineName) {
Engine removed = engineMap.remove(engineName);
if (removed != null && MAIN_ENGINE_NAME.equals(removed.name)) {
Engine.MAIN_ENGINE = null;
}
return removed;
}
/**
* Set main engine
*/
public synchronized static void setMainEngine(Engine engine) {
if (engine == null) {
throw new IllegalArgumentException("Engine can not be null");
}
engine.name = Engine.MAIN_ENGINE_NAME;
engineMap.put(Engine.MAIN_ENGINE_NAME, engine);
Engine.MAIN_ENGINE = engine;
}
/**
* Get template with file name
*/
public Template getTemplate(String fileName) {
if (fileName.charAt(0) != '/') {
char[] arr = new char[fileName.length() + 1];
fileName.getChars(0, fileName.length(), arr, 1);
arr[0] = '/';
fileName = new String(arr);
}
Template template = templateCache.get(fileName);
if (template == null) {
template = buildTemplateByFileStringSource(fileName);
templateCache.put(fileName, template);
} else if (devMode) {
if (template.isModified()) {
template = buildTemplateByFileStringSource(fileName);
templateCache.put(fileName, template);
}
}
return template;
}
private Template buildTemplateByFileStringSource(String fileName) {
FileStringSource fileStringSource = new FileStringSource(config.getBaseTemplatePath(), fileName, config.getEncoding());
Env env = new Env(config);
Parser parser = new Parser(env, fileStringSource.getContent(), fileName);
if (devMode) {
env.addStringSource(fileStringSource);
}
Stat stat = parser.parse();
Template template = new Template(env, stat);
return template;
}
/**
* Get template by string content
*/
public Template getTemplateByString(String content) {
String key = HashKit.md5(content);
Template template = templateCache.get(key);
if (template == null) {
template = buildTemplateByStringSource(new MemoryStringSource(content));
templateCache.put(key, template);
} else if (devMode) {
if (template.isModified()) {
template = buildTemplateByStringSource(new MemoryStringSource(content));
templateCache.put(key, template);
}
}
return template;
}
/**
* Get template with implementation of IStringSource
*/
public Template getTemplate(IStringSource stringSource) {
String key = stringSource.getKey();
Template template = templateCache.get(key);
if (template == null) {
template = buildTemplateByStringSource(stringSource);
templateCache.put(key, template);
} else if (devMode) {
if (template.isModified()) {
template = buildTemplateByStringSource(stringSource);
templateCache.put(key, template);
}
}
return template;
}
private Template buildTemplateByStringSource(IStringSource stringSource) {
Env env = new Env(config);
Parser parser = new Parser(env, stringSource.getContent(), null);
if (devMode) {
env.addStringSource(stringSource);
}
Stat stat = parser.parse();
Template template = new Template(env, stat);
return template;
}
/**
* Add shared function with file
*/
public Engine addSharedFunction(String fileName) {
config.addSharedFunction(fileName);
return this;
}
/**
* Add shared function by IStringSource
*/
public Engine addSharedFunction(IStringSource stringSource) {
config.addSharedFunction(stringSource);
return this;
}
/**
* Add shared function with files
*/
public Engine addSharedFunction(String... fileNames) {
config.addSharedFunction(fileNames);
return this;
}
/**
* Add shared function by string content
*/
public Engine addSharedFunctionByString(String content) {
config.addSharedFunctionByString(content);
return this;
}
/**
* Add shared object
*/
public Engine addSharedObject(String name, Object object) {
config.addSharedObject(name, object);
return this;
}
/**
* Set output directive factory
*/
public Engine setOutputDirectiveFactory(IOutputDirectiveFactory outputDirectiveFactory) {
config.setOutputDirectiveFactory(outputDirectiveFactory);
return this;
}
/**
* Add directive
*/
public Engine addDirective(String directiveName, Directive directive) {
config.addDirective(directiveName, directive);
return this;
}
/**
* Remove directive
*/
public Engine removeDirective(String directiveName) {
config.removeDirective(directiveName);
return this;
}
/**
* Add shared method from object
*/
public Engine addSharedMethod(Object sharedMethodFromObject) {
config.addSharedMethod(sharedMethodFromObject);
return this;
}
/**
* Add shared static method of Class
*/
public Engine addSharedStaticMethod(Class<?> sharedClass) {
config.addSharedStaticMethod(sharedClass);
return this;
}
/**
* Remove shared Method with method name
*/
public Engine removeSharedMethod(String methodName) {
config.removeSharedMethod(methodName);
return this;
}
/**
* Remove shared Method of the Class
*/
public Engine removeSharedMethod(Class<?> clazz) {
config.removeSharedMethod(clazz);
return this;
}
/**
* Remove shared Method
*/
public Engine removeSharedMethod(Method method) {
config.removeSharedMethod(method);
return this;
}
/**
* Remove template cache with template key
*/
public void removeTemplateCache(String templateKey) {
templateCache.remove(templateKey);
}
/**
* Remove all template cache
*/
public void removeAllTemplateCache() {
templateCache.clear();
}
public String getName() {
return name;
}
public String toString() {
return "Template Engine: " + name;
}
// Engine config below ---------
public EngineConfig getEngineConfig() {
return config;
}
public Engine setDevMode(boolean devMode) {
this.devMode = devMode;
this.config.setDevMode(devMode);
if (this.devMode) {
removeAllTemplateCache();
}
return this;
}
public boolean getDevMode() {
return devMode;
}
public Engine setBaseTemplatePath(String baseTemplatePath) {
config.setBaseTemplatePath(baseTemplatePath);
return this;
}
public String getBaseTemplatePath() {
return config.getBaseTemplatePath();
}
public Engine setDatePattern(String datePattern) {
config.setDatePattern(datePattern);
return this;
}
public String getDatePattern() {
return config.getDatePattern();
}
public Engine setEncoding(String encoding) {
config.setEncoding(encoding);
return this;
}
public String getEncoding() {
return config.getEncoding();
}
/**
* Engine 独立设置为 devMode 可以方便模板文件在修改后立即生效,
* 但如果在 devMode 之下并不希望对 addSharedFunction(...),
* 添加的模板进行是否被修改的检测可以通过此方法设置 false 参进去
*
* 注意:Engine 在生产环境下(devMode 为 false),该参数无效
*/
public Engine setReloadModifiedSharedFunctionInDevMode(boolean reloadModifiedSharedFunctionInDevMode) {
config.setReloadModifiedSharedFunctionInDevMode(reloadModifiedSharedFunctionInDevMode);
return this;
}
}