You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by he...@apache.org on 2007/11/14 23:42:43 UTC

svn commit: r595103 - in /velocity/texen/trunk/src/java/org/apache/texen: Texen.java ant/TexenTask.java

Author: henning
Date: Wed Nov 14 14:42:43 2007
New Revision: 595103

URL: http://svn.apache.org/viewvc?rev=595103&view=rev
Log:
Factor out the actual Texen engine from the Ant task. Use Contributor and Log to tie the engine back into the task.

Added:
    velocity/texen/trunk/src/java/org/apache/texen/Texen.java   (with props)
Modified:
    velocity/texen/trunk/src/java/org/apache/texen/ant/TexenTask.java

Added: velocity/texen/trunk/src/java/org/apache/texen/Texen.java
URL: http://svn.apache.org/viewvc/velocity/texen/trunk/src/java/org/apache/texen/Texen.java?rev=595103&view=auto
==============================================================================
--- velocity/texen/trunk/src/java/org/apache/texen/Texen.java (added)
+++ velocity/texen/trunk/src/java/org/apache/texen/Texen.java Wed Nov 14 14:42:43 2007
@@ -0,0 +1,431 @@
+package org.apache.texen;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.io.File;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.apache.commons.collections.set.ListOrderedSet;
+import org.apache.commons.lang.StringUtils;
+import org.apache.tools.ant.BuildException;
+import org.apache.velocity.VelocityContext;
+import org.apache.velocity.app.VelocityEngine;
+import org.apache.velocity.context.Context;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
+
+/**
+ * This is the Texen engine.
+ *
+ * @author <a href="mailto:dev@velocity.apache.org">The Velocity developers community</a>
+ * @version $Id$
+ */
+public class Texen
+        implements TexenContributor
+{
+    private String templatePath = null;
+
+    private boolean useClasspath = false;
+
+    private boolean useResourceLoaderCache = false;
+
+    private int resourceLoaderModificationCheckInterval = 2;
+
+    private String logFile = null;
+
+    private File outputDirectory = null;
+
+    private String inputEncoding = "ISO-8859-1";
+
+    private String outputEncoding = "ISO-8859-1";
+
+    private String outputFileName = null;
+
+    private TexenLog log = null;
+
+    private String controlTemplate = null;
+
+    private Set contributors = ListOrderedSet.decorate(new HashSet());
+
+    private final VelocityEngine ve;
+
+    /**
+     * Returns a new Texen engine.
+     */
+    public Texen()
+    {
+        ve = new VelocityEngine();
+        contributors.add(this);
+    }
+
+    /**
+     * Set the input file encoding.
+     *
+     * @param inputEncoding Input file encoding. Must be a valid code set name.
+     */
+    public void setInputEncoding(final String inputEncoding)
+    {
+        this.inputEncoding = inputEncoding;
+    }
+
+    /**
+     * Set the name of the report logfile.
+     *
+     * @param logFile Name of the report logfile.
+     */
+    public void setLogFile(final String logFile)
+    {
+        this.logFile = logFile;
+    }
+
+    /**
+     * Set the output directory.
+     * @param outputDirectory A File object representing a directory. The directory must exists.
+     */
+    public void setOutputDirectory(final File outputDirectory)
+    {
+        this.outputDirectory = outputDirectory;
+    }
+
+    /**
+     * Set the output file encoding.
+     *
+     * @param outputEncoding Output file encoding. Must be a valid code set name.
+     */
+    public void setOutputEncoding(final String outputEncoding)
+    {
+        this.outputEncoding = outputEncoding;
+    }
+
+    /**
+     * Set the name of the Output File.
+     * @param outputFileName The name of the output file.
+     */
+    public void setOutputFileName(final String outputFileName)
+    {
+        this.outputFileName = outputFileName;
+    }
+
+    /**
+     * Sets the interval in which the resource loader will check for modifications of the source files.
+     *
+     * @param resourceLoaderModificationCheckInterval The interval in seconds.
+     */
+    public void setResourceLoaderModificationCheckInterval(
+            final int resourceLoaderModificationCheckInterval)
+    {
+        this.resourceLoaderModificationCheckInterval = resourceLoaderModificationCheckInterval;
+    }
+
+    /**
+     * Set the path for the templates to be used for rendering.
+     *
+     * @param templatePath The path for the templates to render.
+     */
+    public void setTemplatePath(final String templatePath)
+    {
+        this.templatePath = templatePath;
+    }
+
+    /**
+     * Set this value to true if you want to load templates and properties form
+     * the classpath.
+     *
+     * @param useClasspath True if you want to load templates and property files from the classpath.
+     */
+    public void setUseClasspath(final boolean useClasspath)
+    {
+        this.useClasspath = useClasspath;
+    }
+
+    /**
+     * Set this value to true if you want to cache the loaded templates.
+     *
+     * @param useResourceLoaderCache True if you want to cache the loaded templates.
+     */
+    public void setUseResourceLoaderCache(final boolean useResourceLoaderCache)
+    {
+        this.useResourceLoaderCache = useResourceLoaderCache;
+    }
+
+    protected String getInputEncoding()
+    {
+        return inputEncoding;
+    }
+
+    protected String getLogFile()
+    {
+        return logFile;
+    }
+
+    protected File getOutputDirectory()
+    {
+        return outputDirectory;
+    }
+
+    protected String getOutputEncoding()
+    {
+        return outputEncoding;
+    }
+
+    protected String getOutputFileName()
+    {
+        return outputFileName;
+    }
+
+    protected int getResourceLoaderModificationCheckInterval()
+    {
+        return resourceLoaderModificationCheckInterval;
+    }
+
+    protected String getTemplatePath()
+    {
+        return templatePath;
+    }
+
+    protected boolean isUseClasspath()
+    {
+        return useClasspath;
+    }
+
+    protected boolean isUseResourceLoaderCache()
+    {
+        return useResourceLoaderCache;
+    }
+
+    protected TexenLog getLog()
+    {
+        return log;
+    }
+
+    public void setLog(final TexenLog log)
+    {
+        this.log = log;
+    }
+
+    /**
+     * Sets the name of the control template.
+     *
+     * @param controlTemplate Name of the control template.
+     */
+    public void setControlTemplate(final String controlTemplate)
+    {
+        this.controlTemplate = controlTemplate;
+    }
+
+    public String getControlTemplate()
+    {
+        return controlTemplate;
+    }
+
+    /**
+     * Add all contributors in a given collection.
+     *
+     * @param contributors A collection containing Contributor elements.
+     */
+    public void addContributors(final Collection contributors)
+    {
+        this.contributors.addAll(contributors);
+    }
+
+
+    public Collection getContributors()
+    {
+        return contributors;
+    }
+
+    /**
+     * Initialize the Texen engine. Can be overridden by
+     * subclasses.
+     */
+    protected void init() throws Exception
+    {
+        setVelocityEngineProperties(ve);
+        ve.init();
+    }
+
+    /**
+     * Execute the input script with Velocity
+     *
+     * @throws BuildException
+     * BuildExceptions are thrown when required attributes are missing.
+     * Exceptions thrown by Velocity are rethrown as BuildExceptions.
+     */
+    public void execute()
+        throws Exception
+    {
+        init();
+
+        // Create the text generator.
+        Generator generator = Generator.getInstance();
+        generator.setVelocityEngine(ve);
+        generator.setOutputPath(getOutputDirectory());
+        generator.setInputEncoding(getInputEncoding());
+        generator.setOutputEncoding(getOutputEncoding());
+
+        if (getTemplatePath() != null)
+        {
+            generator.setTemplatePath(getTemplatePath());
+        }
+
+        // Make sure the output directory exists, if it doesn't
+        // then create it.
+        if (!getOutputDirectory().exists())
+        {
+            getOutputDirectory().mkdirs();
+        }
+
+        File path = new File(getOutputDirectory(), getOutputFileName());
+        getLog().info("Generating to file " + path);
+        Writer writer = generator.getWriter(path, getOutputEncoding());
+
+        // The generator and the output path should
+        // be placed in the init context here and
+        // not in the generator class itself.
+        Context c = initContext();
+
+        // Everything in the generator class should be
+        // pulled out and placed in here. What the generator
+        // class does can probably be added to the Velocity
+        // class and the generator class can probably
+        // be removed all together.
+        runPopulateContext(c);
+
+        writer.write(generator.parse(getControlTemplate(), c));
+        writer.flush();
+        writer.close();
+        generator.shutdown();
+
+        runCleanup();
+    }
+
+    protected Context initContext()
+    {
+        return new VelocityContext();
+    }
+
+    /**
+     * <p>Place useful objects into the initial context.</p>
+     *
+     * <p>TexenTask places <code>Date().toString()</code> into the
+     * context as <code>$now</code>.  Subclasses who want to vary the
+     * objects in the context should override this method.</p>
+     *
+     * <p><code>$generator</code> is not put into the context in this
+     * method.</p>
+     *
+     * @param context The context to populate, as retrieved from
+     * {@link #initControlContext()}.
+     *
+     * @throws Exception Error while populating context.  The {@link
+     * #execute()} method will catch and rethrow as a
+     * <code>BuildException</code>.
+     */
+    protected void runPopulateContext(final Context context)
+        throws Exception
+    {
+        for (Iterator it = contributors.iterator(); it.hasNext(); )
+        {
+            TexenContributor contributor = (TexenContributor) it.next();
+            contributor.populateContext(context);
+        }
+    }
+
+    protected void runCleanup()
+    {
+        for (Iterator it = contributors.iterator(); it.hasNext(); )
+        {
+            TexenContributor contributor = (TexenContributor) it.next();
+            contributor.cleanup();
+        }
+    }
+
+    /**
+     * A hook method called at the end of {@link #execute()} which can
+     * be overridden to perform any necessary cleanup activities (such
+     * as the release of database connections, etc.).  By default,
+     * does nothing.
+     *
+     * @exception Exception Problem cleaning up.
+     */
+    public void cleanup()
+    {
+    }
+
+    /**
+     * <p>Place useful objects into the initial context.</p>
+     *
+     * <p>Texen places a Date object into the
+     * context as <code>$now</code>.  Subclasses who want to vary the
+     * objects in the context should override this method.</p>
+     *
+     * <p><code>$generator</code> is not put into the context in this
+     * method.</p>
+     *
+     * @param context The context to populate, as retrieved from
+     * {@link #initControlContext()}.
+     *
+     * @throws Exception Error while populating context.  The {@link
+     * #execute()} method will catch and rethrow as a
+     * <code>BuildException</code>.
+     */
+    public void populateContext(final Context context)
+    {
+        context.put("now", new Date());
+    }
+
+    /**
+     * Set the properties of the velocity engine.
+     * This may be overridden by a subclass.
+     *
+     * @param ve the velocity engine which properties should be set, not null.
+     *
+     * @throws NullPointerException if ve is null.
+     */
+    protected void setVelocityEngineProperties(VelocityEngine ve)
+    {
+        // Setup the Velocity Runtime.
+        if (StringUtils.isNotEmpty(getTemplatePath()))
+        {
+            ve.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, getTemplatePath());
+        }
+
+        if (isUseClasspath())
+        {
+            getLog().info("Using classpath");
+            ve.addProperty(VelocityEngine.RESOURCE_LOADER, "classpath");
+
+            String prefix = "classpath." + VelocityEngine.RESOURCE_LOADER;
+            ve.setProperty(prefix + ".class", ClasspathResourceLoader.class.getName());
+            ve.setProperty(prefix + ".cache", Boolean.valueOf(isUseResourceLoaderCache()));
+            ve.setProperty(prefix + ".modificationCheckInterval", String.valueOf(getResourceLoaderModificationCheckInterval()));
+        }
+
+        if (StringUtils.isNotEmpty(getLogFile()))
+        {
+            ve.setProperty(RuntimeConstants.RUNTIME_LOG, getLogFile());
+        }
+    }
+}

