jai removed from license part 1
[mir.git] / source / mir / generator / VelocityGenerator.java
1 /*
2  * Copyright (C) 2005 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  * You must obey the GNU General Public License in all respects for all of the code used
23  * other than the above mentioned libraries.  If you modify this file, you may extend this
24  * exception to your version of the file, but you are not obligated to do so.
25  * If you do not wish to do so, delete this exception statement from your version.
26  */
27 package mir.generator;
28
29 import java.io.File;
30 import java.io.PrintWriter;
31 import java.io.StringWriter;
32 import java.util.AbstractList;
33 import java.util.ArrayList;
34 import java.util.Date;
35 import java.util.HashMap;
36 import java.util.Iterator;
37 import java.util.List;
38 import java.util.Map;
39
40 import mir.log.LoggerWrapper;
41 import mir.util.GeneratorFormatAdapters;
42 import mir.util.RewindableIterator;
43
44 import org.apache.commons.beanutils.MethodUtils;
45 import org.apache.commons.beanutils.PropertyUtils;
46 import org.apache.velocity.Template;
47 import org.apache.velocity.app.VelocityEngine;
48 import org.apache.velocity.context.Context;
49 import org.apache.velocity.exception.ParseErrorException;
50 import org.apache.velocity.exception.ResourceNotFoundException;
51 import org.apache.velocity.runtime.RuntimeServices;
52 import org.apache.velocity.runtime.log.LogSystem;
53
54 public class VelocityGenerator implements Generator {
55   private String templateIdentifier;
56   private VelocityGeneratorLibrary library;
57   private static LoggerWrapper logger = new LoggerWrapper("Generator.velocity");
58
59   /**
60    *
61    * @param aTemplate
62    * @param aLibrary
63    */
64
65   public VelocityGenerator(String aTemplate, VelocityGeneratorLibrary aLibrary) {
66     templateIdentifier = aTemplate;
67     library = aLibrary;
68   }
69
70   /**
71    *
72    * @param anOutputWriter
73    * @param aValues
74    * @param aLogger
75    * @throws GeneratorExc
76    * @throws GeneratorFailure
77    */
78   public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {
79     Template template;
80     Context context = makeMapAdapter(aValues);
81     StringWriter stringWriter = new StringWriter();
82
83     try {
84       template = library.engine.getTemplate(templateIdentifier);
85       if (template == null) {
86         throw new GeneratorExc("VelocityGeneratorLibrary: Can't find template " + templateIdentifier);
87       }
88       template.merge(context, stringWriter);
89
90       ( (PrintWriter) anOutputWriter).print(stringWriter.toString());
91     }
92     catch (ResourceNotFoundException t) {
93       throw new GeneratorExc("VelocityGeneratorLibrary: Can't find template " + templateIdentifier);
94     }
95     catch (ParseErrorException t) {
96       ( (PrintWriter) anOutputWriter).print(t.toString());
97     }
98     catch (Throwable t) {
99       throw new GeneratorFailure(t);
100     }
101
102   }
103
104   /**
105    *
106    * @param aMap
107    * @return
108    */
109   private static Context makeMapAdapter(Map aMap)  {
110     return new MapAdapter(aMap);
111   }
112
113   /**
114    *
115    * @param anIterator
116    * @return
117    */
118   private static List makeIteratorAdapter(Iterator anIterator) {
119     return new IteratorAdapter(anIterator);
120   }
121
122   /**
123    *
124    * @param aList
125    * @return
126    */
127   private static List makeListAdapter(List aList) {
128     return new ListAdapter(aList);
129   }
130
131   /**
132    *
133    * @param aFunction
134    * @return
135    */
136   private static Object makeFunctionAdapter(Generator.Function aFunction) {
137     return new FunctionAdapter(aFunction);
138   }
139
140   /**
141    *
142    * @param anObject
143    * @return
144    */
145   private static Object makeBeanAdapter(Object anObject)  {
146     return new BeanAdapter(anObject);
147   }
148
149   /**
150    *
151    * <p>Title: </p>
152    * <p>Description: </p>
153    * <p>Copyright: Copyright (c) 2003</p>
154    * <p>Company: </p>
155    * @author not attributable
156    * @version 1.0
157    */
158   private interface VelocityAdapter {
159     public Object getOriginal();
160   }
161
162   /**
163    *
164    * @param anObject
165    * @return
166    */
167   public static Object unmakeAdapter(Object anObject) {
168     if (anObject instanceof VelocityAdapter) {
169       return ((VelocityAdapter) anObject).getOriginal();
170     }
171                 return anObject;
172   }
173
174   /**
175    *
176    * @param anObject
177    * @return
178    */
179   public static Object makeAdapter(Object anObject) {
180     if (anObject == null)
181       return null;
182
183     if (anObject instanceof Context)
184       return anObject;
185
186     else if (anObject instanceof Generator.Function)
187       return makeFunctionAdapter((Generator.Function) anObject);
188     else if (anObject instanceof Integer)
189       return anObject;
190     else if (anObject instanceof Boolean)
191       return anObject;
192     else if (anObject instanceof String)
193       return anObject;
194     else if (anObject instanceof Map)
195       return makeMapAdapter((Map) anObject);
196     else if (anObject instanceof Iterator)
197       return makeIteratorAdapter((Iterator) anObject);
198     else if (anObject instanceof List)
199       return makeListAdapter(((List) anObject));
200     else if (anObject instanceof Number)
201       return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) anObject));
202     else if (anObject instanceof Date)
203       return makeAdapter(new GeneratorFormatAdapters.DateFormatAdapter((Date) anObject));
204     else
205       return makeBeanAdapter(anObject);
206   }
207
208   /**
209    *
210    * <p>Title: </p>
211    * <p>Description: </p>
212    * <p>Copyright: Copyright (c) 2003</p>
213    * <p>Company: </p>
214    * @author not attributable
215    * @version 1.0
216    */
217   public static class FunctionAdapter implements VelocityAdapter {
218     private Function function;
219
220     public Object getOriginal() {
221       return function;
222     }
223
224     private FunctionAdapter(Function aFunction) {
225       function = aFunction;
226     }
227
228     public Object call(Object aParameters[]) throws GeneratorExc {
229       List parameters = new ArrayList();
230
231       for (int i = 0; i<aParameters.length; i++) {
232         parameters.add(unmakeAdapter(aParameters[i]));
233       }
234
235       return makeAdapter(function.perform(parameters));
236     }
237
238     public Object call() throws GeneratorExc {
239       return makeAdapter(function.perform(new ArrayList()));
240     }
241
242     public Object call(Object anObject) throws GeneratorExc {
243       return call(new Object[] { anObject });
244     }
245
246     public Object call(Object anObject1, Object anObject2) throws GeneratorExc {
247       return call(new Object[] { anObject1, anObject2 });
248     }
249
250     public Object call(Object anObject1, Object anObject2, Object anObject3) throws GeneratorExc {
251       return call(new Object[] { anObject1, anObject2, anObject3 });
252     }
253
254     public Object call(Object anObject1, Object anObject2, Object anObject3, Object anObject4) throws GeneratorExc {
255       return call(new Object[] { anObject1, anObject2, anObject3, anObject4 });
256     }
257   }
258
259
260   /**
261    *
262    * <p>Title: </p>
263    * <p>Description: </p>
264    * <p>Copyright: Copyright (c) 2003</p>
265    * <p>Company: </p>
266    * @author not attributable
267    * @version 1.0
268    */
269   private static class MapAdapter implements Context, VelocityAdapter  {
270     private Map map;
271     private Map valuesCache;
272
273     private MapAdapter(Map aMap) {
274       map = aMap;
275       valuesCache = new HashMap();
276     }
277
278     public Object getOriginal() {
279       return map;
280     }
281
282     public boolean containsKey(Object aKey) {
283       return map.containsKey(aKey);
284     }
285
286     public Object get(String aKey) {
287       try {
288         if (!valuesCache.containsKey(aKey)) {
289           Object value = map.get(aKey);
290
291           if (value == null && !map.containsKey(aKey)) {
292             return "no key "+aKey+" available";
293           }
294           else
295             valuesCache.put(aKey, makeAdapter(value));
296         }
297
298         return valuesCache.get(aKey);
299       }
300       catch (Throwable t) {
301         throw new GeneratorFailure(t);
302       }
303     }
304
305     public Object[] getKeys() {
306       return new Object[] {};
307     }
308
309     public Object put(String aKey, Object aValue) {
310       valuesCache.remove(aKey);
311       map.put(aKey, unmakeAdapter(aValue));
312
313       return aValue;
314     }
315
316     public Object remove(java.lang.Object key) {
317       return null;
318     }
319   }
320
321   /**
322    *
323    * <p>Title: </p>
324    * <p>Description: </p>
325    * <p>Copyright: Copyright (c) 2003</p>
326    * <p>Company: </p>
327    * @author not attributable
328    * @version 1.0
329    */
330   private static class IteratorAdapter extends AbstractList implements VelocityAdapter  {
331     private Iterator iterator;
332     private List valuesCache;
333
334     private IteratorAdapter(Iterator anIterator) {
335       iterator = anIterator;
336
337       valuesCache = new ArrayList();
338
339       if (iterator instanceof RewindableIterator) {
340         ((RewindableIterator) iterator).rewind();
341       }
342     }
343
344     private void getUntil(int anIndex) {
345       while ((anIndex==-1 || valuesCache.size()<=anIndex) && iterator.hasNext())
346       {
347         valuesCache.add(makeAdapter(iterator.next()));
348       }
349     }
350
351     public Object getOriginal() {
352       return iterator;
353     }
354
355     public Object get(int anIndex) {
356       Object result;
357
358       getUntil(anIndex);
359
360       if (anIndex<valuesCache.size())
361       {
362         result = valuesCache.get(anIndex);
363
364         return result;
365       }
366                         throw new RuntimeException( "Iterator out of bounds" );
367     }
368
369     public int size() {
370       getUntil(-1);
371       return valuesCache.size();
372     }
373
374   }
375
376   /**
377    *
378    * <p>Title: </p>
379    * <p>Description: </p>
380    * <p>Copyright: Copyright (c) 2003</p>
381    * <p>Company: </p>
382    * @author not attributable
383    * @version 1.0
384    */
385   private static class ListAdapter extends AbstractList implements VelocityAdapter  {
386     private List list;
387     private List valuesCache;
388
389     private ListAdapter(List aList) {
390       list = aList;
391
392       valuesCache = new ArrayList();
393     }
394
395     private void getUntil(int anIndex) {
396       while ((anIndex==-1 || valuesCache.size()<=anIndex) && valuesCache.size()<list.size())
397       {
398         valuesCache.add(makeAdapter(list.get(valuesCache.size())));
399       }
400     }
401
402     public Object getOriginal() {
403       return list;
404     }
405
406     public Object get(int anIndex) {
407       Object result;
408
409       getUntil(anIndex);
410
411       if (anIndex<valuesCache.size())
412       {
413         result = valuesCache.get(anIndex);
414
415         return result;
416       }
417                         throw new RuntimeException( "Iterator out of bounds" );
418     }
419
420     public int size() {
421       return list.size();
422     }
423
424   }
425
426   /**
427    *
428    * <p>Title: </p>
429    * <p>Description: </p>
430    * <p>Copyright: Copyright (c) 2003</p>
431    * <p>Company: </p>
432    * @author not attributable
433    * @version 1.0
434    */
435   private static class BeanAdapter implements Context, VelocityAdapter {
436     private Object object;
437
438     public BeanAdapter(Object anObject) {
439       object = anObject;
440     }
441
442     public boolean containsKey(Object key) {
443       return true;
444     }
445
446     public Object getOriginal() {
447       return object;
448     }
449
450     public Object get(String aKey) {
451       try {
452         if (PropertyUtils.isReadable(object, aKey))
453           return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));
454         else
455           return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));
456       }
457       catch (Throwable t) {
458         throw new GeneratorFailure(t);
459       }
460     }
461
462     public Object[] getKeys() {
463       return new Object[] {};
464     }
465
466     public Object put(String aKey, Object aValue) {
467       try {
468         if (PropertyUtils.isWriteable(object, aKey))
469           PropertyUtils.setSimpleProperty(object, aKey, unmakeAdapter(aValue));
470         else
471           MethodUtils.invokeExactMethod(object, "set", new Object[] {aKey, unmakeAdapter(aValue)});
472
473         return this;
474       }
475       catch (Throwable t) {
476         throw new GeneratorFailure(t);
477       }
478     }
479
480     public Object remove(Object aKey) {
481       throw new RuntimeException("BeanAdapter.remove not supported");
482     }
483   }
484
485   /**
486    *
487    * <p>Title: </p>
488    * <p>Description: </p>
489    * <p>Copyright: Copyright (c) 2003</p>
490    * <p>Company: </p>
491    * @author not attributable
492    * @version 1.0
493    */
494   private static class VelocityLoggerWrapper implements LogSystem {
495    
496     public VelocityLoggerWrapper(LoggerWrapper aLogger) {
497       logger = aLogger;
498     }
499
500     public void init(RuntimeServices aRuntimeServices) {
501     }
502
503     public void logVelocityMessage(int aLevel, String aMessage) {
504       switch (aLevel) {
505         case DEBUG_ID:
506           logger.debug(aMessage);
507           break;
508         case ERROR_ID:
509           logger.error(aMessage);
510           break;
511         case INFO_ID:
512           logger.info(aMessage);
513           break;
514         default:
515           logger.warn(aMessage);
516           break;
517       }
518     }
519   }
520
521   /**
522    *
523    * <p>Title: </p>
524    * <p>Description: </p>
525    * <p>Copyright: Copyright (c) 2003</p>
526    * <p>Company: </p>
527    * @author not attributable
528    * @version 1.0
529    */
530   public static class VelocityGeneratorLibrary implements Library {
531     private VelocityEngine engine;
532
533     public VelocityGeneratorLibrary(File aTemplateRoot) throws GeneratorExc, GeneratorFailure {
534       try {
535         engine = new VelocityEngine();
536         try {
537           engine.setProperty(VelocityEngine.RESOURCE_LOADER, "file");
538           engine.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
539           engine.setProperty("file.resource.loader.path", aTemplateRoot.getAbsolutePath());
540           engine.setProperty("file.resource.loader.cache", "true");
541         }
542         catch (Throwable t) {
543           logger.error("Error while constructing library: " + t.toString());
544
545           throw new GeneratorFailure(t);
546         }
547
548         try {
549           engine.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM, new VelocityLoggerWrapper(logger));
550         }
551         catch (Throwable t) {
552           logger.error(VelocityEngine.RUNTIME_LOG_LOGSYSTEM);
553
554         }
555         engine.init();
556       }
557       catch (Throwable t) {
558         logger.error("Failed to set up a VelocityGeneratorLibrary: " + t.toString());
559         throw new GeneratorFailure(t);
560       }
561     }
562
563     public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {
564       return new VelocityGenerator(anIdentifier, this);
565     }
566   }
567
568   /**
569    *
570    * <p>Title: </p>
571    * <p>Description: </p>
572    * <p>Copyright: Copyright (c) 2003</p>
573    * <p>Company: </p>
574    * @author not attributable
575    * @version 1.0
576    */
577   public static class VelocityGeneratorLibraryFactory implements LibraryFactory {
578     private File basePath;
579
580     public VelocityGeneratorLibraryFactory(File aBasePath) {
581       basePath = aBasePath;
582     }
583
584     public Library makeLibrary(String anInitializationString) throws GeneratorExc, GeneratorFailure {
585       return new VelocityGeneratorLibrary(new File(basePath, anInitializationString));
586     }
587   }
588 }