small changes here and there
[mir.git] / source / mir / generator / FreemarkerGenerator.java
1 /*\r
2  * Copyright (C) 2001, 2002 The Mir-coders group\r
3  *\r
4  * This file is part of Mir.\r
5  *\r
6  * Mir is free software; you can redistribute it and/or modify\r
7  * it under the terms of the GNU General Public License as published by\r
8  * the Free Software Foundation; either version 2 of the License, or\r
9  * (at your option) any later version.\r
10  *\r
11  * Mir is distributed in the hope that it will be useful,\r
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
14  * GNU General Public License for more details.\r
15  *\r
16  * You should have received a copy of the GNU General Public License\r
17  * along with Mir; if not, write to the Free Software\r
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
19  *\r
20  * In addition, as a special exception, The Mir-coders gives permission to link\r
21  * the code of this program with  any library licensed under the Apache Software License,\r
22  * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library\r
23  * (or with modified versions of the above that use the same license as the above),\r
24  * and distribute linked combinations including the two.  You must obey the\r
25  * GNU General Public License in all respects for all of the code used other than\r
26  * the above mentioned libraries.  If you modify this file, you may extend this\r
27  * exception to your version of the file, but you are not obligated to do so.\r
28  * If you do not wish to do so, delete this exception statement from your version.\r
29  */\r
30 package mir.generator;\r
31 \r
32 import java.io.PrintWriter;\r
33 import java.util.HashMap;\r
34 import java.util.Iterator;\r
35 import java.util.List;\r
36 import java.util.Map;\r
37 import java.util.Vector;\r
38 \r
39 import org.apache.commons.beanutils.MethodUtils;\r
40 import org.apache.commons.beanutils.PropertyUtils;\r
41 \r
42 import freemarker.template.FileTemplateCache;\r
43 import freemarker.template.SimpleScalar;\r
44 import freemarker.template.Template;\r
45 import freemarker.template.TemplateHashModel;\r
46 import freemarker.template.TemplateListModel;\r
47 import freemarker.template.TemplateMethodModel;\r
48 import freemarker.template.TemplateModel;\r
49 import freemarker.template.TemplateModelException;\r
50 import freemarker.template.TemplateModelRoot;\r
51 import freemarker.template.TemplateScalarModel;\r
52 \r
53 import mir.log.LoggerWrapper;\r
54 import mir.util.GeneratorFormatAdapters;\r
55 import mir.util.RewindableIterator;\r
56 \r
57 \r
58 public class FreemarkerGenerator implements Generator {\r
59   private Template template;\r
60 \r
61   public FreemarkerGenerator(Template aTemplate) {\r
62     template = aTemplate;\r
63   }\r
64 \r
65   public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {\r
66     if (!(anOutputWriter instanceof PrintWriter))\r
67       throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter");\r
68 \r
69     try {\r
70       template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter);\r
71     }\r
72     catch (Throwable t) {\r
73       t.printStackTrace();\r
74       aLogger.error("Exception occurred: "+t.getMessage());\r
75       t.printStackTrace(aLogger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));\r
76       throw new GeneratorFailure( t );\r
77     }\r
78   }\r
79 \r
80   private static TemplateScalarModel makeStringAdapter(String aString) {\r
81     return new SimpleScalar(aString);\r
82   }\r
83 \r
84   private static TemplateHashModel makeMapAdapter(Map aMap)  {\r
85     return new MapAdapter(aMap);\r
86   }\r
87 \r
88   private static TemplateListModel makeIteratorAdapter(Iterator anIterator) {\r
89     return new IteratorAdapter(anIterator);\r
90   }\r
91 \r
92   private static TemplateMethodModel makeFunctionAdapter(Generator.GeneratorFunction aFunction) {\r
93     return new FunctionAdapter(aFunction);\r
94   }\r
95 \r
96   private static TemplateHashModel makeBeanAdapter(Object anObject)  {\r
97     return new BeanAdapter(anObject);\r
98   }\r
99 \r
100   public static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {\r
101     if (anObject == null)\r
102       return null;\r
103 \r
104     if (anObject instanceof TemplateModel)\r
105       return (TemplateModel) anObject;\r
106     else if (anObject instanceof Generator.GeneratorFunction)\r
107       return makeFunctionAdapter((Generator.GeneratorFunction) anObject);\r
108     else if (anObject instanceof Integer)\r
109       return makeStringAdapter(((Integer) anObject).toString());\r
110     else if (anObject instanceof Boolean) {\r
111       if (((Boolean) anObject).booleanValue())\r
112         return makeStringAdapter("1");\r
113       else\r
114         return makeStringAdapter("0");\r
115     }\r
116     else if (anObject instanceof String)\r
117       return makeStringAdapter((String) anObject);\r
118     else if (anObject instanceof Map)\r
119       return makeMapAdapter((Map) anObject);\r
120     else if (anObject instanceof Iterator)\r
121       return makeIteratorAdapter((Iterator) anObject);\r
122     else if (anObject instanceof List)\r
123       return makeIteratorAdapter(((List) anObject).iterator());\r
124     else if (anObject instanceof Number)\r
125       return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) anObject));\r
126     else\r
127       return makeBeanAdapter(anObject);\r
128   }\r
129 \r
130   private static class MapAdapter implements TemplateModelRoot {\r
131     private Map map;\r
132     private Map valuesCache;\r
133 \r
134     private MapAdapter(Map aMap) {\r
135       map = aMap;\r
136       valuesCache = new HashMap();\r
137     }\r
138 \r
139     public void put(String aKey, TemplateModel aModel) {\r
140       valuesCache.put(aKey, aModel);\r
141     }\r
142 \r
143     public void remove(String aKey) {\r
144       // ML: kinda tricky...\r
145     }\r
146 \r
147     public boolean isEmpty() {\r
148       return map.isEmpty();\r
149     }\r
150 \r
151     public TemplateModel get(String aKey) throws TemplateModelException {\r
152       try {\r
153         if (!valuesCache.containsKey(aKey)) {\r
154           Object value = map.get(aKey);\r
155 \r
156           if (value == null && !map.containsKey(aKey)) {\r
157             throw new TemplateModelException("MapAdapter: no key "+aKey+" available");\r
158           }\r
159 \r
160           valuesCache.put(aKey, makeAdapter(value));\r
161         }\r
162 \r
163         return (TemplateModel) valuesCache.get(aKey);\r
164       }\r
165       catch (TemplateModelException e) {\r
166         throw e;\r
167       }\r
168       catch (Throwable t) {\r
169         throw new TemplateModelException(t.getMessage());\r
170       }\r
171     }\r
172   }\r
173 \r
174   private static class IteratorAdapter implements TemplateListModel {\r
175     private Iterator iterator;\r
176     private List valuesCache;\r
177     private int position;\r
178 \r
179     private IteratorAdapter(Iterator anIterator) {\r
180       iterator = anIterator;\r
181 \r
182       valuesCache = new Vector();\r
183       position=0;\r
184 \r
185 \r
186       if (iterator instanceof RewindableIterator) {\r
187         ((RewindableIterator) iterator).rewind();\r
188       }\r
189     }\r
190 \r
191     public boolean isEmpty() {\r
192       return valuesCache.isEmpty() && !iterator.hasNext();\r
193     }\r
194 \r
195     private void getUntil(int anIndex) throws TemplateModelException {\r
196       while (valuesCache.size()<=anIndex && iterator.hasNext())\r
197       {\r
198         valuesCache.add(makeAdapter(iterator.next()));\r
199       }\r
200     };\r
201 \r
202     public TemplateModel get(int anIndex) throws TemplateModelException {\r
203       TemplateModel result;\r
204 \r
205       getUntil(anIndex);\r
206 \r
207       if (anIndex<valuesCache.size())\r
208       {\r
209         result = (TemplateModel) valuesCache.get(anIndex);\r
210 \r
211         return result;\r
212       }\r
213       else\r
214         throw new TemplateModelException( "Iterator out of bounds" );\r
215     }\r
216 \r
217     public boolean hasNext() {\r
218       return position<valuesCache.size() || iterator.hasNext();\r
219     }\r
220 \r
221     public boolean isRewound() {\r
222       return position==0;\r
223     }\r
224 \r
225     public TemplateModel next() throws TemplateModelException {\r
226       TemplateModel result;\r
227 \r
228       if (hasNext()) {\r
229         result = get(position);\r
230         position++;\r
231       }\r
232       else\r
233         throw new TemplateModelException( "Iterator out of bounds" );\r
234 \r
235       return result;\r
236     }\r
237 \r
238     public void rewind() {\r
239       position=0;\r
240     }\r
241   }\r
242 \r
243   private static class ListAdapter implements TemplateListModel {\r
244     List list;\r
245     List valuesCache;\r
246     int position;\r
247 \r
248     private ListAdapter(List aList) {\r
249       list = aList;\r
250       valuesCache = new Vector();\r
251       position=0;\r
252     }\r
253 \r
254     public boolean isEmpty() {\r
255       return list.isEmpty();\r
256     }\r
257 \r
258     public TemplateModel get(int i) throws TemplateModelException {\r
259 \r
260       if (i>=valuesCache.size() && i<list.size()) {\r
261         for(int j=valuesCache.size(); j<=i; j++) {\r
262           valuesCache.add(makeAdapter(list.get(j)));\r
263         }\r
264       }\r
265 \r
266       if (i<valuesCache.size())\r
267         return (TemplateModel) valuesCache.get(i);\r
268       else\r
269         throw new TemplateModelException( "Iterator out of bounds" );\r
270     }\r
271 \r
272     public boolean hasNext() {\r
273       return position<list.size();\r
274     }\r
275 \r
276     public boolean isRewound() {\r
277       return position==0;\r
278     }\r
279 \r
280     public TemplateModel next() throws TemplateModelException {\r
281       TemplateModel result;\r
282 \r
283       if (hasNext()) {\r
284         result = get(position);\r
285         position++;\r
286       }\r
287       else {\r
288         throw new TemplateModelException( "Iterator out of bounds" );\r
289       }\r
290 \r
291       return result;\r
292     }\r
293 \r
294     public void rewind() {\r
295       position = 0;\r
296     }\r
297   }\r
298 \r
299   private static class FunctionAdapter implements TemplateMethodModel {\r
300     private Generator.GeneratorFunction function;\r
301 \r
302     public FunctionAdapter(Generator.GeneratorFunction aFunction) {\r
303       function = aFunction;\r
304     }\r
305 \r
306     public TemplateModel exec(List anArguments) throws TemplateModelException {\r
307       try {\r
308         return makeAdapter(function.perform(anArguments));\r
309       }\r
310       catch (Throwable t) {\r
311         throw new TemplateModelException(t.getMessage());\r
312       }\r
313     }\r
314 \r
315     public boolean isEmpty() {\r
316       return false;\r
317     }\r
318 \r
319   }\r
320 \r
321   private static class BeanAdapter implements TemplateHashModel {\r
322     private Object object;\r
323 \r
324     public BeanAdapter(Object anObject) {\r
325       object = anObject;\r
326     }\r
327 \r
328     public void put(String aKey, TemplateModel aModel)  throws TemplateModelException  {\r
329       throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.put not supported");\r
330     }\r
331 \r
332     public void remove(String aKey) throws TemplateModelException  {\r
333       throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.remove not supported");\r
334     }\r
335 \r
336     public boolean isEmpty() {\r
337       return false;\r
338     }\r
339 \r
340     public TemplateModel get(String aKey) throws TemplateModelException {\r
341       try {\r
342         if (PropertyUtils.isReadable(object, aKey))\r
343           return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));\r
344         else\r
345           return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));\r
346       }\r
347       catch (Throwable t) {\r
348         throw new TemplateModelException(t.getMessage());\r
349       }\r
350     }\r
351   }\r
352 \r
353   public static class FreemarkerGeneratorLibrary implements GeneratorLibrary {\r
354     private FileTemplateCache templateCache;\r
355 \r
356     public FreemarkerGeneratorLibrary(String aTemplateRoot) {\r
357       templateCache = new FileTemplateCache( aTemplateRoot+"/" );\r
358       templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);\r
359     }\r
360 \r
361     public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {\r
362       Template template = (Template) templateCache.getItem(anIdentifier, "template");\r
363 \r
364       if (template==null) {\r
365         throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template "+templateCache.getDirectory()+anIdentifier);\r
366       }\r
367 \r
368       return new FreemarkerGenerator(template);\r
369     }\r
370   }\r
371 \r
372   public static class FreemarkerGeneratorLibraryFactory implements GeneratorLibraryFactory {\r
373     private String basePath;\r
374 \r
375     public FreemarkerGeneratorLibraryFactory(String aBasePath) {\r
376       basePath = aBasePath;\r
377     }\r
378 \r
379     public GeneratorLibrary makeLibrary(String anInitializationString) {\r
380       return new FreemarkerGeneratorLibrary(basePath+anInitializationString);\r
381     };\r
382   }\r
383 }\r