Propchange: velocity/texen/trunk/src/java/org/apache/texen/Texen.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/texen/trunk/src/java/org/apache/texen/Texen.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Modified: velocity/texen/trunk/src/java/org/apache/texen/ant/TexenTask.java
URL: http://svn.apache.org/viewvc/velocity/texen/trunk/src/java/org/apache/texen/ant/TexenTask.java?rev=595103&r1=595102&r2=595103&view=diff
==============================================================================
--- velocity/texen/trunk/src/java/org/apache/texen/ant/TexenTask.java (original)
+++ velocity/texen/trunk/src/java/org/apache/texen/ant/TexenTask.java Wed Nov 14 14:42:43 2007
@@ -16,31 +16,33 @@
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  * KIND, either express or implied.  See the License for the
  * specific language governing permissions and limitations
- * under the License.    
+ * under the License.
  */
 
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
-import java.io.Writer;
-import java.util.Date;
+import java.util.Collections;
 import java.util.Iterator;
-import java.util.StringTokenizer;
 
 import org.apache.commons.collections.ExtendedProperties;
+import org.apache.commons.collections.iterators.ArrayIterator;
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.StringUtils;
+import org.apache.texen.Texen;
+import org.apache.texen.TexenContributor;
+import org.apache.texen.TexenFileResolver;
+import org.apache.texen.TexenLog;
+import org.apache.texen.TexenUtils;
 import org.apache.tools.ant.BuildException;
 import org.apache.tools.ant.Project;
 import org.apache.tools.ant.Task;
 import org.apache.velocity.VelocityContext;
