summaryrefslogtreecommitdiffstats
path: root/src/java/net/sf/antcontrib/logic
diff options
context:
space:
mode:
authormattinger <[email protected]>2006-07-06 21:53:00 +0000
committermattinger <[email protected]>2006-07-06 21:53:00 +0000
commit1159111b7a71b72eb04326df33211e1733f7e742 (patch)
treef0a80c384f633e521649654ab78e6239cf5e0d6f /src/java/net/sf/antcontrib/logic
Initial addition into subversion with build script changes
git-svn-id: file:///home/sven/projects/JOGL/temp/ant-contrib/svn/ant-contrib-code/trunk/ant-contrib@5 32d7a393-a5a9-423c-abd3-5d954feb1f2f
Diffstat (limited to 'src/java/net/sf/antcontrib/logic')
-rw-r--r--src/java/net/sf/antcontrib/logic/AntCallBack.java644
-rw-r--r--src/java/net/sf/antcontrib/logic/AntFetch.java638
-rw-r--r--src/java/net/sf/antcontrib/logic/Assert.java81
-rw-r--r--src/java/net/sf/antcontrib/logic/ForEach.java423
-rw-r--r--src/java/net/sf/antcontrib/logic/ForTask.java433
-rw-r--r--src/java/net/sf/antcontrib/logic/IfTask.java221
-rw-r--r--src/java/net/sf/antcontrib/logic/OutOfDate.java675
-rw-r--r--src/java/net/sf/antcontrib/logic/RunTargetTask.java50
-rw-r--r--src/java/net/sf/antcontrib/logic/Switch.java207
-rw-r--r--src/java/net/sf/antcontrib/logic/Throw.java50
-rw-r--r--src/java/net/sf/antcontrib/logic/TimestampSelector.java285
-rw-r--r--src/java/net/sf/antcontrib/logic/TryCatchTask.java232
12 files changed, 3939 insertions, 0 deletions
diff --git a/src/java/net/sf/antcontrib/logic/AntCallBack.java b/src/java/net/sf/antcontrib/logic/AntCallBack.java
new file mode 100644
index 0000000..cbd09dc
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/AntCallBack.java
@@ -0,0 +1,644 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.*;
+import org.apache.tools.ant.taskdefs.Property;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Identical (copy and paste, even) to the 'Ant' task, with the exception that
+ * properties from the new project can be copied back into the original project.
+ * Further modified to emulate "antcall". Build a sub-project. <pre>
+ * &lt;target name=&quot;foo&quot; depends=&quot;init&quot;&gt;
+ * &lt;ant antfile=&quot;build.xml&quot; target=&quot;bar&quot; &gt;
+ * &lt;property name=&quot;property1&quot; value=&quot;aaaaa&quot; /&gt;
+ * &lt;property name=&quot;foo&quot; value=&quot;baz&quot; /&gt;
+ * &lt;/ant&gt;</SPAN> &lt;/target&gt;</SPAN> &lt;target name=&quot;bar&quot;
+ * depends=&quot;init&quot;&gt; &lt;echo message=&quot;prop is ${property1}
+ * ${foo}&quot; /&gt; &lt;/target&gt; </pre>
+ * <p>Developed for use with Antelope, migrated to ant-contrib Oct 2003.
+ * <p>Credit to Costin for the original &lt;ant&gt; task, on which this is based.
+ *
+ * @author [email protected]
+ * @author Dale Anson, [email protected]
+ * @since Ant 1.1
+ * @ant.task category="control"
+ */
+public class AntCallBack extends Task {
+
+ /** the basedir where is executed the build file */
+ private File dir = null;
+
+ /**
+ * the build.xml file (can be absolute) in this case dir will be ignored
+ */
+ private String antFile = null;
+
+ /** the target to call if any */
+ private String target = null;
+
+ /** the output */
+ private String output = null;
+
+ /** should we inherit properties from the parent ? */
+ private boolean inheritAll = true;
+
+ /** should we inherit references from the parent ? */
+ private boolean inheritRefs = false;
+
+ /** the properties to pass to the new project */
+ private Vector properties = new Vector();
+
+ /** the references to pass to the new project */
+ private Vector references = new Vector();
+
+ /** the temporary project created to run the build file */
+ private Project newProject;
+
+ /** The stream to which output is to be written. */
+ private PrintStream out = null;
+
+ /** the name of the property to fetch from the new project */
+ private String returnName = null;
+
+
+ /**
+ * If true, pass all properties to the new Ant project. Defaults to true.
+ *
+ * @param value The new inheritAll value
+ */
+ public void setInheritAll( boolean value ) {
+ inheritAll = value;
+ }
+
+
+ /**
+ * If true, pass all references to the new Ant project. Defaults to false.
+ *
+ * @param value The new inheritRefs value
+ */
+ public void setInheritRefs( boolean value ) {
+ inheritRefs = value;
+ }
+
+
+ /** Creates a Project instance for the project to call. */
+ public void init() {
+ newProject = new Project();
+ newProject.setJavaVersionProperty();
+ newProject.addTaskDefinition( "property",
+ (Class)getProject().getTaskDefinitions()
+ .get( "property" ) );
+ }
+
+
+ /**
+ * Called in execute or createProperty if newProject is null. <p>
+ *
+ * This can happen if the same instance of this task is run twice as
+ * newProject is set to null at the end of execute (to save memory and help
+ * the GC).</p> <p>
+ *
+ * Sets all properties that have been defined as nested property elements.
+ * </p>
+ */
+ private void reinit() {
+ init();
+ final int count = properties.size();
+ for ( int i = 0; i < count; i++ ) {
+ Property p = (Property)properties.elementAt( i );
+ Property newP = (Property)newProject.createTask( "property" );
+ newP.setName( p.getName() );
+ if ( p.getValue() != null ) {
+ newP.setValue( p.getValue() );
+ }
+ if ( p.getFile() != null ) {
+ newP.setFile( p.getFile() );
+ }
+ if ( p.getResource() != null ) {
+ newP.setResource( p.getResource() );
+ }
+ if ( p.getPrefix() != null ) {
+ newP.setPrefix( p.getPrefix() );
+ }
+ if ( p.getRefid() != null ) {
+ newP.setRefid( p.getRefid() );
+ }
+ if ( p.getEnvironment() != null ) {
+ newP.setEnvironment( p.getEnvironment() );
+ }
+ if ( p.getClasspath() != null ) {
+ newP.setClasspath( p.getClasspath() );
+ }
+ properties.setElementAt( newP, i );
+ }
+ }
+
+
+ /**
+ * Attaches the build listeners of the current project to the new project,
+ * configures a possible logfile, transfers task and data-type definitions,
+ * transfers properties (either all or just the ones specified as user
+ * properties to the current project, depending on inheritall), transfers the
+ * input handler.
+ */
+ private void initializeProject() {
+ newProject.setInputHandler( getProject().getInputHandler() );
+
+ Vector listeners = getProject().getBuildListeners();
+ final int count = listeners.size();
+ for ( int i = 0; i < count; i++ ) {
+ newProject.addBuildListener( (BuildListener)listeners.elementAt( i ) );
+ }
+
+ if ( output != null ) {
+ File outfile = null;
+ if ( dir != null ) {
+ outfile = FileUtils.newFileUtils().resolveFile( dir, output );
+ }
+ else {
+ outfile = getProject().resolveFile( output );
+ }
+ try {
+ out = new PrintStream( new FileOutputStream( outfile ) );
+ DefaultLogger logger = new DefaultLogger();
+ logger.setMessageOutputLevel( Project.MSG_INFO );
+ logger.setOutputPrintStream( out );
+ logger.setErrorPrintStream( out );
+ newProject.addBuildListener( logger );
+ }
+ catch ( IOException ex ) {
+ log( "Ant: Can't set output to " + output );
+ }
+ }
+
+ Hashtable taskdefs = getProject().getTaskDefinitions();
+ Enumeration et = taskdefs.keys();
+ while ( et.hasMoreElements() ) {
+ String taskName = (String)et.nextElement();
+ if ( taskName.equals( "property" ) ) {
+ // we have already added this taskdef in #init
+ continue;
+ }
+ Class taskClass = (Class)taskdefs.get( taskName );
+ newProject.addTaskDefinition( taskName, taskClass );
+ }
+
+ Hashtable typedefs = getProject().getDataTypeDefinitions();
+ Enumeration e = typedefs.keys();
+ while ( e.hasMoreElements() ) {
+ String typeName = (String)e.nextElement();
+ Class typeClass = (Class)typedefs.get( typeName );
+ newProject.addDataTypeDefinition( typeName, typeClass );
+ }
+
+ // set user-defined properties
+ getProject().copyUserProperties( newProject );
+
+ if ( !inheritAll ) {
+ // set Java built-in properties separately,
+ // b/c we won't inherit them.
+ newProject.setSystemProperties();
+
+ }
+ else {
+ // set all properties from calling project
+
+ Hashtable props = getProject().getProperties();
+ e = props.keys();
+ while ( e.hasMoreElements() ) {
+ String arg = e.nextElement().toString();
+ if ( "basedir".equals( arg ) || "ant.file".equals( arg ) ) {
+ // basedir and ant.file get special treatment in execute()
+ continue;
+ }
+
+ String value = props.get( arg ).toString();
+ // don't re-set user properties, avoid the warning message
+ if ( newProject.getProperty( arg ) == null ) {
+ // no user property
+ newProject.setNewProperty( arg, value );
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Pass output sent to System.out to the new project.
+ *
+ * @param line Description of the Parameter
+ * @since Ant 1.5
+ */
+ protected void handleOutput( String line ) {
+ if ( newProject != null ) {
+ newProject.demuxOutput( line, false );
+ }
+ else {
+ super.handleOutput( line );
+ }
+ }
+
+
+ /**
+ * Pass output sent to System.err to the new project.
+ *
+ * @param line Description of the Parameter
+ * @since Ant 1.5
+ */
+ protected void handleErrorOutput( String line ) {
+ if ( newProject != null ) {
+ newProject.demuxOutput( line, true );
+ }
+ else {
+ super.handleErrorOutput( line );
+ }
+ }
+
+
+ /**
+ * Do the execution.
+ *
+ * @exception BuildException Description of the Exception
+ */
+ public void execute() throws BuildException {
+ setAntfile( getProject().getProperty( "ant.file" ) );
+
+ File savedDir = dir;
+ String savedAntFile = antFile;
+ String savedTarget = target;
+ try {
+ if ( newProject == null ) {
+ reinit();
+ }
+
+ if ( ( dir == null ) && ( inheritAll ) ) {
+ dir = getProject().getBaseDir();
+ }
+
+ initializeProject();
+
+ if ( dir != null ) {
+ newProject.setBaseDir( dir );
+ if ( savedDir != null ) { // has been set explicitly
+ newProject.setInheritedProperty( "basedir",
+ dir.getAbsolutePath() );
+ }
+ }
+ else {
+ dir = getProject().getBaseDir();
+ }
+
+ overrideProperties();
+
+ if ( antFile == null ) {
+ throw new BuildException( "Attribute target is required.",
+ getLocation() );
+ //antFile = "build.xml";
+ }
+
+ File file = FileUtils.newFileUtils().resolveFile( dir, antFile );
+ antFile = file.getAbsolutePath();
+
+ log( "calling target " + ( target != null ? target : "[default]" )
+ + " in build file " + antFile.toString(),
+ Project.MSG_VERBOSE );
+ newProject.setUserProperty( "ant.file", antFile );
+ ProjectHelper.configureProject( newProject, new File( antFile ) );
+
+ if ( target == null ) {
+ target = newProject.getDefaultTarget();
+ }
+
+ addReferences();
+
+ // Are we trying to call the target in which we are defined?
+ if ( newProject.getBaseDir().equals( getProject().getBaseDir() ) &&
+ newProject.getProperty( "ant.file" ).equals( getProject().getProperty( "ant.file" ) ) &&
+ getOwningTarget() != null &&
+ target.equals( this.getOwningTarget().getName() ) ) {
+
+ throw new BuildException( "antcallback task calling its own parent "
+ + "target" );
+ }
+
+ newProject.executeTarget( target );
+
+ // copy back the props if possible
+ if ( returnName != null ) {
+ StringTokenizer st = new StringTokenizer( returnName, "," );
+ while ( st.hasMoreTokens() ) {
+ String name = st.nextToken().trim();
+ String value = newProject.getUserProperty( name );
+ if ( value != null ) {
+ getProject().setUserProperty( name, value );
+ }
+ else {
+ value = newProject.getProperty( name );
+ if ( value != null ) {
+ getProject().setProperty( name, value );
+ }
+ }
+ }
+ }
+ }
+ finally {
+ // help the gc
+ newProject = null;
+ if ( output != null && out != null ) {
+ try {
+ out.close();
+ }
+ catch ( final Exception e ) {
+ //ignore
+ }
+ }
+ dir = savedDir;
+ antFile = savedAntFile;
+ target = savedTarget;
+ }
+ }
+
+
+ /**
+ * Override the properties in the new project with the one explicitly defined
+ * as nested elements here.
+ *
+ * @exception BuildException Description of the Exception
+ */
+ private void overrideProperties() throws BuildException {
+ Enumeration e = properties.elements();
+ while ( e.hasMoreElements() ) {
+ Property p = (Property)e.nextElement();
+ p.setProject( newProject );
+ p.execute();
+ }
+ getProject().copyInheritedProperties( newProject );
+ }
+
+
+ /**
+ * Add the references explicitly defined as nested elements to the new
+ * project. Also copy over all references that don't override existing
+ * references in the new project if inheritrefs has been requested.
+ *
+ * @exception BuildException Description of the Exception
+ */
+ private void addReferences() throws BuildException {
+ Hashtable thisReferences = (Hashtable)getProject().getReferences().clone();
+ Hashtable newReferences = newProject.getReferences();
+ Enumeration e;
+ if ( references.size() > 0 ) {
+ for ( e = references.elements(); e.hasMoreElements(); ) {
+ Reference ref = (Reference)e.nextElement();
+ String refid = ref.getRefId();
+ if ( refid == null ) {
+ throw new BuildException( "the refid attribute is required"
+ + " for reference elements" );
+ }
+ if ( !thisReferences.containsKey( refid ) ) {
+ log( "Parent project doesn't contain any reference '"
+ + refid + "'",
+ Project.MSG_WARN );
+ continue;
+ }
+
+ thisReferences.remove( refid );
+ String toRefid = ref.getToRefid();
+ if ( toRefid == null ) {
+ toRefid = refid;
+ }
+ copyReference( refid, toRefid );
+ }
+ }
+
+ // Now add all references that are not defined in the
+ // subproject, if inheritRefs is true
+ if ( inheritRefs ) {
+ for ( e = thisReferences.keys(); e.hasMoreElements(); ) {
+ String key = (String)e.nextElement();
+ if ( newReferences.containsKey( key ) ) {
+ continue;
+ }
+ copyReference( key, key );
+ }
+ }
+ }
+
+
+ /**
+ * Try to clone and reconfigure the object referenced by oldkey in the parent
+ * project and add it to the new project with the key newkey. <p>
+ *
+ * If we cannot clone it, copy the referenced object itself and keep our
+ * fingers crossed.</p>
+ *
+ * @param oldKey Description of the Parameter
+ * @param newKey Description of the Parameter
+ */
+ private void copyReference( String oldKey, String newKey ) {
+ Object orig = getProject().getReference( oldKey );
+ Class c = orig.getClass();
+ Object copy = orig;
+ try {
+ Method cloneM = c.getMethod( "clone", new Class[0] );
+ if ( cloneM != null ) {
+ copy = cloneM.invoke( orig, new Object[0] );
+ }
+ }
+ catch ( Exception e ) {
+ // not Clonable
+ }
+
+ if ( copy instanceof ProjectComponent ) {
+ ( (ProjectComponent)copy ).setProject( newProject );
+ }
+ else {
+ try {
+ Method setProjectM =
+ c.getMethod( "setProject", new Class[]{Project.class} );
+ if ( setProjectM != null ) {
+ setProjectM.invoke( copy, new Object[]{newProject} );
+ }
+ }
+ catch ( NoSuchMethodException e ) {
+ // ignore this if the class being referenced does not have
+ // a set project method.
+ }
+ catch ( Exception e2 ) {
+ String msg = "Error setting new project instance for "
+ + "reference with id " + oldKey;
+ throw new BuildException( msg, e2, getLocation() );
+ }
+ }
+ newProject.addReference( newKey, copy );
+ }
+
+
+ /**
+ * The directory to use as a base directory for the new Ant project. Defaults
+ * to the current project's basedir, unless inheritall has been set to false,
+ * in which case it doesn't have a default value. This will override the
+ * basedir setting of the called project.
+ *
+ * @param d The new dir value
+ */
+ public void setDir( File d ) {
+ this.dir = d;
+ }
+
+
+ /**
+ * The build file to use. Defaults to "build.xml". This file is expected to
+ * be a filename relative to the dir attribute given.
+ *
+ * @param s The new antfile value
+ */
+ public void setAntfile( String s ) {
+ // @note: it is a string and not a file to handle relative/absolute
+ // otherwise a relative file will be resolved based on the current
+ // basedir.
+ this.antFile = s;
+ }
+
+
+ /**
+ * The target of the new Ant project to execute. Defaults to the new
+ * project's default target.
+ *
+ * @param s The new target value
+ */
+ public void setTarget( String s ) {
+ this.target = s;
+ }
+
+
+ /**
+ * Filename to write the output to. This is relative to the value of the dir
+ * attribute if it has been set or to the base directory of the current
+ * project otherwise.
+ *
+ * @param s The new output value
+ */
+ public void setOutput( String s ) {
+ this.output = s;
+ }
+
+
+ /**
+ * Property to pass to the new project. The property is passed as a 'user
+ * property'
+ *
+ * @return Description of the Return Value
+ */
+ public Property createProperty() {
+ if ( newProject == null ) {
+ reinit();
+ }
+ /*
+ * Property p = new Property( true, getProject() );
+ */
+ Property p = new Property();
+ p.setProject( newProject );
+ p.setTaskName( "property" );
+ properties.addElement( p );
+ return p;
+ }
+
+
+ /**
+ * Property to pass to the invoked target.
+ */
+ public Property createParam() {
+ return createProperty();
+ }
+
+ /**
+ * Set the property or properties that are set in the new project to be
+ * transfered back to the original project. As with all properties, if the
+ * property already exists in the original project, it will not be overridden
+ * by a different value from the new project.
+ *
+ * @param r the name of a property in the new project to set in the original
+ * project. This may be a comma separate list of properties.
+ */
+ public void setReturn( String r ) {
+ returnName = r;
+ }
+
+
+ /**
+ * Reference element identifying a data type to carry over to the new
+ * project.
+ *
+ * @param r The feature to be added to the Reference attribute
+ */
+ public void addReference( Reference r ) {
+ references.addElement( r );
+ }
+
+
+ /**
+ * Helper class that implements the nested &lt;reference&gt; element of
+ * &lt;ant&gt; and &lt;antcall&gt;.
+ *
+ * @author danson
+ */
+ public static class Reference
+ extends org.apache.tools.ant.types.Reference {
+
+ /** Creates a reference to be configured by Ant */
+ public Reference() {
+ super();
+ }
+
+
+ private String targetid = null;
+
+
+ /**
+ * Set the id that this reference to be stored under in the new project.
+ *
+ * @param targetid the id under which this reference will be passed to
+ * the new project
+ */
+ public void setToRefid( String targetid ) {
+ this.targetid = targetid;
+ }
+
+
+ /**
+ * Get the id under which this reference will be stored in the new project
+ *
+ * @return the id of the reference in the new project.
+ */
+ public String getToRefid() {
+ return targetid;
+ }
+ }
+}
+
diff --git a/src/java/net/sf/antcontrib/logic/AntFetch.java b/src/java/net/sf/antcontrib/logic/AntFetch.java
new file mode 100644
index 0000000..fa2b13f
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/AntFetch.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.BuildListener;
+import org.apache.tools.ant.DefaultLogger;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.ProjectComponent;
+import org.apache.tools.ant.ProjectHelper;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Property;
+import org.apache.tools.ant.util.FileUtils;
+
+/**
+ * Identical (copy and paste, even) to the 'Ant' task, with the exception that
+ * properties from the new project can be copied back into the original project.
+ * Build a sub-project. <pre>
+ * &lt;target name=&quot;foo&quot; depends=&quot;init&quot;&gt;
+ * &lt;ant antfile=&quot;build.xml&quot; target=&quot;bar&quot; &gt;
+ * &lt;property name=&quot;property1&quot; value=&quot;aaaaa&quot; /&gt;
+ * &lt;property name=&quot;foo&quot; value=&quot;baz&quot; /&gt;
+ * &lt;/ant&gt;</SPAN> &lt;/target&gt;</SPAN> &lt;target name=&quot;bar&quot;
+ * depends=&quot;init&quot;&gt; &lt;echo message=&quot;prop is ${property1}
+ * ${foo}&quot; /&gt; &lt;/target&gt; </pre>
+ * <p>Developed for use with Antelope, migrated to ant-contrib Oct 2003.
+ * <p>Credit to Costin for the original &lt;ant&gt; task, on which this is based.
+ *
+ * @author [email protected]
+ * @author Dale Anson, [email protected]
+ * @since Ant 1.1
+ * @ant.task category="control"
+ */
+public class AntFetch extends Task {
+
+ /** the basedir where is executed the build file */
+ private File dir = null;
+
+ /**
+ * the build.xml file (can be absolute) in this case dir will be ignored
+ */
+ private String antFile = null;
+
+ /** the target to call if any */
+ private String target = null;
+
+ /** the output */
+ private String output = null;
+
+ /** should we inherit properties from the parent ? */
+ private boolean inheritAll = true;
+
+ /** should we inherit references from the parent ? */
+ private boolean inheritRefs = false;
+
+ /** the properties to pass to the new project */
+ private Vector properties = new Vector();
+
+ /** the references to pass to the new project */
+ private Vector references = new Vector();
+
+ /** the temporary project created to run the build file */
+ private Project newProject;
+
+ /** The stream to which output is to be written. */
+ private PrintStream out = null;
+
+ /** the name of the property to fetch from the new project */
+ private String returnName = null;
+
+
+ /**
+ * If true, pass all properties to the new Ant project. Defaults to true.
+ *
+ * @param value The new inheritAll value
+ */
+ public void setInheritAll( boolean value ) {
+ inheritAll = value;
+ }
+
+
+ /**
+ * If true, pass all references to the new Ant project. Defaults to false.
+ *
+ * @param value The new inheritRefs value
+ */
+ public void setInheritRefs( boolean value ) {
+ inheritRefs = value;
+ }
+
+
+ /** Creates a Project instance for the project to call. */
+ public void init() {
+ newProject = new Project();
+ newProject.setJavaVersionProperty();
+ newProject.addTaskDefinition( "property",
+ (Class)getProject().getTaskDefinitions()
+ .get( "property" ) );
+ }
+
+
+ /**
+ * Called in execute or createProperty if newProject is null. <p>
+ *
+ * This can happen if the same instance of this task is run twice as
+ * newProject is set to null at the end of execute (to save memory and help
+ * the GC).</p> <p>
+ *
+ * Sets all properties that have been defined as nested property elements.
+ * </p>
+ */
+ private void reinit() {
+ init();
+ final int count = properties.size();
+ for ( int i = 0; i < count; i++ ) {
+ Property p = (Property)properties.elementAt( i );
+ Property newP = (Property)newProject.createTask( "property" );
+ newP.setName( p.getName() );
+ if ( p.getValue() != null ) {
+ newP.setValue( p.getValue() );
+ }
+ if ( p.getFile() != null ) {
+ newP.setFile( p.getFile() );
+ }
+ if ( p.getResource() != null ) {
+ newP.setResource( p.getResource() );
+ }
+ if ( p.getPrefix() != null ) {
+ newP.setPrefix( p.getPrefix() );
+ }
+ if ( p.getRefid() != null ) {
+ newP.setRefid( p.getRefid() );
+ }
+ if ( p.getEnvironment() != null ) {
+ newP.setEnvironment( p.getEnvironment() );
+ }
+ if ( p.getClasspath() != null ) {
+ newP.setClasspath( p.getClasspath() );
+ }
+ properties.setElementAt( newP, i );
+ }
+ }
+
+
+ /**
+ * Attaches the build listeners of the current project to the new project,
+ * configures a possible logfile, transfers task and data-type definitions,
+ * transfers properties (either all or just the ones specified as user
+ * properties to the current project, depending on inheritall), transfers the
+ * input handler.
+ */
+ private void initializeProject() {
+ newProject.setInputHandler( getProject().getInputHandler() );
+
+ Vector listeners = getProject().getBuildListeners();
+ final int count = listeners.size();
+ for ( int i = 0; i < count; i++ ) {
+ newProject.addBuildListener( (BuildListener)listeners.elementAt( i ) );
+ }
+
+ if ( output != null ) {
+ File outfile = null;
+ if ( dir != null ) {
+ outfile = FileUtils.newFileUtils().resolveFile( dir, output );
+ }
+ else {
+ outfile = getProject().resolveFile( output );
+ }
+ try {
+ out = new PrintStream( new FileOutputStream( outfile ) );
+ DefaultLogger logger = new DefaultLogger();
+ logger.setMessageOutputLevel( Project.MSG_INFO );
+ logger.setOutputPrintStream( out );
+ logger.setErrorPrintStream( out );
+ newProject.addBuildListener( logger );
+ }
+ catch ( IOException ex ) {
+ log( "Ant: Can't set output to " + output );
+ }
+ }
+
+ Hashtable taskdefs = getProject().getTaskDefinitions();
+ Enumeration et = taskdefs.keys();
+ while ( et.hasMoreElements() ) {
+ String taskName = (String)et.nextElement();
+ if ( taskName.equals( "property" ) ) {
+ // we have already added this taskdef in #init
+ continue;
+ }
+ Class taskClass = (Class)taskdefs.get( taskName );
+ newProject.addTaskDefinition( taskName, taskClass );
+ }
+
+ Hashtable typedefs = getProject().getDataTypeDefinitions();
+ Enumeration e = typedefs.keys();
+ while ( e.hasMoreElements() ) {
+ String typeName = (String)e.nextElement();
+ Class typeClass = (Class)typedefs.get( typeName );
+ newProject.addDataTypeDefinition( typeName, typeClass );
+ }
+
+ // set user-defined properties
+ getProject().copyUserProperties( newProject );
+
+ if ( !inheritAll ) {
+ // set Java built-in properties separately,
+ // b/c we won't inherit them.
+ newProject.setSystemProperties();
+
+ }
+ else {
+ // set all properties from calling project
+
+ Hashtable props = getProject().getProperties();
+ e = props.keys();
+ while ( e.hasMoreElements() ) {
+ String arg = e.nextElement().toString();
+ if ( "basedir".equals( arg ) || "ant.file".equals( arg ) ) {
+ // basedir and ant.file get special treatment in execute()
+ continue;
+ }
+
+ String value = props.get( arg ).toString();
+ // don't re-set user properties, avoid the warning message
+ if ( newProject.getProperty( arg ) == null ) {
+ // no user property
+ newProject.setNewProperty( arg, value );
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Pass output sent to System.out to the new project.
+ *
+ * @param line Description of the Parameter
+ * @since Ant 1.5
+ */
+ protected void handleOutput( String line ) {
+ if ( newProject != null ) {
+ newProject.demuxOutput( line, false );
+ }
+ else {
+ super.handleOutput( line );
+ }
+ }
+
+
+ /**
+ * Pass output sent to System.err to the new project.
+ *
+ * @param line Description of the Parameter
+ * @since Ant 1.5
+ */
+ protected void handleErrorOutput( String line ) {
+ if ( newProject != null ) {
+ newProject.demuxOutput( line, true );
+ }
+ else {
+ super.handleErrorOutput( line );
+ }
+ }
+
+
+ /**
+ * Do the execution.
+ *
+ * @exception BuildException Description of the Exception
+ */
+ public void execute() throws BuildException {
+ File savedDir = dir;
+ String savedAntFile = antFile;
+ String savedTarget = target;
+ try {
+ if ( newProject == null ) {
+ reinit();
+ }
+
+ if ( ( dir == null ) && ( inheritAll ) ) {
+ dir = getProject().getBaseDir();
+ }
+
+ initializeProject();
+
+ if ( dir != null ) {
+ newProject.setBaseDir( dir );
+ if ( savedDir != null ) { // has been set explicitly
+ newProject.setInheritedProperty( "basedir",
+ dir.getAbsolutePath() );
+ }
+ }
+ else {
+ dir = getProject().getBaseDir();
+ }
+
+ overrideProperties();
+
+ if ( antFile == null ) {
+ antFile = "build.xml";
+ }
+
+ File file = FileUtils.newFileUtils().resolveFile( dir, antFile );
+ antFile = file.getAbsolutePath();
+
+ log( "calling target " + ( target != null ? target : "[default]" )
+ + " in build file " + antFile.toString(),
+ Project.MSG_VERBOSE );
+ newProject.setUserProperty( "ant.file", antFile );
+ ProjectHelper.configureProject( newProject, new File( antFile ) );
+
+ if ( target == null ) {
+ target = newProject.getDefaultTarget();
+ }
+
+ addReferences();
+
+ // Are we trying to call the target in which we are defined?
+ if ( newProject.getBaseDir().equals( getProject().getBaseDir() ) &&
+ newProject.getProperty( "ant.file" ).equals( getProject().getProperty( "ant.file" ) ) &&
+ getOwningTarget() != null &&
+ target.equals( this.getOwningTarget().getName() ) ) {
+
+ throw new BuildException( "ant task calling its own parent "
+ + "target" );
+ }
+
+ newProject.executeTarget( target );
+
+ // copy back the props if possible
+ if ( returnName != null ) {
+ StringTokenizer st = new StringTokenizer( returnName, "," );
+ while ( st.hasMoreTokens() ) {
+ String name = st.nextToken().trim();
+ String value = newProject.getUserProperty( name );
+ if ( value != null ) {
+ getProject().setUserProperty( name, value );
+ }
+ else {
+ value = newProject.getProperty( name );
+ if ( value != null ) {
+ getProject().setProperty( name, value );
+ }
+ }
+ }
+ }
+ }
+ finally {
+ // help the gc
+ newProject = null;
+ if ( output != null && out != null ) {
+ try {
+ out.close();
+ }
+ catch ( final Exception e ) {
+ //ignore
+ }
+ }
+ dir = savedDir;
+ antFile = savedAntFile;
+ target = savedTarget;
+ }
+ }
+
+
+ /**
+ * Override the properties in the new project with the one explicitly defined
+ * as nested elements here.
+ *
+ * @exception BuildException Description of the Exception
+ */
+ private void overrideProperties() throws BuildException {
+ Enumeration e = properties.elements();
+ while ( e.hasMoreElements() ) {
+ Property p = (Property)e.nextElement();
+ p.setProject( newProject );
+ p.execute();
+ }
+ getProject().copyInheritedProperties( newProject );
+ }
+
+
+ /**
+ * Add the references explicitly defined as nested elements to the new
+ * project. Also copy over all references that don't override existing
+ * references in the new project if inheritrefs has been requested.
+ *
+ * @exception BuildException Description of the Exception
+ */
+ private void addReferences() throws BuildException {
+ Hashtable thisReferences = (Hashtable)getProject().getReferences().clone();
+ Hashtable newReferences = newProject.getReferences();
+ Enumeration e;
+ if ( references.size() > 0 ) {
+ for ( e = references.elements(); e.hasMoreElements(); ) {
+ Reference ref = (Reference)e.nextElement();
+ String refid = ref.getRefId();
+ if ( refid == null ) {
+ throw new BuildException( "the refid attribute is required"
+ + " for reference elements" );
+ }
+ if ( !thisReferences.containsKey( refid ) ) {
+ log( "Parent project doesn't contain any reference '"
+ + refid + "'",
+ Project.MSG_WARN );
+ continue;
+ }
+
+ thisReferences.remove( refid );
+ String toRefid = ref.getToRefid();
+ if ( toRefid == null ) {
+ toRefid = refid;
+ }
+ copyReference( refid, toRefid );
+ }
+ }
+
+ // Now add all references that are not defined in the
+ // subproject, if inheritRefs is true
+ if ( inheritRefs ) {
+ for ( e = thisReferences.keys(); e.hasMoreElements(); ) {
+ String key = (String)e.nextElement();
+ if ( newReferences.containsKey( key ) ) {
+ continue;
+ }
+ copyReference( key, key );
+ }
+ }
+ }
+
+
+ /**
+ * Try to clone and reconfigure the object referenced by oldkey in the parent
+ * project and add it to the new project with the key newkey. <p>
+ *
+ * If we cannot clone it, copy the referenced object itself and keep our
+ * fingers crossed.</p>
+ *
+ * @param oldKey Description of the Parameter
+ * @param newKey Description of the Parameter
+ */
+ private void copyReference( String oldKey, String newKey ) {
+ Object orig = getProject().getReference( oldKey );
+ Class c = orig.getClass();
+ Object copy = orig;
+ try {
+ Method cloneM = c.getMethod( "clone", new Class[0] );
+ if ( cloneM != null ) {
+ copy = cloneM.invoke( orig, new Object[0] );
+ }
+ }
+ catch ( Exception e ) {
+ // not Clonable
+ }
+
+ if ( copy instanceof ProjectComponent ) {
+ ( (ProjectComponent)copy ).setProject( newProject );
+ }
+ else {
+ try {
+ Method setProjectM =
+ c.getMethod( "setProject", new Class[]{Project.class} );
+ if ( setProjectM != null ) {
+ setProjectM.invoke( copy, new Object[]{newProject} );
+ }
+ }
+ catch ( NoSuchMethodException e ) {
+ // ignore this if the class being referenced does not have
+ // a set project method.
+ }
+ catch ( Exception e2 ) {
+ String msg = "Error setting new project instance for "
+ + "reference with id " + oldKey;
+ throw new BuildException( msg, e2, getLocation() );
+ }
+ }
+ newProject.addReference( newKey, copy );
+ }
+
+
+ /**
+ * The directory to use as a base directory for the new Ant project. Defaults
+ * to the current project's basedir, unless inheritall has been set to false,
+ * in which case it doesn't have a default value. This will override the
+ * basedir setting of the called project.
+ *
+ * @param d The new dir value
+ */
+ public void setDir( File d ) {
+ this.dir = d;
+ }
+
+
+ /**
+ * The build file to use. Defaults to "build.xml". This file is expected to
+ * be a filename relative to the dir attribute given.
+ *
+ * @param s The new antfile value
+ */
+ public void setAntfile( String s ) {
+ // @note: it is a string and not a file to handle relative/absolute
+ // otherwise a relative file will be resolved based on the current
+ // basedir.
+ this.antFile = s;
+ }
+
+
+ /**
+ * The target of the new Ant project to execute. Defaults to the new
+ * project's default target.
+ *
+ * @param s The new target value
+ */
+ public void setTarget( String s ) {
+ this.target = s;
+ }
+
+
+ /**
+ * Filename to write the output to. This is relative to the value of the dir
+ * attribute if it has been set or to the base directory of the current
+ * project otherwise.
+ *
+ * @param s The new output value
+ */
+ public void setOutput( String s ) {
+ this.output = s;
+ }
+
+
+ /**
+ * Property to pass to the new project. The property is passed as a 'user
+ * property'
+ *
+ * @return Description of the Return Value
+ */
+ public Property createProperty() {
+ if ( newProject == null ) {
+ reinit();
+ }
+ /*
+ * Property p = new Property( true, getProject() );
+ */
+ Property p = new Property();
+ p.setProject( newProject );
+ p.setTaskName( "property" );
+ properties.addElement( p );
+ return p;
+ }
+
+
+ /**
+ * Set the property or properties that are set in the new project to be
+ * transfered back to the original project. As with all properties, if the
+ * property already exists in the original project, it will not be overridden
+ * by a different value from the new project.
+ *
+ * @param r the name of a property in the new project to set in the original
+ * project. This may be a comma separate list of properties.
+ */
+ public void setReturn( String r ) {
+ returnName = r;
+ }
+
+
+ /**
+ * Reference element identifying a data type to carry over to the new
+ * project.
+ *
+ * @param r The feature to be added to the Reference attribute
+ */
+ public void addReference( Reference r ) {
+ references.addElement( r );
+ }
+
+
+ /**
+ * Helper class that implements the nested &lt;reference&gt; element of
+ * &lt;ant&gt; and &lt;antcall&gt;.
+ *
+ * @author danson
+ */
+ public static class Reference
+ extends org.apache.tools.ant.types.Reference {
+
+ /** Creates a reference to be configured by Ant */
+ public Reference() {
+ super();
+ }
+
+
+ private String targetid = null;
+
+
+ /**
+ * Set the id that this reference to be stored under in the new project.
+ *
+ * @param targetid the id under which this reference will be passed to
+ * the new project
+ */
+ public void setToRefid( String targetid ) {
+ this.targetid = targetid;
+ }
+
+
+ /**
+ * Get the id under which this reference will be stored in the new project
+ *
+ * @return the id of the reference in the new project.
+ */
+ public String getToRefid() {
+ return targetid;
+ }
+ }
+}
diff --git a/src/java/net/sf/antcontrib/logic/Assert.java b/src/java/net/sf/antcontrib/logic/Assert.java
new file mode 100644
index 0000000..38ac6e8
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/Assert.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.taskdefs.Exit;
+import org.apache.tools.ant.taskdefs.Sequential;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.condition.ConditionBase;
+
+
+/**
+ *
+ */
+public class Assert
+ extends ConditionBase
+ implements TaskContainer {
+
+ private List tasks = new ArrayList();
+ private String message;
+ private boolean failOnError;
+
+
+ public void setFailOnError(boolean failOnError) {
+ this.failOnError = failOnError;
+ }
+
+ public void setMessage(String message) {
+ this.message = message;
+ }
+
+ public void addTask(Task task) {
+ tasks.add(task);
+ }
+
+ public void execute() {
+ if (countConditions() == 0) {
+ throw new BuildException("There is no condition specified.");
+ }
+ else if (countConditions() > 1) {
+ throw new BuildException("There must be exactly one condition specified.");
+ }
+
+ Sequential sequential = (Sequential) getProject().createTask("sequential");
+ Condition c = (Condition) getConditions().nextElement();
+ if (! c.eval()) {
+ if (failOnError) {
+ Exit fail = (Exit) getProject().createTask("fail");
+ fail.setMessage(message);
+ sequential.addTask(fail);
+ }
+ }
+ else {
+ Iterator it = tasks.iterator();
+ while (it.hasNext()) {
+ sequential.addTask((Task)it.next());
+ }
+ }
+ }
+
+
+}
diff --git a/src/java/net/sf/antcontrib/logic/ForEach.java b/src/java/net/sf/antcontrib/logic/ForEach.java
new file mode 100644
index 0000000..ad822f0
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/ForEach.java
@@ -0,0 +1,423 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.TaskContainer;
+import org.apache.tools.ant.taskdefs.Ant;
+import org.apache.tools.ant.taskdefs.CallTarget;
+import org.apache.tools.ant.taskdefs.Property;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileNameMapper;
+
+import net.sf.antcontrib.util.ThreadPool;
+import net.sf.antcontrib.util.ThreadPoolThread;
+
+/***
+ * Task definition for the foreach task. The foreach task iterates
+ * over a list, a list of filesets, or both.
+ *
+ * <pre>
+ *
+ * Usage:
+ *
+ * Task declaration in the project:
+ * <code>
+ * &lt;taskdef name="foreach" classname="net.sf.antcontrib.logic.ForEach" /&gt;
+ * </code>
+ *
+ * Call Syntax:
+ * <code>
+ * &lt;foreach list="values" target="targ" param="name"
+ * [parallel="true|false"]
+ * [delimiter="delim"] /&gt;
+ * </code>
+ *
+ * Attributes:
+ * list --> The list of values to process, with the delimiter character,
+ * indicated by the "delim" attribute, separating each value
+ * target --> The target to call for each token, passing the token as the
+ * parameter with the name indicated by the "param" attribute
+ * param --> The name of the parameter to pass the tokens in as to the
+ * target
+ * delimiter --> The delimiter string that separates the values in the "list"
+ * parameter. The default is ","
+ * parallel --> Should all targets execute in parallel. The default is false.
+ * trim --> Should we trim the list item before calling the target?
+ *
+ * </pre>
+ * @author <a href="mailto:[email protected]">Matthew Inger</a>
+ */
+public class ForEach extends Task
+{
+ private String list;
+ private String param;
+ private String delimiter;
+ private String target;
+ private boolean inheritAll;
+ private boolean inheritRefs;
+ private Vector params;
+ private Vector references;
+ private Path currPath;
+ private boolean parallel;
+ private boolean trim;
+ private int maxThreads;
+ private Mapper mapper;
+
+ /***
+ * Default Constructor
+ */
+ public ForEach()
+ {
+ super();
+ this.list = null;
+ this.param = null;
+ this.delimiter = ",";
+ this.target = null;
+ this.inheritAll = false;
+ this.inheritRefs = false;
+ this.params = new Vector();
+ this.references = new Vector();
+ this.parallel = false;
+ this.maxThreads = 5;
+ }
+
+ private void executeParallel(Vector tasks)
+ {
+ ThreadPool pool = new ThreadPool(maxThreads);
+ Enumeration e = tasks.elements();
+ Runnable r = null;
+ Vector threads = new Vector();
+
+ // start each task in it's own thread, using the
+ // pool to ensure that we don't exceed the maximum
+ // amount of threads
+ while (e.hasMoreElements())
+ {
+ // Create the Runnable object
+ final Task task = (Task)e.nextElement();
+ r = new Runnable()
+ {
+ public void run()
+ {
+ task.execute();
+ }
+ };
+
+ // Get a thread, and start the task.
+ // If there is no thread available, this will
+ // block until one becomes available
+ try
+ {
+ ThreadPoolThread tpt = pool.borrowThread();
+ tpt.setRunnable(r);
+ tpt.start();
+ threads.addElement(tpt);
+ }
+ catch (Exception ex)
+ {
+ throw new BuildException(ex);
+ }
+
+ }
+
+ // Wait for all threads to finish before we
+ // are allowed to return.
+ Enumeration te = threads.elements();
+ Thread t= null;
+ while (te.hasMoreElements())
+ {
+ t = (Thread)te.nextElement();
+ if (t.isAlive())
+ {
+ try
+ {
+ t.join();
+ }
+ catch (InterruptedException ex)
+ {
+ throw new BuildException(ex);
+ }
+ }
+ }
+ }
+
+ private void executeSequential(Vector tasks)
+ {
+ TaskContainer tc = (TaskContainer) getProject().createTask("sequential");
+ Enumeration e = tasks.elements();
+ Task t = null;
+ while (e.hasMoreElements())
+ {
+ t = (Task)e.nextElement();
+ tc.addTask(t);
+ }
+
+ ((Task)tc).execute();
+ }
+
+ public void execute()
+ throws BuildException
+ {
+ if (list == null && currPath == null) {
+ throw new BuildException("You must have a list or path to iterate through");
+ }
+ if (param == null)
+ throw new BuildException("You must supply a property name to set on each iteration in param");
+ if (target == null)
+ throw new BuildException("You must supply a target to perform");
+
+ Vector values = new Vector();
+
+ // Take Care of the list attribute
+ if (list != null)
+ {
+ StringTokenizer st = new StringTokenizer(list, delimiter);
+
+ while (st.hasMoreTokens())
+ {
+ String tok = st.nextToken();
+ if (trim) tok = tok.trim();
+ values.addElement(tok);
+ }
+ }
+
+ String[] pathElements = new String[0];
+ if (currPath != null) {
+ pathElements = currPath.list();
+ }
+
+ for (int i=0;i<pathElements.length;i++)
+ {
+ if (mapper != null)
+ {
+ FileNameMapper m = mapper.getImplementation();
+ String mapped[] = m.mapFileName(pathElements[i]);
+ for (int j=0;j<mapped.length;j++)
+ values.addElement(mapped[j]);
+ }
+ else
+ {
+ values.addElement(new File(pathElements[i]));
+ }
+ }
+
+ Vector tasks = new Vector();
+
+ int sz = values.size();
+ CallTarget ct = null;
+ Object val = null;
+ Property p = null;
+
+ for (int i = 0; i < sz; i++) {
+ val = values.elementAt(i);
+ ct = createCallTarget();
+ p = ct.createParam();
+ p.setName(param);
+
+ if (val instanceof File)
+ p.setLocation((File)val);
+ else
+ p.setValue((String)val);
+
+ tasks.addElement(ct);
+ }
+
+ if (parallel && maxThreads > 1)
+ {
+ executeParallel(tasks);
+ }
+ else
+ {
+ executeSequential(tasks);
+ }
+ }
+
+ public void setTrim(boolean trim)
+ {
+ this.trim = trim;
+ }
+
+ public void setList(String list)
+ {
+ this.list = list;
+ }
+
+ public void setDelimiter(String delimiter)
+ {
+ this.delimiter = delimiter;
+ }
+
+ public void setParam(String param)
+ {
+ this.param = param;
+ }
+
+ public void setTarget(String target)
+ {
+ this.target = target;
+ }
+
+ public void setParallel(boolean parallel)
+ {
+ this.parallel = parallel;
+ }
+
+ /**
+ * Corresponds to <code>&lt;antcall&gt;</code>'s <code>inheritall</code>
+ * attribute.
+ */
+ public void setInheritall(boolean b) {
+ this.inheritAll = b;
+ }
+
+ /**
+ * Corresponds to <code>&lt;antcall&gt;</code>'s <code>inheritrefs</code>
+ * attribute.
+ */
+ public void setInheritrefs(boolean b) {
+ this.inheritRefs = b;
+ }
+
+
+ /***
+ * Set the maximum amount of threads we're going to allow
+ * at once to execute
+ * @param maxThreads
+ */
+ public void setMaxThreads(int maxThreads)
+ {
+ this.maxThreads = maxThreads;
+ }
+
+
+ /**
+ * Corresponds to <code>&lt;antcall&gt;</code>'s nested
+ * <code>&lt;param&gt;</code> element.
+ */
+ public void addParam(Property p) {
+ params.addElement(p);
+ }
+
+ /**
+ * Corresponds to <code>&lt;antcall&gt;</code>'s nested
+ * <code>&lt;reference&gt;</code> element.
+ */
+ public void addReference(Ant.Reference r) {
+ references.addElement(r);
+ }
+
+ /**
+ * @deprecated Use createPath instead.
+ */
+ public void addFileset(FileSet set)
+ {
+ log("The nested fileset element is deprectated, use a nested path "
+ + "instead",
+ Project.MSG_WARN);
+ createPath().addFileset(set);
+ }
+
+ public Path createPath() {
+ if (currPath == null) {
+ currPath = new Path(getProject());
+ }
+ return currPath;
+ }
+
+ public Mapper createMapper()
+ {
+ mapper = new Mapper(getProject());
+ return mapper;
+ }
+
+ private CallTarget createCallTarget() {
+ CallTarget ct = (CallTarget) getProject().createTask("antcall");
+ ct.setOwningTarget(getOwningTarget());
+ ct.init();
+ ct.setTarget(target);
+ ct.setInheritAll(inheritAll);
+ ct.setInheritRefs(inheritRefs);
+ Enumeration e = params.elements();
+ while (e.hasMoreElements()) {
+ Property param = (Property) e.nextElement();
+ Property toSet = ct.createParam();
+ toSet.setName(param.getName());
+ if (param.getValue() != null) {
+ toSet.setValue(param.getValue());
+ }
+ if (param.getFile() != null) {
+ toSet.setFile(param.getFile());
+ }
+ if (param.getResource() != null) {
+ toSet.setResource(param.getResource());
+ }
+ if (param.getPrefix() != null) {
+ toSet.setPrefix(param.getPrefix());
+ }
+ if (param.getRefid() != null) {
+ toSet.setRefid(param.getRefid());
+ }
+ if (param.getEnvironment() != null) {
+ toSet.setEnvironment(param.getEnvironment());
+ }
+ if (param.getClasspath() != null) {
+ toSet.setClasspath(param.getClasspath());
+ }
+ }
+
+ e = references.elements();
+ while (e.hasMoreElements()) {
+ ct.addReference((Ant.Reference) e.nextElement());
+ }
+
+ return ct;
+ }
+
+ protected void handleOutput(String line)
+ {
+ try {
+ super.handleOutput(line);
+ }
+ // This is needed so we can run with 1.5 and 1.5.1
+ catch (IllegalAccessError e) {
+ super.handleOutput(line);
+ }
+ }
+
+ protected void handleErrorOutput(String line)
+ {
+ try {
+ super.handleErrorOutput(line);
+ }
+ // This is needed so we can run with 1.5 and 1.5.1
+ catch (IllegalAccessError e) {
+ super.handleErrorOutput(line);
+ }
+ }
+
+}
+
+
diff --git a/src/java/net/sf/antcontrib/logic/ForTask.java b/src/java/net/sf/antcontrib/logic/ForTask.java
new file mode 100644
index 0000000..516773b
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/ForTask.java
@@ -0,0 +1,433 @@
+/*
+ * Copyright (c) 2003-2005 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.MacroDef;
+import org.apache.tools.ant.taskdefs.MacroInstance;
+import org.apache.tools.ant.taskdefs.Parallel;
+import org.apache.tools.ant.types.DirSet;
+import org.apache.tools.ant.types.FileSet;
+import org.apache.tools.ant.types.Path;
+
+/***
+ * Task definition for the for task. This is based on
+ * the foreach task but takes a sequential element
+ * instead of a target and only works for ant >= 1.6Beta3
+ * @author Peter Reilly
+ */
+public class ForTask extends Task {
+
+ private String list;
+ private String param;
+ private String delimiter = ",";
+ private Path currPath;
+ private boolean trim;
+ private boolean keepgoing = false;
+ private MacroDef macroDef;
+ private List hasIterators = new ArrayList();
+ private boolean parallel = false;
+ private Integer threadCount;
+ private Parallel parallelTasks;
+
+ /**
+ * Creates a new <code>For</code> instance.
+ * This checks if the ant version is correct to run this task.
+ */
+ public ForTask() {
+ }
+
+ /**
+ * Attribute whether to execute the loop in parallel or in sequence.
+ * @param parallel if true execute the tasks in parallel. Default is false.
+ */
+ public void setParallel(boolean parallel) {
+ this.parallel = parallel;
+ }
+
+ /***
+ * Set the maximum amount of threads we're going to allow
+ * to execute in parallel
+ * @param threadCount the number of threads to use
+ */
+ public void setThreadCount(int threadCount) {
+ if (threadCount < 1) {
+ throw new BuildException("Illegal value for threadCount " + threadCount
+ + " it should be > 0");
+ }
+ this.threadCount = new Integer(threadCount);
+ }
+
+ /**
+ * Set the trim attribute.
+ *
+ * @param trim if true, trim the value for each iterator.
+ */
+ public void setTrim(boolean trim) {
+ this.trim = trim;
+ }
+
+ /**
+ * Set the keepgoing attribute, indicating whether we
+ * should stop on errors or continue heedlessly onward.
+ *
+ * @param keepgoing a boolean, if <code>true</code> then we act in
+ * the keepgoing manner described.
+ */
+ public void setKeepgoing(boolean keepgoing) {
+ this.keepgoing = keepgoing;
+ }
+
+ /**
+ * Set the list attribute.
+ *
+ * @param list a list of delimiter separated tokens.
+ */
+ public void setList(String list) {
+ this.list = list;
+ }
+
+ /**
+ * Set the delimiter attribute.
+ *
+ * @param delimiter the delimiter used to separate the tokens in
+ * the list attribute. The default is ",".
+ */
+ public void setDelimiter(String delimiter) {
+ this.delimiter = delimiter;
+ }
+
+ /**
+ * Set the param attribute.
+ * This is the name of the macrodef attribute that
+ * gets set for each iterator of the sequential element.
+ *
+ * @param param the name of the macrodef attribute.
+ */
+ public void setParam(String param) {
+ this.param = param;
+ }
+
+ private Path getOrCreatePath() {
+ if (currPath == null) {
+ currPath = new Path(getProject());
+ }
+ return currPath;
+ }
+
+ /**
+ * This is a path that can be used instread of the list
+ * attribute to interate over. If this is set, each
+ * path element in the path is used for an interator of the
+ * sequential element.
+ *
+ * @param path the path to be set by the ant script.
+ */
+ public void addConfigured(Path path) {
+ getOrCreatePath().append(path);
+ }
+
+ /**
+ * This is a path that can be used instread of the list
+ * attribute to interate over. If this is set, each
+ * path element in the path is used for an interator of the
+ * sequential element.
+ *
+ * @param path the path to be set by the ant script.
+ */
+ public void addConfiguredPath(Path path) {
+ addConfigured(path);
+ }
+
+ /**
+ * @return a MacroDef#NestedSequential object to be configured
+ */
+ public Object createSequential() {
+ macroDef = new MacroDef();
+ macroDef.setProject(getProject());
+ return macroDef.createSequential();
+ }
+
+ /**
+ * Run the for task.
+ * This checks the attributes and nested elements, and
+ * if there are ok, it calls doTheTasks()
+ * which constructes a macrodef task and a
+ * for each interation a macrodef instance.
+ */
+ public void execute() {
+ if (parallel) {
+ parallelTasks = (Parallel) getProject().createTask("parallel");
+ if (threadCount != null) {
+ parallelTasks.setThreadCount(threadCount.intValue());
+ }
+ }
+ if (list == null && currPath == null && hasIterators.size() == 0) {
+ throw new BuildException(
+ "You must have a list or path to iterate through");
+ }
+ if (param == null) {
+ throw new BuildException(
+ "You must supply a property name to set on"
+ + " each iteration in param");
+ }
+ if (macroDef == null) {
+ throw new BuildException(
+ "You must supply an embedded sequential "
+ + "to perform");
+ }
+ doTheTasks();
+ if (parallel) {
+ parallelTasks.perform();
+ }
+ }
+
+
+ private void doSequentialIteration(String val) {
+ MacroInstance instance = new MacroInstance();
+ instance.setProject(getProject());
+ instance.setOwningTarget(getOwningTarget());
+ instance.setMacroDef(macroDef);
+ instance.setDynamicAttribute(param.toLowerCase(),
+ val);
+ if (!parallel) {
+ instance.execute();
+ } else {
+ parallelTasks.addTask(instance);
+ }
+ }
+
+ private void doTheTasks() {
+ int errorCount = 0;
+ int taskCount = 0;
+
+ // Create a macro attribute
+ MacroDef.Attribute attribute = new MacroDef.Attribute();
+ attribute.setName(param);
+ macroDef.addConfiguredAttribute(attribute);
+ // Take Care of the list attribute
+ if (list != null) {
+ StringTokenizer st = new StringTokenizer(list, delimiter);
+
+ while (st.hasMoreTokens()) {
+ String tok = st.nextToken();
+ if (trim) {
+ tok = tok.trim();
+ }
+ try {
+ taskCount++;
+ doSequentialIteration(tok);
+ } catch (BuildException bx) {
+ if (keepgoing) {
+ log(tok + ": " + bx.getMessage(), Project.MSG_ERR);
+ errorCount++;
+ } else {
+ throw bx;
+ }
+ }
+ }
+ }
+ if (keepgoing && (errorCount != 0)) {
+ throw new BuildException(
+ "Keepgoing execution: " + errorCount
+ + " of " + taskCount + " iterations failed.");
+ }
+
+ // Take Care of the path element
+ String[] pathElements = new String[0];
+ if (currPath != null) {
+ pathElements = currPath.list();
+ }
+ for (int i = 0; i < pathElements.length; i++) {
+ File nextFile = new File(pathElements[i]);
+ try {
+ taskCount++;
+ doSequentialIteration(nextFile.getAbsolutePath());
+ } catch (BuildException bx) {
+ if (keepgoing) {
+ log(nextFile + ": " + bx.getMessage(), Project.MSG_ERR);
+ errorCount++;
+ } else {
+ throw bx;
+ }
+ }
+ }
+ if (keepgoing && (errorCount != 0)) {
+ throw new BuildException(
+ "Keepgoing execution: " + errorCount
+ + " of " + taskCount + " iterations failed.");
+ }
+
+ // Take care of iterators
+ for (Iterator i = hasIterators.iterator(); i.hasNext();) {
+ Iterator it = ((HasIterator) i.next()).iterator();
+ while (it.hasNext()) {
+ String s = it.next().toString();
+ try {
+ taskCount++;
+ doSequentialIteration(s);
+ } catch (BuildException bx) {
+ if (keepgoing) {
+ log(s + ": " + bx.getMessage(), Project.MSG_ERR);
+ errorCount++;
+ } else {
+ throw bx;
+ }
+ }
+ }
+ }
+ if (keepgoing && (errorCount != 0)) {
+ throw new BuildException(
+ "Keepgoing execution: " + errorCount
+ + " of " + taskCount + " iterations failed.");
+ }
+ }
+
+ /**
+ * Add a Map, iterate over the values
+ *
+ * @param map a Map object - iterate over the values.
+ */
+ public void add(Map map) {
+ hasIterators.add(new MapIterator(map));
+ }
+
+ /**
+ * Add a fileset to be iterated over.
+ *
+ * @param fileset a <code>FileSet</code> value
+ */
+ public void add(FileSet fileset) {
+ getOrCreatePath().addFileset(fileset);
+ }
+
+ /**
+ * Add a fileset to be iterated over.
+ *
+ * @param fileset a <code>FileSet</code> value
+ */
+ public void addFileSet(FileSet fileset) {
+ add(fileset);
+ }
+
+ /**
+ * Add a dirset to be iterated over.
+ *
+ * @param dirset a <code>DirSet</code> value
+ */
+ public void add(DirSet dirset) {
+ getOrCreatePath().addDirset(dirset);
+ }
+
+ /**
+ * Add a dirset to be iterated over.
+ *
+ * @param dirset a <code>DirSet</code> value
+ */
+ public void addDirSet(DirSet dirset) {
+ add(dirset);
+ }
+
+ /**
+ * Add a collection that can be iterated over.
+ *
+ * @param collection a <code>Collection</code> value.
+ */
+ public void add(Collection collection) {
+ hasIterators.add(new ReflectIterator(collection));
+ }
+
+ /**
+ * Add an iterator to be iterated over.
+ *
+ * @param iterator an <code>Iterator</code> value
+ */
+ public void add(Iterator iterator) {
+ hasIterators.add(new IteratorIterator(iterator));
+ }
+
+ /**
+ * Add an object that has an Iterator iterator() method
+ * that can be iterated over.
+ *
+ * @param obj An object that can be iterated over.
+ */
+ public void add(Object obj) {
+ hasIterators.add(new ReflectIterator(obj));
+ }
+
+ /**
+ * Interface for the objects in the iterator collection.
+ */
+ private interface HasIterator {
+ Iterator iterator();
+ }
+
+ private static class IteratorIterator implements HasIterator {
+ private Iterator iterator;
+ public IteratorIterator(Iterator iterator) {
+ this.iterator = iterator;
+ }
+ public Iterator iterator() {
+ return this.iterator;
+ }
+ }
+
+ private static class MapIterator implements HasIterator {
+ private Map map;
+ public MapIterator(Map map) {
+ this.map = map;
+ }
+ public Iterator iterator() {
+ return map.values().iterator();
+ }
+ }
+
+ private static class ReflectIterator implements HasIterator {
+ private Object obj;
+ private Method method;
+ public ReflectIterator(Object obj) {
+ this.obj = obj;
+ try {
+ method = obj.getClass().getMethod(
+ "iterator", new Class[] {});
+ } catch (Throwable t) {
+ throw new BuildException(
+ "Invalid type " + obj.getClass() + " used in For task, it does"
+ + " not have a public iterator method");
+ }
+ }
+
+ public Iterator iterator() {
+ try {
+ return (Iterator) method.invoke(obj, new Object[] {});
+ } catch (Throwable t) {
+ throw new BuildException(t);
+ }
+ }
+ }
+}
diff --git a/src/java/net/sf/antcontrib/logic/IfTask.java b/src/java/net/sf/antcontrib/logic/IfTask.java
new file mode 100644
index 0000000..4d538df
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/IfTask.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+package net.sf.antcontrib.logic;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Sequential;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.taskdefs.condition.ConditionBase;
+
+/**
+ * Perform some tasks based on whether a given condition holds true or
+ * not.
+ *
+ * <p>This task is heavily based on the Condition framework that can
+ * be found in Ant 1.4 and later, therefore it cannot be used in
+ * conjunction with versions of Ant prior to 1.4.</p>
+ *
+ * <p>This task doesn't have any attributes, the condition to test is
+ * specified by a nested element - see the documentation of your
+ * <code>&lt;condition&gt;</code> task (see
+ * <a href="http://jakarta.apache.org/ant/manual/CoreTasks/condition.html">the
+ * online documentation</a> for example) for a complete list of nested
+ * elements.</p>
+ *
+ * <p>Just like the <code>&lt;condition&gt;</code> task, only a single
+ * condition can be specified - you combine them using
+ * <code>&lt;and&gt;</code> or <code>&lt;or&gt;</code> conditions.</p>
+ *
+ * <p>In addition to the condition, you can specify three different
+ * child elements, <code>&lt;elseif&gt;</code>, <code>&lt;then&gt;</code> and
+ * <code>&lt;else&gt;</code>. All three subelements are optional.
+ *
+ * Both <code>&lt;then&gt;</code> and <code>&lt;else&gt;</code> must not be
+ * used more than once inside the if task. Both are
+ * containers for Ant tasks, just like Ant's
+ * <code>&lt;parallel&gt;</code> and <code>&lt;sequential&gt;</code>
+ * tasks - in fact they are implemented using the same class as Ant's
+ * <code>&lt;sequential&gt;</code> task.</p>
+ *
+ * The <code>&lt;elseif&gt;</code> behaves exactly like an <code>&lt;if&gt;</code>
+ * except that it cannot contain the <code>&lt;else&gt;</code> element
+ * inside of it. You may specify as may of these as you like, and the
+ * order they are specified is the order they are evaluated in. If the
+ * condition on the <code>&lt;if&gt;</code> is false, then the first
+ * <code>&lt;elseif&gt;</code> who's conditional evaluates to true
+ * will be executed. The <code>&lt;else&gt;</code> will be executed
+ * only if the <code>&lt;if&gt;</code> and all <code>&lt;elseif&gt;</code>
+ * conditions are false.
+ *
+ * <p>Use the following task to define the <code>&lt;if&gt;</code>
+ * task before you use it the first time:</p>
+ *
+ * <pre><code>
+ * &lt;taskdef name="if" classname="net.sf.antcontrib.logic.IfTask" /&gt;
+ * </code></pre>
+ *
+ * <h3>Crude Example</h3>
+ *
+ * <pre><code>
+ * &lt;if&gt;
+ * &lt;equals arg1=&quot;${foo}&quot; arg2=&quot;bar&quot; /&gt;
+ * &lt;then&gt;
+ * &lt;echo message=&quot;The value of property foo is bar&quot; /&gt;
+ * &lt;/then&gt;
+ * &lt;else&gt;
+ * &lt;echo message=&quot;The value of property foo is not bar&quot; /&gt;
+ * &lt;/else&gt;
+ * &lt;/if&gt;
+ * </code>
+ *
+ * <code>
+ * &lt;if&gt;
+ * &lt;equals arg1=&quot;${foo}&quot; arg2=&quot;bar&quot; /&gt;
+ * &lt;then&gt;
+ * &lt;echo message=&quot;The value of property foo is 'bar'&quot; /&gt;
+ * &lt;/then&gt;
+ *
+ * &lt;elseif&gt;
+ * &lt;equals arg1=&quot;${foo}&quot; arg2=&quot;foo&quot; /&gt;
+ * &lt;then&gt;
+ * &lt;echo message=&quot;The value of property foo is 'foo'&quot; /&gt;
+ * &lt;/then&gt;
+ * &lt;/elseif&gt;
+ *
+ * &lt;else&gt;
+ * &lt;echo message=&quot;The value of property foo is not 'foo' or 'bar'&quot; /&gt;
+ * &lt;/else&gt;
+ * &lt;/if&gt;
+ * </code></pre>
+ *
+ * @author <a href="mailto:[email protected]">Stefan Bodewig</a>
+ */
+public class IfTask extends ConditionBase {
+
+ public static final class ElseIf
+ extends ConditionBase
+ {
+ private Sequential thenTasks = null;
+
+ public void addThen(Sequential t)
+ {
+ if (thenTasks != null)
+ {
+ throw new BuildException("You must not nest more than one <then> into <elseif>");
+ }
+ thenTasks = t;
+ }
+
+ public boolean eval()
+ throws BuildException
+ {
+ if (countConditions() > 1) {
+ throw new BuildException("You must not nest more than one condition into <elseif>");
+ }
+ if (countConditions() < 1) {
+ throw new BuildException("You must nest a condition into <elseif>");
+ }
+ Condition c = (Condition) getConditions().nextElement();
+
+ return c.eval();
+ }
+
+ public void execute()
+ throws BuildException
+ {
+ if (thenTasks != null)
+ {
+ thenTasks.execute();
+ }
+ }
+ }
+
+ private Sequential thenTasks = null;
+ private Vector elseIfTasks = new Vector();
+ private Sequential elseTasks = null;
+
+ /***
+ * A nested Else if task
+ */
+ public void addElseIf(ElseIf ei)
+ {
+ elseIfTasks.addElement(ei);
+ }
+
+ /**
+ * A nested &lt;then&gt; element - a container of tasks that will
+ * be run if the condition holds true.
+ *
+ * <p>Not required.</p>
+ */
+ public void addThen(Sequential t) {
+ if (thenTasks != null) {
+ throw new BuildException("You must not nest more than one <then> into <if>");
+ }
+ thenTasks = t;
+ }
+
+ /**
+ * A nested &lt;else&gt; element - a container of tasks that will
+ * be run if the condition doesn't hold true.
+ *
+ * <p>Not required.</p>
+ */
+ public void addElse(Sequential e) {
+ if (elseTasks != null) {
+ throw new BuildException("You must not nest more than one <else> into <if>");
+ }
+ elseTasks = e;
+ }
+
+ public void execute() throws BuildException {
+ if (countConditions() > 1) {
+ throw new BuildException("You must not nest more than one condition into <if>");
+ }
+ if (countConditions() < 1) {
+ throw new BuildException("You must nest a condition into <if>");
+ }
+ Condition c = (Condition) getConditions().nextElement();
+ if (c.eval()) {
+ if (thenTasks != null) {
+ thenTasks.execute();
+ }
+ }
+ else
+ {
+ boolean done = false;
+ int sz = elseIfTasks.size();
+ for (int i=0;i<sz && ! done;i++)
+ {
+
+ ElseIf ei = (ElseIf)(elseIfTasks.elementAt(i));
+ if (ei.eval())
+ {
+ done = true;
+ ei.execute();
+ }
+ }
+
+ if (!done && elseTasks != null)
+ {
+ elseTasks.execute();
+ }
+ }
+ }
+}
diff --git a/src/java/net/sf/antcontrib/logic/OutOfDate.java b/src/java/net/sf/antcontrib/logic/OutOfDate.java
new file mode 100644
index 0000000..014a15e
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/OutOfDate.java
@@ -0,0 +1,675 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+
+import java.io.File;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Hashtable;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Parallel;
+import org.apache.tools.ant.taskdefs.Sequential;
+import org.apache.tools.ant.taskdefs.condition.Condition;
+import org.apache.tools.ant.types.Mapper;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.util.FileNameMapper;
+import org.apache.tools.ant.util.FileUtils;
+
+import org.apache.tools.ant.types.EnumeratedAttribute;
+
+/**
+* Task to help in calling tasks if generated files are older
+* than source files.
+* Sets a given property or runs an internal task.
+*
+* Based on
+* org.apache.org.apache.tools.ant.taskdefs.UpToDate
+*
+* @author peter reilly
+*/
+
+public class OutOfDate extends Task implements Condition {
+
+ /**
+ * Enumerated type for collection attribute
+ *
+ * @see EnumeratedAttribute
+ */
+ public static class CollectionEnum extends EnumeratedAttribute {
+ /** Constants for the enumerations */
+ public static final int
+ SOURCES = 0, TARGETS = 1, ALLSOURCES = 2, ALLTARGETS = 3;
+
+ /**
+ * get the values
+ * @return an array of the allowed values for this attribute.
+ */
+ public String[] getValues() {
+ return new String[] {"sources", "targets", "allsources", "alltargets"};
+ }
+ }
+
+ // attributes and nested elements
+ private Task doTask = null;
+ private String property;
+ private String value = "true";
+ private boolean force = false;
+ private int verbosity = Project.MSG_VERBOSE;
+ private Vector mappers = new Vector();
+ private Path targetpaths = null;
+ private Path sourcepaths = null;
+ private String outputSources = null;
+ private String outputSourcesPath = null;
+ private String outputTargets = null;
+ private String outputTargetsPath = null;
+ private String allTargets = null;
+ private String allTargetsPath = null;
+ private String separator = " ";
+ private DeleteTargets deleteTargets = null;
+ private int collection = CollectionEnum.SOURCES;
+
+ // variables
+ private Hashtable targetSet = new Hashtable();
+ private Hashtable sourceSet = new Hashtable();
+ private Hashtable allTargetSet = new Hashtable();
+ private Hashtable allSourceSet = new Hashtable();
+
+ /**
+ * Set the collection attribute, controls what is
+ * returned by the iterator method.
+ * <dl>
+ * <li>"sources" the sources that are newer than the corresponding targets.</li>
+ * <li>"targets" the targets that are older or not present than the corresponding
+ * sources.</li>
+ * <li>"allsources" all the sources</li>
+ * <li>"alltargets" all the targets</li>
+ * </dl>
+ * @param collection "sources" the changes
+ */
+ public void setCollection(CollectionEnum collection) {
+ this.collection = collection.getIndex();
+ }
+
+ /**
+ * Defines the FileNameMapper to use (nested mapper element).
+ * @return Mappper to be configured
+ */
+ public Mapper createMapper() {
+ MyMapper mapper = new MyMapper(getProject());
+ mappers.addElement(mapper);
+ return mapper;
+ }
+
+ /**
+ * The property to set if any of the target files are outofdate with
+ * regard to any of the source files.
+ *
+ * @param property the name of the property to set if Target is outofdate.
+ */
+ public void setProperty(String property) {
+ this.property = property;
+ }
+
+ /**
+ * The separator to use to separate the files
+ * @param separator separator used in outout properties
+ */
+
+ public void setSeparator(String separator) {
+ this.separator = separator;
+ }
+
+ /**
+ * The value to set the named property to the target files
+ * are outofdate
+ *
+ * @param value the value to set the property
+ */
+ public void setValue(String value) {
+ this.value = value;
+ }
+
+ /**
+ * whether to allways be outofdate
+ * @param force true means that outofdate is always set, default
+ * false
+ */
+ public void setForce(boolean force) {
+ this.force = force;
+ }
+
+ /**
+ * whether to have verbose output
+ * @param verbose true means that outofdate outputs debug info
+ */
+ public void setVerbose(boolean verbose) {
+ if (verbose) {
+ this.verbosity = Project.MSG_INFO;
+ } else {
+ this.verbosity = Project.MSG_VERBOSE;
+ }
+ }
+
+ /**
+ * Add to the target files
+ *
+ * @return a path to be configured
+ */
+ public Path createTargetfiles() {
+ if (targetpaths == null) {
+ targetpaths = new Path(getProject());
+ }
+ return targetpaths;
+ }
+
+ /**
+ * Add to the source files
+ *
+ * @return a path to be configured
+ */
+ public Path createSourcefiles() {
+ if (sourcepaths == null) {
+ sourcepaths = new Path(getProject());
+ }
+ return sourcepaths;
+ }
+
+ /**
+ * A property to contain the output source files
+ *
+ * @param outputSources the name of the property
+ */
+ public void setOutputSources(String outputSources) {
+ this.outputSources = outputSources;
+ }
+
+ /**
+ * A property to contain the output target files
+ *
+ * @param outputTargets the name of the property
+ */
+ public void setOutputTargets(String outputTargets) {
+ this.outputTargets = outputTargets;
+ }
+
+ /**
+ * A reference to contain the path of target files that
+ * are outofdate
+ *
+ * @param outputTargetsPath the name of the reference
+ */
+ public void setOutputTargetsPath(String outputTargetsPath) {
+ this.outputTargetsPath = outputTargetsPath;
+ }
+
+ /**
+ * A refernce to contain the path of all the targets
+ *
+ * @param allTargetsPath the name of the reference
+ */
+ public void setAllTargetsPath(String allTargetsPath) {
+ this.allTargetsPath = allTargetsPath;
+ }
+
+ /**
+ * A property to contain all the target filenames
+ *
+ * @param allTargets the name of the property
+ */
+ public void setAllTargets(String allTargets) {
+ this.allTargets = allTargets;
+ }
+
+ /**
+ * A reference to the path containing all the sources files.
+ *
+ * @param outputSourcesPath the name of the reference
+ */
+ public void setOutputSourcesPath(String outputSourcesPath) {
+ this.outputSourcesPath = outputSourcesPath;
+ }
+
+ /**
+ * optional nested delete element
+ * @return an element to be configured
+ */
+ public DeleteTargets createDeleteTargets() {
+ deleteTargets = new DeleteTargets();
+ return deleteTargets;
+ }
+
+ /**
+ * Embedded do parallel
+ * @param doTask the parallel to embed
+ */
+ public void addParallel(Parallel doTask) {
+ if (this.doTask != null) {
+ throw new BuildException(
+ "You must not nest more that one <parallel> or <sequential>"
+ + " into <outofdate>");
+ }
+ this.doTask = doTask;
+ }
+
+ /**
+ * Embedded do sequential.
+ * @param doTask the sequential to embed
+ */
+ public void addSequential(Sequential doTask) {
+ if (this.doTask != null) {
+ throw new BuildException(
+ "You must not nest more that one <parallel> or <sequential>"
+ + " into <outofdate>");
+ }
+ this.doTask = doTask;
+ }
+
+ /**
+ * Evaluate (all) target and source file(s) to
+ * see if the target(s) is/are outoutdate.
+ * @return true if any of the targets are outofdate
+ */
+ public boolean eval() {
+ boolean ret = false;
+ FileUtils fileUtils = FileUtils.newFileUtils();
+ if (sourcepaths == null) {
+ throw new BuildException(
+ "You must specify a <sourcefiles> element.");
+ }
+
+ if (targetpaths == null && mappers.size() == 0) {
+ throw new BuildException(
+ "You must specify a <targetfiles> or <mapper> element.");
+ }
+
+ // Source Paths
+ String[] spaths = sourcepaths.list();
+
+ for (int i = 0; i < spaths.length; i++) {
+ File sourceFile = new File(spaths[i]);
+ if (!sourceFile.exists()) {
+ throw new BuildException(sourceFile.getAbsolutePath()
+ + " not found.");
+ }
+ }
+
+ // Target Paths
+
+ if (targetpaths != null) {
+ String[] paths = targetpaths.list();
+ for (int i = 0; i < paths.length; ++i) {
+ if (targetNeedsGen(paths[i], spaths)) {
+ ret = true;
+ }
+ }
+ }
+
+ // Mapper Paths
+ for (Enumeration e = mappers.elements(); e.hasMoreElements();) {
+ MyMapper mapper = (MyMapper) e.nextElement();
+
+ File relativeDir = mapper.getDir();
+ File baseDir = new File(getProject().getProperty("basedir"));
+ if (relativeDir == null) {
+ relativeDir = baseDir;
+ }
+ String[] rpaths = new String[spaths.length];
+ for (int i = 0; i < spaths.length; ++i) {
+ rpaths[i] = fileUtils.removeLeadingPath(relativeDir, new File(spaths[i]));
+ }
+
+ FileNameMapper fileNameMapper = mapper.getImplementation();
+ for (int i = 0; i < spaths.length; ++i) {
+ String[] mapped = fileNameMapper.mapFileName(rpaths[i]);
+ if (mapped != null) {
+ for (int j = 0; j < mapped.length; ++j) {
+ if (outOfDate(new File(spaths[i]),
+ fileUtils.resolveFile(
+ baseDir, mapped[j]))) {
+ ret = true;
+ }
+ }
+ }
+ }
+ }
+
+ if (allTargets != null) {
+ this.getProject().setNewProperty(
+ allTargets, setToString(allTargetSet));
+ }
+
+ if (allTargetsPath != null) {
+ this.getProject().addReference(
+ allTargetsPath, setToPath(allTargetSet));
+ }
+
+ if (outputSources != null) {
+ this.getProject().setNewProperty(
+ outputSources, setToString(sourceSet));
+ }
+
+ if (outputTargets != null) {
+ this.getProject().setNewProperty(
+ outputTargets, setToString(targetSet));
+ }
+
+ if (outputSourcesPath != null) {
+ this.getProject().addReference(
+ outputSourcesPath, setToPath(sourceSet));
+ }
+
+ if (outputTargetsPath != null) {
+ this.getProject().addReference(
+ outputTargetsPath, setToPath(targetSet));
+ }
+
+ if (force) {
+ ret = true;
+ }
+
+ if (ret && deleteTargets != null) {
+ deleteTargets.execute();
+ }
+
+ if (ret) {
+ if (property != null) {
+ this.getProject().setNewProperty(property, value);
+ }
+ }
+
+ return ret;
+ }
+
+ private boolean targetNeedsGen(String target, String[] spaths) {
+ boolean ret = false;
+ File targetFile = new File(target);
+ for (int i = 0; i < spaths.length; i++) {
+ if (outOfDate(new File(spaths[i]), targetFile)) {
+ ret = true;
+ }
+ }
+ // Special case : there are no source files, make sure the
+ // targets exist
+ if (spaths.length == 0) {
+ if (outOfDate(null, targetFile)) {
+ ret = true;
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Call evalute and return an iterator over the result
+ * @return an iterator over the result
+ */
+ public Iterator iterator() {
+ // Perhaps should check the result and return
+ // an empty set if it returns false
+ eval();
+
+ switch (collection) {
+ case CollectionEnum.SOURCES:
+ return sourceSet.values().iterator();
+ case CollectionEnum.TARGETS:
+ return targetSet.values().iterator();
+ case CollectionEnum.ALLSOURCES:
+ return allSourceSet.values().iterator();
+ case CollectionEnum.ALLTARGETS:
+ return allTargetSet.values().iterator();
+ default:
+ return sourceSet.values().iterator();
+ }
+ }
+
+ /**
+ * Sets property to true and/or executes embedded do
+ * if any of the target file(s) do not have a more recent timestamp
+ * than (each of) the source file(s).
+ */
+ public void execute() {
+ if (!eval()) {
+ return;
+ }
+
+ if (doTask != null) {
+ doTask.perform();
+ }
+
+ }
+
+
+ private boolean outOfDate(File sourceFile, File targetFile) {
+ boolean ret = false;
+ if (sourceFile != null) {
+ allSourceSet.put(sourceFile, sourceFile);
+ }
+ allTargetSet.put(targetFile, targetFile);
+ if (!targetFile.exists()) {
+ ret = true;
+ }
+ if ((!ret) && (sourceFile != null)) {
+ ret = sourceFile.lastModified() > targetFile.lastModified();
+ }
+ if (ret) {
+ if ((sourceFile != null && sourceSet.get(sourceFile) == null)
+ || targetSet.get(targetFile) == null) {
+ log("SourceFile " + sourceFile + " outofdate "
+ + "with regard to " + targetFile, verbosity);
+ }
+ if (sourceFile != null) {
+ sourceSet.put(sourceFile, sourceFile);
+ }
+ targetSet.put(targetFile, targetFile);
+ }
+ return ret;
+ }
+
+ private String setToString(Hashtable set) {
+ StringBuffer b = new StringBuffer();
+ for (Enumeration e = set.keys(); e.hasMoreElements();) {
+ File v = (File) e.nextElement();
+ if (b.length() != 0) {
+ b.append(separator);
+ }
+ String s = v.getAbsolutePath();
+ // DOTO: The following needs more work!
+ // Handle paths contains sep
+ if (s.indexOf(separator) != -1) {
+ if (s.indexOf("\"") != -1) {
+ s = "'" + s + "'";
+ } else {
+ s = "\"" + s + "\"";
+ }
+ }
+ b.append(s);
+ }
+ return b.toString();
+ }
+
+ private Path setToPath(Hashtable set) {
+ Path ret = new Path(getProject());
+ for (Enumeration e = set.keys(); e.hasMoreElements();) {
+ File v = (File) e.nextElement();
+ Path.PathElement el = ret.createPathElement();
+ el.setLocation(v);
+ }
+ return ret;
+ }
+
+ /**
+ * nested delete targets
+ */
+ public class DeleteTargets {
+ private boolean all = false;
+ private boolean quiet = false;
+ private boolean failOnError = false;
+
+ private int myLogging = Project.MSG_INFO;
+
+ /**
+ * whether to delete all the targets
+ * or just those that are newer than the
+ * corresponding sources.
+ * @param all true to delete all, default false
+ */
+ public void setAll(boolean all) {
+ this.all = all;
+ }
+
+ /**
+ * @param quiet if true suppress messages on deleting files
+ */
+ public void setQuiet(boolean quiet) {
+ this.quiet = quiet;
+ myLogging = quiet ? Project.MSG_VERBOSE : Project.MSG_INFO;
+ }
+
+ /**
+ * @param failOnError if true halt if there is a failure to delete
+ */
+ public void setFailOnError(boolean failOnError) {
+ this.failOnError = failOnError;
+ }
+
+ private void execute() {
+ if (myLogging != Project.MSG_INFO) {
+ myLogging = verbosity;
+ }
+
+ // Quiet overrides failOnError
+ if (quiet) {
+ failOnError = false;
+ }
+
+ Path toBeDeleted = null;
+ if (all) {
+ toBeDeleted = setToPath(allTargetSet);
+ } else {
+ toBeDeleted = setToPath(targetSet);
+ }
+
+ String[] names = toBeDeleted.list();
+ for (int i = 0; i < names.length; ++i) {
+ File file = new File(names[i]);
+ if (!file.exists()) {
+ continue;
+ }
+ if (file.isDirectory()) {
+ removeDir(file);
+ continue;
+ }
+ log("Deleting " + file.getAbsolutePath(), myLogging);
+ if (!file.delete()) {
+ String message =
+ "Unable to delete file " + file.getAbsolutePath();
+ if (failOnError) {
+ throw new BuildException(message);
+ } else {
+ log(message, myLogging);
+ }
+ }
+ }
+ }
+
+ private static final int DELETE_RETRY_SLEEP_MILLIS = 10;
+ /**
+ * Attempt to fix possible race condition when deleting
+ * files on WinXP. If the delete does not work,
+ * wait a little and try again.
+ */
+ private boolean delete(File f) {
+ if (!f.delete()) {
+ try {
+ Thread.sleep(DELETE_RETRY_SLEEP_MILLIS);
+ return f.delete();
+ } catch (InterruptedException ex) {
+ return f.delete();
+ }
+ }
+ return true;
+ }
+
+ private void removeDir(File d) {
+ String[] list = d.list();
+ if (list == null) {
+ list = new String[0];
+ }
+ for (int i = 0; i < list.length; i++) {
+ String s = list[i];
+ File f = new File(d, s);
+ if (f.isDirectory()) {
+ removeDir(f);
+ } else {
+ log("Deleting " + f.getAbsolutePath(), myLogging);
+ if (!f.delete()) {
+ String message = "Unable to delete file "
+ + f.getAbsolutePath();
+ if (failOnError) {
+ throw new BuildException(message);
+ } else {
+ log(message, myLogging);
+ }
+ }
+ }
+ }
+ log("Deleting directory " + d.getAbsolutePath(), myLogging);
+ if (!delete(d)) {
+ String message = "Unable to delete directory "
+ + d.getAbsolutePath();
+ if (failOnError) {
+ throw new BuildException(message);
+ } else {
+ log(message, myLogging);
+ }
+ }
+ }
+ }
+
+ /**
+ * Wrapper for mapper - includes dir
+ */
+ public static class MyMapper extends Mapper {
+ private File dir = null;
+ /**
+ * Creates a new <code>MyMapper</code> instance.
+ *
+ * @param project the current project
+ */
+ public MyMapper(Project project) {
+ super(project);
+ }
+
+ /**
+ * @param dir the directory that the from files are relative to
+ */
+ public void setDir(File dir) {
+ this.dir = dir;
+ }
+
+ /**
+ * @return the directory that the from files are relative to
+ */
+ public File getDir() {
+ return dir;
+ }
+ }
+}
+
+
diff --git a/src/java/net/sf/antcontrib/logic/RunTargetTask.java b/src/java/net/sf/antcontrib/logic/RunTargetTask.java
new file mode 100644
index 0000000..16b87ca
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/RunTargetTask.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+
+/**
+ * Ant task that runs a target without creating a new project.
+ *
+ * @author Nicola Ken Barozzi [email protected]
+ */
+public class RunTargetTask extends Task {
+
+ private String target = null;
+
+ /**
+ * The target attribute
+ *
+ * @param target the name of a target to execute
+ */
+ public void setTarget(String target) {
+ this.target = target;
+ }
+
+ /**
+ * execute the target
+ *
+ * @exception BuildException if a target is not specified
+ */
+ public void execute() throws BuildException {
+ if (target == null) {
+ throw new BuildException("target property required");
+ }
+
+ getProject().executeTarget(target);
+ }
+}
diff --git a/src/java/net/sf/antcontrib/logic/Switch.java b/src/java/net/sf/antcontrib/logic/Switch.java
new file mode 100644
index 0000000..14f31ec
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/Switch.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Sequential;
+
+/***
+ * Task definition for the ANT task to switch on a particular value.
+ *
+ * <pre>
+ *
+ * Usage:
+ *
+ * Task declaration in the project:
+ * <code>
+ * &lt;taskdef name="switch" classname="net.sf.antcontrib.logic.Switch" /&gt;
+ * </code>
+ *
+ * Task calling syntax:
+ * <code>
+ * &lt;switch value="value" [caseinsensitive="true|false"] &gt;
+ * &lt;case value="val"&gt;
+ * &lt;property name="propname" value="propvalue" /&gt; |
+ * &lt;antcall target="targetname" /&gt; |
+ * any other tasks
+ * &lt;/case&gt;
+ * [
+ * &lt;default&gt;
+ * &lt;property name="propname" value="propvalue" /&gt; |
+ * &lt;antcall target="targetname" /&gt; |
+ * any other tasks
+ * &lt;/default&gt;
+ * ]
+ * &lt;/switch&gt;
+ * </code>
+ *
+ *
+ * Attributes:
+ * value -&gt; The value to switch on
+ * caseinsensitive -&gt; Should we do case insensitive comparisons?
+ * (default is false)
+ *
+ * Subitems:
+ * case --&gt; An individual case to consider, if the value that
+ * is being switched on matches to value attribute of
+ * the case, then the nested tasks will be executed.
+ * default --&gt; The default case for when no match is found.
+ *
+ *
+ * Crude Example:
+ *
+ * <code>
+ * &lt;switch value=&quot;${foo}&quot;&gt;
+ * &lt;case value=&quot;bar&quot;&gt;
+ * &lt;echo message=&quot;The value of property foo is bar&quot; /&gt;
+ * &lt;/case&gt;
+ * &lt;case value=&quot;baz&quot;&gt;
+ * &lt;echo message=&quot;The value of property foo is baz&quot; /&gt;
+ * &lt;/case&gt;
+ * &lt;default&gt;
+ * &lt;echo message=&quot;The value of property foo is not sensible&quot; /&gt;
+ * &lt;/default&gt;
+ * &lt;/switch&gt;
+ * </code>
+ *
+ * </pre>
+ *
+ * @author <a href="mailto:[email protected]">Matthew Inger</a>
+ * @author <a href="mailto:[email protected]">Stefan Bodewig</a>
+ */
+public class Switch extends Task
+{
+ private String value;
+ private Vector cases;
+ private Sequential defaultCase;
+ private boolean caseInsensitive;
+
+ /***
+ * Default Constructor
+ */
+ public Switch()
+ {
+ cases = new Vector();
+ }
+
+ public void execute()
+ throws BuildException
+ {
+ if (value == null)
+ throw new BuildException("Value is missing");
+ if (cases.size() == 0 && defaultCase == null)
+ throw new BuildException("No cases supplied");
+
+ Sequential selectedCase = defaultCase;
+
+ int sz = cases.size();
+ for (int i=0;i<sz;i++)
+ {
+ Case c = (Case)(cases.elementAt(i));
+
+ String cvalue = c.value;
+ if (cvalue == null) {
+ throw new BuildException("Value is required for case.");
+ }
+ String mvalue = value;
+
+ if (caseInsensitive)
+ {
+ cvalue = cvalue.toUpperCase();
+ mvalue = mvalue.toUpperCase();
+ }
+
+ if (cvalue.equals(mvalue) && c != defaultCase)
+ selectedCase = c;
+ }
+
+ if (selectedCase == null) {
+ throw new BuildException("No case matched the value " + value
+ + " and no default has been specified.");
+ }
+ selectedCase.perform();
+ }
+
+ /***
+ * Sets the value being switched on
+ */
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ public void setCaseInsensitive(boolean c)
+ {
+ caseInsensitive = c;
+ }
+
+ public final class Case extends Sequential
+ {
+ private String value;
+
+ public Case()
+ {
+ super();
+ }
+
+ public void setValue(String value)
+ {
+ this.value = value;
+ }
+
+ public void execute()
+ throws BuildException
+ {
+ super.execute();
+ }
+
+ public boolean equals(Object o)
+ {
+ boolean res = false;
+ Case c = (Case)o;
+ if (c.value.equals(value))
+ res = true;
+ return res;
+ }
+ }
+
+ /***
+ * Creates the &lt;case&gt; tag
+ */
+ public Switch.Case createCase()
+ throws BuildException
+ {
+ Switch.Case res = new Switch.Case();
+ cases.addElement(res);
+ return res;
+ }
+
+ /***
+ * Creates the &lt;default&gt; tag
+ */
+ public void addDefault(Sequential res)
+ throws BuildException
+ {
+ if (defaultCase != null)
+ throw new BuildException("Cannot specify multiple default cases");
+
+ defaultCase = res;
+ }
+
+}
diff --git a/src/java/net/sf/antcontrib/logic/Throw.java b/src/java/net/sf/antcontrib/logic/Throw.java
new file mode 100644
index 0000000..552de46
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/Throw.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.taskdefs.Exit;
+import org.apache.tools.ant.types.Reference;
+
+/**
+ * Extension of <code>&lt;fail&gt;</code> that can throw an exception
+ * that is a reference in the project.
+ *
+ * <p>This may be useful inside the <code>&lt;catch&gt;</code> block
+ * of a <code>&lt;trycatch&gt;</code> task if you want to rethrow the
+ * exception just caught.</p>
+ */
+public class Throw extends Exit {
+
+ private Reference ref;
+
+ /**
+ * The reference that points to a BuildException.
+ */
+ public void setRefid(Reference ref) {
+ this.ref = ref;
+ }
+
+ public void execute() throws BuildException {
+ Object reffed = ref != null
+ ? ref.getReferencedObject(getProject())
+ : null;
+ if (reffed != null && reffed instanceof BuildException) {
+ throw (BuildException) reffed;
+ }
+ super.execute();
+ }
+}
diff --git a/src/java/net/sf/antcontrib/logic/TimestampSelector.java b/src/java/net/sf/antcontrib/logic/TimestampSelector.java
new file mode 100644
index 0000000..da4ccd8
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/TimestampSelector.java
@@ -0,0 +1,285 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+
+import java.io.File;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.types.Path;
+import org.apache.tools.ant.types.Reference;
+
+
+/***
+ * Task definition for the foreach task. The foreach task iterates
+ * over a list, a list of filesets, or both.
+ *
+ * <pre>
+ *
+ * Usage:
+ *
+ * Task declaration in the project:
+ * <code>
+ * &lt;taskdef name="latesttimestamp" classname="net.sf.antcontrib.logic.TimestampSelector" /&gt;
+ * </code>
+ *
+ * Call Syntax:
+ * <code>
+ * &lt;timestampselector
+ * [property="prop" | outputsetref="id"]
+ * [count="num"]
+ * [age="eldest|youngest"]
+ * [pathSep=","]
+ * [pathref="ref"] &gt;
+ * &lt;path&gt;
+ * ...
+ * &lt;/path&gt;
+ * &lt;/latesttimestamp&gt;
+ * </code>
+ *
+ * Attributes:
+ * outputsetref --> The reference of the output Path set which will contain the
+ * files with the latest timestamps.
+ * property --> The name of the property to set with file having the latest
+ * timestamp. If you specify the "count" attribute, you will get
+ * the lastest N files. These will be the absolute pathnames
+ * count --> How many of the latest files do you wish to find
+ * pathSep --> What to use as the path separator when using the "property"
+ * attribute, in conjunction with the "count" attribute
+ * pathref --> The reference of the path which is the input set of files.
+ *
+ * </pre>
+ * @author <a href="mailto:[email protected]">Matthew Inger</a>
+ */
+public class TimestampSelector extends Task
+{
+ private static final String AGE_ELDEST = "eldest";
+ private static final String AGE_YOUNGEST = "youngest";
+
+ private String property;
+ private Path path;
+ private String outputSetId;
+ private int count = 1;
+ private char pathSep = ',';
+ private String age = AGE_YOUNGEST;
+
+
+ /***
+ * Default Constructor
+ */
+ public TimestampSelector()
+ {
+ super();
+ }
+
+
+ public void doFileSetExecute(String paths[])
+ throws BuildException
+ {
+
+ }
+
+ // Sorts entire array
+ public void sort(Vector array)
+ {
+ sort(array, 0, array.size() - 1);
+ }
+
+ // Sorts partial array
+ protected void sort(Vector array, int start, int end)
+ {
+ int p;
+ if (end > start)
+ {
+ p = partition(array, start, end);
+ sort(array, start, p-1);
+ sort(array, p+1, end);
+ }
+ }
+
+ protected int compare(File a, File b)
+ {
+ if (age.equalsIgnoreCase(AGE_ELDEST))
+ return new Long(a.lastModified()).compareTo(new Long(b.lastModified()));
+ else
+ return new Long(b.lastModified()).compareTo(new Long(a.lastModified()));
+ }
+
+ protected int partition(Vector array, int start, int end)
+ {
+ int left, right;
+ File partitionElement;
+
+ partitionElement = (File)array.elementAt(end);
+
+ left = start - 1;
+ right = end;
+ for (;;)
+ {
+ while (compare(partitionElement, (File)array.elementAt(++left)) == 1)
+ {
+ if (left == end) break;
+ }
+ while (compare(partitionElement, (File)array.elementAt(--right)) == -1)
+ {
+ if (right == start) break;
+ }
+ if (left >= right) break;
+ swap(array, left, right);
+ }
+ swap(array, left, end);
+
+ return left;
+ }
+
+ protected void swap(Vector array, int i, int j)
+ {
+ Object temp;
+
+ temp = array.elementAt(i);
+ array.setElementAt(array.elementAt(j), i);
+ array.setElementAt(temp, j);
+ }
+
+ public void execute()
+ throws BuildException
+ {
+ if (property == null && outputSetId == null)
+ throw new BuildException("Property or OutputSetId must be specified.");
+ if (path == null)
+ throw new BuildException("A path element or pathref attribute must be specified.");
+
+ // Figure out the list of existing file elements
+ // from the designated path
+ String s[] = path.list();
+ Vector v = new Vector();
+ for (int i=0;i<s.length;i++)
+ {
+ File f = new File(s[i]);
+ if (f.exists())
+ v.addElement(f);
+ }
+
+ // Sort the vector, need to make java 1.1 compliant
+ sort(v);
+
+ // Pull off the first N items
+ Vector v2 = new Vector();
+ int sz = v.size();
+ for (int i=0;i<sz && i<count;i++)
+ v2.add(v.elementAt(i));
+
+
+
+
+ // Build the resulting Path object
+ Path path = new Path(getProject());
+ sz = v2.size();
+ for (int i=0;i<sz;i++)
+ {
+ File f = (File)(v.elementAt(i));
+ Path p = new Path(getProject(), f.getAbsolutePath());
+ path.addExisting(p);
+ }
+
+
+ if (outputSetId != null)
+ {
+ // Add the reference to the project
+ getProject().addReference(outputSetId, path);
+ }
+ else
+ {
+ // Concat the paths, and put them in a property
+ // which is separated list of the files, using the
+ // "pathSep" attribute as the separator
+ String paths[] = path.list();
+ StringBuffer sb = new StringBuffer();
+ for (int i=0;i<paths.length;i++)
+ {
+ if (i != 0) sb.append(pathSep);
+ sb.append(paths[i]);
+ }
+
+ if (paths.length != 0)
+ getProject().setProperty(property, sb.toString());
+ }
+ }
+
+
+ public void setProperty(String property)
+ {
+ if (outputSetId != null)
+ throw new BuildException("Cannot set both Property and OutputSetId.");
+
+ this.property = property;
+ }
+
+ public void setCount(int count)
+ {
+ this.count = count;
+ }
+
+ public void setAge(String age)
+ {
+ if (age.equalsIgnoreCase(AGE_ELDEST) ||
+ age.equalsIgnoreCase(AGE_YOUNGEST))
+ this.age = age;
+ else
+ throw new BuildException("Invalid age: " + age);
+ }
+
+ public void setPathSep(char pathSep)
+ {
+ this.pathSep = pathSep;
+ }
+
+ public void setOutputSetId(String outputSetId)
+ {
+ if (property != null)
+ throw new BuildException("Cannot set both Property and OutputSetId.");
+ this.outputSetId = outputSetId;
+ }
+
+ public void setPathRef(Reference ref)
+ throws BuildException
+ {
+ if (path == null)
+ {
+ path = new Path(getProject());
+ path.setRefid(ref);
+ }
+ else
+ {
+ throw new BuildException("Path element already specified.");
+ }
+ }
+
+
+ public Path createPath()
+ throws BuildException
+ {
+ if (path == null)
+ path = new Path(getProject());
+ else
+ throw new BuildException("Path element already specified.");
+ return path;
+ }
+
+}
+
+
diff --git a/src/java/net/sf/antcontrib/logic/TryCatchTask.java b/src/java/net/sf/antcontrib/logic/TryCatchTask.java
new file mode 100644
index 0000000..4933e7d
--- /dev/null
+++ b/src/java/net/sf/antcontrib/logic/TryCatchTask.java
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2001-2004 Ant-Contrib project. All rights reserved.
+ *
+ * Licensed 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.
+ */
+package net.sf.antcontrib.logic;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.Task;
+import org.apache.tools.ant.taskdefs.Sequential;
+
+/**
+ * A wrapper that lets you run a set of tasks and optionally run a
+ * different set of tasks if the first set fails and yet another set
+ * after the first one has finished.
+ *
+ * <p>This mirrors Java's try/catch/finally.</p>
+ *
+ * <p>The tasks inside of the required <code>&lt;try&gt;</code>
+ * element will be run. If one of them should throw a {@link
+ * org.apache.tools.ant.BuildException BuildException} several things
+ * can happen:</p>
+ *
+ * <ul>
+ * <li>If there is no <code>&lt;catch&gt;</code> block, the
+ * exception will be passed through to Ant.</li>
+ *
+ * <li>If the property attribute has been set, a property of the
+ * given name will be set to the message of the exception.</li>
+ *
+ * <li>If the reference attribute has been set, a reference of the
+ * given id will be created and point to the exception object.</li>
+ *
+ * <li>If there is a <code>&lt;catch&gt;</code> block, the tasks
+ * nested into it will be run.</li>
+ * </ul>
+ *
+ * <p>If a <code>&lt;finally&gt;</code> block is present, the task
+ * nested into it will be run, no matter whether the first tasks have
+ * thrown an exception or not.</p>
+ *
+ * <p><strong>Attributes:</strong></p>
+ *
+ * <table>
+ * <tr>
+ * <td>Name</td>
+ * <td>Description</td>
+ * <td>Required</td>
+ * </tr>
+ * <tr>
+ * <td>property</td>
+ * <td>Name of a property that will receive the message of the
+ * exception that has been caught (if any)</td>
+ * <td>No</td>
+ * </tr>
+ * <tr>
+ * <td>reference</td>
+ * <td>Id of a reference that will point to the exception object
+ * that has been caught (if any)</td>
+ * <td>No</td>
+ * </tr>
+ * </table>
+ *
+ * <p>Use the following task to define the <code>&lt;trycatch&gt;</code>
+ * task before you use it the first time:</p>
+ *
+ * <pre><code>
+ * &lt;taskdef name="trycatch"
+ * classname="net.sf.antcontrib.logic.TryCatchTask" /&gt;
+ * </code></pre>
+ *
+ * <h3>Crude Example</h3>
+ *
+ * <pre><code>
+ * &lt;trycatch property=&quot;foo&quot; reference=&quot;bar&quot;&gt;
+ * &lt;try&gt;
+ * &lt;fail&gt;Tada!&lt;/fail&gt;
+ * &lt;/try&gt;
+ *
+ * &lt;catch&gt;
+ * &lt;echo&gt;In &amp;lt;catch&amp;gt;.&lt;/echo&gt;
+ * &lt;/catch&gt;
+ *
+ * &lt;finally&gt;
+ * &lt;echo&gt;In &amp;lt;finally&amp;gt;.&lt;/echo&gt;
+ * &lt;/finally&gt;
+ * &lt;/trycatch&gt;
+ *
+ * &lt;echo&gt;As property: ${foo}&lt;/echo&gt;
+ * &lt;property name=&quot;baz&quot; refid=&quot;bar&quot; /&gt;
+ * &lt;echo&gt;From reference: ${baz}&lt;/echo&gt;
+ * </code></pre>
+ *
+ * <p>results in</p>
+ *
+ * <pre><code>
+ * [trycatch] Caught exception: Tada!
+ * [echo] In &lt;catch&gt;.
+ * [echo] In &lt;finally&gt;.
+ * [echo] As property: Tada!
+ * [echo] From reference: Tada!
+ * </code></pre>
+ *
+ * @author <a href="mailto:[email protected]">Stefan Bodewig</a>
+ * @author <a href="mailto:[email protected]">Dan Ritchey</a>
+ */
+public class TryCatchTask extends Task {
+
+ public static final class CatchBlock extends Sequential {
+ private String throwable = BuildException.class.getName();
+
+ public CatchBlock() {
+ super();
+ }
+
+ public void setThrowable(String throwable) {
+ this.throwable = throwable;
+ }
+
+ public boolean execute(Throwable t) throws BuildException {
+ try {
+ Class c = Thread.currentThread().getContextClassLoader().loadClass(throwable);
+ if (c.isAssignableFrom(t.getClass())) {
+ execute();
+ return true;
+ }
+ return false;
+ }
+ catch (ClassNotFoundException e) {
+ throw new BuildException(e);
+ }
+ }
+ }
+
+
+ private Sequential tryTasks = null;
+ private Vector catchBlocks = new Vector();
+ private Sequential finallyTasks = null;
+ private String property = null;
+ private String reference = null;
+
+ /**
+ * Adds a nested &lt;try&gt; block - one is required, more is
+ * forbidden.
+ */
+ public void addTry(Sequential seq) throws BuildException {
+ if (tryTasks != null) {
+ throw new BuildException("You must not specify more than one <try>");
+ }
+
+ tryTasks = seq;
+ }
+
+ public void addCatch(CatchBlock cb) {
+ catchBlocks.add(cb);
+ }
+
+ /**
+ * Adds a nested &lt;finally&gt; block - at most one is allowed.
+ */
+ public void addFinally(Sequential seq) throws BuildException {
+ if (finallyTasks != null) {
+ throw new BuildException("You must not specify more than one <finally>");
+ }
+
+ finallyTasks = seq;
+ }
+
+ /**
+ * Sets the property attribute.
+ */
+ public void setProperty(String p) {
+ property = p;
+ }
+
+ /**
+ * Sets the reference attribute.
+ */
+ public void setReference(String r) {
+ reference = r;
+ }
+
+ /**
+ * The heart of the task.
+ */
+ public void execute() throws BuildException {
+ if (tryTasks == null) {
+ throw new BuildException("A nested <try> element is required");
+ }
+
+ try {
+ tryTasks.perform();
+ } catch (Throwable e) {
+ if (property != null) {
+ /*
+ * Using setProperty instead of setNewProperty to
+ * be able to compile with Ant < 1.5.
+ */
+ getProject().setProperty(property, e.getMessage());
+ }
+
+ if (reference != null) {
+ getProject().addReference(reference, e);
+ }
+
+ boolean executed = false;
+ Enumeration blocks = catchBlocks.elements();
+ while (blocks.hasMoreElements() && ! executed) {
+ CatchBlock cb = (CatchBlock)blocks.nextElement();
+ executed = cb.execute(e);
+ }
+ } finally {
+ if (finallyTasks != null) {
+ finallyTasks.perform();
+ }
+ }
+ }
+
+}