anti-abuse filter upgrade
[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       // ML: kinda tricky...\r
146     }\r
147 \r
148     public boolean isEmpty() {\r
149       return map.isEmpty();\r
150     }\r
151 \r
152     public TemplateModel get(String aKey) throws TemplateModelException {\r
153       try {\r
154         if (!valuesCache.containsKey(aKey)) {\r
155           Object value = map.get(aKey);\r
156 \r
157           if (value == null && !map.containsKey(aKey)) {\r
158             throw new TemplateModelException("MapAdapter: no key "+aKey+" available");\r
159           }\r
160 \r
161           valuesCache.put(aKey, makeAdapter(value));\r
162         }\r
163 \r
164         return (TemplateModel) valuesCache.get(aKey);\r
165       }\r
166       catch (TemplateModelException e) {\r
167         throw e;\r
168       }\r
169       catch (Throwable t) {\r
170         throw new TemplateModelException(t.getMessage());\r
171       }\r
172     }\r
173   }\r
174 \r
175   private static class IteratorAdapter implements TemplateListModel {\r
176     private Iterator iterator;\r
177     private List valuesCache;\r
178     private int position;\r
179 \r
180     private IteratorAdapter(Iterator anIterator) {\r
181       iterator = anIterator;\r
182 \r
183       valuesCache = new Vector();\r
184       position=0;\r
185 \r
186 \r
187       if (iterator instanceof RewindableIterator) {\r
188         ((RewindableIterator) iterator).rewind();\r
189       }\r
190     }\r
191 \r
192     public boolean isEmpty() {\r
193       return valuesCache.isEmpty() && !iterator.hasNext();\r
194     }\r
195 \r
196     private void getUntil(int anIndex) throws TemplateModelException {\r
197       while (valuesCache.size()<=anIndex && iterator.hasNext())\r
198       {\r
199         valuesCache.add(makeAdapter(iterator.next()));\r
200       }\r
201     };\r
202 \r
203     public TemplateModel get(int anIndex) throws TemplateModelException {\r
204       TemplateModel result;\r
205 \r
206       getUntil(anIndex);\r
207 \r
208       if (anIndex<valuesCache.size())\r
209       {\r
210         result = (TemplateModel) valuesCache.get(anIndex);\r
211 \r
212         return result;\r
213       }\r
214       else\r
215         throw new TemplateModelException( "Iterator out of bounds" );\r
216     }\r
217 \r
218     public boolean hasNext() {\r
219       return position<valuesCache.size() || iterator.hasNext();\r
220     }\r
221 \r
222     public boolean isRewound() {\r
223       return position==0;\r
224     }\r
225 \r
226     public TemplateModel next() throws TemplateModelException {\r
227       TemplateModel result;\r
228 \r
229       if (hasNext()) {\r
230         result = get(position);\r
231         position++;\r
232       }\r
233       else\r
234         throw new TemplateModelException( "Iterator out of bounds" );\r
235 \r
236       return result;\r
237     }\r
238 \r
239     public void rewind() {\r
240       position=0;\r
241     }\r
242   }\r
243 \r
244   private static class ListAdapter implements TemplateListModel {\r
245     List list;\r
246     List valuesCache;\r
247     int position;\r
248 \r
249     private ListAdapter(List aList) {\r
250       list = aList;\r
251       valuesCache = new Vector();\r
252       position=0;\r
253     }\r
254 \r
255     public boolean isEmpty() {\r
256       return list.isEmpty();\r
257     }\r
258 \r
259     public TemplateModel get(int i) throws TemplateModelException {\r
260 \r
261       if (i>=valuesCache.size() && i<list.size()) {\r
262         for(int j=valuesCache.size(); j<=i; j++) {\r
263           valuesCache.add(makeAdapter(list.get(j)));\r
264         }\r
265       }\r
266 \r
267       if (i<valuesCache.size())\r
268         return (TemplateModel) valuesCache.get(i);\r
269       else\r
270         throw new TemplateModelException( "Iterator out of bounds" );\r
271     }\r
272 \r
273     public boolean hasNext() {\r
274       return position<list.size();\r
275     }\r
276 \r
277     public boolean isRewound() {\r
278       return position==0;\r
279     }\r
280 \r
281     public TemplateModel next() throws TemplateModelException {\r
282       TemplateModel result;\r
283 \r
284       if (hasNext()) {\r
285         result = get(position);\r
286         position++;\r
287       }\r
288       else {\r
289         throw new TemplateModelException( "Iterator out of bounds" );\r
290       }\r
291 \r
292       return result;\r
293     }\r
294 \r
295     public void rewind() {\r
296       position = 0;\r
297     }\r
298   }\r
299 \r
300   private static class FunctionAdapter implements TemplateMethodModel {\r
301     private Generator.GeneratorFunction function;\r
302 \r
303     public FunctionAdapter(Generator.GeneratorFunction aFunction) {\r
304       function = aFunction;\r
305     }\r
306 \r
307     public TemplateModel exec(List anArguments) throws TemplateModelException {\r
308       try {\r
309         return makeAdapter(function.perform(anArguments));\r
310       }\r
311       catch (Throwable t) {\r
312         throw new TemplateModelException(t.getMessage());\r
313       }\r
314     }\r
315 \r
316     public boolean isEmpty() {\r
317       return false;\r
318     }\r
319 \r
320   }\r
321 \r
322   private static class BeanAdapter implements TemplateHashModel {\r
323     private Object object;\r
324 \r
325     public BeanAdapter(Object anObject) {\r
326       object = anObject;\r
327     }\r
328 \r
329     public void put(String aKey, TemplateModel aModel)  throws TemplateModelException  {\r
330       throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.put not supported");\r
331     }\r
332 \r
333     public void remove(String aKey) throws TemplateModelException  {\r
334       throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.remove not supported");\r
335     }\r
336 \r
337     public boolean isEmpty() {\r
338       return false;\r
339     }\r
340 \r
341     public TemplateModel get(String aKey) throws TemplateModelException {\r
342       try {\r
343         if (PropertyUtils.isReadable(object, aKey))\r
344           return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));\r
345         else\r
346           return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));\r
347       }\r
348       catch (Throwable t) {\r
349         throw new TemplateModelException(t.getMessage());\r
350       }\r
351     }\r
352   }\r
353 \r
354   public static class FreemarkerGeneratorLibrary implements GeneratorLibrary {\r
355     private FileTemplateCache templateCache;\r
356 \r
357     public FreemarkerGeneratorLibrary(String aTemplateRoot) {\r
358       templateCache = new FileTemplateCache( aTemplateRoot+"/" );\r
359       templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);\r
360     }\r
361 \r
362     public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {\r
363       Template template = (Template) templateCache.getItem(anIdentifier, "template");\r
364 \r
365       if (template==null) {\r
366         throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template "+templateCache.getDirectory()+anIdentifier);\r
367       }\r
368 \r
369       return new FreemarkerGenerator(template);\r
370     }\r
371   }\r
372 \r
373   public static class FreemarkerGeneratorLibraryFactory implements GeneratorLibraryFactory {\r
374     private String basePath;\r
375 \r
376     public FreemarkerGeneratorLibraryFactory(String aBasePath) {\r
377       basePath = aBasePath;\r
378     }\r
379 \r
380     public GeneratorLibrary makeLibrary(String anInitializationString) {\r
381       return new FreemarkerGeneratorLibrary(basePath+anInitializationString);\r
382     };\r
383   }\r
384 }\r