org.zodiac.template.base.impl.TemplateServiceImpl Maven / Gradle / Ivy
The newest version!
package org.zodiac.template.base.impl;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.zodiac.core.configuration.ProductionModeAware;
import org.zodiac.core.service.AbstractService;
import org.zodiac.sdk.toolkit.util.AssertUtil;
import org.zodiac.sdk.toolkit.util.collection.CollUtil;
import org.zodiac.sdk.toolkit.util.file.FileToolUtil;
import org.zodiac.sdk.toolkit.util.lang.StrUtil;
import org.zodiac.template.base.TemplateContext;
import org.zodiac.template.base.TemplateEngine;
import org.zodiac.template.base.TemplateException;
import org.zodiac.template.base.TemplateNotFoundException;
import org.zodiac.template.base.TemplateService;
import org.zodiac.template.base.constants.TemplateConstants;
public class TemplateServiceImpl extends AbstractService
implements TemplateService, ProductionModeAware {
/*engineName -> engine*/
private Map engines;
/*ext -> engine*/
private Map engineMappings;
/*ext -> engineName*/
private Map engineNameMappings;
private String defaultExtension = TemplateConstants.DEFAULT_DEFAULT_EXTENSION;
private boolean searchExtensions;
private boolean searchLocalizedTemplates;
private TemplateSearchingStrategy[] strategies;
private Boolean cacheEnabled;
private boolean productionMode = true;
private Map matchedTemplates;
public TemplateServiceImpl() {
}
public TemplateServiceImpl setEngines(Map engines) {
this.engines = engines;
return this;
}
public TemplateServiceImpl setEngineNameMappings(Map engineNameMappings) {
this.engineNameMappings = engineNameMappings;
return this;
}
public TemplateServiceImpl setDefaultExtension(String defaultExtension) {
this.defaultExtension = defaultExtension;
return this;
}
public TemplateServiceImpl setSearchExtensions(boolean searchExtensions) {
this.searchExtensions = searchExtensions;
return this;
}
public TemplateServiceImpl setSearchLocalizedTemplates(boolean searchLocalizedTemplates) {
this.searchLocalizedTemplates = searchLocalizedTemplates;
return this;
}
public Boolean isCacheEnabled() {
return cacheEnabled;
}
public TemplateServiceImpl setCacheEnabled(Boolean cacheEnabled) {
this.cacheEnabled = cacheEnabled;
return this;
}
public boolean isProductionMode() {
return productionMode;
}
public void setProductionMode(boolean productionMode) {
this.productionMode = productionMode;
}
@Override
protected void init() {
if (engines == null) {
engines = CollUtil.map();
}
if (engines.isEmpty()) {
getLogger().warn("No Template Engine registered for TemplateService: id={}", getBeanName());
}
if (cacheEnabled == null) {
/*如果未指定cacheEnabled,则默认当productionMode时,打开cache。*/
cacheEnabled = productionMode;
}
if (cacheEnabled) {
matchedTemplates = CollUtil.concurrentMap();
}
Set remappedNames = CollUtil.set();
engineMappings = CollUtil.treeMap();
/*生成engineMappings。*/
if (engineNameMappings != null) {
for (Map.Entry entry : engineNameMappings.entrySet()) {
String ext = entry.getKey();
String engineName = entry.getValue();
AssertUtil.assertTrue(StrUtil.isNotEmpty(ext) && !ext.startsWith("."), "Invalid extension: %s", ext);
AssertUtil.assertTrue(engines.containsKey(engineName),
"TemplateEngine \"%s\" not defined. Defined names: %s", engineName, engines.keySet());
remappedNames.add(engineName);
engineMappings.put(ext, engines.get(engineName));
getLogger().debug("Template Name \"*.{}\" mapped to Template Engine: {}", ext, engineName);
}
engineNameMappings = null;
}
/*对于没有指定mapping的engine,取得其默认后缀。*/
for (Map.Entry entry : engines.entrySet()) {
String engineName = entry.getKey();
TemplateEngine engine = entry.getValue();
if (!remappedNames.contains(engineName)) {
String[] exts = engine.getDefaultExtensions();
for (String ext : exts) {
ext = FileToolUtil.normalizeExtension(ext);
AssertUtil.assertNotNull(ext, "Default extensions for engine: %s", engine);
engineMappings.put(ext, engine);
getLogger().debug("Template Name \"*.{}\" mapped to Template Engine: {}", ext, engineName);
}
}
}
/*Searching strategies.*/
defaultExtension = FileToolUtil.normalizeExtension(defaultExtension);
List strategyList = CollUtil.linkedList();
if (defaultExtension != null) {
strategyList.add(new DefaultExtensionStrategy(defaultExtension));
}
if (searchExtensions) {
strategyList.add(new SearchExtensionsStrategy(getSupportedExtensions()));
}
if (searchLocalizedTemplates) {
strategyList.add(new SearchLocalizedTemplatesStrategy());
}
strategies = strategyList.toArray(new TemplateSearchingStrategy[strategyList.size()]);
}
/**
* 取得指定模板名后缀对应的engine。
*
* @param engineName 引擎名称
* @return 模板引擎
*/
public TemplateEngine getEngineOfName(String engineName) {
return engines.get(engineName);
}
/**
* 取得指定模板名后缀对应的engine。
*
* @param extension 扩展名
* @return 模板引擎
*/
public TemplateEngine getTemplateEngine(String extension) {
if (extension == null) {
return null; // prevent treemap from throwing npe
}
return engineMappings.get(extension);
}
/**
* 取得所有被登记的文件名后缀。
*
* @return 扩展名列表
*/
public String[] getSupportedExtensions() {
return engineMappings.keySet().toArray(new String[engineMappings.size()]);
}
/**
* 判断模板是否存在。
*
* @param templateName 模板名称
* @return 是对存在
*/
public boolean exists(String templateName) {
try {
findTemplate(templateName);
return true;
} catch (TemplateNotFoundException e) {
return false;
}
}
/**
* 渲染模板,并以字符串的形式取得渲染的结果。
*
* @param templateName 模板名称
* @param context 上下文
* @return 内容
* @throws TemplateException TemplateException
* @throws IOException IOException
*/
public String getText(String templateName, TemplateContext context) throws TemplateException, IOException {
TemplateMatchResult result = findTemplate(templateName);
TemplateEngine engine = AssertUtil.assertNotNull(result.getEngine(), "templateEngine");
return engine.getText(result.getTemplateName(), context);
}
/**
* @param templateName 模板名称
* @param context 上下文
* @param ostream 输出流
* @throws TemplateException TemplateException
* @throws IOException IOException
*/
public void writeTo(String templateName, TemplateContext context, OutputStream ostream)
throws TemplateException, IOException {
TemplateMatchResult result = findTemplate(templateName);
TemplateEngine engine = AssertUtil.assertNotNull(result.getEngine(), "templateEngine");
engine.writeTo(result.getTemplateName(), context, ostream);
}
/**
* 渲染模板,并将渲染的结果送到字符输出流中。
* param templateName 模板名称
* @param context 上下文
* @param writer 输出流
* @throws TemplateException TemplateException
* @throws IOException IOException
*/
public void writeTo(String templateName, TemplateContext context, Writer writer)
throws TemplateException, IOException {
TemplateMatchResult result = findTemplate(templateName);
TemplateEngine engine = AssertUtil.assertNotNull(result.getEngine(), "templateEngine");
engine.writeTo(result.getTemplateName(), context, writer);
}
/**
* 查找指定名称的模板。
* @param templateName 模板名称
* @return 模板查找结果
*/
TemplateMatchResult findTemplate(String templateName) {
assertInitialized();
TemplateKey key = new TemplateKey(templateName, strategies);
TemplateMatchResult result;
if (cacheEnabled) {
result = matchedTemplates.get(key);
if (result != null) {
return result;
}
}
TemplateMatcher matcher = new TemplateMatcher(key) {
private int i;
@Override
public boolean findTemplate() {
boolean found = false;
/*保存状态,假如没有匹配,则恢复状态*/
String savedTemplateNameWithoutExtension = getTemplateNameWithoutExtension();
String savedExtension = getExtension();
TemplateEngine savedEngine = getEngine();
int savedStrategyIndex = i;
try {
if (i < strategies.length) {
found = strategies[i++].findTemplate(this);
} else {
found = findTemplateInTemplateEngine(this);
}
} finally {
if (!found) {
/*恢复状态,以便尝试其它平级strategies*/
setTemplateNameWithoutExtension(savedTemplateNameWithoutExtension);
setExtension(savedExtension);
setEngine(savedEngine);
i = savedStrategyIndex;
}
}
return found;
}
};
if (!matcher.findTemplate()) {
throw new TemplateNotFoundException(
"Could not find template \"" + matcher.getOriginalTemplateName() + "\"");
}
if (cacheEnabled) {
result = new TemplateMatchResultImpl(matcher.getTemplateName(), matcher.getEngine());
matchedTemplates.put(key, result);
} else {
result = matcher;
}
return result;
}
/* 查找模板的最终strategy结点。 */
private boolean findTemplateInTemplateEngine(TemplateMatcher matcher) {
TemplateEngine engine = getTemplateEngine(matcher.getExtension());
matcher.setEngine(engine);
if (engine == null) {
return false;
}
String templateName = matcher.getTemplateName();
getLogger().trace("Searching for template \"{}\" using {}", templateName, engine);
return engine.exists(templateName);
}
}