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