You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@ant.apache.org by bu...@apache.org on 2003/01/21 00:49:01 UTC

DO NOT REPLY [Bug 16279] New: - Velocity task

DO NOT REPLY TO THIS EMAIL, BUT PLEASE POST YOUR BUG 
RELATED COMMENTS THROUGH THE WEB INTERFACE AVAILABLE AT
<http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16279>.
ANY REPLY MADE TO THIS MESSAGE WILL NOT BE COLLECTED AND 
INSERTED IN THE BUG DATABASE.

http://nagoya.apache.org/bugzilla/show_bug.cgi?id=16279

Velocity task

           Summary: Velocity task
           Product: Ant
           Version: 1.5.1
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: Enhancement
          Priority: Other
         Component: Optional Tasks
        AssignedTo: ant-dev@jakarta.apache.org
        ReportedBy: hnakamur@v003.vaio.ne.jp


The <velocity> task is meant to be a richer version of filter function
of the <copy> task. I have been using the filter function for replacing
parameters in configuration files. But I need more functionality than
just replacing. I think this is where Velocity comes in. This template
engine has directives like #if and #foreach, so it is more powerful.

The <velocity> task has attributes and nested elements similar to the
<copy> task. I thought it would be easy to use. But not all of <copy>
attributes are supported. Please see the velocity.html in the patch.

Here is a example of calling a <velocity> task:

    <velocity todir="c" propertyfile="velocity.properties">
      <velocitycontext>
        <contextdata key="a" value="A1"/>
        <contextdata key="b" value="B1" if="SOME_PROP"/>
        <contextdata key="b" value="B2" unless="SOME_PROP"/>
        <contextdata key="str" classname="my.package.StringTool"/>
      </velocitycontext>
      <fileset dir="a">
        <include name="**/*.vm"/>
      </fileset>
      <mapper type="glob" from="*.vm" to="*.html"/>
    </velocity>

Opinions and suggestions are welcome.

Here is the patch for the <velocity> task:

