ant-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Kirk, Christopher" <christopher.k...@convergys.com>
Subject RE: [PATCH] Velocity task
Date Tue, 21 Jan 2003 10:27:25 GMT

This is a very nice proposal, and I definately think that a velocity ant
task would be a great addition to Ant. So thanks to Hiroaki for it.



I've spent some time using Velocity inside Ant too and based on my
experience I recommend the following changes/additions to the current
proposal.   



If people like the proposals then depending on Hiroaki then two decisions
need to be made 1) who does the work (either Hiroaki and/or myself? .. I
already have a good code base that could be mined for this stuff) and 2)
should the XML suggestion be added to Hiroaki's patch or a new
compliementory task added?


---- The proposals:


1) I have found the real power of Ant+Velocity comes out when Ant is used to
load an XML file (that forms the velocity context) and then a set of XPath
mappers that specify when Velocity is to be fired and what file is to be
generated.   

This has the benefits of offering better modelling structure than property
files and will handle files of any character set (where property files must
be ascii).


As such my proposal is the addition of the following functionality (that I
am more than willing to help implement - also note, this does not replace
any of the original proposal but instead compliments it):


- Load an XML file using the Jakarta digester (the digester is great for
mapping XML to a decent java object model).
- Reuse this object model between 1+ velocity calls.
- Offer the ability to pass the entire object tree to velocity, or a
subtree.
- Specify target files based on values inside the loaded object tree.
- Specify a different velocity template for each invocation.

(I have found XPath a great way for configuring which parts of the tree are
used from inside Ant).



Ok.. so time for some examples.

Example 1:

Lets imagine we want to model a relational Db schema in XML, and then
generate the SQL to create the tables for both Oracle and MSQL.


First we load the XML into a suitable object model..


<read-xml  id="schemaID" reader="digest reader" file="schema.xml"/>


Now that it is loaded, lets generate the Oracle SQL.

<velocity 
    template="createOracle.vm" 
    xml="schemaID" 
    tofile="${out.gen.dir}/oracle_schema.sql"  
    propertyfile="velocity.properties"
/> 

And then the MSQL SQL.

<velocity 
    template="createMSQL.vm" 
    xml="schemaID" 
    tofile="${out.gen.dir}/msql_schema.sql"  
    propertyfile="velocity.properties"
/> 



Example 2: (more complicated)

For every table in the schema, we want to create a table cacher (doesn't
matter what kind of cacher :)

<velocity 
    template="tableCacher.vm" 
    xml="schemaID" 
    foreach="tables"
    tofile="${out.gen.dir}/src/com/foo/caches/(name)Cache.java"  
    propertyfile="velocity.properties"
/> 



If a table named 'Account' existed in the XML.. then a cache called
AccountCache.java would be created by invoking Velocity with the
tableCacher.vm template and the account table object in the context (as key
xmlroot <-- fixed name not configurable).

This works by using XPath on the foreach attribute.. so it starts from the
XML root and looks for the 'tables' property  (eg   getTables() which
returns all of the tables). Then for each of these objects it writes to
${out.gen.dir}/src/com/foo/caches/........Cache.java     where ...... is
specified by calling getName() on the specific table object  (or the name
field if there is no getter).




2) My 2nd proposal is that the propertyFile and context could receive
defaults... specified by a new task.

eg:   

<velocity-defaults>
  <ini-file>velocity.properties</ini-file>
  <velocity-context.... see Hiroaki proposal ...
     everything specified here would be placed into every context that will
be created... name clashes with later contexts will be resolved via the
Velocity Context chaining concept.  That is, the newest mapping takes
precedence.
 />
</velocity-defaults>





- Chris.



-----Original Message-----
From: Hiroaki Nakamura [mailto:hnakamur@v003.vaio.ne.jp]
Sent: Monday, January 20, 2003 11:20 PM
To: ant-dev@jakarta.apache.org
Subject: [PATCH] Velocity task


Hi.
I made the Jakarta-Velocity task for Ant.
Please take a look and consider to accept it as a new optional task.

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.

--
)Hiroaki Nakamura) hnakamur@v003.vaio.ne.jp


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.prope
rties
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.prope
rties 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.JarLibAvai
lableTask
 
jarlib-resolve=org.apache.tools.ant.taskdefs.optional.extension.JarLibResolv
eTask
 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/Veloc
ityTask.java
jakarta-ant-1.5.1/src/main/org/apache/tools/ant/taskdefs/optional/VelocityTa
sk.java
---
jakarta-ant-1.5.1.orig/src/main/org/apache/tools/ant/taskdefs/optional/Veloc
ityTask.java 1970-01-01 09:00:00.000000000 +0900
+++
jakarta-ant-1.5.1/src/main/org/apache/tools/ant/taskdefs/optional/VelocityTa
sk.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/Velo
cityContext.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/Velo
cityContext.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:   <mailto:ant-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:ant-dev-help@jakarta.apache.org>


--

NOTICE:  The information contained in this electronic mail transmission is
intended by Convergys Corporation for the use of the named individual or
entity to which it is directed and may contain information that is
privileged or otherwise confidential.  If you have received this electronic
mail transmission in error, please delete it from your system without
copying or forwarding it, and notify the sender of the error by reply email
or by telephone (collect), so that the sender's address records can be
corrected.


--
To unsubscribe, e-mail:   <mailto:ant-dev-unsubscribe@jakarta.apache.org>
For additional commands, e-mail: <mailto:ant-dev-help@jakarta.apache.org>


Mime
View raw message