2 * Copyright (C) 2001, 2002 The Mir-coders group
\r
4 * This file is part of Mir.
\r
6 * Mir is free software; you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation; either version 2 of the License, or
\r
9 * (at your option) any later version.
\r
11 * Mir is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with Mir; if not, write to the Free Software
\r
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
20 * In addition, as a special exception, The Mir-coders gives permission to link
\r
21 * the code of this program with any library licensed under the Apache Software License,
\r
22 * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
\r
23 * (or with modified versions of the above that use the same license as the above),
\r
24 * and distribute linked combinations including the two. You must obey the
\r
25 * GNU General Public License in all respects for all of the code used other than
\r
26 * the above mentioned libraries. If you modify this file, you may extend this
\r
27 * exception to your version of the file, but you are not obligated to do so.
\r
28 * If you do not wish to do so, delete this exception statement from your version.
\r
30 package mir.generator;
\r
32 import java.io.PrintWriter;
\r
33 import java.io.StringWriter;
\r
34 import java.util.AbstractList;
\r
35 import java.util.Date;
\r
36 import java.util.HashMap;
\r
37 import java.util.Iterator;
\r
38 import java.util.List;
\r
39 import java.util.Map;
\r
40 import java.util.Vector;
\r
42 import org.apache.commons.beanutils.MethodUtils;
\r
43 import org.apache.commons.beanutils.PropertyUtils;
\r
44 import org.apache.velocity.Template;
\r
45 import org.apache.velocity.VelocityContext;
\r
46 import org.apache.velocity.app.VelocityEngine;
\r
47 import org.apache.velocity.context.Context;
\r
48 import org.apache.velocity.exception.ParseErrorException;
\r
49 import org.apache.velocity.exception.ResourceNotFoundException;
\r
50 import org.apache.velocity.runtime.RuntimeServices;
\r
51 import org.apache.velocity.runtime.log.LogSystem;
\r
52 import mir.log.LoggerWrapper;
\r
53 import mir.util.GeneratorFormatAdapters;
\r
54 import mir.util.RewindableIterator;
\r
56 public class VelocityGenerator implements Generator {
\r
57 private String templateIdentifier;
\r
58 private VelocityGeneratorLibrary library;
\r
59 private static LoggerWrapper logger = new LoggerWrapper("Generator.velocity");
\r
61 public VelocityGenerator(String aTemplate, VelocityGeneratorLibrary aLibrary) {
\r
62 templateIdentifier = aTemplate;
\r
66 public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {
\r
68 Context context = makeMapAdapter(aValues);
\r
69 StringWriter stringWriter = new StringWriter();
\r
72 template = library.engine.getTemplate(templateIdentifier);
\r
73 if (template == null) {
\r
74 throw new GeneratorExc("VelocityGeneratorLibrary: Can't find template " + templateIdentifier);
\r
76 template.merge(context, stringWriter);
\r
78 ( (PrintWriter) anOutputWriter).print(stringWriter.toString());
\r
80 catch (ResourceNotFoundException t) {
\r
81 throw new GeneratorExc("VelocityGeneratorLibrary: Can't find template " + templateIdentifier);
\r
83 catch (ParseErrorException t) {
\r
84 ( (PrintWriter) anOutputWriter).print(t.toString());
\r
86 catch (Throwable t) {
\r
87 throw new GeneratorFailure(t);
\r
92 private static class ContextAdapter implements Context {
\r
93 public boolean containsKey(java.lang.Object key) {
\r
97 public Object get(java.lang.String key) {
\r
101 public Object[] getKeys() {
\r
102 return new Object[] {};
\r
105 public Object put(java.lang.String key, java.lang.Object value) {
\r
109 public Object remove(java.lang.Object key) {
\r
114 private static Context makeMapAdapter(Map aMap) {
\r
115 return new MapAdapter(aMap);
\r
118 private static List makeIteratorAdapter(Iterator anIterator) {
\r
119 return new IteratorAdapter(anIterator);
\r
122 private static List makeListAdapter(List aList) {
\r
123 return new ListAdapter(aList);
\r
127 private static Object makeFunctionAdapter(Generator.GeneratorFunction aFunction) {
\r
128 return new FunctionAdapter(aFunction);
\r
131 private static Object makeBeanAdapter(Object anObject) {
\r
132 return new BeanAdapter(anObject);
\r
135 private interface VelocityAdapter {
\r
136 public Object getOriginal();
\r
139 public static Object unmakeAdapter(Object anObject) {
\r
140 if (anObject instanceof VelocityAdapter) {
\r
141 return ((VelocityAdapter) anObject).getOriginal();
\r
147 public static Object makeAdapter(Object anObject) {
\r
148 if (anObject == null)
\r
151 if (anObject instanceof Context)
\r
154 else if (anObject instanceof Generator.GeneratorFunction)
\r
155 return makeFunctionAdapter((Generator.GeneratorFunction) anObject);
\r
156 else if (anObject instanceof Integer)
\r
158 else if (anObject instanceof Boolean)
\r
160 else if (anObject instanceof String)
\r
162 else if (anObject instanceof Map)
\r
163 return makeMapAdapter((Map) anObject);
\r
164 else if (anObject instanceof Iterator)
\r
165 return makeIteratorAdapter((Iterator) anObject);
\r
166 else if (anObject instanceof List)
\r
167 return makeListAdapter(((List) anObject));
\r
168 else if (anObject instanceof Number)
\r
169 return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) anObject));
\r
170 else if (anObject instanceof Date)
\r
171 return makeAdapter(new GeneratorFormatAdapters.DateFormatAdapter((Date) anObject));
\r
173 return makeBeanAdapter(anObject);
\r
176 public static class FunctionAdapter implements VelocityAdapter {
\r
177 private GeneratorFunction function;
\r
179 public Object getOriginal() {
\r
183 private FunctionAdapter(GeneratorFunction aFunction) {
\r
184 function = aFunction;
\r
187 public Object call(Object aParameters[]) throws GeneratorExc {
\r
188 List parameters = new Vector();
\r
190 for (int i = 0; i<aParameters.length; i++) {
\r
191 parameters.add(unmakeAdapter(aParameters[i]));
\r
194 return makeAdapter(function.perform(parameters));
\r
197 public Object call() throws GeneratorExc {
\r
198 return makeAdapter(function.perform(new Vector()));
\r
201 public Object call(Object anObject) throws GeneratorExc {
\r
202 return call(new Object[] { anObject });
\r
205 public Object call(Object anObject1, Object anObject2) throws GeneratorExc {
\r
206 return call(new Object[] { anObject1, anObject2 });
\r
209 public Object call(Object anObject1, Object anObject2, Object anObject3) throws GeneratorExc {
\r
210 return call(new Object[] { anObject1, anObject2, anObject3 });
\r
213 public Object call(Object anObject1, Object anObject2, Object anObject3, Object anObject4) throws GeneratorExc {
\r
214 return call(new Object[] { anObject1, anObject2, anObject3, anObject4 });
\r
219 private static class MapAdapter implements Context, VelocityAdapter {
\r
221 private Map valuesCache;
\r
223 private MapAdapter(Map aMap) {
\r
225 valuesCache = new HashMap();
\r
228 public Object getOriginal() {
\r
232 public boolean containsKey(Object aKey) {
\r
233 return map.containsKey(aKey);
\r
236 public Object get(String aKey) {
\r
238 if (!valuesCache.containsKey(aKey)) {
\r
239 Object value = map.get(aKey);
\r
241 if (value == null && !map.containsKey(aKey)) {
\r
242 return "no key "+aKey+" available";
\r
245 valuesCache.put(aKey, makeAdapter(value));
\r
248 return valuesCache.get(aKey);
\r
250 catch (Throwable t) {
\r
251 throw new GeneratorFailure(t);
\r
255 public Object[] getKeys() {
\r
256 return new Object[] {};
\r
259 public Object put(String aKey, Object aValue) {
\r
260 valuesCache.remove(aKey);
\r
261 map.put(aKey, unmakeAdapter(aValue));
\r
266 public Object remove(java.lang.Object key) {
\r
271 private static class IteratorAdapter extends AbstractList implements VelocityAdapter {
\r
272 private Iterator iterator;
\r
273 private List valuesCache;
\r
274 private int position;
\r
276 private IteratorAdapter(Iterator anIterator) {
\r
277 iterator = anIterator;
\r
279 valuesCache = new Vector();
\r
283 if (iterator instanceof RewindableIterator) {
\r
284 ((RewindableIterator) iterator).rewind();
\r
288 private void getUntil(int anIndex) {
\r
289 while ((anIndex==-1 || valuesCache.size()<=anIndex) && iterator.hasNext())
\r
291 valuesCache.add(makeAdapter(iterator.next()));
\r
295 public Object getOriginal() {
\r
299 public Object get(int anIndex) {
\r
304 if (anIndex<valuesCache.size())
\r
306 result = valuesCache.get(anIndex);
\r
311 throw new RuntimeException( "Iterator out of bounds" );
\r
314 public int size() {
\r
316 return valuesCache.size();
\r
321 private static class ListAdapter extends AbstractList implements VelocityAdapter {
\r
323 private List valuesCache;
\r
324 private int position;
\r
326 private ListAdapter(List aList) {
\r
329 valuesCache = new Vector();
\r
333 private void getUntil(int anIndex) {
\r
334 while ((anIndex==-1 || valuesCache.size()<=anIndex) && valuesCache.size()<list.size())
\r
336 valuesCache.add(makeAdapter(list.get(valuesCache.size())));
\r
340 public Object getOriginal() {
\r
344 public Object get(int anIndex) {
\r
349 if (anIndex<valuesCache.size())
\r
351 result = valuesCache.get(anIndex);
\r
356 throw new RuntimeException( "Iterator out of bounds" );
\r
359 public int size() {
\r
360 return list.size();
\r
366 private static class FunctionAdapter implements TemplateMethodModel {
\r
367 private Generator.GeneratorFunction function;
\r
369 public FunctionAdapter(Generator.GeneratorFunction aFunction) {
\r
370 function = aFunction;
\r
373 public TemplateModel exec(List anArguments) throws TemplateModelException {
\r
375 return makeAdapter(function.perform(anArguments));
\r
377 catch (Throwable t) {
\r
378 throw new TemplateModelException(t.getMessage());
\r
382 public boolean isEmpty() {
\r
390 private static class BeanAdapter implements Context, VelocityAdapter {
\r
391 private Object object;
\r
393 public BeanAdapter(Object anObject) {
\r
397 public boolean containsKey(Object key) {
\r
401 public Object getOriginal() {
\r
405 public Object get(String aKey) {
\r
407 if (PropertyUtils.isReadable(object, aKey))
\r
408 return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));
\r
410 return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));
\r
412 catch (Throwable t) {
\r
413 throw new GeneratorFailure(t);
\r
417 public Object[] getKeys() {
\r
418 return new Object[] {};
\r
421 public Object put(String aKey, Object aValue) {
\r
423 if (PropertyUtils.isWriteable(object, aKey))
\r
424 PropertyUtils.setSimpleProperty(object, aKey, unmakeAdapter(aValue));
\r
426 MethodUtils.invokeExactMethod(object, "set", new Object[] {aKey, unmakeAdapter(aValue)});
\r
430 catch (Throwable t) {
\r
431 throw new GeneratorFailure(t);
\r
435 public Object remove(Object aKey) {
\r
436 throw new RuntimeException("BeanAdapter.remove not supported");
\r
440 private static class VelocityLoggerWrapper implements LogSystem {
\r
441 private LoggerWrapper logger;
\r
443 public VelocityLoggerWrapper(LoggerWrapper aLogger) {
\r
447 public void init(RuntimeServices aRuntimeServices) {
\r
450 public void logVelocityMessage(int aLevel, String aMessage) {
\r
453 logger.debug(aMessage);
\r
456 logger.error(aMessage);
\r
459 logger.info(aMessage);
\r
462 logger.warn(aMessage);
\r
468 public static class VelocityGeneratorLibrary implements GeneratorLibrary {
\r
469 private VelocityEngine engine;
\r
471 public VelocityGeneratorLibrary(String aTemplateRoot) throws GeneratorExc, GeneratorFailure {
\r
473 engine = new VelocityEngine();
\r
475 engine.setProperty(VelocityEngine.RESOURCE_LOADER, "file");
\r
477 catch (Throwable t) {
\r
478 logger.error(VelocityEngine.RESOURCE_LOADER);
\r
482 engine.setProperty("file.resource.loader.class", "org.apache.velocity.runtime.resource.loader.FileResourceLoader");
\r
484 catch (Throwable t) {
\r
485 logger.error("file.resource.loader.class");
\r
489 engine.setProperty("file.resource.loader.path", aTemplateRoot);
\r
491 catch (Throwable t) {
\r
492 logger.error("file.resource.loader.path");
\r
496 engine.setProperty("file.resource.loader.cache", "true");
\r
498 catch (Throwable t) {
\r
499 logger.error("file.resource.loader.cache");
\r
503 engine.setProperty("file.resource.loader.modificationCheckInterval", "10");
\r
505 catch (Throwable t) {
\r
506 logger.error("file.resource.loader.modificationCheckInterval");
\r
511 engine.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM, new VelocityLoggerWrapper(logger));
\r
513 catch (Throwable t) {
\r
514 logger.error(VelocityEngine.RUNTIME_LOG_LOGSYSTEM);
\r
518 engine.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS, null);
\r
520 catch (Throwable t) {
\r
521 logger.error(VelocityEngine.RUNTIME_LOG_LOGSYSTEM_CLASS);
\r
528 catch (Throwable t) {
\r
529 t.printStackTrace(logger.asPrintWriter(logger.ERROR_MESSAGE));
\r
531 logger.error("Failed to set up a VelocityGeneratorLibrary: " + t.toString());
\r
532 throw new GeneratorFailure(t);
\r
536 public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {
\r
537 return new VelocityGenerator(anIdentifier, this);
\r
541 public static class VelocityGeneratorLibraryFactory implements GeneratorLibraryFactory {
\r
542 private String basePath;
\r
544 public VelocityGeneratorLibraryFactory(String aBasePath) {
\r
545 basePath = aBasePath;
\r
548 public GeneratorLibrary makeLibrary(String anInitializationString) throws GeneratorExc, GeneratorFailure {
\r
549 return new VelocityGeneratorLibrary(basePath+anInitializationString);
\r