-import org.apache.velocity.app.VelocityEngine;
 import org.apache.velocity.context.Context;
 import org.apache.velocity.exception.MethodInvocationException;
 import org.apache.velocity.exception.ParseErrorException;
 import org.apache.velocity.exception.ResourceNotFoundException;
-import org.apache.velocity.runtime.RuntimeConstants;
-import org.apache.texen.Generator;
-import org.apache.velocity.util.StringUtils;
 
 /**
  * An ant task for generating output by using Velocity
@@ -50,18 +52,10 @@
  * @version $Id$
  */
 public class TexenTask
-    extends Task
+        extends Task
+        implements TexenLog, TexenContributor
 {
     /**
-     * This message fragment (telling users to consult the log or
-     * invoke ant with the -debug flag) is appended to rethrown
-     * exception messages.
-     */
-    private final static String ERR_MSG_FRAGMENT =
-        ". For more information consult the velocity log, or invoke ant " +
-        "with the -debug flag.";
-
-    /**
      * This is the control template that governs the output.
      * It may or may not invoke the services of worker
      * templates.
@@ -72,13 +66,13 @@
      * This is where Velocity will look for templates
      * using the file template loader.
      */
-    protected String templatePath;
+    protected File [] templatePaths = new File [0];
 
     /**
      * This is where texen will place all the output
      * that is a product of the generation process.
      */
-    protected String outputDirectory;
+    protected File outputDirectory;
 
     /**
      * This is the file where the generated text
@@ -89,13 +83,13 @@
     /**
      * This is the encoding for the output file(s).
      */
-    protected String outputEncoding;
+    protected String outputEncoding = "ISO-8859-1";
 
     /**
      * This is the encoding for the input file(s)
      * (templates).
      */
-    protected String inputEncoding;
+    protected String inputEncoding = "ISO-8859-1";
 
     /**
      * <p>
@@ -129,29 +123,26 @@
      * So initial context values can be set with
      * properties file.
      */
-    protected ExtendedProperties contextProperties;
+    protected ExtendedProperties contextProperties = new ExtendedProperties();
 
     /**
      * Property which controls whether the classpath
      * will be used when trying to locate templates.
      */
-    protected boolean useClasspath;
+    protected boolean useClasspath = false;
 
     /**
      * The LogFile (incl. path) to log to.
      */
-    protected String logFile;
+    protected String logFile = null;
 
     /**
      *   Property which controls whether the resource
      *   loader will be told to cache.  Default false
      */
+    protected boolean useResourceLoaderCache = false;
 
-    protected String useResourceLoaderCache = "false";
-    /**
-     *
-     */
-    protected String resourceLoaderModificationCheckInterval = "2";
+    protected int resourceLoaderModificationCheckInterval = 2;
 
     /**
      * [REQUIRED] Set the control template for the
@@ -164,41 +155,21 @@
     }
 
     /**
-     * Get the control template for the
-     * generating process.
-     * @return The current control template.
-     */
-    public String getControlTemplate()
-    {
-        return controlTemplate;
-    }
-
-    /**
      * [REQUIRED] Set the path where Velocity will look
      * for templates using the file template
      * loader.
      * @param templatePath
      * @throws Exception
      */
-
     public void setTemplatePath(String templatePath) throws Exception
     {
-        StringBuffer resolvedPath = new StringBuffer();
-        StringTokenizer st = new StringTokenizer(templatePath, ",");
-        while ( st.hasMoreTokens() )
+        String [] paths = StringUtils.stripAll(StringUtils.split(templatePath, ','));
+        templatePaths = new File [paths.length];
+
+        for (int i = 0 ; i < paths.length; i++)
         {
-            // resolve relative path from basedir and leave
-            // absolute path untouched.
-            File fullPath = project.resolveFile(st.nextToken());
-            resolvedPath.append(fullPath.getCanonicalPath());
-            if ( st.hasMoreTokens() )
-            {
-                resolvedPath.append(",");
-            }
+            templatePaths[i] = new File(paths[i]);
         }
-        this.templatePath = resolvedPath.toString();
-
-        System.out.println(templatePath);
      }
 
     /**
@@ -208,34 +179,31 @@
      * @return The template path.
      */
     public String getTemplatePath()
+        throws IOException
     {
-        return templatePath;
-    }
+        StringBuffer sb = new StringBuffer();
 
-    /**
-     * [REQUIRED] Set the output directory. It will be
-     * created if it doesn't exist.
-     * @param outputDirectory
-     */
-    public void setOutputDirectory(File outputDirectory)
-    {
-        try
+        for (Iterator it = new ArrayIterator(templatePaths); it.hasNext(); )
         {
-            this.outputDirectory = outputDirectory.getCanonicalPath();
-        }
-        catch (java.io.IOException ioe)
-        {
-            throw new BuildException(ioe);
+            File path = (File) it.next();
+            sb.append(path.getCanonicalPath());
+            if (it.hasNext())
+            {
+                sb.append(", ");
+            }
         }
+
+        return sb.toString();
     }
 
     /**
-     * Get the output directory.
-     * @return The output directory.
+     * [REQUIRED] Set the output directory. It will be
+     * created if it doesn't exist.
+     * @param outputDirectory
      */
-    public String getOutputDirectory()
+    public void setOutputDirectory(final File outputDirectory)
     {
-        return outputDirectory;
+        this.outputDirectory = outputDirectory;
     }
 
     /**
@@ -243,7 +211,7 @@
      * generation process.
      * @param outputFile
      */
-    public void setOutputFile(String outputFile)
+    public void setOutputFile(final String outputFile)
     {
         this.outputFile = outputFile;
     }
@@ -252,7 +220,7 @@
      * Set the output encoding.
      * @param outputEncoding
      */
-    public void setOutputEncoding(String outputEncoding)
+    public void setOutputEncoding(final String outputEncoding)
     {
         this.outputEncoding = outputEncoding;
     }
@@ -261,37 +229,18 @@
      * Set the input (template) encoding.
      * @param inputEncoding
      */
-    public void setInputEncoding(String inputEncoding)
+    public void setInputEncoding(final String inputEncoding)
     {
         this.inputEncoding = inputEncoding;
     }
 
     /**
-     * Get the output file for the
-     * generation process.
-     * @return The output file.
-     */
-    public String getOutputFile()
-    {
-        return outputFile;
-    }
-
-    /**
      * Sets the log file.
      * @param log
      */
-    public void setLogFile(String log)
-    {
-        this.logFile = log;
-    }
-
-    /**
-     * Gets the log file.
-     * @return The log file.
-     */
-    public String getLogFile()
+    public void setLogFile(final String logFile)
     {
-        return this.logFile;
+        this.logFile = logFile;
     }
 
     /**
@@ -300,10 +249,10 @@
      * generating process starts.
      * @param file
      */
-    public void setContextProperties( String file )
+    public void setContextProperties(final String file)
+        throws IOException
     {
         String[] sources = StringUtils.split(file,",");
-        contextProperties = new ExtendedProperties();
 
         // Always try to get the context properties resource
         // from a file first. Templates may be taken from a JAR
@@ -314,60 +263,47 @@
         for (int i = 0; i < sources.length; i++)
         {
             ExtendedProperties source = new ExtendedProperties();
+            InputStream inputStream = null;
 
             try
             {
                 // resolve relative path from basedir and leave
                 // absolute path untouched.
-                File fullPath = project.resolveFile(sources[i]);
-                log("Using contextProperties file: " + fullPath);
-                source.load(new FileInputStream(fullPath));
-            }
-            catch (IOException e)
-            {
-                ClassLoader classLoader = this.getClass().getClassLoader();
+                File fullPath = getProject().resolveFile(sources[i]);
 
-                try
+                if (fullPath.exists() && fullPath.isFile())
                 {
-                    InputStream inputStream = classLoader.getResourceAsStream(sources[i]);
-
-                    if (inputStream == null)
-                    {
-                        throw new BuildException("Context properties file " + sources[i] +
-                            " could not be found in the file system or on the classpath!");
-                    }
-                    else
-                    {
-                        source.load(inputStream);
-                    }
+                    log("Using contextProperties file: " + fullPath);
+                    inputStream = new FileInputStream(fullPath);
                 }
-                catch (IOException ioe)
+                else
                 {
-                    source = null;
+                    ClassLoader classLoader = this.getClass().getClassLoader();
+                    inputStream = classLoader.getResourceAsStream(sources[i]);
+                }
+
+                if (inputStream == null)
+                {
+                    throw new BuildException("Context properties file " + sources[i] + " could not be found in the file system or on the classpath!");
                 }
-            }
 
-            if (source != null)
+                source.load(inputStream);
+            }
+            finally
             {
-                for (Iterator j = source.getKeys(); j.hasNext(); )
+                if (inputStream != null)
                 {
-                    String name = (String) j.next();
-                    String value = StringUtils.nullTrim(source.getString(name));
-                    contextProperties.setProperty(name,value);
+                    inputStream.close();
                 }
             }
-        }
-    }
 
