8b5ec61d95a387b5b5da65a5d422cbabfddbb2e0
[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.Date;\r
34 import java.util.HashMap;\r
35 import java.util.Iterator;\r
36 import java.util.List;\r
37 import java.util.Map;\r
38 import java.util.Vector;\r
39 \r
40 import org.apache.commons.beanutils.MethodUtils;\r
41 import org.apache.commons.beanutils.PropertyUtils;\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 import mir.log.LoggerWrapper;\r
53 import mir.util.GeneratorFormatAdapters;\r
54 import mir.util.RewindableIterator;\r
55 \r
56 \r
57 public class FreemarkerGenerator implements Generator {\r
58   private Template template;\r
59 \r
60   public FreemarkerGenerator(Template aTemplate) {\r
61     template = aTemplate;\r
62   }\r
63 \r
64   public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {\r
65     if (!(anOutputWriter instanceof PrintWriter))\r
66       throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter");\r
67 \r
68     try {\r
69       template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter);\r
70     }\r
71     catch (Throwable t) {\r
72       t.printStackTrace();\r
73       aLogger.error("Exception occurred: "+t.getMessage());\r
74       t.printStackTrace(aLogger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));\r
75       throw new GeneratorFailure( t );\r
76     }\r
77   }\r
78 \r
79   private static TemplateScalarModel makeStringAdapter(String aString) {\r
80     return new SimpleScalar(aString);\r
81   }\r
82 \r
83   private static TemplateHashModel makeMapAdapter(Map aMap)  {\r
84     return new MapAdapter(aMap);\r
85   }\r
86 \r
87   private static TemplateListModel makeIteratorAdapter(Iterator anIterator) {\r
88     return new IteratorAdapter(anIterator);\r
89   }\r
90 \r
91   private static TemplateMethodModel makeFunctionAdapter(Generator.GeneratorFunction aFunction) {\r
92     return new FunctionAdapter(aFunction);\r
93   }\r
94 \r
95   private static TemplateHashModel makeBeanAdapter(Object anObject)  {\r
96     return new BeanAdapter(anObject);\r
97   }\r
98 \r
99   public static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {\r
100     if (anObject == null)\r
101       return null;\r
102 \r
103     if (anObject instanceof TemplateModel)\r
104       return (TemplateModel) anObject;\r
105     else if (anObject instanceof Generator.GeneratorFunction)\r
106       return makeFunctionAdapter((Generator.GeneratorFunction) anObject);\r
107     else if (anObject instanceof Integer)\r
108       return makeStringAdapter(((Integer) anObject).toString());\r
109     else if (anObject instanceof Boolean) {\r
110       if (((Boolean) anObject).booleanValue())\r
111         return makeStringAdapter("1");\r
112       else\r
113         return makeStringAdapter("0");\r
114     }\r
115     else if (anObject instanceof String)\r
116       return makeStringAdapter((String) anObject);\r
117     else if (anObject instanceof Map)\r
118       return makeMapAdapter((Map) anObject);\r
119     else if (anObject instanceof Iterator)\r
120       return makeIteratorAdapter((Iterator) anObject);\r
121     else if (anObject instanceof List)\r
122       return makeIteratorAdapter(((List) anObject).iterator());\r
123     else if (anObject instanceof Number)\r
124       return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) anObject));\r
125     else if (anObject instanceof Date)\r
126       return makeAdapter(new GeneratorFormatAdapters.DateFormatAdapter((Date) anObject));\r
127     else\r
128       return makeBeanAdapter(anObject);\r
129   }\r
130 \r
131   private static class MapAdapter implements TemplateModelRoot {\r
132     private Map map;\r
133     private Map valuesCache;\r
134 \r
135     private MapAdapter(Map aMap) {\r
136       map = aMap;\r
137       valuesCache = new HashMap();\r
138     }\r
139 \r
140     public void put(String aKey, TemplateModel aModel) {\r
141       valuesCache.put(aKey, aModel);\r
142     }\r
143 \r
144     public void remove(String aKey) {\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