2 * Copyright (C) 2001, 2002 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 * 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.
30 package mir.generator;
33 import java.io.PrintWriter;
34 import java.util.ArrayList;
35 import java.util.Date;
36 import java.util.HashMap;
37 import java.util.Iterator;
38 import java.util.List;
40 import java.util.Collection;
42 import mir.log.LoggerWrapper;
43 import mir.util.GeneratorFormatAdapters;
44 import mir.util.RewindableIterator;
46 import org.apache.commons.beanutils.MethodUtils;
47 import org.apache.commons.beanutils.PropertyUtils;
49 import freemarker.template.FileTemplateCache;
50 import freemarker.template.SimpleScalar;
51 import freemarker.template.Template;
52 import freemarker.template.TemplateHashModel;
53 import freemarker.template.TemplateListModel;
54 import freemarker.template.TemplateMethodModel;
55 import freemarker.template.TemplateModel;
56 import freemarker.template.TemplateModelException;
57 import freemarker.template.TemplateModelRoot;
58 import freemarker.template.TemplateScalarModel;
61 public class FreemarkerGenerator implements Generator {
62 private Template template;
64 public FreemarkerGenerator(Template aTemplate) {
68 public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {
69 if (!(anOutputWriter instanceof PrintWriter)) {
70 throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter");
74 template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter);
77 aLogger.error("Exception occurred: "+t.getMessage(), t);
79 throw new GeneratorFailure(t);
83 private static TemplateScalarModel makeStringAdapter(String aString) {
84 return new SimpleScalar(aString);
87 private static TemplateHashModel makeMapAdapter(Map aMap) {
88 return new MapAdapter(aMap);
91 private static TemplateListModel makeIteratorAdapter(Iterator anIterator) {
92 return new IteratorAdapter(anIterator);
95 private static TemplateMethodModel makeFunctionAdapter(Generator.Function aFunction) {
96 return new FunctionAdapter(aFunction);
99 private static TemplateHashModel makeBeanAdapter(Object anObject) {
100 return new BeanAdapter(anObject);
103 public static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {
104 if (anObject == null) {
108 if (anObject instanceof TemplateModel) {
109 return (TemplateModel) anObject;
111 else if (anObject instanceof Generator.Function) {
112 return makeFunctionAdapter((Generator.Function) anObject);
114 else if (anObject instanceof Integer) {
115 return makeStringAdapter(anObject.toString());
117 else if (anObject instanceof Boolean) {
118 if (((Boolean) anObject).booleanValue()) {
119 return makeStringAdapter("1");
122 return makeStringAdapter("0");
125 else if (anObject instanceof String) {
126 return makeStringAdapter((String) anObject);
128 else if (anObject instanceof Map) {
129 return makeMapAdapter((Map) anObject);
131 else if (anObject instanceof Iterator) {
132 return makeIteratorAdapter((Iterator) anObject);
134 else if (anObject instanceof Collection) {
135 return makeIteratorAdapter(((Collection) anObject).iterator());
137 else if (anObject instanceof Number) {
138 return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) anObject));
140 else if (anObject instanceof Date) {
141 return makeAdapter(new GeneratorFormatAdapters.DateFormatAdapter((Date) anObject));
144 return makeBeanAdapter(anObject);
148 private static class MapAdapter implements TemplateModelRoot {
150 private Map valuesCache;
152 private MapAdapter(Map aMap) {
154 valuesCache = new HashMap();
157 public void put(String aKey, TemplateModel aModel) {
158 valuesCache.put(aKey, aModel);
161 public void remove(String aKey) {
164 public boolean isEmpty() {
165 return map.isEmpty();
168 public TemplateModel get(String aKey) throws TemplateModelException {
170 if (!valuesCache.containsKey(aKey)) {
171 Object value = map.get(aKey);
173 if (value == null && !map.containsKey(aKey)) {
174 throw new TemplateModelException("MapAdapter: no key "+aKey+" available");
177 valuesCache.put(aKey, makeAdapter(value));
180 return (TemplateModel) valuesCache.get(aKey);
182 catch (TemplateModelException e) {
185 catch (Throwable t) {
186 throw new TemplateModelException(t.getMessage());
191 private static class IteratorAdapter implements TemplateListModel {
192 private Iterator iterator;
193 private List valuesCache;
194 private int position;
196 private IteratorAdapter(Iterator anIterator) {
197 iterator = anIterator;
199 valuesCache = new ArrayList();
203 if (iterator instanceof RewindableIterator) {
204 ((RewindableIterator) iterator).rewind();
208 public boolean isEmpty() {
209 return valuesCache.isEmpty() && !iterator.hasNext();
212 private void getUntil(int anIndex) throws TemplateModelException {
213 while (valuesCache.size()<=anIndex && iterator.hasNext())
215 valuesCache.add(makeAdapter(iterator.next()));
219 public TemplateModel get(int anIndex) throws TemplateModelException {
222 if (anIndex<valuesCache.size()) {
223 return (TemplateModel) valuesCache.get(anIndex);
225 throw new TemplateModelException( "Iterator out of bounds" );
228 public boolean hasNext() {
229 return position<valuesCache.size() || iterator.hasNext();
232 public boolean isRewound() {
236 public TemplateModel next() throws TemplateModelException {
237 TemplateModel result;
240 result = get(position);
244 throw new TemplateModelException( "Iterator out of bounds" );
249 public void rewind() {
254 private static class FunctionAdapter implements TemplateMethodModel {
255 private Generator.Function function;
257 FunctionAdapter(Generator.Function aFunction) {
258 function = aFunction;
261 public TemplateModel exec(List anArguments) throws TemplateModelException {
263 return makeAdapter(function.perform(anArguments));
265 catch (Throwable t) {
266 throw new TemplateModelException(t.getMessage());
270 public boolean isEmpty() {
276 private static class BeanAdapter implements TemplateHashModel {
277 private Object object;
279 public BeanAdapter(Object anObject) {
283 public void put(String aKey, TemplateModel aModel) throws TemplateModelException {
284 throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.put not supported");
287 public void remove(String aKey) throws TemplateModelException {
288 throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.remove not supported");
291 public boolean isEmpty() {
295 public TemplateModel get(String aKey) throws TemplateModelException {
297 if (PropertyUtils.isReadable(object, aKey)) {
298 return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));
300 return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));
302 catch (Throwable t) {
303 throw new TemplateModelException(t.getMessage());
308 public static class FreemarkerGeneratorLibrary implements Library {
309 private FileTemplateCache templateCache;
311 public FreemarkerGeneratorLibrary(File aTemplateRoot) {
312 templateCache = new FileTemplateCache(aTemplateRoot);
313 templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);
316 public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {
317 Template template = (Template) templateCache.getItem(anIdentifier, "template");
319 if (template==null) {
320 throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template " +
321 templateCache.getDirectory() + File.separatorChar + anIdentifier);
324 return new FreemarkerGenerator(template);
328 public static class FreemarkerGeneratorLibraryFactory implements LibraryFactory {
329 private File basePath;
331 public FreemarkerGeneratorLibraryFactory(File aBasePath) {
332 basePath = aBasePath;
335 public Library makeLibrary(String anInitializationString) {
336 // todo: the initialization string should be parsed
337 return new FreemarkerGeneratorLibrary(new File(basePath, anInitializationString));