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