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