001package org.andromda.templateengines.freemarker; 002 003import java.io.IOException; 004import java.io.StringReader; 005import java.io.StringWriter; 006import java.io.Writer; 007import java.net.URL; 008import java.util.ArrayList; 009import java.util.Enumeration; 010import java.util.HashMap; 011import java.util.List; 012import java.util.Map; 013import org.andromda.core.common.AndroMDALogger; 014import org.andromda.core.common.ExceptionUtils; 015import org.andromda.core.templateengine.TemplateEngine; 016import org.andromda.core.templateengine.TemplateEngineException; 017import org.apache.log4j.Appender; 018import org.apache.log4j.FileAppender; 019import org.apache.log4j.Logger; 020import org.apache.log4j.PatternLayout; 021import freemarker.template.Configuration; 022import freemarker.template.ObjectWrapper; 023import freemarker.template.Template; 024 025/** 026 * The TemplateEngine implementation for the FreeMarker template processor. 027 * 028 * @author Chad Brandon 029 * @author Olaf Muliawan 030 * @author Matthias Bohlen (usage of context classloader so that it works in Maven environments) 031 * @see <a href="http://www.freemarker.org">Freemarker</a> 032 */ 033public class FreeMarkerTemplateEngine implements TemplateEngine 034{ 035 /** 036 * @see org.andromda.core.templateengine.TemplateEngine#initialize(String) 037 */ 038 public void initialize(String namespace) throws Exception 039 { 040 this.initLogger(namespace); 041 } 042 043 /** 044 * The main Configuration object of Freemarker. 045 */ 046 protected Configuration configuration = null; 047 048 /** 049 * @see org.andromda.core.templateengine.TemplateEngine#processTemplate(String, 050 * java.util.Map, java.io.Writer) 051 */ 052 public void processTemplate(String templateFile, Map<String, Object> templateObjects, Writer output) throws Exception 053 { 054 ExceptionUtils.checkEmpty("templateFile", templateFile); 055 ExceptionUtils.checkNull("output", output); 056 057 if (this.configuration == null) 058 { 059 this.configuration = new Configuration(); 060 061 // tell FreeMarker it should use the ContextClassLoader when 062 // searching for templates 063 this.configuration.setTemplateLoader(new ContextResourceLoader()); 064 065 // use Bean Wrapper, in order to get maximal reflection 066 // capabilities 067 this.configuration.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER); 068 } 069 070 // create the template 071 final Template template = this.configuration.getTemplate(templateFile); 072 073 if (templateObjects == null) 074 { 075 templateObjects = new HashMap<String, Object>(); 076 } 077 078 // - merge data model with template 079 template.process(templateObjects, output); 080 } 081 082 /** 083 * Template loader which accesses the context classloader because otherwise 084 * templates will not be found properly in a multi-classloader environment. 085 */ 086 private static class ContextResourceLoader extends freemarker.cache.URLTemplateLoader 087 { 088 protected URL getURL(String name) 089 { 090 return Thread.currentThread().getContextClassLoader().getResource(name); 091 } 092 } 093 094 /** 095 * @see org.andromda.core.templateengine.TemplateEngine#shutdown() 096 */ 097 public void shutdown() 098 { 099 this.shutdownLogger(); 100 this.configuration = null; 101 } 102 103 /** 104 * Stores the macro libraries the template. 105 */ 106 private List<String> macroLibraries = new ArrayList<String>(); 107 108 /** 109 * @see org.andromda.core.templateengine.TemplateEngine#getMacroLibraries() 110 */ 111 public List<String> getMacroLibraries() 112 { 113 return this.macroLibraries; 114 } 115 116 /** 117 * @see org.andromda.core.templateengine.TemplateEngine#addMacroLibrary(String) 118 */ 119 public void addMacroLibrary(String macroLibrary) 120 { 121 this.macroLibraries.add(macroLibrary); 122 } 123 124 /** 125 * @see org.andromda.core.templateengine.TemplateEngine#setMergeLocation(String) 126 */ 127 public void setMergeLocation(String mergeLocation) 128 { 129 } 130 131 /** 132 * The name of the temporary string template. 133 */ 134 private static final String STRING_TEMPLATE = "stringTemplate"; 135 136 /** 137 * @see org.andromda.core.templateengine.TemplateEngine#getEvaluatedExpression(String, 138 * java.util.Map) 139 */ 140 public String getEvaluatedExpression(final String expression, Map<String, Object> templateObjects) 141 { 142 try 143 { 144 // - create the template 145 final Template template = new Template(STRING_TEMPLATE, new StringReader(expression), new Configuration()); 146 147 if (templateObjects == null) 148 { 149 templateObjects = new HashMap<String, Object>(); 150 } 151 152 final StringWriter output = new StringWriter(); 153 154 // - merge data model with template 155 template.process(templateObjects, output); 156 157 return output.toString(); 158 } 159 catch (final Throwable throwable) 160 { 161 throw new TemplateEngineException(throwable); 162 } 163 } 164 165 /** 166 * null logger 167 */ 168 protected static Logger logger = null; 169 170 /** 171 * Opens a log file for this plugin. 172 * 173 * @throws IOException 174 * if the file cannot be opened 175 */ 176 private void initLogger(String pluginName) throws IOException 177 { 178 logger = AndroMDALogger.getNamespaceLogger(pluginName); 179 logger.setAdditivity(false); 180 FileAppender appender = new FileAppender(new PatternLayout("%-5p %d - %m%n"), AndroMDALogger 181 .getNamespaceLogFileName(pluginName), true); 182 logger.addAppender(appender); 183 } 184 185 /** 186 * Shutdown the associated logger. 187 */ 188 private void shutdownLogger() 189 { 190 Enumeration appenders = logger.getAllAppenders(); 191 while (appenders.hasMoreElements()) 192 { 193 Appender appender = (Appender) appenders.nextElement(); 194 if (appender.getName() != null) 195 { 196 appender.close(); 197 } 198 } 199 } 200}