diff -ruN jakarta-ant-1.5.1.orig/docs/manual/OptionalTasks/velocity.html 
jakarta-ant-1.5.1/docs/manual/OptionalTasks/velocity.html
--- jakarta-ant-1.5.1.orig/docs/manual/OptionalTasks/velocity.html 1970-01-01 
09:00:00.000000000 +0900
+++ jakarta-ant-1.5.1/docs/manual/OptionalTasks/velocity.html 2003-01-21 
07:39:03.000000000 +0900
@@ -0,0 +1,232 @@
+<html>
+
+<head>
+<meta http-equiv="Content-Language" content="en-us">
+<title>Velocity Task</title>
+</head>
+
+<body>
+
+<h2><a name="velocity">Velocity</a></h2>
+<h3>Description</h3>
+<p>Run <a href="http://jakarta.apache.org/velocity/index.html" 
target="_top">Jakarta-Velocity</a> on a file or FileSet to a new
file or directory.
+By default, files are only copied if the source file is newer than the
+destination file, or when the destination file does not exist.  However,
+you can explicitly overwrite files with the <code>overwrite</code> 
attribute.</p>
+<p><a href="../CoreTypes/fileset.html">FileSet</a>s are used to select a
+set of files to copy.
+To use a <code>&lt;fileset&gt;</code>, the <code>todir</code> attribute
+must be set.</p>
+<h3>Parameters</h3>
+<table border="1" cellpadding="2" cellspacing="0">
+  <tr>
+    <td valign="top"><b>Attribute</b></td>
+    <td valign="top"><b>Description</b></td>
+    <td align="center" valign="top"><b>Required</b></td>
+  </tr>
+  <tr>
+    <td valign="top">propertyfile</td>
+     <td valign="top">The property file which is used to initialize Velocity.
+     </td>
+     <td valign="top" align="center">Yes</td>
+  </tr>
+  <tr>
+    <td valign="top">file</td>
+    <td valign="top">The file to copy.</td>
+    <td valign="top" align="center">Yes, unless a nested
+    <code>&lt;fileset&gt;</code> element is used.</td>
+  </tr>
+  <tr>
+    <td valign="top">tofile</td>
+    <td valign="top">The file to copy to.</td>
+    <td valign="top" align="center" rowspan="2">With the <code>file</code>
+      attribute, either <code>tofile</code> or <code>todir</code> can be used.
+      With nested <code>&lt;fileset&gt;</code> elements, if the set of files
+      is greater than 1, or if only the <code>dir</code> attribute is
+      specified in the <code>&lt;fileset&gt;</code>, or if the
+      <code>file</code> attribute is also specified, then only
+      <code>todir</code> is allowed.</td>
+  </tr>
+  <tr>
+    <td valign="top">todir</td>
+    <td valign="top">The directory to copy to.</td>
+  </tr>
+  <tr>
+    <td valign="top">overwrite</td>
+    <td valign="top">Overwrite existing files even if the destination
+      files are newer.</td>
+    <td valign="top" align="center">No; defaults to false.</td>
+  </tr>
+  <tr>
+    <td valign="top">flatten</td>
+    <td valign="top">Ignore the directory structure of the source files,
+      and copy all files into the directory specified by the <code>todir</code>
+      attribute.  Note that you can achieve the same effect by using a
+      <a href="../CoreTypes/mapper.html#flatten-mapper">flatten 
mapper</a>.</td>
+    <td valign="top" align="center">No; defaults to false.</td>
+  </tr>
+  <tr>
+    <td valign="top">failonerror</td>
+     <td valign="top">Log a warning message, but do not stop the build,
+       when the file to copy does not exist.
+       Only meaningful when copying a single file.
+     </td>
+     <td valign="top" align="center">No; defaults to true.</td>
+  </tr>
+  <tr>
+    <td valign="top">templatebasedir</td>
+     <td valign="top">The template base directory. This is used to
+       caclutate a relative path of a template file. Velocity receives a
+       relative path and searches a template file in Velocity's search paths.
+     </td>
+     <td valign="top" align="center">No; defaults to project basedir.</td>
+  </tr>
+</table>
+<h3>Parameters specified as nested elements</h3>
+
+<h4>velocitycontext</h4>
+ <p>A &lt;velocitycontext&gt; is used to specify the VelocityContext data.
+ It has nested &lt;contextdata&gt; elements.</p>
+
+<h4>contextdata</h4>
+ <p>A &lt;contextdata&gt; is a nested element of &lt;velocitycontext&gt;
+ and is used to specify a VelocityContext datum.</p>
+
+<table border="1" cellpadding="2" cellspacing="0">
+  <tr>
+    <td valign="top"><b>Attribute</b></td>
+    <td valign="top"><b>Description</b></td>
+    <td align="center" valign="top"><b>Required</b></td>
+  </tr>
+  <tr>
+    <td valign="top">key</td>
+     <td valign="top">The key of a VelocityContext datum.
+     </td>
+     <td valign="top" align="center">Yes</td>
+  </tr>
+  <tr>
+    <td valign="top">value</td>
+    <td valign="top">The value of a VelocityContext datum.</td>
+    <td valign="top" align="center" rowspan="2">One of either <var>value</var>
+    or <var>classname</var>.</td>
+  </tr>
+  <tr>
+    <td valign="top">classname</td>
+    <td valign="top">An instance of this classname is used as a VelocityContext
+    datum. The specified class must have the public non-argument constructor.
+    And if the class has a public method
+    <code>setContext(org.apache.velocity.context.Context)</code>,
+    it is invoked by reflection.
+    </td>
+  </tr>
+  <tr>
+    <td valign="top">if</td>
+    <td valign="top">Only use this data if the named property is set.</td>
+    <td align="center" valign="top">No</td>
+  </tr>
+  <tr>
+    <td valign="top">unless</td>
+    <td valign="top">Only use this data if the named property is
+       <b>not</b> set.</td>
+    <td align="center" valign="top">No</td>
+  </tr>
+</table>
+
+<h4>fileset</h4>
+ <p><a href="../CoreTypes/fileset.html">FileSet</a>s are used to select
+sets of files to run velocity on.
+ To use a fileset, the <code>todir</code> attribute must be set.</p>
+
+<h4>mapper</h4>
+ <p>You can define filename transformations by using a nested <a
+ href="../CoreTypes/mapper.html">mapper</a> element. The default mapper used by
+ <code>&lt;velocity&gt;</code> is the <a
+ href="../CoreTypes/mapper.html#identity-mapper">identity mapper</a>.</p>
+
+<h3>Examples</h3>
+<p><b>Run velocity on a single file</b></p>
+<pre>
+  &lt;velocity file=&quot;myfile.txt&quot; tofile=&quot;mycopy.txt&quot;
+      propertyfile=&quot;velocity.properties&quot;&gt;
+    &lt;velocitycontext&gt;
+      &lt;contextdata key=&quot;key1&quot; value=&quot;value1&quot;/&gt;
+      &lt;contextdata key=&quot;key2&quot; 
classname=&quot;some.Class1&quot;/&gt;
+      &lt;contextdata key=&quot;key3&quot; value=&quot;value3&quot; 
if=&quot;SOME_PROPERTY&quot;/&gt;
+      &lt;contextdata key=&quot;key4&quot; value=&quot;value4&quot; 
unless=&quot;SOME_PROPERTY&quot;/&gt;
+    &lt;/velocitycontext&gt;
+  &lt;/velocity&gt;
+</pre>
+<p><b>Run velocity on a single file to a directory</b></p>
+<pre>
+  &lt;velocity file=&quot;myfile.txt&quot; todir=&quot;../some/other/dir&quot;
+      propertyfile=&quot;velocity.properties&quot;&gt;
+    &lt;velocitycontext&gt;
+      &lt;contextdata key=&quot;key1&quot; value=&quot;value1&quot;/&gt;
+      &lt;contextdata key=&quot;key2&quot; 
classname=&quot;some.Class1&quot;/&gt;
+      &lt;contextdata key=&quot;key3&quot; value=&quot;value3&quot; 
if=&quot;SOME_PROPERTY&quot;/&gt;
+      &lt;contextdata key=&quot;key4&quot; value=&quot;value4&quot; 
unless=&quot;SOME_PROPERTY&quot;/&gt;
+    &lt;/velocitycontext&gt;
+  &lt;/velocity&gt;
+</pre>
+<p><b>Run velocity on a set of files to a directory</b></p>
+<pre>
+  &lt;velocity todir=&quot;../dest/dir&quot;
+      propertyfile=&quot;velocity.properties&quot;&gt;
+    &lt;velocitycontext&gt;
+      &lt;contextdata key=&quot;key1&quot; value=&quot;value1&quot;/&gt;
+      &lt;contextdata key=&quot;key2&quot; 
classname=&quot;some.Class1&quot;/&gt;
+      &lt;contextdata key=&quot;key3&quot; value=&quot;value3&quot; 
if=&quot;SOME_PROPERTY&quot;/&gt;
+      &lt;contextdata key=&quot;key4&quot; value=&quot;value4&quot; 
unless=&quot;SOME_PROPERTY&quot;/&gt;
+    &lt;/velocitycontext&gt;
+    &lt;fileset dir=&quot;src_dir&quot;&gt;
+      &lt;exclude name=&quot;**/*.java&quot;/&gt;
+    &lt;/fileset&gt;
+  &lt;/velocity&gt;
+
+  &lt;velocity todir=&quot;../dest/dir&quot;
+      propertyfile=&quot;velocity.properties&quot;&gt;
+    &lt;velocitycontext&gt;
+      &lt;contextdata key=&quot;key1&quot; value=&quot;value1&quot;/&gt;
+      &lt;contextdata key=&quot;key2&quot; 
classname=&quot;some.Class1&quot;/&gt;
+      &lt;contextdata key=&quot;key3&quot; value=&quot;value3&quot; 
if=&quot;SOME_PROPERTY&quot;/&gt;
+      &lt;contextdata key=&quot;key4&quot; value=&quot;value4&quot; 
unless=&quot;SOME_PROPERTY&quot;/&gt;
+    &lt;/velocitycontext&gt;
+    &lt;fileset dir=&quot;src_dir&quot; excludes=&quot;**/*.java&quot;/&gt;
+  &lt;/velocity&gt;
+</pre>
+<p><b>Run velocity on a set of files to a directory, appending
+<code>.bak</code> to the file name on the fly</b></p>
+<pre>
+  &lt;velocity todir=&quot;../backup/dir&quot;
+      propertyfile=&quot;velocity.properties&quot;&gt;
+    &lt;velocitycontext&gt;
+      &lt;contextdata key=&quot;key1&quot; value=&quot;value1&quot;/&gt;
+      &lt;contextdata key=&quot;key2&quot; 
classname=&quot;some.Class1&quot;/&gt;
+      &lt;contextdata key=&quot;key3&quot; value=&quot;value3&quot; 
if=&quot;SOME_PROPERTY&quot;/&gt;
+      &lt;contextdata key=&quot;key4&quot; value=&quot;value4&quot; 
unless=&quot;SOME_PROPERTY&quot;/&gt;
+    &lt;/velocitycontext&gt;
+    &lt;fileset dir=&quot;src_dir&quot;/&gt;
+    &lt;mapper type=&quot;glob&quot; from=&quot;*&quot; 
to=&quot;*.bak&quot;/&gt;
+  &lt;/velocity&gt;
+</pre>
+
+<p><strong>Unix Note:</strong> Destination file permissions are not the same as
+the template files; they end up with the default <code>UMASK</code> permissions
+instead. This
+is caused by the lack of any means to query or set file permissions in the
+current Java runtimes.
+</p>
+
+<p><strong>Windows Note:</strong> If you a destination file already exists,
+but with different casing, the destination file takes on the case of the
+original. The workaround is to
+<a href="delete.html">delete</a>
+the file in the destination directory before you run velocity.
+</p>
+
+<hr><p align="center">Copyright &copy; 2003 Apache Software Foundation.
+All rights Reserved.</p>
+
+</body>
+</html>
+
diff -ruN jakarta-ant-1.5.1.orig/docs/manual/install.html jakarta-ant-
1.5.1/docs/manual/install.html
--- jakarta-ant-1.5.1.orig/docs/manual/install.html 2002-10-02 
11:10:18.000000000 +0900
+++ jakarta-ant-1.5.1/docs/manual/install.html 2003-01-21 07:37:06.000000000 
+0900
@@ -381,6 +381,12 @@
     <td><a href="http://www.clarkware.com/software/JDepend.html"
         target="_top">http://www.clarkware.com/software/JDepend.html</a></td>
   </tr>
