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