- experimental opensessions
[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 the com.oreilly.servlet library, any library
22  * licensed under the Apache Software License, The Sun (tm) Java Advanced
23  * Imaging library (JAI), The Sun JIMI library (or with modified versions of
24  * the above that use the same license as the above), and distribute linked
25  * combinations including the two.  You must obey the GNU General Public
26  * License in all respects for all of the code used other than the above
27  * mentioned libraries.  If you modify this file, you may extend this exception
28  * to your version of the file, but you are not obligated to do so.  If you do
29  * not wish to do so, delete this exception statement from your version.
30  */
31
32 package mir.generator;
33
34 import java.io.PrintWriter;
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 org.apache.struts.util.MessageResources;
42 import org.apache.commons.beanutils.PropertyUtils;
43
44
45 import freemarker.template.FileTemplateCache;
46 import freemarker.template.SimpleScalar;
47 import freemarker.template.Template;
48 import freemarker.template.TemplateHashModel;
49 import freemarker.template.TemplateListModel;
50 import freemarker.template.TemplateMethodModel;
51 import freemarker.template.TemplateModel;
52 import freemarker.template.TemplateModelException;
53 import freemarker.template.TemplateModelRoot;
54 import freemarker.template.TemplateScalarModel;
55
56 import mir.util.RewindableIterator;
57 import mir.util.*;
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, PrintWriter 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.println("Exception occurred: "+t.getMessage());
77       t.printStackTrace(aLogger);
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
106     if (anObject instanceof TemplateModel)
107       return (TemplateModel) anObject;
108     else if (anObject instanceof Generator.GeneratorFunction)
109       return makeFunctionAdapter((Generator.GeneratorFunction) anObject);
110     else if (anObject instanceof Integer)
111       return makeStringAdapter(((Integer) anObject).toString());
112     else if (anObject instanceof Boolean) {
113       if (((Boolean) anObject).booleanValue())
114         return makeStringAdapter("1");
115       else
116         return makeStringAdapter("0");
117     }
118     else if (anObject instanceof String)
119       return makeStringAdapter((String) anObject);
120     else if (anObject instanceof Map)
121       return makeMapAdapter((Map) anObject);
122     else if (anObject instanceof Iterator)
123       return makeIteratorAdapter((Iterator) anObject);
124     else if (anObject instanceof List)
125       return makeIteratorAdapter(((List) anObject).iterator());
126     else
127       return makeBeanAdapter(anObject);
128 //      throw new TemplateModelException("Unadaptable class: " + anObject.getClass().getName());
129   }
130
131   private static class MapAdapter implements TemplateModelRoot {
132     private Map map;
133     private Map valuesCache;
134
135     private MapAdapter(Map aMap) {
136       map = aMap;
137       valuesCache = new HashMap();
138     }
139
140     public void put(String aKey, TemplateModel aModel) {
141       valuesCache.put(aKey, aModel);
142     }
143
144     public void remove(String aKey) {
145       // ML: kinda tricky...
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 Vector();
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 Vector();
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.GeneratorFunction function;
302
303     public FunctionAdapter(Generator.GeneratorFunction 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         return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));
344       }
345       catch (Throwable t) {
346         throw new TemplateModelException(t.getMessage());
347       }
348     }
349   }
350
351   public static class FreemarkerGeneratorLibrary implements GeneratorLibrary {
352     private FileTemplateCache templateCache;
353
354     public FreemarkerGeneratorLibrary(String aTemplateRoot) {
355       templateCache = new FileTemplateCache( aTemplateRoot+"/" );
356       templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);
357     }
358
359     public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {
360       Template template = (Template) templateCache.getItem(anIdentifier, "template");
361
362       if (template==null) {
363         throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template "+templateCache.getDirectory()+anIdentifier);
364       }
365
366       return new FreemarkerGenerator(template);
367     }
368   }
369
370   public static class FreemarkerGeneratorLibraryFactory implements GeneratorLibraryFactory {
371     private String basePath;
372
373     public FreemarkerGeneratorLibraryFactory(String aBasePath) {
374       basePath = aBasePath;
375     }
376
377     public GeneratorLibrary makeLibrary(String anInitializationString) {
378       return new FreemarkerGeneratorLibrary(basePath+anInitializationString);
379     };
380   }
381 }