/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * 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.alibaba.citrus.service.freemarker.impl; import static com.alibaba.citrus.service.freemarker.FreeMarkerConfiguration.*; import static com.alibaba.citrus.util.StringUtil.*; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.StringWriter; import java.io.Writer; import java.util.Locale; import com.alibaba.citrus.service.AbstractService; import com.alibaba.citrus.service.configuration.ProductionModeAware; import com.alibaba.citrus.service.freemarker.FreeMarkerEngine; import com.alibaba.citrus.service.template.TemplateContext; import com.alibaba.citrus.service.template.TemplateException; import com.alibaba.citrus.util.i18n.LocaleUtil; import freemarker.core.Environment; import freemarker.core.ParseException; import freemarker.log.Logger; import freemarker.template.Template; import org.springframework.context.ResourceLoaderAware; import org.springframework.core.io.ResourceLoader; /** * FreeMarker模板引擎。 * * @author Michael Zhou */ public class FreeMarkerEngineImpl extends AbstractService<FreeMarkerEngine> implements FreeMarkerEngine, ResourceLoaderAware, ProductionModeAware { private final FreeMarkerConfigurationImpl configuration = new FreeMarkerConfigurationImpl(getLogger()); // 初始化slf4j日志。 static { try { Logger.selectLoggerLibrary(Logger.LIBRARY_SLF4J); Logger.setCategoryPrefix(""); } catch (Throwable ee) { } } public FreeMarkerConfigurationImpl getConfiguration() { return configuration; } public void setResourceLoader(ResourceLoader loader) { configuration.setResourceLoader(loader); } public void setProductionMode(boolean productionMode) { configuration.setProductionMode(productionMode); } /** 初始化engine。 */ @Override protected void init() { configuration.init(); getLogger().debug("FreeMarker Engine Configurations: {}", configuration); } /** * 取得默认的模板名后缀列表。 * <p> * 当<code>TemplateService</code>没有指定到当前engine的mapping时,将取得本方法所返回的后缀名列表。 * </p> */ public String[] getDefaultExtensions() { return new String[] { "ftl" }; } /** 判定模板是否存在。 */ public boolean exists(String templateName) { try { return configuration.getTemplateLoader().findTemplateSource(templateName) != null; } catch (IOException e) { return false; } } /** 渲染模板,并以字符串的形式取得渲染的结果。 */ public String getText(String templateName, TemplateContext context) throws TemplateException, IOException { StringWriter out = new StringWriter(); render(templateName, context, out, null, null, null); return out.toString(); } /** 渲染模板,并将渲染的结果送到字节输出流中。 */ public void writeTo(String templateName, TemplateContext context, OutputStream ostream) throws TemplateException, IOException { render(templateName, context, null, ostream, null, null); } /** 渲染模板,并将渲染的结果送到字符输出流中。 */ public void writeTo(String templateName, TemplateContext context, Writer writer) throws TemplateException, IOException { render(templateName, context, writer, null, null, null); } public String mergeTemplate(String templateName, Object context, String inputCharset) throws TemplateException, IOException { StringWriter out = new StringWriter(); render(templateName, context, out, null, inputCharset, null); return out.toString(); } public void mergeTemplate(String templateName, Object context, OutputStream ostream, String inputCharset, String outputCharset) throws TemplateException, IOException { render(templateName, context, null, ostream, inputCharset, outputCharset); } public void mergeTemplate(String templateName, Object context, Writer out, String inputCharset) throws TemplateException, IOException { render(templateName, context, out, null, inputCharset, null); } /** 渲染模板到指定输出流。 */ private void render(String templateName, Object context, Writer writer, OutputStream ostream, String inputCharset, String outputCharset) throws TemplateException, IOException { Locale locale = LocaleUtil.getContext().getLocale(); // inputCharset依次为:指定值、engine初始化时的默认值、DEFAULT_CHARSET if (isEmpty(inputCharset)) { inputCharset = configuration.getConfiguration().getDefaultEncoding(); } inputCharset = defaultIfEmpty(inputCharset, DEFAULT_CHARSET); // outputCharset依次为:指定值、engine初始化时的默认值、DEFAULT_CHARSET if (isEmpty(outputCharset)) { outputCharset = configuration.getConfiguration().getOutputEncoding(); } outputCharset = defaultIfEmpty(outputCharset, DEFAULT_CHARSET); if (writer == null) { if (ostream == null) { throw new IllegalArgumentException("missing output writer"); } writer = new OutputStreamWriter(ostream, outputCharset); } try { Template template = configuration.getConfiguration().getTemplate(templateName, locale, inputCharset); Environment env = template.createProcessingEnvironment(context, writer); env.setLocale(locale); env.setOutputEncoding(outputCharset); env.process(); } catch (freemarker.template.TemplateException e) { error(templateName, e); } catch (ParseException e) { error(templateName, e); } } private void error(String templateName, Throwable e) { throw new TemplateException("Error rendering FreeMarker template: " + templateName, e); } }