1.1 restoration
[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.io.File;
34 import java.util.ArrayList;
35 import java.util.Date;
36 import java.util.HashMap;
37 import java.util.Iterator;
38 import java.util.List;
39 import java.util.Map;
40
41 import freemarker.template.FileTemplateCache;
42 import freemarker.template.SimpleScalar;
43 import freemarker.template.Template;
44 import freemarker.template.TemplateHashModel;
45 import freemarker.template.TemplateListModel;
46 import freemarker.template.TemplateMethodModel;
47 import freemarker.template.TemplateModel;
48 import freemarker.template.TemplateModelException;
49 import freemarker.template.TemplateModelRoot;
50 import freemarker.template.TemplateScalarModel;
51 import mir.log.LoggerWrapper;
52 import mir.util.GeneratorFormatAdapters;
53 import mir.util.RewindableIterator;
54 import org.apache.commons.beanutils.MethodUtils;
55 import org.apache.commons.beanutils.PropertyUtils;
56
57
58 public class FreemarkerGenerator implements Generator {
59   private Template template;
60
61   public FreemarkerGenerator(Template aTemplate) {
62     template = aTemplate;
63   }
64
65   public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {
66     if (!(anOutputWriter instanceof PrintWriter))
67       throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter");
68
69     try {
70       template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter);
71     }
72     catch (Throwable t) {
73       t.printStackTrace();
74       aLogger.error("Exception occurred: "+t.getMessage());
75       t.printStackTrace(aLogger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
76       throw new GeneratorFailure( t );
77     }
78   }
79
80   private static TemplateScalarModel makeStringAdapter(String aString) {
81     return new SimpleScalar(aString);
82   }
83
84   private static TemplateHashModel makeMapAdapter(Map aMap)  {
85     return new MapAdapter(aMap);
86   }
87
88   private static TemplateListModel makeIteratorAdapter(Iterator anIterator) {
89     return new IteratorAdapter(anIterator);
90   }
91
92   private static TemplateMethodModel makeFunctionAdapter(Generator.Function aFunction) {
93     return new FunctionAdapter(aFunction);
94   }
95
96   private static TemplateHashModel makeBeanAdapter(Object anObject)  {
97     return new BeanAdapter(anObject);
98   }
99
100   public static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {
101     if (anObject == null)
102       return null;
103
104     if (anObject instanceof TemplateModel)
105       return (TemplateModel) anObject;
106     else if (anObject instanceof Generator.Function)
107       return makeFunctionAdapter((Generator.Function) anObject);
108     else if (anObject instanceof Integer)
109       return makeStringAdapter(anObject.toString());
110     else if (anObject instanceof Boolean) {
111       if (((Boolean) anObject).booleanValue())
112         return makeStringAdapter("1");
113       else
114         return makeStringAdapter("0");
115     }
116     else if (anObject instanceof String)
117       return makeStringAdapter((String) anObject);
118     else if (anObject instanceof Map)
119       return makeMapAdapter((Map) anObject);
120     else if (anObject instanceof Iterator)
121       return makeIteratorAdapter((Iterator) anObject);
122     else if (anObject instanceof List)
123       return makeIteratorAdapter(((List) anObject).iterator());
124     else if (anObject instanceof Number)
125       return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) anObject));
126     else if (anObject instanceof Date)
127       return makeAdapter(new GeneratorFormatAdapters.DateFormatAdapter((Date) anObject));
128     else
129       return makeBeanAdapter(anObject);
130   }
131
132   private static class MapAdapter implements TemplateModelRoot {
133     private Map map;
134     private Map valuesCache;
135
136     private MapAdapter(Map aMap) {
137       map = aMap;
138       valuesCache = new HashMap();
139     }
140
141     public void put(String aKey, TemplateModel aModel) {
142       valuesCache.put(aKey, aModel);
143     }
144
145     public void remove(String aKey) {
146     }
147
148     public boolean isEmpty() {
149       return map.isEmpty();
150     }
151
152     public TemplateModel get(String aKey) throws TemplateModelException {
153       try {
154         if (!valuesCache.containsKey(aKey)) {
155           Object value = map.get(aKey);
156
157           if (value == null && !map.containsKey(aKey)) {
158             throw new TemplateModelException("MapAdapter: no key "+aKey+" available");
159           }
160
161           valuesCache.put(aKey, makeAdapter(value));
162         }
163
164         return (TemplateModel) valuesCache.get(aKey);
165       }
166       catch (TemplateModelException e) {
167         throw e;
168       }
169       catch (Throwable t) {
170         throw new TemplateModelException(t.getMessage());
171       }
172     }
173   }
174
175   private static class IteratorAdapter implements TemplateListModel {
176     private Iterator iterator;
177     private List valuesCache;
178     private int position;
179
180     private IteratorAdapter(Iterator anIterator) {
181       iterator = anIterator;
182
183       valuesCache = new ArrayList();
184       position=0;
185
186
187       if (iterator instanceof RewindableIterator) {
188         ((RewindableIterator) iterator).rewind();
189       }
190     }
191
192     public boolean isEmpty() {
193       return valuesCache.isEmpty() && !iterator.hasNext();
194     }
195
196     private void getUntil(int anIndex) throws TemplateModelException {
197       while (valuesCache.size()<=anIndex && iterator.hasNext())
198       {
199         valuesCache.add(makeAdapter(iterator.next()));
200       }
201     };
202
203     public TemplateModel get(int anIndex) throws TemplateModelException {
204       TemplateModel result;
205
206       getUntil(anIndex);
207
208       if (anIndex<valuesCache.size())
209       {
210         result = (TemplateModel) valuesCache.get(anIndex);
211
212         return result;
213       }
214       else
215         throw new TemplateModelException( "Iterator out of bounds" );
216     }
217
218     public boolean hasNext() {
219       return position<valuesCache.size() || iterator.hasNext();
220     }
221
222     public boolean isRewound() {
223       return position==0;
224     }
225
226     public TemplateModel next() throws TemplateModelException {
227       TemplateModel result;
228
229       if (hasNext()) {
230         result = get(position);
231         position++;
232       }
233       else
234         throw new TemplateModelException( "Iterator out of bounds" );
235
236       return result;
237     }
238
239     public void rewind() {
240       position=0;
241     }
242   }
243
244   private static class ListAdapter implements TemplateListModel {
245     List list;
246     List valuesCache;
247     int position;
248
249     private ListAdapter(List aList) {
250       list = aList;
251       valuesCache = new ArrayList();
252       position=0;
253     }
254
255     public boolean isEmpty() {
256       return list.isEmpty();
257     }
258
259     public TemplateModel get(int i) throws TemplateModelException {
260
261       if (i>=valuesCache.size() && i<list.size()) {
262         for(int j=valuesCache.size(); j<=i; j++) {
263           valuesCache.add(makeAdapter(list.get(j)));
264         }
265       }
266
267       if (i<valuesCache.size())
268         return (TemplateModel) valuesCache.get(i);
269       else
270         throw new TemplateModelException( "Iterator out of bounds" );
271     }
272
273     public boolean hasNext() {
274       return position<list.size();
275     }
276
277     public boolean isRewound() {
278       return position==0;
279     }
280
281     public TemplateModel next() throws TemplateModelException {
282       TemplateModel result;
283
284       if (hasNext()) {
285         result = get(position);
286         position++;
287       }
288       else {
289         throw new TemplateModelException( "Iterator out of bounds" );
290       }
291
292       return result;
293     }
294
295     public void rewind() {
296       position = 0;
297     }
298   }
299
300   private static class FunctionAdapter implements TemplateMethodModel {
301     private Generator.Function function;
302
303     public FunctionAdapter(Generator.Function aFunction) {
304       function = aFunction;
305     }
306
307     public TemplateModel exec(List anArguments) throws TemplateModelException {
308       try {
309         return makeAdapter(function.perform(anArguments));
310       }
311       catch (Throwable t) {
312         throw new TemplateModelException(t.getMessage());
313       }
314     }
315
316     public boolean isEmpty() {
317       return false;
318     }
319
320   }
321
322   private static class BeanAdapter implements TemplateHashModel {
323     private Object object;
324
325     public BeanAdapter(Object anObject) {
326       object = anObject;
327     }
328
329     public void put(String aKey, TemplateModel aModel)  throws TemplateModelException  {
330       throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.put not supported");
331     }
332
333     public void remove(String aKey) throws TemplateModelException  {
334       throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.remove not supported");
335     }
336
337     public boolean isEmpty() {
338       return false;
339     }
340
341     public TemplateModel get(String aKey) throws TemplateModelException {
342       try {
343         if (PropertyUtils.isReadable(object, aKey))
344           return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));
345         else
346           return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));
347       }
348       catch (Throwable t) {
349         throw new TemplateModelException(t.getMessage());
350       }
351     }
352   }
353
354   public static class FreemarkerGeneratorLibrary implements Library {
355     private FileTemplateCache templateCache;
356
357     public FreemarkerGeneratorLibrary(File aTemplateRoot) {
358       templateCache = new FileTemplateCache(aTemplateRoot);
359       templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);
360     }
361
362     public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {
363       Template template = (Template) templateCache.getItem(anIdentifier, "template");
364
365       if (template==null) {
366         throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template "+templateCache.getDirectory()+"/"+anIdentifier);
367       }
368
369       return new FreemarkerGenerator(template);
370     }
371   }
372
373   public static class FreemarkerGeneratorLibraryFactory implements LibraryFactory {
374     private File basePath;
375
376     public FreemarkerGeneratorLibraryFactory(File aBasePath) {
377       basePath = aBasePath;
378     }
379
380     public Library makeLibrary(String anInitializationString) {
381       // todo: the initialization string should be parsed
382       return new FreemarkerGeneratorLibrary(new File(basePath, anInitializationString));
383     };
384   }
385 }