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