-    /**
-     * Get the context properties that will be
-     * fed into the initial context be the
-     * generating process starts.
-     * @return The current context properties.
-     */
-    public ExtendedProperties getContextProperties()
-    {
-        return contextProperties;
+            for (Iterator it = source.getKeys(); it.hasNext(); )
+            {
+                String name = (String) it.next();
+                String value = StringUtils.trim(source.getString(name));
+                contextProperties.setProperty(name, value);
+            }
+        }
     }
 
     /**
@@ -375,7 +311,7 @@
      *
      * @param useClasspath true means the classpath will be used.
      */
-    public void setUseClasspath(boolean useClasspath)
+    public void setUseClasspath(final boolean useClasspath)
     {
         this.useClasspath = useClasspath;
     }
@@ -383,7 +319,7 @@
     /**
      * @param useResourceLoaderCache
      */
-    public void setUseResourceLoaderCache(String useResourceLoaderCache)
+    public void setUseResourceLoaderCache(final boolean useResourceLoaderCache)
     {
         this.useResourceLoaderCache = useResourceLoaderCache;
     }
@@ -391,10 +327,12 @@
     /**
      * @param resourceLoaderModificationCheckInterval
      */
-    public void setResourceLoaderModificationCheckInterval(String resourceLoaderModificationCheckInterval)
+    public void setResourceLoaderModificationCheckInterval(final int resourceLoaderModificationCheckInterval)
     {
         this.resourceLoaderModificationCheckInterval = resourceLoaderModificationCheckInterval;
     }
