9de464800280c4b249f1b5280143a2bcfb61fbfd
[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 the com.oreilly.servlet library, any library\r
22  * licensed under the Apache Software License, The Sun (tm) Java Advanced\r
23  * Imaging library (JAI), The Sun JIMI library (or with modified versions of\r
24  * the above that use the same license as the above), and distribute linked\r
25  * combinations including the two.  You must obey the GNU General Public\r
26  * License in all respects for all of the code used other than the above\r
27  * mentioned libraries.  If you modify this file, you may extend this exception\r
28  * to your version of the file, but you are not obligated to do so.  If you do\r
29  * not wish to do so, delete this exception statement from your version.\r
30  */\r
31 \r
32 package mir.generator;\r
33 \r
34 import java.io.PrintWriter;\r
35 import java.util.HashMap;\r
36 import java.util.Iterator;\r
37 import java.util.List;\r
38 import java.util.Map;\r
39 import java.util.Vector;\r
40 \r
41 import mir.misc.MessageMethodModel;\r
42 import mir.util.RewindableIterator;\r
43 \r
44 import org.apache.struts.util.MessageResources;\r
45 \r
46 import freemarker.template.FileTemplateCache;\r
47 import freemarker.template.SimpleScalar;\r
48 import freemarker.template.Template;\r
49 import freemarker.template.TemplateHashModel;\r
50 import freemarker.template.TemplateListModel;\r
51 import freemarker.template.TemplateMethodModel;\r
52 import freemarker.template.TemplateModel;\r
53 import freemarker.template.TemplateModelException;\r
54 import freemarker.template.TemplateModelRoot;\r
55 import freemarker.template.TemplateScalarModel;\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, PrintWriter 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.println("Exception occurred: "+t.getMessage());\r
74       t.printStackTrace(aLogger);\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   public static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {\r
96     if (anObject == null)\r
97       return null;\r
98 \r
99     if (anObject instanceof TemplateModel)\r
100       return (TemplateModel) anObject;\r
101     else if (anObject instanceof Generator.GeneratorFunction)\r
102       return makeFunctionAdapter((Generator.GeneratorFunction) anObject);\r
103     else if (anObject instanceof MessageResources)\r
104       return new MessageMethodModel((MessageResources) anObject);\r
105     else if (anObject instanceof Integer)\r
106       return makeStringAdapter(((Integer) anObject).toString());\r
107     else if (anObject instanceof Boolean) {\r
108       if (((Boolean) anObject).booleanValue())\r
109         return makeStringAdapter("1");\r
110       else\r
111         return makeStringAdapter("0");\r
112     }\r
113     else if (anObject instanceof String)\r
114       return makeStringAdapter((String) anObject);\r
115     else if (anObject instanceof Map)\r
116       return makeMapAdapter((Map) anObject);\r
117     else if (anObject instanceof Iterator)\r
118       return makeIteratorAdapter((Iterator) anObject);\r
119     else if (anObject instanceof List)\r
120       return makeIteratorAdapter(((List) anObject).iterator());\r
121     else\r
122       throw new TemplateModelException("Unadaptable class: " + anObject.getClass().getName());\r
123   }\r
124 \r
125   private static class MapAdapter implements TemplateModelRoot {\r
126     Map map;\r
127     Map valuesCache;\r
128 \r
129     private MapAdapter(Map aMap) {\r
130       map = aMap;\r
131       valuesCache = new HashMap();\r
132     }\r
133 \r
134     public void put(String aKey, TemplateModel aModel) {\r
135       valuesCache.put(aKey, aModel);\r
136     }\r
137 \r
138     public void remove(String aKey) {\r
139       // ML: kinda tricky...\r
140     }\r
141 \r
142     public boolean isEmpty() {\r
143       return map.isEmpty();\r
144     }\r
145 \r
146     public TemplateModel get(String aKey) throws TemplateModelException {\r
147       try {\r
148         if (!valuesCache.containsKey(aKey)) {\r
149           Object value = map.get(aKey);\r
150 \r
151           if (value == null && !map.containsKey(aKey)) {\r
152             throw new TemplateModelException("MapAdapter: no key "+aKey+" available");\r
153           }\r
154 \r
155           valuesCache.put(aKey, makeAdapter(value));\r
156         }\r
157 \r
158         return (TemplateModel) valuesCache.get(aKey);\r
159       }\r
160       catch (TemplateModelException e) {\r
161         throw e;\r
162       }\r
163       catch (Throwable t) {\r
164         throw new TemplateModelException(t.getMessage());\r
165       }\r
166     }\r
167   }\r
168 \r
169   private static class IteratorAdapter implements TemplateListModel {\r
170     Iterator iterator;\r
171     List valuesCache;\r
172     int position;\r
173 \r
174     private IteratorAdapter(Iterator anIterator) {\r
175       iterator = anIterator;\r
176 \r
177       valuesCache = new Vector();\r
178       position=0;\r
179 \r
180 \r
181       if (iterator instanceof RewindableIterator) {\r
182         ((RewindableIterator) iterator).rewind();\r
183       }\r
184     }\r
185 \r
186     public boolean isEmpty() {\r
187       return valuesCache.isEmpty() && !iterator.hasNext();\r
188     }\r
189 \r
190     private void getUntil(int anIndex) throws TemplateModelException {\r
191       while (valuesCache.size()<=anIndex && iterator.hasNext())\r
192       {\r
193         valuesCache.add(makeAdapter(iterator.next()));\r
194       }\r
195     };\r
196 \r
197     public TemplateModel get(int anIndex) throws TemplateModelException {\r
198       TemplateModel result;\r
199 \r
200       getUntil(anIndex);\r
201 \r
202       if (anIndex<valuesCache.size())\r
203       {\r
204         result = (TemplateModel) valuesCache.get(anIndex);\r
205 \r
206         return result;\r
207       }\r
208       else\r
209         throw new TemplateModelException( "Iterator out of bounds" );\r
210     }\r
211 \r
212     public boolean hasNext() {\r
213       return position<valuesCache.size() || iterator.hasNext();\r
214     }\r
215 \r
216     public boolean isRewound() {\r
217       return position==0;\r
218     }\r
219 \r
220     public TemplateModel next() throws TemplateModelException {\r
221       TemplateModel result;\r
222 \r
223       if (hasNext()) {\r
224         result = get(position);\r
225         position++;\r
226       }\r
227       else\r
228         throw new TemplateModelException( "Iterator out of bounds" );\r
229 \r
230       return result;\r
231     }\r
232 \r
233     public void rewind() {\r
234       position=0;\r
235     }\r
236   }\r
237 \r
238   private static class ListAdapter implements TemplateListModel {\r
239     List list;\r
240     List valuesCache;\r
241     int position;\r
242 \r
243     private ListAdapter(List aList) {\r
244       list = aList;\r
245       valuesCache = new Vector();\r
246       position=0;\r
247     }\r
248 \r
249     public boolean isEmpty() {\r
250       return list.isEmpty();\r
251     }\r
252 \r
253     public TemplateModel get(int i) throws TemplateModelException {\r
254 \r
255       if (i>=valuesCache.size() && i<list.size()) {\r
256         for(int j=valuesCache.size(); j<=i; j++) {\r
257           valuesCache.add(makeAdapter(list.get(j)));\r
258         }\r
259       }\r
260 \r
261       if (i<valuesCache.size())\r
262         return (TemplateModel) valuesCache.get(i);\r
263       else\r
264         throw new TemplateModelException( "Iterator out of bounds" );\r
265     }\r
266 \r
267     public boolean hasNext() {\r
268       return position<list.size();\r
269     }\r
270 \r
271     public boolean isRewound() {\r
272       return position==0;\r
273     }\r
274 \r
275     public TemplateModel next() throws TemplateModelException {\r
276       TemplateModel result;\r
277 \r
278       if (hasNext()) {\r
279         result = get(position);\r
280         position++;\r
281       }\r
282       else {\r
283         throw new TemplateModelException( "Iterator out of bounds" );\r
284       }\r
285 \r
286       return result;\r
287     }\r
288 \r
289     public void rewind() {\r
290       position = 0;\r
291     }\r
292   }\r
293 \r
294   private static class FunctionAdapter implements TemplateMethodModel {\r
295     Generator.GeneratorFunction function;\r
296 \r
297     public FunctionAdapter(Generator.GeneratorFunction aFunction) {\r
298       function = aFunction;\r
299     }\r
300 \r
301     public TemplateModel exec(List anArguments) throws TemplateModelException {\r
302       try {\r
303         return makeAdapter(function.perform(anArguments));\r
304       }\r
305       catch (Throwable t) {\r
306         throw new TemplateModelException(t.getMessage());\r
307       }\r
308     }\r
309 \r
310     public boolean isEmpty() {\r
311       return false;\r
312     }\r
313 \r
314   }\r
315 \r
316   public static class FreemarkerGeneratorLibrary implements GeneratorLibrary {\r
317     private FileTemplateCache templateCache;\r
318 \r
319     public FreemarkerGeneratorLibrary(String aTemplateRoot) {\r
320       templateCache = new FileTemplateCache( aTemplateRoot+"/" );\r
321       templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);\r
322     }\r
323 \r
324     public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {\r
325       Template template = (Template) templateCache.getItem(anIdentifier, "template");\r
326 \r
327       if (template==null) {\r
328         throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template "+templateCache.getDirectory()+anIdentifier);\r
329       }\r
330 \r
331       return new FreemarkerGenerator(template);\r
332     }\r
333   }\r
334 \r
335   public static class FreemarkerGeneratorLibraryFactory implements GeneratorLibraryFactory {\r
336     private String basePath;\r
337 \r
338     public FreemarkerGeneratorLibraryFactory(String aBasePath) {\r
339       basePath = aBasePath;\r
340     }\r
341 \r
342     public GeneratorLibrary makeLibrary(String anInitializationString) {\r
343       return new FreemarkerGeneratorLibrary(basePath+anInitializationString);\r
344     };\r
345   }\r
346 }\r