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