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.util.ArrayList;
32 import java.util.Date;
33 import java.util.HashMap;
34 import java.util.Iterator;
35 import java.util.List;
37 import java.util.Collection;
39 import mir.log.LoggerWrapper;
40 import mir.util.GeneratorFormatAdapters;
41 import mir.util.RewindableIterator;
43 import org.apache.commons.beanutils.MethodUtils;
44 import org.apache.commons.beanutils.PropertyUtils;
46 import freemarker.template.FileTemplateCache;
47 import freemarker.template.SimpleScalar;
48 import freemarker.template.Template;
49 import freemarker.template.TemplateHashModel;
50 import freemarker.template.TemplateListModel;
51 import freemarker.template.TemplateMethodModel;
52 import freemarker.template.TemplateModel;
53 import freemarker.template.TemplateModelException;
54 import freemarker.template.TemplateModelRoot;
55 import freemarker.template.TemplateScalarModel;
58 public class FreemarkerGenerator implements Generator {
59 private Template template;
60 private Interceptor interceptor;
62 public FreemarkerGenerator(Template aTemplate, Interceptor anInterceptor) {
64 interceptor = anInterceptor;
67 public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {
68 if (!(anOutputWriter instanceof PrintWriter)) {
69 throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter");
73 template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter);
76 aLogger.error("Exception occurred: "+t.getMessage(), t);
78 throw new GeneratorFailure(t);
82 private static TemplateScalarModel makeStringAdapter(String aString) {
83 return new SimpleScalar(aString);
86 private TemplateHashModel makeMapAdapter(Map aMap) {
87 return new MapAdapter(aMap);
90 private TemplateListModel makeIteratorAdapter(Iterator anIterator) {
91 return new IteratorAdapter(anIterator);
94 private TemplateMethodModel makeFunctionAdapter(Generator.Function aFunction) {
95 return new FunctionAdapter(aFunction);
98 private TemplateHashModel makeBeanAdapter(Object anObject) {
99 return new BeanAdapter(anObject);
102 public TemplateModel makeAdapter(Object anObject) throws TemplateModelException {
103 Object object = anObject;
104 if (interceptor != null) {
105 object = interceptor.intercept(object);
108 if (object == null) {
111 if (object instanceof TemplateModel) {
112 return (TemplateModel) object;
114 else if (object instanceof Generator.Function) {
115 return makeFunctionAdapter((Generator.Function) object);
117 else if (object instanceof Integer) {
118 return makeStringAdapter(object.toString());
120 else if (object instanceof Boolean) {
121 if (((Boolean) object).booleanValue()) {
122 return makeStringAdapter("1");
125 return makeStringAdapter("0");
128 else if (object instanceof String) {
129 return makeStringAdapter((String) object);
131 else if (object instanceof Map) {
132 return makeMapAdapter((Map) object);
134 else if (object instanceof Iterator) {
135 return makeIteratorAdapter((Iterator) object);
137 else if (object instanceof Collection) {
138 return makeIteratorAdapter(((Collection) object).iterator());
140 else if (object instanceof Number) {
141 return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) object));
143 else if (object instanceof Date) {
144 return makeAdapter(new GeneratorFormatAdapters.DateFormatAdapter((Date) object));
147 return makeBeanAdapter(object);
151 private class MapAdapter implements TemplateModelRoot {
153 private Map valuesCache;
155 private MapAdapter(Map aMap) {
157 valuesCache = new HashMap();
160 public void put(String aKey, TemplateModel aModel) {
161 valuesCache.put(aKey, aModel);
164 public void remove(String aKey) {
167 public boolean isEmpty() {
168 return map.isEmpty();
171 public TemplateModel get(String aKey) throws TemplateModelException {
173 if (!valuesCache.containsKey(aKey)) {
174 Object value = map.get(aKey);
176 if (value == null && !map.containsKey(aKey)) {
177 throw new TemplateModelException("MapAdapter: no key "+aKey+" available");
180 valuesCache.put(aKey, makeAdapter(value));
183 return (TemplateModel) valuesCache.get(aKey);
185 catch (TemplateModelException e) {
188 catch (Throwable t) {
189 throw new TemplateModelException(t.getMessage());
194 private class IteratorAdapter implements TemplateListModel {
195 private Iterator iterator;
196 private List valuesCache;
197 private int position;
199 private IteratorAdapter(Iterator anIterator) {
200 iterator = anIterator;
202 valuesCache = new ArrayList();
206 if (iterator instanceof RewindableIterator) {
207 ((RewindableIterator) iterator).rewind();
211 public boolean isEmpty() {
212 return valuesCache.isEmpty() && !iterator.hasNext();
215 private void getUntil(int anIndex) throws TemplateModelException {
216 while (valuesCache.size()<=anIndex && iterator.hasNext())
218 valuesCache.add(makeAdapter(iterator.next()));
222 public TemplateModel get(int anIndex) throws TemplateModelException {
225 if (anIndex<valuesCache.size()) {
226 return (TemplateModel) valuesCache.get(anIndex);
228 throw new TemplateModelException( "Iterator out of bounds" );
231 public boolean hasNext() {
232 return position<valuesCache.size() || iterator.hasNext();
235 public boolean isRewound() {
239 public TemplateModel next() throws TemplateModelException {
240 TemplateModel result;
243 result = get(position);
247 throw new TemplateModelException( "Iterator out of bounds" );
252 public void rewind() {
257 private class FunctionAdapter implements TemplateMethodModel {
258 private Generator.Function function;
260 FunctionAdapter(Generator.Function aFunction) {
261 function = aFunction;
264 public TemplateModel exec(List anArguments) throws TemplateModelException {
266 return makeAdapter(function.perform(anArguments));
268 catch (Throwable t) {
269 throw new TemplateModelException(t.getMessage());
273 public boolean isEmpty() {
279 private class BeanAdapter implements TemplateHashModel {
280 private Object object;
282 public BeanAdapter(Object anObject) {
286 public void put(String aKey, TemplateModel aModel) throws TemplateModelException {
287 throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.put not supported");
290 public void remove(String aKey) throws TemplateModelException {
291 throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.remove not supported");
294 public boolean isEmpty() {
298 public TemplateModel get(String aKey) throws TemplateModelException {
300 if (PropertyUtils.isReadable(object, aKey)) {
301 return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));
303 return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));
305 catch (Throwable t) {
306 throw new TemplateModelException(t.getMessage());
311 public static class FreemarkerGeneratorLibrary implements Library {
312 private FileTemplateCache templateCache;
314 public FreemarkerGeneratorLibrary(File aTemplateRoot) {
315 templateCache = new FileTemplateCache(aTemplateRoot);
316 templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);
319 public Generator makeGenerator(String anIdentifier, Interceptor anInterceptor) throws GeneratorExc, GeneratorFailure {
320 Template template = (Template) templateCache.getItem(anIdentifier, "template");
322 if (template==null) {
323 throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template " +
324 templateCache.getDirectory() + File.separatorChar + anIdentifier);
327 return new FreemarkerGenerator(template, anInterceptor);
332 public static class FreemarkerGeneratorLibraryFactory implements LibraryFactory {
333 private File basePath;
335 public FreemarkerGeneratorLibraryFactory(File aBasePath) {
336 basePath = aBasePath;
339 public Library makeLibrary(String anInitializationString) {
340 // todo: the initialization string should be parsed
341 return new FreemarkerGeneratorLibrary(new File(basePath, anInitializationString));