+  <tr>
+    <td>Velocity JAR(s)</td>
+    <td>velocity task</td>
+    <td><a href="http://jakarta.apache.org/velocity/index.html"
+        target="_top">http://jakarta.apache.org/velocity/index.html</a></td>
+  </tr>
 </table>
 <br>
 <hr>
diff -ruN jakarta-ant-1.5.1.orig/docs/manual/optionaltasklist.html jakarta-ant-
1.5.1/docs/manual/optionaltasklist.html
--- jakarta-ant-1.5.1.orig/docs/manual/optionaltasklist.html 2002-10-02 
11:10:06.000000000 +0900
+++ jakarta-ant-1.5.1/docs/manual/optionaltasklist.html 2003-01-20 
09:40:43.000000000 +0900
@@ -62,6 +62,7 @@
 <a href="OptionalTasks/test.html">Test</a><br>
 <a href="OptionalTasks/translate.html">Translate</a><br>
 <a href="Integration/VAJAntTool.html#tasks">Visual Age for Java Tasks</a><br>
+<a href="OptionalTasks/velocity.html">Velocity</a><br>
 <a href="OptionalTasks/vss.html#tasks">Microsoft Visual SourceSafe 
Tasks</a><br>
 <a href="OptionalTasks/wljspc.html">Weblogic JSP Compiler</a><br>
 <a href="OptionalTasks/xmlvalidate.html">XmlValidate</a><br>
