2 * Copyright (C) 2005 The Mir-coders group
4 * This file is part of Mir.
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.
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.
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
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.
27 package mir.generator;
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;
40 import mir.log.LoggerWrapper;
41 import mir.util.GeneratorFormatAdapters;
42 import mir.util.RewindableIterator;
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;
54 public class VelocityGenerator implements Generator {
55 private String templateIdentifier;
56 private VelocityGeneratorLibrary library;
57 private static LoggerWrapper logger = new LoggerWrapper("Generator.velocity");
65 public VelocityGenerator(String aTemplate, VelocityGeneratorLibrary aLibrary) {
66 templateIdentifier = aTemplate;
72 * @param anOutputWriter
75 * @throws GeneratorExc
76 * @throws GeneratorFailure
78 public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {
80 Context context = makeMapAdapter(aValues);
81 StringWriter stringWriter = new StringWriter();
84 template = library.engine.getTemplate(templateIdentifier);
85 if (template == null) {
86 throw new GeneratorExc("VelocityGeneratorLibrary: Can't find template " + templateIdentifier);
88 template.merge(context, stringWriter);
90 ( (PrintWriter) anOutputWriter).print(stringWriter.toString());
92 catch (ResourceNotFoundException t) {
93 throw new GeneratorExc("VelocityGeneratorLibrary: Can't find template " + templateIdentifier);
95 catch (ParseErrorException t) {
96 ( (PrintWriter) anOutputWriter).print(t.toString());
99 throw new GeneratorFailure(t);
109 private static Context makeMapAdapter(Map aMap) {
110 return new MapAdapter(aMap);
118 private static List makeIteratorAdapter(Iterator anIterator) {
119 return new IteratorAdapter(anIterator);
127 private static List makeListAdapter(List aList) {
128 return new ListAdapter(aList);
136 private static Object makeFunctionAdapter(Generator.Function aFunction) {
137 return new FunctionAdapter(aFunction);
145 private static Object makeBeanAdapter(Object anObject) {
146 return new BeanAdapter(anObject);
152 * <p>Description: </p>
153 * <p>Copyright: Copyright (c) 2003</p>
155 * @author not attributable
158 private interface VelocityAdapter {
159 public Object getOriginal();
167 public static Object unmakeAdapter(Object anObject) {
168 if (anObject instanceof VelocityAdapter) {
169 return ((VelocityAdapter) anObject).getOriginal();
179 public static Object makeAdapter(Object anObject) {
180 if (anObject == null)
183 if (anObject instanceof Context)
186 else if (anObject instanceof Generator.Function)
187 return makeFunctionAdapter((Generator.Function) anObject);
188 else if (anObject instanceof Integer)
190 else if (anObject instanceof Boolean)
192 else if (anObject instanceof String)
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));
205 return makeBeanAdapter(anObject);
211 * <p>Description: </p>
212 * <p>Copyright: Copyright (c) 2003</p>
214 * @author not attributable
217 public static class FunctionAdapter implements VelocityAdapter {
218 private Function function;
220 public Object getOriginal() {
224 private FunctionAdapter(Function aFunction) {
225 function = aFunction;
228 public Object call(Object aParameters[]) throws GeneratorExc {
229 List parameters = new ArrayList();
231 for (int i = 0; i<aParameters.length; i++) {
232 parameters.add(unmakeAdapter(aParameters[i]));
235 return makeAdapter(function.perform(parameters));
238 public Object call() throws GeneratorExc {
239 return makeAdapter(function.perform(new ArrayList()));
242 public Object call(Object anObject) throws GeneratorExc {
243 return call(new Object[] { anObject });
246 public Object call(Object anObject1, Object anObject2) throws GeneratorExc {
247 return call(new Object[] { anObject1, anObject2 });
250 public Object call(Object anObject1, Object anObject2, Object anObject3) throws GeneratorExc {
251 return call(new Object[] { anObject1, anObject2, anObject3 });
254 public Object call(Object anObject1, Object anObject2, Object anObject3, Object anObject4) throws GeneratorExc {
255 return call(new Object[] { anObject1, anObject2, anObject3, anObject4 });
263 * <p>Description: </p>
264 * <p>Copyright: Copyright (c) 2003</p>
266 * @author not attributable
269 private static class MapAdapter implements Context, VelocityAdapter {
271 private Map valuesCache;
273 private MapAdapter(Map aMap) {
275 valuesCache = new HashMap();
278 public Object getOriginal() {
282 public boolean containsKey(Object aKey) {
283 return map.containsKey(aKey);
286 public Object get(String aKey) {
288 if (!valuesCache.containsKey(aKey)) {
289 Object value = map.get(aKey);
291 if (value == null && !map.containsKey(aKey)) {
292 return "no key "+aKey+" available";
295 valuesCache.put(aKey, makeAdapter(value));
298 return valuesCache.get(aKey);
300 catch (Throwable t) {
301 throw new GeneratorFailure(t);
305 public Object[] getKeys() {
306 return new Object[] {};
309 public Object put(String aKey, Object aValue) {
310 valuesCache.remove(aKey);
311 map.put(aKey, unmakeAdapter(aValue));
316 public Object remove(java.lang.Object key) {
324 * <p>Description: </p>
325 * <p>Copyright: Copyright (c) 2003</p>
327 * @author not attributable
330 private static class IteratorAdapter extends AbstractList implements VelocityAdapter {
331 private Iterator iterator;
332 private List valuesCache;
334 private IteratorAdapter(Iterator anIterator) {
335 iterator = anIterator;
337 valuesCache = new ArrayList();
339 if (iterator instanceof RewindableIterator) {
340 ((RewindableIterator) iterator).rewind();
344 private void getUntil(int anIndex) {
345 while ((anIndex==-1 || valuesCache.size()<=anIndex) && iterator.hasNext())
347 valuesCache.add(makeAdapter(iterator.next()));
351 public Object getOriginal() {
355 public Object get(int anIndex) {
360 if (anIndex<valuesCache.size())
362 result = valuesCache.get(anIndex);
366 throw new RuntimeException( "Iterator out of bounds" );
371 return valuesCache.size();
379 * <p>Description: </p>
380 * <p>Copyright: Copyright (c) 2003</p>
382 * @author not attributable
385 private static class ListAdapter extends AbstractList implements VelocityAdapter {
387 private List valuesCache;
389 private ListAdapter(List aList) {
392 valuesCache = new ArrayList();
395 private void getUntil(int anIndex) {
396 while ((anIndex==-1 || valuesCache.size()<=anIndex) && valuesCache.size()<list.size())
398 valuesCache.add(makeAdapter(list.get(valuesCache.size())));
402 public Object getOriginal() {
406 public Object get(int anIndex) {
411 if (anIndex<valuesCache.size())
413 result = valuesCache.get(anIndex);
417 throw new RuntimeException( "Iterator out of bounds" );
429 * <p>Description: </p>
430 * <p>Copyright: Copyright (c) 2003</p>
432 * @author not attributable
435 private static class BeanAdapter implements Context, VelocityAdapter {
436 private Object object;
438 public BeanAdapter(Object anObject) {
442 public boolean containsKey(Object key) {
446 public Object getOriginal() {
450 public Object get(String aKey) {
452 if (PropertyUtils.isReadable(object, aKey))
453 return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));
455 return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));
457 catch (Throwable t) {
458 throw new GeneratorFailure(t);
462 public Object[] getKeys() {
463 return new Object[] {};
466 public Object put(String aKey, Object aValue) {
468 if (PropertyUtils.isWriteable(object, aKey))
469 PropertyUtils.setSimpleProperty(object, aKey, unmakeAdapter(aValue));
471 MethodUtils.invokeExactMethod(object, "set", new Object[] {aKey, unmakeAdapter(aValue)});
475 catch (Throwable t) {
476 throw new GeneratorFailure(t);
480 public Object remove(Object aKey) {
481 throw new RuntimeException("BeanAdapter.remove not supported");
488 * <p>Description: </p>
489 * <p>Copyright: Copyright (c) 2003</p>
491 * @author not attributable
494 private static class VelocityLoggerWrapper implements LogSystem {
496 public VelocityLoggerWrapper(LoggerWrapper aLogger) {
500 public void init(RuntimeServices aRuntimeServices) {
503 public void logVelocityMessage(int aLevel, String aMessage) {
506 logger.debug(aMessage);
509 logger.error(aMessage);
512 logger.info(aMessage);
515 logger.warn(aMessage);
524 * <p>Description: </p>
525 * <p>Copyright: Copyright (c) 2003</p>
527 * @author not attributable
530 public static class VelocityGeneratorLibrary implements Library {
531 private VelocityEngine engine;
533 public VelocityGeneratorLibrary(File aTemplateRoot) throws GeneratorExc, GeneratorFailure {
535 engine = new VelocityEngine();
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");
542 catch (Throwable t) {
543 logger.error("Error while constructing library: " + t.toString());
545 throw new GeneratorFailure(t);
549 engine.setProperty(VelocityEngine.RUNTIME_LOG_LOGSYSTEM, new VelocityLoggerWrapper(logger));
551 catch (Throwable t) {
552 logger.error(VelocityEngine.RUNTIME_LOG_LOGSYSTEM);
557 catch (Throwable t) {
558 logger.error("Failed to set up a VelocityGeneratorLibrary: " + t.toString());
559 throw new GeneratorFailure(t);
563 public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {
564 return new VelocityGenerator(anIdentifier, this);
571 * <p>Description: </p>
572 * <p>Copyright: Copyright (c) 2003</p>
574 * @author not attributable
577 public static class VelocityGeneratorLibraryFactory implements LibraryFactory {
578 private File basePath;
580 public VelocityGeneratorLibraryFactory(File aBasePath) {
581 basePath = aBasePath;
584 public Library makeLibrary(String anInitializationString) throws GeneratorExc, GeneratorFailure {
585 return new VelocityGeneratorLibrary(new File(basePath, anInitializationString));