+
+
     /**
      * Creates a VelocityContext.
      *
@@ -408,6 +346,16 @@
         return new VelocityContext();
     }
 
+    public void info(final String message)
+    {
+        log(message, Project.MSG_INFO);
+    }
+
+    public void error(final String message)
+    {
+        log(message, Project.MSG_ERR);
+    }
+
     /**
      * Execute the input script with Velocity
      *
@@ -415,11 +363,11 @@
      * BuildExceptions are thrown when required attributes are missing.
      * Exceptions thrown by Velocity are rethrown as BuildExceptions.
      */
-    public void execute ()
+    public void execute()
         throws BuildException
     {
         // Make sure the template path is set.
-        if (templatePath == null && useClasspath == false)
+        if (ArrayUtils.isEmpty(templatePaths) && useClasspath == false)
         {
             throw new BuildException(
                 "The template path needs to be defined if you are not using " +
@@ -444,230 +392,75 @@
             throw new BuildException("The output file needs to be defined!");
         }
 
-        VelocityEngine ve = new VelocityEngine();
-
         try
         {
-            setVelocityEngineProperties(ve);
-            ve.init();
-
-            // Create the text generator.
-            Generator generator = createGenerator();
-            generator.setVelocityEngine(ve);
-            generator.setOutputPath(outputDirectory);
-            generator.setInputEncoding(inputEncoding);
-            generator.setOutputEncoding(outputEncoding);
-
-            if (templatePath != null)
-            {
-                generator.setTemplatePath(templatePath);
-            }
-
-            // Make sure the output directory exists, if it doesn't
-            // then create it.
-            File file = new File(outputDirectory);
-            if (! file.exists())
-            {
-                file.mkdirs();
-            }
-
-            String path = outputDirectory + File.separator + outputFile;
-            log("Generating to file " + path, Project.MSG_INFO);
-            Writer writer = generator.getWriter(path, outputEncoding);
-
-            // The generator and the output path should
-            // be placed in the init context here and
-            // not in the generator class itself.
-            Context c = initControlContext();
-
-            // Everything in the generator class should be
-            // pulled out and placed in here. What the generator
-            // class does can probably be added to the Velocity
-            // class and the generator class can probably
-            // be removed all together.
-            populateInitialContext(c);
-
-            // Feed all the options into the initial
-            // control context so they are available
-            // in the control/worker templates.
-            if (contextProperties != null)
-            {
-                Iterator i = contextProperties.getKeys();
-
-                while (i.hasNext())
-                {
-                    String property = (String) i.next();
-                    String value = StringUtils.nullTrim(contextProperties.getString(property));
-
-                    // Now lets quickly check to see if what
-                    // we have is numeric and try to put it
-                    // into the context as an Integer.
-                    try
-                    {
-                        c.put(property, new Integer(value));
-                    }
-                    catch (NumberFormatException nfe)
-                    {
-                        // Now we will try to place the value into
-                        // the context as a boolean value if it
-                        // maps to a valid boolean value.
-                        String booleanString =
-                            contextProperties.testBoolean(value);
-
-                        if (booleanString != null)
-                        {
-                            c.put(property, Boolean.valueOf(booleanString));
-                        }
-                        else
-                        {
-                            // We are going to do something special
-                            // for properties that have a ".file.contents"
-                            // suffix: for these properties will pull
-                            // in the contents of the file and make
-                            // them available in the context. So for
-                            // a line like the following in a properties file:
-                            //
-                            // license.file.contents = license.txt
-                            //
-                            // We will pull in the contents of license.txt
-                            // and make it available in the context as
-                            // $license. This should make texen a little
-                            // more flexible.
-                            final String FILE_CONTENTS = ".file.contents";
-                            if (property.endsWith(FILE_CONTENTS))
-                            {
-                                // We need to turn the license file from relative to
-                                // absolute, and let Ant help :)
-                                value = StringUtils.fileContentsToString(
-                                    project.resolveFile(value).getCanonicalPath());
-
-                                property = property.substring(
-                                    0, property.indexOf(FILE_CONTENTS));
-                            }
-
-                            c.put(property, value);
-                        }
-                    }
-                }
-            }
+            Texen texen = new Texen();
+            texen.setInputEncoding(inputEncoding);
+            texen.setLogFile(logFile);
+            texen.setOutputDirectory(outputDirectory);
+            texen.setOutputEncoding(outputEncoding);
+            texen.setOutputFileName(outputFile);
+            texen.setResourceLoaderModificationCheckInterval(resourceLoaderModificationCheckInterval);
+            texen.setTemplatePath(getTemplatePath());
+            texen.setUseClasspath(useClasspath);
+            texen.setUseResourceLoaderCache(useResourceLoaderCache);
+            texen.setLog(this);
+            texen.setControlTemplate(controlTemplate);
+            texen.addContributors(Collections.singleton(this));
 
-            writer.write(generator.parse(controlTemplate, c));
-            writer.flush();
-            writer.close();
-            generator.shutdown();
-            cleanup();
+            texen.execute();
         }
-        catch( BuildException e)
-        {
-            throw e;
-        }
-        catch( MethodInvocationException e )
+        catch(MethodInvocationException e)
         {
             throw new BuildException(
-                "Exception thrown by '" + e.getReferenceName() + "." +
-                    e.getMethodName() +"'" + ERR_MSG_FRAGMENT,
-                        e.getWrappedThrowable());
-        }
-        catch( ParseErrorException e )
-        {
-            throw new BuildException("Velocity syntax error" + ERR_MSG_FRAGMENT ,e);
-        }
-        catch( ResourceNotFoundException e )
-        {
-            throw new BuildException("Resource not found" + ERR_MSG_FRAGMENT,e);
-        }
-        catch( Exception e )
-        {
-            throw new BuildException("Generation failed" + ERR_MSG_FRAGMENT ,e);
-        }
-    }
-
-    /** 
-     * Retrieve a new instance of the generator.  This may be overridden by a subclass.
-     * @return
-     */
-    protected Generator createGenerator() 
-    {
-        return Generator.getInstance();
-    }
-
-    /**
-     * Set the properties of the velocity engine.
-     * This may be overridden by a subclass.
-     * 
-     * @param ve the velocity engine which properties should be set, not null.
-     * 
-     * @throws NullPointerException if ve is null.
-     */
-    protected void setVelocityEngineProperties(VelocityEngine ve)
-    {
-        // Setup the Velocity Runtime.
-        if (templatePath != null)
-        {
-            log("Using templatePath: " + templatePath, Project.MSG_VERBOSE);
-            ve.setProperty(
-                RuntimeConstants.FILE_RESOURCE_LOADER_PATH, templatePath);
-        }
-
-        if (useClasspath)
-        {
-            log("Using classpath");
-            ve.addProperty(
-                VelocityEngine.RESOURCE_LOADER, "classpath");
-
-            ve.setProperty(
-                "classpath." + VelocityEngine.RESOURCE_LOADER + ".class",
-                    "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
-
-            ve.setProperty(
-                "classpath." + VelocityEngine.RESOURCE_LOADER +
-                    ".cache", useResourceLoaderCache);
-
-            ve.setProperty(
-                "classpath." + VelocityEngine.RESOURCE_LOADER +
-                    ".modificationCheckInterval", resourceLoaderModificationCheckInterval);
-        }
-
-        if (this.logFile != null)
-        {
-            ve.setProperty(RuntimeConstants.RUNTIME_LOG, this.logFile);
-        }        
+                    "Exception thrown by '" + e.getReferenceName() + "." + e.getMethodName()
+                    + "'. For more information consult the velocity log, or invoke ant with the -debug flag.",
+                    e.getWrappedThrowable());
+         }
+         catch(ParseErrorException e)
+         {
+             throw new BuildException("Velocity syntax error. For more information consult the velocity log, or invoke ant with the -debug flag.", e);
+         }
+         catch(ResourceNotFoundException e)
+         {
+             throw new BuildException("Resource not found. For more information consult the velocity log, or invoke ant with the -debug flag.", e);
+         }
+         catch (RuntimeException re)
+         {
+             throw re;
+         }
+         catch(Exception e)
+         {
+             throw new BuildException("Generation failed. For more information consult the velocity log, or invoke ant with the -debug flag.", e);
+         }
     }
