View Javadoc
1   package org.andromda.templateengines.freemarker;
2   
3   import java.io.IOException;
4   import java.io.StringReader;
5   import java.io.StringWriter;
6   import java.io.Writer;
7   import java.net.URL;
8   import java.util.ArrayList;
9   import java.util.Enumeration;
10  import java.util.HashMap;
11  import java.util.List;
12  import java.util.Map;
13  import org.andromda.core.common.AndroMDALogger;
14  import org.andromda.core.common.ExceptionUtils;
15  import org.andromda.core.templateengine.TemplateEngine;
16  import org.andromda.core.templateengine.TemplateEngineException;
17  import org.apache.log4j.Appender;
18  import org.apache.log4j.FileAppender;
19  import org.apache.log4j.Logger;
20  import org.apache.log4j.PatternLayout;
21  import freemarker.template.Configuration;
22  import freemarker.template.ObjectWrapper;
23  import freemarker.template.Template;
24  
25  /**
26   * The TemplateEngine implementation for the FreeMarker template processor.
27   *
28   * @author Chad Brandon
29   * @author Olaf Muliawan
30   * @author Matthias Bohlen (usage of context classloader so that it works in Maven environments)
31   * @see <a href="http://www.freemarker.org">Freemarker</a>
32   */
33  public class FreeMarkerTemplateEngine implements TemplateEngine
34  {
35      /**
36       * @see org.andromda.core.templateengine.TemplateEngine#initialize(String)
37       */
38      public void initialize(String namespace) throws Exception
39      {
40          this.initLogger(namespace);
41      }
42  
43      /**
44       * The main Configuration object of Freemarker.
45       */
46      protected Configuration configuration = null;
47  
48      /**
49       * @see org.andromda.core.templateengine.TemplateEngine#processTemplate(String,
50       *      java.util.Map, java.io.Writer)
51       */
52      public void processTemplate(String templateFile, Map<String, Object> templateObjects, Writer output) throws Exception
53      {
54          ExceptionUtils.checkEmpty("templateFile", templateFile);
55          ExceptionUtils.checkNull("output", output);
56  
57          if (this.configuration == null)
58          {
59              this.configuration = new Configuration();
60  
61              // tell FreeMarker it should use the ContextClassLoader when
62              // searching for templates
63              this.configuration.setTemplateLoader(new ContextResourceLoader());
64  
65              // use Bean Wrapper, in order to get maximal reflection
66              // capabilities
67              this.configuration.setObjectWrapper(ObjectWrapper.BEANS_WRAPPER);
68          }
69  
70          // create the template
71          final Template template = this.configuration.getTemplate(templateFile);
72  
73          if (templateObjects == null)
74          {
75              templateObjects = new HashMap<String, Object>();
76          }
77  
78          // - merge data model with template
79          template.process(templateObjects, output);
80      }
81  
82      /**
83       * Template loader which accesses the context classloader because otherwise
84       * templates will not be found properly in a multi-classloader environment.
85       */
86      private static class ContextResourceLoader extends freemarker.cache.URLTemplateLoader
87      {
88          protected URL getURL(String name)
89          {
90              return Thread.currentThread().getContextClassLoader().getResource(name);
91          }
92      }
93  
94      /**
95       * @see org.andromda.core.templateengine.TemplateEngine#shutdown()
96       */
97      public void shutdown()
98      {
99          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 }