diff options
author | mattinger <[email protected]> | 2006-07-06 21:53:00 +0000 |
---|---|---|
committer | mattinger <[email protected]> | 2006-07-06 21:53:00 +0000 |
commit | 1159111b7a71b72eb04326df33211e1733f7e742 (patch) | |
tree | f0a80c384f633e521649654ab78e6239cf5e0d6f /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.java | 644 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/AntFetch.java | 638 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/Assert.java | 81 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/ForEach.java | 423 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/ForTask.java | 433 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/IfTask.java | 221 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/OutOfDate.java | 675 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/RunTargetTask.java | 50 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/Switch.java | 207 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/Throw.java | 50 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/TimestampSelector.java | 285 | ||||
-rw-r--r-- | src/java/net/sf/antcontrib/logic/TryCatchTask.java | 232 |
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> + * <target name="foo" depends="init"> + * <ant antfile="build.xml" target="bar" > + * <property name="property1" value="aaaaa" /> + * <property name="foo" value="baz" /> + * </ant></SPAN> </target></SPAN> <target name="bar" + * depends="init"> <echo message="prop is ${property1} + * ${foo}" /> </target> </pre> + * <p>Developed for use with Antelope, migrated to ant-contrib Oct 2003. + * <p>Credit to Costin for the original <ant> 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 <reference> element of + * <ant> and <antcall>. + * + * @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> + * <target name="foo" depends="init"> + * <ant antfile="build.xml" target="bar" > + * <property name="property1" value="aaaaa" /> + * <property name="foo" value="baz" /> + * </ant></SPAN> </target></SPAN> <target name="bar" + * depends="init"> <echo message="prop is ${property1} + * ${foo}" /> </target> </pre> + * <p>Developed for use with Antelope, migrated to ant-contrib Oct 2003. + * <p>Credit to Costin for the original <ant> 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 <reference> element of + * <ant> and <antcall>. + * + * @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> + * <taskdef name="foreach" classname="net.sf.antcontrib.logic.ForEach" /> + * </code> + * + * Call Syntax: + * <code> + * <foreach list="values" target="targ" param="name" + * [parallel="true|false"] + * [delimiter="delim"] /> + * </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><antcall></code>'s <code>inheritall</code> + * attribute. + */ + public void setInheritall(boolean b) { + this.inheritAll = b; + } + + /** + * Corresponds to <code><antcall></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><antcall></code>'s nested + * <code><param></code> element. + */ + public void addParam(Property p) { + params.addElement(p); + } + + /** + * Corresponds to <code><antcall></code>'s nested + * <code><reference></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><condition></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><condition></code> task, only a single + * condition can be specified - you combine them using + * <code><and></code> or <code><or></code> conditions.</p> + * + * <p>In addition to the condition, you can specify three different + * child elements, <code><elseif></code>, <code><then></code> and + * <code><else></code>. All three subelements are optional. + * + * Both <code><then></code> and <code><else></code> must not be + * used more than once inside the if task. Both are + * containers for Ant tasks, just like Ant's + * <code><parallel></code> and <code><sequential></code> + * tasks - in fact they are implemented using the same class as Ant's + * <code><sequential></code> task.</p> + * + * The <code><elseif></code> behaves exactly like an <code><if></code> + * except that it cannot contain the <code><else></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><if></code> is false, then the first + * <code><elseif></code> who's conditional evaluates to true + * will be executed. The <code><else></code> will be executed + * only if the <code><if></code> and all <code><elseif></code> + * conditions are false. + * + * <p>Use the following task to define the <code><if></code> + * task before you use it the first time:</p> + * + * <pre><code> + * <taskdef name="if" classname="net.sf.antcontrib.logic.IfTask" /> + * </code></pre> + * + * <h3>Crude Example</h3> + * + * <pre><code> + * <if> + * <equals arg1="${foo}" arg2="bar" /> + * <then> + * <echo message="The value of property foo is bar" /> + * </then> + * <else> + * <echo message="The value of property foo is not bar" /> + * </else> + * </if> + * </code> + * + * <code> + * <if> + * <equals arg1="${foo}" arg2="bar" /> + * <then> + * <echo message="The value of property foo is 'bar'" /> + * </then> + * + * <elseif> + * <equals arg1="${foo}" arg2="foo" /> + * <then> + * <echo message="The value of property foo is 'foo'" /> + * </then> + * </elseif> + * + * <else> + * <echo message="The value of property foo is not 'foo' or 'bar'" /> + * </else> + * </if> + * </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 <then> 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 <else> 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> + * <taskdef name="switch" classname="net.sf.antcontrib.logic.Switch" /> + * </code> + * + * Task calling syntax: + * <code> + * <switch value="value" [caseinsensitive="true|false"] > + * <case value="val"> + * <property name="propname" value="propvalue" /> | + * <antcall target="targetname" /> | + * any other tasks + * </case> + * [ + * <default> + * <property name="propname" value="propvalue" /> | + * <antcall target="targetname" /> | + * any other tasks + * </default> + * ] + * </switch> + * </code> + * + * + * Attributes: + * value -> The value to switch on + * caseinsensitive -> Should we do case insensitive comparisons? + * (default is false) + * + * Subitems: + * case --> 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 --> The default case for when no match is found. + * + * + * Crude Example: + * + * <code> + * <switch value="${foo}"> + * <case value="bar"> + * <echo message="The value of property foo is bar" /> + * </case> + * <case value="baz"> + * <echo message="The value of property foo is baz" /> + * </case> + * <default> + * <echo message="The value of property foo is not sensible" /> + * </default> + * </switch> + * </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 <case> tag + */ + public Switch.Case createCase() + throws BuildException + { + Switch.Case res = new Switch.Case(); + cases.addElement(res); + return res; + } + + /*** + * Creates the <default> 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><fail></code> that can throw an exception + * that is a reference in the project. + * + * <p>This may be useful inside the <code><catch></code> block + * of a <code><trycatch></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> + * <taskdef name="latesttimestamp" classname="net.sf.antcontrib.logic.TimestampSelector" /> + * </code> + * + * Call Syntax: + * <code> + * <timestampselector + * [property="prop" | outputsetref="id"] + * [count="num"] + * [age="eldest|youngest"] + * [pathSep=","] + * [pathref="ref"] > + * <path> + * ... + * </path> + * </latesttimestamp> + * </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><try></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><catch></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><catch></code> block, the tasks + * nested into it will be run.</li> + * </ul> + * + * <p>If a <code><finally></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><trycatch></code> + * task before you use it the first time:</p> + * + * <pre><code> + * <taskdef name="trycatch" + * classname="net.sf.antcontrib.logic.TryCatchTask" /> + * </code></pre> + * + * <h3>Crude Example</h3> + * + * <pre><code> + * <trycatch property="foo" reference="bar"> + * <try> + * <fail>Tada!</fail> + * </try> + * + * <catch> + * <echo>In &lt;catch&gt;.</echo> + * </catch> + * + * <finally> + * <echo>In &lt;finally&gt;.</echo> + * </finally> + * </trycatch> + * + * <echo>As property: ${foo}</echo> + * <property name="baz" refid="bar" /> + * <echo>From reference: ${baz}</echo> + * </code></pre> + * + * <p>results in</p> + * + * <pre><code> + * [trycatch] Caught exception: Tada! + * [echo] In <catch>. + * [echo] In <finally>. + * [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 <try> 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 <finally> 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(); + } + } + } + +} |