-    
 
     /**
-     * <p>Place useful objects into the initial context.</p>
-     *
-     * <p>TexenTask places <code>Date().toString()</code> into the
-     * context as <code>$now</code>.  Subclasses who want to vary the
-     * objects in the context should override this method.</p>
-     *
-     * <p><code>$generator</code> is not put into the context in this
-     * method.</p>
-     *
-     * @param context The context to populate, as retrieved from
-     * {@link #initControlContext()}.
+     * A hook method called at the end of {@link #execute()} which can
+     * be overridden to perform any necessary cleanup activities (such
+     * as the release of database connections, etc.).  By default,
+     * does nothing.
      *
-     * @throws Exception Error while populating context.  The {@link
-     * #execute()} method will catch and rethrow as a
-     * <code>BuildException</code>.
+     * @exception Exception Problem cleaning up.
      */
-    protected void populateInitialContext(Context context)
-        throws Exception
+    public void cleanup()
     {
-        context.put("now", new Date().toString());
     }
 
     /**
-     * A hook method called at the end of {@link #execute()} which can
-     * be overridden to perform any necessary cleanup activities (such
-     * as the release of database connections, etc.).  By default,
-     * does nothing.
+     * Populate the context with the parameters from the Ant task.
      *
-     * @exception Exception Problem cleaning up.
+     * @see TexenContributor
      */
-    protected void cleanup()
-        throws Exception
+    public void populateContext(final Context context) throws Exception
     {
+        TexenUtils.populateContext(contextProperties, context, new TexenFileResolver()
+            {
+
+                public File resolveFile(String value)
+                {
+                    return getProject().resolveFile(value);
+                }
+            });
     }
 }