small bugfix: converting arrays.
authoridfx <idfx>
Thu, 18 Sep 2003 21:43:06 +0000 (21:43 +0000)
committeridfx <idfx>
Thu, 18 Sep 2003 21:43:06 +0000 (21:43 +0000)
source/mir/generator/FreemarkerGenerator.java

index 8b5ec61..a786a71 100755 (executable)
-/*\r
- * Copyright (C) 2001, 2002 The Mir-coders group\r
- *\r
- * This file is part of Mir.\r
- *\r
- * Mir is free software; you can redistribute it and/or modify\r
- * it under the terms of the GNU General Public License as published by\r
- * the Free Software Foundation; either version 2 of the License, or\r
- * (at your option) any later version.\r
- *\r
- * Mir is distributed in the hope that it will be useful,\r
- * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
- * GNU General Public License for more details.\r
- *\r
- * You should have received a copy of the GNU General Public License\r
- * along with Mir; if not, write to the Free Software\r
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
- *\r
- * In addition, as a special exception, The Mir-coders gives permission to link\r
- * the code of this program with  any library licensed under the Apache Software License,\r
- * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library\r
- * (or with modified versions of the above that use the same license as the above),\r
- * and distribute linked combinations including the two.  You must obey the\r
- * GNU General Public License in all respects for all of the code used other than\r
- * the above mentioned libraries.  If you modify this file, you may extend this\r
- * exception to your version of the file, but you are not obligated to do so.\r
- * If you do not wish to do so, delete this exception statement from your version.\r
- */\r
-package mir.generator;\r
-\r
-import java.io.PrintWriter;\r
-import java.util.Date;\r
-import java.util.HashMap;\r
-import java.util.Iterator;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Vector;\r
-\r
-import org.apache.commons.beanutils.MethodUtils;\r
-import org.apache.commons.beanutils.PropertyUtils;\r
-import freemarker.template.FileTemplateCache;\r
-import freemarker.template.SimpleScalar;\r
-import freemarker.template.Template;\r
-import freemarker.template.TemplateHashModel;\r
-import freemarker.template.TemplateListModel;\r
-import freemarker.template.TemplateMethodModel;\r
-import freemarker.template.TemplateModel;\r
-import freemarker.template.TemplateModelException;\r
-import freemarker.template.TemplateModelRoot;\r
-import freemarker.template.TemplateScalarModel;\r
-import mir.log.LoggerWrapper;\r
-import mir.util.GeneratorFormatAdapters;\r
-import mir.util.RewindableIterator;\r
-\r
-\r
-public class FreemarkerGenerator implements Generator {\r
-  private Template template;\r
-\r
-  public FreemarkerGenerator(Template aTemplate) {\r
-    template = aTemplate;\r
-  }\r
-\r
-  public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {\r
-    if (!(anOutputWriter instanceof PrintWriter))\r
-      throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter");\r
-\r
-    try {\r
-      template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter);\r
-    }\r
-    catch (Throwable t) {\r
-      t.printStackTrace();\r
-      aLogger.error("Exception occurred: "+t.getMessage());\r
-      t.printStackTrace(aLogger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));\r
-      throw new GeneratorFailure( t );\r
-    }\r
-  }\r
-\r
-  private static TemplateScalarModel makeStringAdapter(String aString) {\r
-    return new SimpleScalar(aString);\r
-  }\r
-\r
-  private static TemplateHashModel makeMapAdapter(Map aMap)  {\r
-    return new MapAdapter(aMap);\r
-  }\r
-\r
-  private static TemplateListModel makeIteratorAdapter(Iterator anIterator) {\r
-    return new IteratorAdapter(anIterator);\r
-  }\r
-\r
-  private static TemplateMethodModel makeFunctionAdapter(Generator.GeneratorFunction aFunction) {\r
-    return new FunctionAdapter(aFunction);\r
-  }\r
-\r
-  private static TemplateHashModel makeBeanAdapter(Object anObject)  {\r
-    return new BeanAdapter(anObject);\r
-  }\r
-\r
-  public static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {\r
-    if (anObject == null)\r
-      return null;\r
-\r
-    if (anObject instanceof TemplateModel)\r
-      return (TemplateModel) anObject;\r
-    else if (anObject instanceof Generator.GeneratorFunction)\r
-      return makeFunctionAdapter((Generator.GeneratorFunction) anObject);\r
-    else if (anObject instanceof Integer)\r
-      return makeStringAdapter(((Integer) anObject).toString());\r
-    else if (anObject instanceof Boolean) {\r
-      if (((Boolean) anObject).booleanValue())\r
-        return makeStringAdapter("1");\r
-      else\r
-        return makeStringAdapter("0");\r
-    }\r
-    else if (anObject instanceof String)\r
-      return makeStringAdapter((String) anObject);\r
-    else if (anObject instanceof Map)\r
-      return makeMapAdapter((Map) anObject);\r
-    else if (anObject instanceof Iterator)\r
-      return makeIteratorAdapter((Iterator) anObject);\r
-    else if (anObject instanceof List)\r
-      return makeIteratorAdapter(((List) anObject).iterator());\r
-    else if (anObject instanceof Number)\r
-      return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) anObject));\r
-    else if (anObject instanceof Date)\r
-      return makeAdapter(new GeneratorFormatAdapters.DateFormatAdapter((Date) anObject));\r
-    else\r
-      return makeBeanAdapter(anObject);\r
-  }\r
-\r
-  private static class MapAdapter implements TemplateModelRoot {\r
-    private Map map;\r
-    private Map valuesCache;\r
-\r
-    private MapAdapter(Map aMap) {\r
-      map = aMap;\r
-      valuesCache = new HashMap();\r
-    }\r
-\r
-    public void put(String aKey, TemplateModel aModel) {\r
-      valuesCache.put(aKey, aModel);\r
-    }\r
-\r
-    public void remove(String aKey) {\r
-    }\r
-\r
-    public boolean isEmpty() {\r
-      return map.isEmpty();\r
-    }\r
-\r
-    public TemplateModel get(String aKey) throws TemplateModelException {\r
-      try {\r
-        if (!valuesCache.containsKey(aKey)) {\r
-          Object value = map.get(aKey);\r
-\r
-          if (value == null && !map.containsKey(aKey)) {\r
-            throw new TemplateModelException("MapAdapter: no key "+aKey+" available");\r
-          }\r
-\r
-          valuesCache.put(aKey, makeAdapter(value));\r
-        }\r
-\r
-        return (TemplateModel) valuesCache.get(aKey);\r
-      }\r
-      catch (TemplateModelException e) {\r
-        throw e;\r
-      }\r
-      catch (Throwable t) {\r
-        throw new TemplateModelException(t.getMessage());\r
-      }\r
-    }\r
-  }\r
-\r
-  private static class IteratorAdapter implements TemplateListModel {\r
-    private Iterator iterator;\r
-    private List valuesCache;\r
-    private int position;\r
-\r
-    private IteratorAdapter(Iterator anIterator) {\r
-      iterator = anIterator;\r
-\r
-      valuesCache = new Vector();\r
-      position=0;\r
-\r
-\r
-      if (iterator instanceof RewindableIterator) {\r
-        ((RewindableIterator) iterator).rewind();\r
-      }\r
-    }\r
-\r
-    public boolean isEmpty() {\r
-      return valuesCache.isEmpty() && !iterator.hasNext();\r
-    }\r
-\r
-    private void getUntil(int anIndex) throws TemplateModelException {\r
-      while (valuesCache.size()<=anIndex && iterator.hasNext())\r
-      {\r
-        valuesCache.add(makeAdapter(iterator.next()));\r
-      }\r
-    };\r
-\r
-    public TemplateModel get(int anIndex) throws TemplateModelException {\r
-      TemplateModel result;\r
-\r
-      getUntil(anIndex);\r
-\r
-      if (anIndex<valuesCache.size())\r
-      {\r
-        result = (TemplateModel) valuesCache.get(anIndex);\r
-\r
-        return result;\r
-      }\r
-      else\r
-        throw new TemplateModelException( "Iterator out of bounds" );\r
-    }\r
-\r
-    public boolean hasNext() {\r
-      return position<valuesCache.size() || iterator.hasNext();\r
-    }\r
-\r
-    public boolean isRewound() {\r
-      return position==0;\r
-    }\r
-\r
-    public TemplateModel next() throws TemplateModelException {\r
-      TemplateModel result;\r
-\r
-      if (hasNext()) {\r
-        result = get(position);\r
-        position++;\r
-      }\r
-      else\r
-        throw new TemplateModelException( "Iterator out of bounds" );\r
-\r
-      return result;\r
-    }\r
-\r
-    public void rewind() {\r
-      position=0;\r
-    }\r
-  }\r
-\r
-  private static class ListAdapter implements TemplateListModel {\r
-    List list;\r
-    List valuesCache;\r
-    int position;\r
-\r
-    private ListAdapter(List aList) {\r
-      list = aList;\r
-      valuesCache = new Vector();\r
-      position=0;\r
-    }\r
-\r
-    public boolean isEmpty() {\r
-      return list.isEmpty();\r
-    }\r
-\r
-    public TemplateModel get(int i) throws TemplateModelException {\r
-\r
-      if (i>=valuesCache.size() && i<list.size()) {\r
-        for(int j=valuesCache.size(); j<=i; j++) {\r
-          valuesCache.add(makeAdapter(list.get(j)));\r
-        }\r
-      }\r
-\r
-      if (i<valuesCache.size())\r
-        return (TemplateModel) valuesCache.get(i);\r
-      else\r
-        throw new TemplateModelException( "Iterator out of bounds" );\r
-    }\r
-\r
-    public boolean hasNext() {\r
-      return position<list.size();\r
-    }\r
-\r
-    public boolean isRewound() {\r
-      return position==0;\r
-    }\r
-\r
-    public TemplateModel next() throws TemplateModelException {\r
-      TemplateModel result;\r
-\r
-      if (hasNext()) {\r
-        result = get(position);\r
-        position++;\r
-      }\r
-      else {\r
-        throw new TemplateModelException( "Iterator out of bounds" );\r
-      }\r
-\r
-      return result;\r
-    }\r
-\r
-    public void rewind() {\r
-      position = 0;\r
-    }\r
-  }\r
-\r
-  private static class FunctionAdapter implements TemplateMethodModel {\r
-    private Generator.GeneratorFunction function;\r
-\r
-    public FunctionAdapter(Generator.GeneratorFunction aFunction) {\r
-      function = aFunction;\r
-    }\r
-\r
-    public TemplateModel exec(List anArguments) throws TemplateModelException {\r
-      try {\r
-        return makeAdapter(function.perform(anArguments));\r
-      }\r
-      catch (Throwable t) {\r
-        throw new TemplateModelException(t.getMessage());\r
-      }\r
-    }\r
-\r
-    public boolean isEmpty() {\r
-      return false;\r
-    }\r
-\r
-  }\r
-\r
-  private static class BeanAdapter implements TemplateHashModel {\r
-    private Object object;\r
-\r
-    public BeanAdapter(Object anObject) {\r
-      object = anObject;\r
-    }\r
-\r
-    public void put(String aKey, TemplateModel aModel)  throws TemplateModelException  {\r
-      throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.put not supported");\r
-    }\r
-\r
-    public void remove(String aKey) throws TemplateModelException  {\r
-      throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.remove not supported");\r
-    }\r
-\r
-    public boolean isEmpty() {\r
-      return false;\r
-    }\r
-\r
-    public TemplateModel get(String aKey) throws TemplateModelException {\r
-      try {\r
-        if (PropertyUtils.isReadable(object, aKey))\r
-          return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));\r
-        else\r
-          return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));\r
-      }\r
-      catch (Throwable t) {\r
-        throw new TemplateModelException(t.getMessage());\r
-      }\r
-    }\r
-  }\r
-\r
-  public static class FreemarkerGeneratorLibrary implements GeneratorLibrary {\r
-    private FileTemplateCache templateCache;\r
-\r
-    public FreemarkerGeneratorLibrary(String aTemplateRoot) {\r
-      templateCache = new FileTemplateCache( aTemplateRoot+"/" );\r
-      templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);\r
-    }\r
-\r
-    public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {\r
-      Template template = (Template) templateCache.getItem(anIdentifier, "template");\r
-\r
-      if (template==null) {\r
-        throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template "+templateCache.getDirectory()+anIdentifier);\r
-      }\r
-\r
-      return new FreemarkerGenerator(template);\r
-    }\r
-  }\r
-\r
-  public static class FreemarkerGeneratorLibraryFactory implements GeneratorLibraryFactory {\r
-    private String basePath;\r
-\r
-    public FreemarkerGeneratorLibraryFactory(String aBasePath) {\r
-      basePath = aBasePath;\r
-    }\r
-\r
-    public GeneratorLibrary makeLibrary(String anInitializationString) {\r
-      return new FreemarkerGeneratorLibrary(basePath+anInitializationString);\r
-    };\r
-  }\r
-}\r
+/*
+ * Copyright (C) 2001, 2002 The Mir-coders group
+ *
+ * This file is part of Mir.
+ *
+ * Mir is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Mir is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mir; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * In addition, as a special exception, The Mir-coders gives permission to link
+ * the code of this program with  any library licensed under the Apache Software License,
+ * The Sun (tm) Java Advanced Imaging library (JAI), The Sun JIMI library
+ * (or with modified versions of the above that use the same license as the above),
+ * and distribute linked combinations including the two.  You must obey the
+ * GNU General Public License in all respects for all of the code used other than
+ * the above mentioned libraries.  If you modify this file, you may extend this
+ * exception to your version of the file, but you are not obligated to do so.
+ * If you do not wish to do so, delete this exception statement from your version.
+ */
+package mir.generator;
+
+import java.io.PrintWriter;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Vector;
+
+import mir.log.LoggerWrapper;
+import mir.util.GeneratorFormatAdapters;
+import mir.util.RewindableIterator;
+
+import org.apache.commons.beanutils.MethodUtils;
+import org.apache.commons.beanutils.PropertyUtils;
+
+import freemarker.template.FileTemplateCache;
+import freemarker.template.SimpleScalar;
+import freemarker.template.Template;
+import freemarker.template.TemplateHashModel;
+import freemarker.template.TemplateListModel;
+import freemarker.template.TemplateMethodModel;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.template.TemplateModelRoot;
+import freemarker.template.TemplateScalarModel;
+
+
+public class FreemarkerGenerator implements Generator {
+  private Template template;
+
+  public FreemarkerGenerator(Template aTemplate) {
+    template = aTemplate;
+  }
+
+  public void generate(Object anOutputWriter, Map aValues, LoggerWrapper aLogger) throws GeneratorExc, GeneratorFailure {
+    if (!(anOutputWriter instanceof PrintWriter))
+      throw new GeneratorExc("Writer for a FreemarkerGenerator must be a PrintWriter");
+
+    try {
+      template.process((TemplateModelRoot) makeMapAdapter(aValues), (PrintWriter) anOutputWriter);
+    }
+    catch (Throwable t) {
+      t.printStackTrace();
+      aLogger.error("Exception occurred: "+t.getMessage());
+      t.printStackTrace(aLogger.asPrintWriter(LoggerWrapper.DEBUG_MESSAGE));
+      throw new GeneratorFailure( t );
+    }
+  }
+
+  private static TemplateScalarModel makeStringAdapter(String aString) {
+    return new SimpleScalar(aString);
+  }
+
+  private static TemplateHashModel makeMapAdapter(Map aMap)  {
+    return new MapAdapter(aMap);
+  }
+
+  private static TemplateListModel makeIteratorAdapter(Iterator anIterator) {
+    return new IteratorAdapter(anIterator);
+  }
+
+  private static TemplateMethodModel makeFunctionAdapter(Generator.GeneratorFunction aFunction) {
+    return new FunctionAdapter(aFunction);
+  }
+
+  private static TemplateHashModel makeBeanAdapter(Object anObject)  {
+    return new BeanAdapter(anObject);
+  }
+
+  public static TemplateModel makeAdapter(Object anObject) throws TemplateModelException {
+    if (anObject == null)
+      return null;
+    if (anObject instanceof TemplateModel)
+      return (TemplateModel) anObject;
+    else if (anObject instanceof Generator.GeneratorFunction)
+      return makeFunctionAdapter((Generator.GeneratorFunction) anObject);
+    else if (anObject instanceof Integer)
+      return makeStringAdapter(((Integer) anObject).toString());
+    else if (anObject instanceof Boolean) {
+      if (((Boolean) anObject).booleanValue())
+        return makeStringAdapter("1");
+      else
+        return makeStringAdapter("0");
+    }
+    else if (anObject instanceof String)
+      return makeStringAdapter((String) anObject);
+    else if (anObject instanceof Map)
+      return makeMapAdapter((Map) anObject);
+    else if (anObject instanceof Iterator)
+      return makeIteratorAdapter((Iterator) anObject);
+    else if (anObject instanceof List)
+      return makeIteratorAdapter(((List) anObject).iterator());
+               else if (anObject instanceof Object[]){
+                       if(((Object[])anObject).length <= 1){
+                               Object[] array = (Object[]) anObject;                           
+                               return makeAdapter(array[0]);
+                       } else {
+                               return makeIteratorAdapter(Arrays.asList((Object[]) anObject).iterator());                              
+                       }
+               }
+    else if (anObject instanceof Number)
+      return makeAdapter(new GeneratorFormatAdapters.NumberFormatAdapter((Number) anObject));
+    else if (anObject instanceof Date)
+      return makeAdapter(new GeneratorFormatAdapters.DateFormatAdapter((Date) anObject));
+    else
+      return makeBeanAdapter(anObject);
+  }
+
+  private static class MapAdapter implements TemplateModelRoot {
+    private Map map;
+    private Map valuesCache;
+
+    private MapAdapter(Map aMap) {
+      map = aMap;
+      valuesCache = new HashMap();
+    }
+
+    public void put(String aKey, TemplateModel aModel) {
+      valuesCache.put(aKey, aModel);
+    }
+
+    public void remove(String aKey) {
+    }
+
+    public boolean isEmpty() {
+      return map.isEmpty();
+    }
+
+    public TemplateModel get(String aKey) throws TemplateModelException {
+      try {
+        if (!valuesCache.containsKey(aKey)) {
+          Object value = map.get(aKey);
+
+          if (value == null && !map.containsKey(aKey)) {
+            throw new TemplateModelException("MapAdapter: no key "+aKey+" available");
+          }
+
+          valuesCache.put(aKey, makeAdapter(value));
+        }
+
+        return (TemplateModel) valuesCache.get(aKey);
+      }
+      catch (TemplateModelException e) {
+        throw e;
+      }
+      catch (Throwable t) {
+        throw new TemplateModelException(t.getMessage());
+      }
+    }
+  }
+
+  private static class IteratorAdapter implements TemplateListModel {
+    private Iterator iterator;
+    private List valuesCache;
+    private int position;
+
+    private IteratorAdapter(Iterator anIterator) {
+      iterator = anIterator;
+
+      valuesCache = new Vector();
+      position=0;
+
+
+      if (iterator instanceof RewindableIterator) {
+        ((RewindableIterator) iterator).rewind();
+      }
+    }
+
+    public boolean isEmpty() {
+      return valuesCache.isEmpty() && !iterator.hasNext();
+    }
+
+    private void getUntil(int anIndex) throws TemplateModelException {
+      while (valuesCache.size()<=anIndex && iterator.hasNext())
+      {
+        valuesCache.add(makeAdapter(iterator.next()));
+      }
+    };
+
+    public TemplateModel get(int anIndex) throws TemplateModelException {
+      TemplateModel result;
+
+      getUntil(anIndex);
+
+      if (anIndex<valuesCache.size())
+      {
+        result = (TemplateModel) valuesCache.get(anIndex);
+
+        return result;
+      }
+      else
+        throw new TemplateModelException( "Iterator out of bounds" );
+    }
+
+    public boolean hasNext() {
+      return position<valuesCache.size() || iterator.hasNext();
+    }
+
+    public boolean isRewound() {
+      return position==0;
+    }
+
+    public TemplateModel next() throws TemplateModelException {
+      TemplateModel result;
+
+      if (hasNext()) {
+        result = get(position);
+        position++;
+      }
+      else
+        throw new TemplateModelException( "Iterator out of bounds" );
+
+      return result;
+    }
+
+    public void rewind() {
+      position=0;
+    }
+  }
+
+  private static class ListAdapter implements TemplateListModel {
+    List list;
+    List valuesCache;
+    int position;
+
+    private ListAdapter(List aList) {
+      list = aList;
+      valuesCache = new Vector();
+      position=0;
+    }
+
+    public boolean isEmpty() {
+      return list.isEmpty();
+    }
+
+    public TemplateModel get(int i) throws TemplateModelException {
+
+      if (i>=valuesCache.size() && i<list.size()) {
+        for(int j=valuesCache.size(); j<=i; j++) {
+          valuesCache.add(makeAdapter(list.get(j)));
+        }
+      }
+
+      if (i<valuesCache.size())
+        return (TemplateModel) valuesCache.get(i);
+      else
+        throw new TemplateModelException( "Iterator out of bounds" );
+    }
+
+    public boolean hasNext() {
+      return position<list.size();
+    }
+
+    public boolean isRewound() {
+      return position==0;
+    }
+
+    public TemplateModel next() throws TemplateModelException {
+      TemplateModel result;
+
+      if (hasNext()) {
+        result = get(position);
+        position++;
+      }
+      else {
+        throw new TemplateModelException( "Iterator out of bounds" );
+      }
+
+      return result;
+    }
+
+    public void rewind() {
+      position = 0;
+    }
+  }
+
+  private static class FunctionAdapter implements TemplateMethodModel {
+    private Generator.GeneratorFunction function;
+
+    public FunctionAdapter(Generator.GeneratorFunction aFunction) {
+      function = aFunction;
+    }
+
+    public TemplateModel exec(List anArguments) throws TemplateModelException {
+      try {
+        return makeAdapter(function.perform(anArguments));
+      }
+      catch (Throwable t) {
+        throw new TemplateModelException(t.getMessage());
+      }
+    }
+
+    public boolean isEmpty() {
+      return false;
+    }
+
+  }
+
+  private static class BeanAdapter implements TemplateHashModel {
+    private Object object;
+
+    public BeanAdapter(Object anObject) {
+      object = anObject;
+    }
+
+    public void put(String aKey, TemplateModel aModel)  throws TemplateModelException  {
+      throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.put not supported");
+    }
+
+    public void remove(String aKey) throws TemplateModelException  {
+      throw new TemplateModelException("FreemarkerGenerator$BeanAdapter.remove not supported");
+    }
+
+    public boolean isEmpty() {
+      return false;
+    }
+
+    public TemplateModel get(String aKey) throws TemplateModelException {
+      try {
+        if (PropertyUtils.isReadable(object, aKey))
+          return makeAdapter(PropertyUtils.getSimpleProperty(object, aKey));
+        else
+          return makeAdapter(MethodUtils.invokeExactMethod(object, "get", aKey));
+      }
+      catch (Throwable t) {
+        throw new TemplateModelException(t.getMessage());
+      }
+    }
+  }
+
+  public static class FreemarkerGeneratorLibrary implements GeneratorLibrary {
+    private FileTemplateCache templateCache;
+
+    public FreemarkerGeneratorLibrary(String aTemplateRoot) {
+      templateCache = new FileTemplateCache( aTemplateRoot+"/" );
+      templateCache.setLoadingPolicy(FileTemplateCache.LOAD_ON_DEMAND);
+    }
+
+    public Generator makeGenerator(String anIdentifier) throws GeneratorExc, GeneratorFailure {
+      Template template = (Template) templateCache.getItem(anIdentifier, "template");
+
+      if (template==null) {
+        throw new GeneratorExc("FreemarkerGeneratorLibrary: Can't find template "+templateCache.getDirectory()+anIdentifier);
+      }
+
+      return new FreemarkerGenerator(template);
+    }
+  }
+
+  public static class FreemarkerGeneratorLibraryFactory implements GeneratorLibraryFactory {
+    private String basePath;
+
+    public FreemarkerGeneratorLibraryFactory(String aBasePath) {
+      basePath = aBasePath;
+    }
+
+    public GeneratorLibrary makeLibrary(String anInitializationString) {
+      return new FreemarkerGeneratorLibrary(basePath+anInitializationString);
+    };
+  }
+}