diff -ruN jakarta-ant-
1.5.1.orig/src/main/org/apache/tools/ant/taskdefs/defaults.properties
jakarta-ant-1.5.1/src/main/org/apache/tools/ant/taskdefs/defaults.properties
--- jakarta-ant-
1.5.1.orig/src/main/org/apache/tools/ant/taskdefs/defaults.properties 2002-10-
02 11:08:34.000000000 +0900
+++ jakarta-ant-
1.5.1/src/main/org/apache/tools/ant/taskdefs/defaults.properties 2003-01-21 
07:29:56.000000000 +0900
@@ -169,6 +169,7 @@
 jarlib-
available=org.apache.tools.ant.taskdefs.optional.extension.JarLibAvailableTask
 jarlib-
resolve=org.apache.tools.ant.taskdefs.optional.extension.JarLibResolveTask
 setproxy=org.apache.tools.ant.taskdefs.optional.net.SetProxy
+velocity=org.apache.tools.ant.taskdefs.optional.VelocityTask

 # deprecated ant tasks (kept for back compatibility)
 starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut
diff -ruN jakarta-ant-
1.5.1.orig/src/main/org/apache/tools/ant/taskdefs/optional/VelocityTask.java
jakarta-ant-
1.5.1/src/main/org/apache/tools/ant/taskdefs/optional/VelocityTask.java
--- jakarta-ant-
1.5.1.orig/src/main/org/apache/tools/ant/taskdefs/optional/VelocityTask.java 
1970-01-01 09:00:00.000000000 +0900
+++ jakarta-ant-
1.5.1/src/main/org/apache/tools/ant/taskdefs/optional/VelocityTask.java 2003-01-
20 10:48:52.000000000 +0900
@@ -0,0 +1,478 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.tools.ant.taskdefs.optional;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.OutputStreamWriter;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.DirectoryScanner;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.optional.velocity.VelocityContext;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.util.FlatFileNameMapper;
+import org.apache.tools.ant.util.IdentityMapper;
+import org.apache.velocity.app.Velocity;
+import org.apache.velocity.context.Context;
+
+
+/**
+ * Run velocity on a file or a fileset to a new file
+ * or directory.  Velocity is only run for a file which is newer
+ * than the destination file, or when the destination file does not
+ * exist.  It is possible to explicitly overwrite existing files.</p>
+ *
+ * <p>This task is supposed to be a richer version of filter function
+ * of copy task, and has similar attributes and nested elements.</p>
+ *
+ * @author <a href="mailto:hnakamur@v003.vaio.ne.jp">Hiroaki Nakamura</a>
+ *
+ * @version $Revision$
+ */
+public class VelocityTask extends Task {
+    protected File templateBaseDir = null;
+    protected File file = null; // the template file
+    protected File destFile = null; // the destination file
+    protected File destDir = null;  // the destination directory
+    protected String encoding = System.getProperty("file.encoding"); // 
template and output file encoding
+    protected File propertyFile = null; // the destination file
+    protected VelocityContext velocityContext; // the <velocitycontext> nested 
element
+    protected Vector filesets = new Vector();
+
+    protected boolean forceOverwrite = false;
+    private boolean failonerror = true;
+
+    protected boolean flatten = false;
+    protected Mapper mapperElement = null;
+
+    private FileUtils fileUtils;
+
+    /**
+     * Velocity task constructor.
+     */
+    public VelocityTask() {
+        fileUtils = FileUtils.newFileUtils();
+    }
+
+    protected FileUtils getFileUtils() {
+        return fileUtils;
+    }
+
+    /**
+     * Sets template files base directory.
+     */
+    public void setTemplateBaseDir(File dir) {
+        this.templateBaseDir = dir;
+    }
+
+    /**
+     * @return templatebasedir attribute value or project basedir if 
templatebasedir is not set.
+     */
+    protected File getTemplateBaseDir() {
+        return templateBaseDir == null
+                ? project.getBaseDir() : templateBaseDir;
+    }
+
+    /**
+     * Sets a single source file to copy.
+     */
+    public void setFile(File file) {
+        this.file = file;
+    }
+
+    /**
+     * Sets the destination file.
+     */
+    public void setTofile(File destFile) {
+        this.destFile = destFile;
+    }
+
+    /**
+     * Sets the destination directory.
+     */
+    public void setTodir(File destDir) {
+        this.destDir = destDir;
+    }
+
+    /**
+     * Sets the Velocity propertyfile.
+     */
+    public void setPropertyFile(File propertyFile) {
+        this.propertyFile = propertyFile;
+    }
+
+    /**
+     * Sets the encoding.
+     */
+    public void setEncoding(String encoding) {
+        this.encoding = encoding;
+    }
+
+    /**
+     * Create a nested velocitycontext element.
+    */
+    public VelocityContext createVelocityContext() throws BuildException {
+        if (velocityContext != null) {
+            throw new BuildException("Cannot define more than one 
velocitycontext",
+                                     location);
+        }
+        velocityContext = new VelocityContext();
+        return velocityContext;
+    }
+
+    /**
+     * Adds a set of files to copy.
+     */
+    public void addFileset(FileSet set) {
+        filesets.addElement(set);
+    }
+
+    /**
+     * Overwrite any existing destination file(s).
+     */
+    public void setOverwrite(boolean overwrite) {
+        this.forceOverwrite = overwrite;
+    }
+
+    /**
+     * If false, note errors to the output but keep going.
+     * @param failonerror true or false
+     */
+     public void setFailOnError(boolean failonerror) {
+         this.failonerror = failonerror;
+     }
+
+    /**
+     * When copying directory trees, the files can be "flattened"
+     * into a single directory.  If there are multiple files with
+     * the same name in the source directory tree, only the first
+     * file will be copied into the "flattened" directory, unless
+     * the forceoverwrite attribute is true.
+     */
+    public void setFlatten(boolean flatten) {
+        this.flatten = flatten;
+    }
+
+    /**
+     * Defines the mapper to map source to destination files.
+     */
+    public Mapper createMapper() throws BuildException {
+        if (mapperElement != null) {
+            throw new BuildException("Cannot define more than one mapper",
+                                     location);
+        }
+        mapperElement = new Mapper(project);
+        return mapperElement;
+    }
+
+    /**
+     * Performs the velocity template merge operation.
+     */
+    public void execute() throws BuildException {
+        File savedFile = file; // may be altered in validateAttributes
+        File savedDestFile = destFile;
+        File savedDestDir = destDir;
+        FileSet savedFileSet = null;
+        if (file == null && destFile != null && filesets.size() == 1) {
+            // will be removed in validateAttributes
+            savedFileSet = (FileSet) filesets.elementAt(0);
+        }
+
+        validateAttributes();
+
+        try {
+            initVelocity();
+            Context ctx = createContext();
+            File tmplBaseDir = getTemplateBaseDir();
+
+            if (file != null) {
+                if (!file.exists()) {
+                    String message = "Warning: Could not find template file "
+                        + file.getAbsolutePath() + ".";
+                    if (!failonerror) {
+                        log(message);
+                    } else {
+                        throw new BuildException(message);
+                    }
+                }
+
+                if (destFile == null) {
+                    destFile = new File(destDir, file.getName());
+                }
+
+                mergeTemplateUnlessUpToDate(
+                    tmplBaseDir,
+                    file,
+                    ctx,
+                    destFile);
+            }
+
+            if (filesets != null) {
+                FileNameMapper mapper = getFileNameMapper();
+                for (Enumeration en = filesets.elements(); en.hasMoreElements
();) {
+                    FileSet fs = (FileSet) en.nextElement();
+                    DirectoryScanner ds = fs.getDirectoryScanner(project);
+                    File dsBaseDir = ds.getBasedir();
+                    String[] files = ds.getIncludedFiles();
+                    for (int i = 0; i < files.length; i++) {
+                        String srcRelativePath = files[i];
+                        File srcFile = new File(dsBaseDir, srcRelativePath);
+                        String[] destRelativePaths = mapper.mapFileName
(srcRelativePath);
+                        for (int j = 0; j < destRelativePaths.length; j++) {
+                            String destRelativePath = destRelativePaths[j];
+                            File curDestFile = new File(destDir, 
destRelativePath);
+                            mergeTemplateUnlessUpToDate(
+                                tmplBaseDir,
+                                srcFile,
+                                ctx,
+                                curDestFile);
+                        }
+                    }
+                }
+            }
+        } finally {
+            // clean up again, so this instance can be used a second
+            // time
+            file = savedFile;
+            destFile = savedDestFile;
+            destDir = savedDestDir;
+            if (savedFileSet != null && filesets.size() == 0) {
+                filesets.insertElementAt(savedFileSet, 0);
+            }
+        }
+    }
+
+//************************************************************************
+//  protected and private methods
+//************************************************************************
+
+    /**
+     * Ensure we have a consistent and legal set of attributes, and set
+     * any internal flags necessary based on different combinations
+     * of attributes.
+     */
+    protected void validateAttributes() throws BuildException {
+        if (file == null && filesets.size() == 0) {
+            throw new BuildException("Specify at least one source "
+                                     + "- a file or a fileset.");
+        }
+
+        if (destFile != null && destDir != null) {
+            throw new BuildException("Only one of tofile and todir "
+                                     + "may be set.");
+        }
+
+        if (destFile == null && destDir == null) {
+            throw new BuildException("One of tofile or todir must be set.");
+        }
+
+        if (file != null && file.exists() && file.isDirectory()) {
+            throw new BuildException("Use a fileset to copy directories.");
+        }
+
+        if (destFile != null && filesets.size() > 0) {
+            if (filesets.size() > 1) {
+                throw new BuildException(
+                    "Cannot concatenate multiple files into a single file.");
+            } else {
+                FileSet fs = (FileSet) filesets.elementAt(0);
+                DirectoryScanner ds = fs.getDirectoryScanner(project);
+                String[] srcFiles = ds.getIncludedFiles();
+
+                if (srcFiles.length == 0) {
+                    throw new BuildException(
+                        "Cannot perform operation from directory to file.");
+                } else if (srcFiles.length == 1) {
+                    if (file == null) {
+                        file = new File(ds.getBasedir(), srcFiles[0]);
+                        filesets.removeElementAt(0);
+                    } else {
+                        throw new BuildException("Cannot concatenate multiple "
+                                                 + "files into a single 
file.");
+                    }
+                } else {
+                    throw new BuildException("Cannot concatenate multiple "
+                                             + "files into a single file.");
+                }
+            }
+        }
+
+        if (destFile != null) {
+            destDir = fileUtils.getParentFile(destFile);
+        }
+
+    }
+
+    /**
+     * @return FileNameMapper instance
+     */
+    protected FileNameMapper getFileNameMapper() {
+        FileNameMapper mapper = null;
+        if (mapperElement != null) {
+            mapper = mapperElement.getImplementation();
+        } else if (flatten) {
+            mapper = new FlatFileNameMapper();
+        } else {
+            mapper = new IdentityMapper();
+        }
+        return mapper;
+    }
+
+    /**
+     * Initializes Velocity.
+     */
+    protected void initVelocity() throws BuildException {
+        if (propertyFile == null) {
+            throw new BuildException("propertyfile must be set");
+        }
+        try {
+            Velocity.init(propertyFile.getPath());
+        } catch (Exception ex) {
+            throw new BuildException(ex);
+        }
+    }
+
+    /**
+     * Merge Velocity template file to the corresponding destination file
+     * if the destination file is older than the template file or does not 
exist.
+     */
+    protected void mergeTemplateUnlessUpToDate(
+        File templateBaseDir,
+        File templateFile,
+        Context ctx,
+        File destFile)
+        throws BuildException {
+
+        if (forceOverwrite ||
+            (templateFile.lastModified() > destFile.lastModified())) {
+            String templatePath = getRelativePath(templateFile, 
templateBaseDir);
+            mergeTemplate(templatePath, ctx, destFile);
+        } else {
+            log(templateFile + " omitted as " + destFile
+                + " is up to date.", Project.MSG_VERBOSE);
+        }
+    }
+
+    /**
+     * Merge Velocity template file to the corresponding destination file.
+     */
+    protected void mergeTemplate(
+        String templateRelativePath,
+        Context ctx,
+        File destFile)
+        throws BuildException {
+        try {
+            File destDir = fileUtils.getParentFile(destFile);
+            if (!destDir.exists()) {
+                destDir.mkdirs();
+            }
+
+            OutputStreamWriter osw = null;
+            FileOutputStream fos = new FileOutputStream(destFile);
+            try {
+                osw = new OutputStreamWriter(fos, encoding);
+                try {
+                    Velocity.mergeTemplate(
+                        templateRelativePath,
+                        encoding,
+                        ctx,
+                        osw);
+                } finally {
+                    osw.close();
+                    fos = null;
+                }
+            } finally {
+                if (fos != null) {
+                    fos.close();
+                }
+            }
+        } catch (Exception ex) {
+            throw new BuildException(ex);
+        }
+    }
+
+    /**
+     * @return relative path of file from baseDir
+     */
+    protected String getRelativePath(File file, File baseDir) {
+        int trimLen = baseDir.getAbsolutePath().length();
+        if (!isRootDirectory(baseDir)) {
+            trimLen++;
+        }
+        return file.getAbsolutePath().substring(trimLen);
+    }
+
+    /**
+     * @return whether dir is root directory or not.
+     */
+    protected boolean isRootDirectory(File dir) {
+        return dir.getParent() == null;
+    }
+
+    /**
+     * @return Velocity context created.
+     */
+    protected Context createContext()
+        throws BuildException {
+        return velocityContext.createRealContext();
+    }
+}
diff -ruN jakarta-ant-
1.5.1.orig/src/main/org/apache/tools/ant/types/optional/velocity/VelocityContext
.java
jakarta-ant-
1.5.1/src/main/org/apache/tools/ant/types/optional/velocity/VelocityContext.java
--- jakarta-ant-
1.5.1.orig/src/main/org/apache/tools/ant/types/optional/velocity/VelocityContext
.java 1970-01-01 09:00:00.000000000
+0900
+++ jakarta-ant-
1.5.1/src/main/org/apache/tools/ant/types/optional/velocity/VelocityContext.java
 2003-01-20 11:00:10.000000000 +0900
@@ -0,0 +1,258 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2002 The Apache Software Foundation.  All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ *    any, must include the following acknowlegement:
+ *       "This product includes software developed by the
+ *        Apache Software Foundation (http://www.apache.org/)."
+ *    Alternately, this acknowlegement may appear in the software itself,
+ *    if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ *    Foundation" must not be used to endorse or promote products derived
+ *    from this software without prior written permission. For written
+ *    permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ *    nor may "Apache" appear in their names without prior written
+ *    permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ */
+
+package org.apache.tools.ant.types.optional.velocity;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.types.DataType;
+import org.apache.velocity.context.Context;
+
+/**
+ * A VelocityContext is a nested element of Velocity task.
+ * It has nested <code>&ltcontextdata&gt;</code> elements.
+ *
+ * @author <a href="mailto:hnakamur@v003.vaio.ne.jp">Hiroaki Nakamura</a>
+ *
+ * @version $Revision$
+ */
+public class VelocityContext extends DataType {
+    private Vector contextData = new Vector();
+
+    /**
+     * Creates the nested <code>&ltcontextdata&gt;</code> element.
+     */
+    public ContextData createContextData() throws BuildException {
+        ContextData cd = new ContextData();
+        contextData.addElement(cd);
+        return cd;
+    }
+
+    /**
+     * @return real org.apache.velocity.VelocityContext specified by
+     * <code>&ltvelocitycontext&gt;</code> element.
+     */
+    public Context createRealContext()
+        throws BuildException {
+        Context context = new org.apache.velocity.VelocityContext();
+        populateContext(context);
+        return context;
+    }
+
+    /**
+     * Populates org.apache.velocity.VelocityContext with data specified by
+     * nested <code>&ltcontextdata&gt;</code> elements.
+     */
+    protected void populateContext(Context context)
+        throws BuildException {
+        for (Enumeration en = contextData.elements(); en.hasMoreElements();) {
+            Object o = en.nextElement();
+            ContextData cd = (ContextData) o;
+            cd.addIfValid(context);
+        }
+    }
+
+    /**
+     * ContextData represents a nested <code>&ltcontextdata&gt;</code> element.
+     * The attribute <code>key</code> is necessary. One of attribute 
<code>value</code>
+     * or <code>className</code> is necessary. The attribute <code>value</code>
+     * is used to create immediate Context data. The attribute 
<code>className</code>
+     * is used to create a "tool" object which is later used from template 
files.
+     * The attribute <code>if</code> and <code>unless</code> are optional, 
which
+     * are used to control of enabling or disabling of a 
<code>&ltcontextdata&gt;</code>
+     * element.
+     */
+    public class ContextData {
+        protected String key;
+        protected String value;
+        protected String ifCond;
+        protected String className;
+        protected String unlessCond;
+
+        /**
+         * Sets the key.
+         * @param name The key to set
+         */
+        public void setKey(String key) {
+            this.key = key;
+        }
+
+        /**
+         * Sets the value.
+         * @param value The value to set
+         */
+        public void setValue(String value) {
+            this.value = value;
+        }
+
+        /**
+         * Sets the className.
+         * @param className The className to set
+         */
+        public void setClassName(String className) {
+            this.className = className;
+        }
+
+        /**
+         * Sets the ifCond.
+         * @param ifCond The ifCond to set
+         */
+        public void setIf(String ifCond) {
+            this.ifCond = ifCond;
+        }
+
+        /**
+         * Sets the unlessCond.
+         * @param unlessCond The unlessCond to set
+         */
+        public void setUnless(String unlessCond) {
+            this.unlessCond = unlessCond;
+        }
+
+        /**
+         * @return whether this element is valid or not
+         */
+        public boolean isValid() {
+            Project p = getProject();
+            if (ifCond != null && p.getProperty(ifCond) == null) {
+                return false;
+            } else if (unlessCond != null && p.getProperty(unlessCond) != 
null) {
+                return false;
+            }
+            return true;
+        }
+
+        /**
+         * Add to a context if this element is valid.
+         */
+        public void addIfValid(Context context)
+            throws BuildException {
+            if (!isValid()) {
+                return;
+            }
+
+            if (key == null) {
+                throw new BuildException("name must be set");
+            }
+
+            if (value != null && className != null) {
+                throw new BuildException("value and className cannot both be 
set");
+            } else if (value == null && className == null) {
+                throw new BuildException("value or className must be set");
+            }
+
+            if (value != null) {
+                context.put(key, value);
+            } else if (className != null) {
+                Object instance = createInstance(className);
+                invokeSetContext(instance, context);
+                context.put(key, instance);
+            }
+        }
+
+        /**
+         * @return an instance of the specified class.
+         */
+        protected Object createInstance(String className)
+            throws BuildException {
+            try {
+                return Class.forName(className).newInstance();
+            } catch (ClassNotFoundException ex) {
+                throw new BuildException(ex);
+            } catch (IllegalAccessException ex) {
+                throw new BuildException(ex);
+            } catch (InstantiationException ex) {
+                throw new BuildException(ex);
+            }
+        }
+
+        protected void invokeSetContext(Object instance, Context context)
+            throws BuildException {
+            try {
+                Class[] paramTypes = { Context.class };
+                Method method = instance.getClass().getMethod("setContext", 
paramTypes);
+                Object[] args = { context };
+                method.invoke(instance, args);
+            } catch (NoSuchMethodException ex) {
+                // ignore
+            } catch (IllegalAccessException ex) {
+                throw new BuildException(ex);
+            } catch (InvocationTargetException ex) {
+                throw new BuildException(ex);
+            }
+        }
+
+        /**
+         * @return a string representation of an instance.
+         */
+        public String toString() {
+            return "ContextVar[name="
+                + key
+                + ", value="
+                + value
+                + ", className="
+                + className
+                + ", if="
+                + ifCond
+                + ", unless="
+                + unlessCond
+                + "]";
+        }
+    }
+}

--
To unsubscribe, e-mail:   <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>