diff --git a/proposal/myrmidon/ant1compat.xml b/proposal/myrmidon/ant1compat.xml
new file mode 100644
index 000000000..da4703f4d
--- /dev/null
+++ b/proposal/myrmidon/ant1compat.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/proposal/myrmidon/build.xml b/proposal/myrmidon/build.xml
index 2a2087999..dc13a4d44 100644
--- a/proposal/myrmidon/build.xml
+++ b/proposal/myrmidon/build.xml
@@ -200,7 +200,8 @@ Legal:
-
+
diff --git a/proposal/myrmidon/lib/xalan.jar b/proposal/myrmidon/lib/xalan.jar
new file mode 100644
index 000000000..3681818e1
Binary files /dev/null and b/proposal/myrmidon/lib/xalan.jar differ
diff --git a/proposal/myrmidon/lib/xml-apis.jar b/proposal/myrmidon/lib/xml-apis.jar
new file mode 100644
index 000000000..8331d27f1
Binary files /dev/null and b/proposal/myrmidon/lib/xml-apis.jar differ
diff --git a/proposal/myrmidon/src/ant1compat/README.txt b/proposal/myrmidon/src/ant1compat/README.txt
new file mode 100644
index 000000000..52454358c
--- /dev/null
+++ b/proposal/myrmidon/src/ant1compat/README.txt
@@ -0,0 +1,60 @@
+Myrmidon Ant1 compatibility layer.
+
+This directory contains the source for the Ant1 compatibility layer.
+
+DESCRIPTION
+-----------
+The layer works by reusing most of the Ant1 code, with tasks and datatypes
+being prefixed with "ant1." in build files. Almost all of the main Ant1 tree
+is included in the compatibility layer antlib. To insulate from changes in
+the Ant1 tree, Ant1 class files are extracted from a jar, rather than
+being compiled from scratch.
+
+Here's how it works: The first time an Ant1 task is encountered, an Ant1
+project is created, and stored in the TaskContext. The Ant1 versions of Task
+and Project have been extended, with Task implementing Configurable so that
+it may can mimic the Ant1 configuration policy using the IntrospectionHelper.
+
+The idea is to provide hooks between the Ant1 project and the Myrmidon
+project, eg
+ logging: done
+ properties: done but not quite working
+ references: not done
+ Task definitions: done.
+
+So at present, while a reference works fine in other
+tasks, it's not visible to the rest of the build, and vice-versa.
+
+The task works ok, registering the task with the TypeManager using the
+"ant1." prefix. Only a couple of DataTypes (Path and Patternset) are working
+as top-level types, but this should be just a matter of adding references to
+the Ant1 version of TypeInstanceTask in the descriptor.
+
+The TransformingProjectBuilder (which is now the default builder for files
+of type ".xml", applies a transformation stylesheet to the file, prefixing select
+tasks (all at present) with "ant.". If a version attribute is encountered, the
+file is not transformed
+
+USAGE INSTRUCTIONS
+------------------
+Myrmidon will automatically attempt to upgrade any ".xml" build file that
+doesn't have a version attribute on the root element. So, using an Ant1 build
+file with Myrmidon should be as simple as:
+ [myrmidon-command] -f ant1-build-file.xml
+
+BUILD INSTRUCTIONS
+------------------
+* It is required that Myrmidon is first build by running the default target
+ in the Myrmidon directory.
+* Run "ant -f ant1compat.xml"
+
+TODO
+----
+* Convert this to an Xdoc document
+* Try out automatic registration of tasks - remove everything
+ from ant-descriptor.xml and just use Project.addTaskDefinition()
+ to register tasks? (similar for DataTypes)
+* Get a version of and working
+* Test heaps more tasks
+* Fix problem with classloaders and
+
diff --git a/proposal/myrmidon/src/ant1compat/ant-descriptor.xml b/proposal/myrmidon/src/ant1compat/ant-descriptor.xml
new file mode 100644
index 000000000..9c65710b2
--- /dev/null
+++ b/proposal/myrmidon/src/ant1compat/ant-descriptor.xml
@@ -0,0 +1,310 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/proposal/myrmidon/src/ant1compat/ant1compat.mf b/proposal/myrmidon/src/ant1compat/ant1compat.mf
new file mode 100644
index 000000000..5c7a5a368
--- /dev/null
+++ b/proposal/myrmidon/src/ant1compat/ant1compat.mf
@@ -0,0 +1,12 @@
+Manifest-Version: 1.0
+Created-By: Apache Ant Project
+Extension-Name: ant1.compat
+Specification-Title: Myrmidon Ant1 Compatibility Layer
+Specification-Version: 0.01
+Specification-Vendor: Jakarta Apache
+Implementation-Vendor-Id: org.apache.jakarta
+Implementation-Vendor: Jakarta Apache Project
+Implementation-Version: 0.01
+Extension-List: tools
+tools-Extension-Name: com.sun.tools
+tools-Specification-Version: 1.0
diff --git a/proposal/myrmidon/src/ant1compat/jar/ant.jar b/proposal/myrmidon/src/ant1compat/jar/ant.jar
new file mode 100644
index 000000000..122039d3e
Binary files /dev/null and b/proposal/myrmidon/src/ant1compat/jar/ant.jar differ
diff --git a/proposal/myrmidon/src/ant1compat/jar/optional.jar b/proposal/myrmidon/src/ant1compat/jar/optional.jar
new file mode 100644
index 000000000..f6fa673ca
Binary files /dev/null and b/proposal/myrmidon/src/ant1compat/jar/optional.jar differ
diff --git a/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Ant1CompatProject.java b/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Ant1CompatProject.java
new file mode 100644
index 000000000..8411fbb30
--- /dev/null
+++ b/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Ant1CompatProject.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE.txt file.
+ */
+package org.apache.tools.ant;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Enumeration;
+import java.util.Properties;
+import org.apache.myrmidon.api.TaskContext;
+import org.apache.myrmidon.interfaces.type.DefaultTypeFactory;
+import org.apache.myrmidon.interfaces.type.TypeManager;
+
+/**
+ * Ant1 Project proxy for Myrmidon. Provides hooks between Myrmidon TaskContext
+ * and Ant1 project.
+ * Note that there is no logical separation between Ant1Project and this extension -
+ * they could easily be flattened. Ant1Project is barely modified from the
+ * Ant1 original, this class contains the extensions.
+ *
+ * @author Darrell DeBoer
+ * @version $Revision$ $Date$
+ */
+public class Ant1CompatProject extends Project
+{
+ private TaskContext m_context;
+ public static final String ANT1_TASK_PREFIX = "ant1.";
+
+ public Ant1CompatProject( TaskContext context )
+ {
+ super();
+ m_context = context;
+ setBaseDir( m_context.getBaseDirectory() );
+ }
+
+ /**
+ * Writes a project level message to the log with the given log level.
+ * @param msg The text to log. Should not be null
.
+ * @param msgLevel The priority level to log at.
+ */
+ public void log( String msg, int msgLevel )
+ {
+
+ doLog( msg, msgLevel );
+ super.log( msg, msgLevel );
+ }
+
+ /**
+ * Writes a task level message to the log with the given log level.
+ * @param task The task to use in the log. Must not be null
.
+ * @param msg The text to log. Should not be null
.
+ * @param msgLevel The priority level to log at.
+ */
+ public void log( Task task, String msg, int msgLevel )
+ {
+ doLog( msg, msgLevel );
+ super.log( task, msg, msgLevel );
+ }
+
+ /**
+ * Writes a target level message to the log with the given log level.
+ * @param target The target to use in the log.
+ * Must not be null
.
+ * @param msg The text to log. Should not be null
.
+ * @param msgLevel The priority level to log at.
+ */
+ public void log( Target target, String msg, int msgLevel )
+ {
+ doLog( msg, msgLevel );
+ super.log( target, msg, msgLevel );
+ }
+
+ private void doLog( String msg, int msgLevel )
+ {
+ switch( msgLevel )
+ {
+ case Ant1CompatProject.MSG_ERR:
+ m_context.error( msg );
+ break;
+ case Ant1CompatProject.MSG_WARN:
+ m_context.warn( msg );
+ break;
+ case Ant1CompatProject.MSG_INFO:
+ m_context.info( msg );
+ break;
+ case Ant1CompatProject.MSG_VERBOSE:
+ case Ant1CompatProject.MSG_DEBUG:
+ m_context.debug( msg );
+ }
+ }
+
+ /**
+ * This is a copy of init() from the Ant1 Project, which adds Ant1 tasks and
+ * DataTypes to the underlying Ant1 Project, but calling add methods on the
+ * superclass to avoid adding everything to the TypeManager.
+ *
+ * @exception BuildException if the default task list cannot be loaded
+ */
+ public void init() throws BuildException
+ {
+ setJavaVersionProperty();
+
+ String defs = "/org/apache/tools/ant/taskdefs/defaults.properties";
+
+ try
+ {
+ Properties props = new Properties();
+ InputStream in = this.getClass().getResourceAsStream( defs );
+ if( in == null )
+ {
+ throw new BuildException( "Can't load default task list" );
+ }
+ props.load( in );
+ in.close();
+
+ Enumeration enum = props.propertyNames();
+ while( enum.hasMoreElements() )
+ {
+ String key = (String)enum.nextElement();
+ String value = props.getProperty( key );
+ try
+ {
+ Class taskClass = Class.forName( value );
+
+ // NOTE: Line modified from Ant1 Project.
+ super.addTaskDefinition( key, taskClass );
+
+ }
+ catch( NoClassDefFoundError ncdfe )
+ {
+ log( "Could not load a dependent class ("
+ + ncdfe.getMessage() + ") for task " + key, MSG_DEBUG );
+ }
+ catch( ClassNotFoundException cnfe )
+ {
+ log( "Could not load class (" + value
+ + ") for task " + key, MSG_DEBUG );
+ }
+ }
+ }
+ catch( IOException ioe )
+ {
+ throw new BuildException( "Can't load default task list" );
+ }
+
+ String dataDefs = "/org/apache/tools/ant/types/defaults.properties";
+
+ try
+ {
+ Properties props = new Properties();
+ InputStream in = this.getClass().getResourceAsStream( dataDefs );
+ if( in == null )
+ {
+ throw new BuildException( "Can't load default datatype list" );
+ }
+ props.load( in );
+ in.close();
+
+ Enumeration enum = props.propertyNames();
+ while( enum.hasMoreElements() )
+ {
+ String key = (String)enum.nextElement();
+ String value = props.getProperty( key );
+ try
+ {
+ Class dataClass = Class.forName( value );
+
+ // NOTE: Line modified from Ant1 Project.
+ super.addDataTypeDefinition( key, dataClass );
+
+ }
+ catch( NoClassDefFoundError ncdfe )
+ {
+ // ignore...
+ }
+ catch( ClassNotFoundException cnfe )
+ {
+ // ignore...
+ }
+ }
+ }
+ catch( IOException ioe )
+ {
+ throw new BuildException( "Can't load default datatype list" );
+ }
+
+ setSystemProperties();
+ }
+
+ /**
+ * Adds a new task definition to the project, registering it with the
+ * TypeManager, as well as the underlying Ant1 Project.
+ *
+ * @param taskName The name of the task to add.
+ * Must not be null
.
+ * @param taskClass The full name of the class implementing the task.
+ * Must not be null
.
+ *
+ * @exception BuildException if the class is unsuitable for being an Ant
+ * task. An error level message is logged before
+ * this exception is thrown.
+ *
+ * @see #checkTaskClass(Class)
+ */
+ public void addTaskDefinition( String taskName, Class taskClass )
+ throws BuildException
+ {
+ String ant2name = ANT1_TASK_PREFIX + taskName;
+ try
+ {
+ registerType( org.apache.myrmidon.api.Task.ROLE, ant2name, taskClass );
+ }
+ catch( Exception e )
+ {
+ throw new BuildException( e );
+ }
+
+ super.addTaskDefinition( taskName, taskClass );
+ }
+
+ /**
+ * Utility method to register a type.
+ */
+ protected void registerType( final String roleType,
+ final String typeName,
+ final Class type )
+ throws Exception
+ {
+ final ClassLoader loader = type.getClassLoader();
+ final DefaultTypeFactory factory = new DefaultTypeFactory( loader );
+ factory.addNameClassMapping( typeName, type.getName() );
+
+ TypeManager typeManager = (TypeManager)m_context.getService( TypeManager.class );
+ typeManager.registerType( roleType, typeName, factory );
+ }
+
+
+ // /**
+ // * Sets a property. Any existing property of the same name
+ // * is overwritten, unless it is a user property.
+ // * @param name The name of property to set.
+ // * Must not be null
.
+ // * @param value The new value of the property.
+ // * Must not be null
.
+ // */
+ // public void setProperty( String name, String value )
+ // {
+ // if( null != getProperty( name ) )
+ // {
+ // log( "Overriding previous definition of property " + name,
+ // MSG_VERBOSE );
+ // }
+ //
+ // doSetProperty( name, value );
+ // }
+ //
+ // /**
+ // * Sets a property if no value currently exists. If the property
+ // * exists already, a message is logged and the method returns with
+ // * no other effect.
+ // *
+ // * @param name The name of property to set.
+ // * Must not be null
.
+ // * @param value The new value of the property.
+ // * Must not be null
.
+ // * @since 1.5
+ // */
+ // public void setNewProperty( String name, String value )
+ // {
+ // if( null != getProperty( name ) )
+ // {
+ // log( "Override ignored for property " + name, MSG_VERBOSE );
+ // return;
+ // }
+ // log( "Setting project property: " + name + " -> " +
+ // value, MSG_DEBUG );
+ // doSetProperty( name, value );
+ // }
+ //
+ // private void doSetProperty( String name, String value )
+ // {
+ // try
+ // {
+ // m_context.setProperty( name, value );
+ // }
+ // catch( TaskException e )
+ // {
+ // throw new BuildException( e );
+ // }
+ // }
+ //
+ // /**
+ // * Returns the value of a property, if it is set.
+ // *
+ // * @param name The name of the property.
+ // * May be null
, in which case
+ // * the return value is also null
.
+ // * @return the property value, or null
for no match
+ // * or if a null
name is provided.
+ // */
+ // public String getProperty( String name )
+ // {
+ // if( name == null )
+ // {
+ // return null;
+ // }
+ // Object value = m_context.getProperty( name );
+ // if( value == null )
+ // {
+ // return null;
+ // }
+ // return String.valueOf( value );
+ // }
+ //
+ // /**
+ // * Returns a copy of the properties table.
+ // * @return a hashtable containing all properties
+ // * (including user properties).
+ // */
+ // public Hashtable getProperties()
+ // {
+ // Map properties = m_context.getProperties();
+ // Hashtable propertiesCopy = new Hashtable();
+ //
+ // Iterator iterator = properties.keySet().iterator();
+ // while( iterator.hasNext() )
+ // {
+ // String key = (String)iterator.next();
+ // String value = (String)properties.get( key );
+ //
+ // propertiesCopy.put( key, value );
+ //
+ // }
+ //
+ // return propertiesCopy;
+ // }
+
+}
diff --git a/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Ant1CompatTaskAdapter.java b/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Ant1CompatTaskAdapter.java
new file mode 100644
index 000000000..9c70529cf
--- /dev/null
+++ b/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Ant1CompatTaskAdapter.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE.txt file.
+ */
+package org.apache.tools.ant;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+
+/**
+ * An adapter for running (in Myrmidon) Ant1 tasks which do not extend Task
+ *
+ * @author Darrell DeBoer
+ * @version $Revision$ $Date$
+ */
+public class Ant1CompatTaskAdapter
+ extends TaskAdapter
+{
+ public void configure( Configuration configuration ) throws ConfigurationException
+ {
+ // Create a new instance of the proxy object,
+ // and configure it.
+ String taskName = getAnt1Name( configuration.getName() );
+
+ Class taskClass = (Class)project.getTaskDefinitions().get( taskName );
+
+ if( taskClass == null )
+ {
+ throw new ConfigurationException( "Invalid task name for TaskAdapter: " + taskName );
+ }
+
+ Object adaptedTask = null;
+ try
+ {
+ adaptedTask = taskClass.newInstance();
+ }
+ catch( Exception e )
+ {
+ throw new ConfigurationException( "Could not instantiate adapted task: " + taskClass.getName() );
+ }
+
+ configure( adaptedTask, configuration );
+
+ setProxy( adaptedTask );
+ }
+}
diff --git a/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Ant1CompatTypeInstanceTask.java b/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Ant1CompatTypeInstanceTask.java
new file mode 100644
index 000000000..ce71dcbb0
--- /dev/null
+++ b/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Ant1CompatTypeInstanceTask.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE.txt file.
+ */
+package org.apache.tools.ant;
+
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+
+/**
+ * A task for instantiating Ant1 datatypes.
+ *
+ * @author Darrell DeBoer
+ * @version $Revision$ $Date$
+ */
+public class Ant1CompatTypeInstanceTask
+ extends Task
+{
+ public void configure( Configuration configuration ) throws ConfigurationException
+ {
+ if( configuration.getAttribute( "id", null ) == null )
+ {
+ final String message = "id is required.";
+ throw new ConfigurationException( message );
+ }
+
+ String typeName = configuration.getName();
+ Object datatype = project.createDataType( getAnt1Name( typeName ) );
+
+ // Configure the datatype. The type is added to the project
+ // as a reference during configuration.
+ configure( datatype, configuration );
+ }
+
+ /**
+ * Execute task. Don't do anything.
+ */
+ public void execute()
+ {
+ // Everything is done during configuration.
+ }
+}
diff --git a/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/OriginalAnt1Task.java b/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/OriginalAnt1Task.java
new file mode 100644
index 000000000..782062499
--- /dev/null
+++ b/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/OriginalAnt1Task.java
@@ -0,0 +1,374 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
+ * reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. The end-user documentation included with the redistribution, if
+ * any, must include the following acknowlegement:
+ * "This product includes software developed by the
+ * Apache Software Foundation (http://www.apache.org/)."
+ * Alternately, this acknowlegement may appear in the software itself,
+ * if and wherever such third-party acknowlegements normally appear.
+ *
+ * 4. The names "The Jakarta Project", "Ant", and "Apache Software
+ * Foundation" must not be used to endorse or promote products derived
+ * from this software without prior written permission. For written
+ * permission, please contact apache@apache.org.
+ *
+ * 5. Products derived from this software may not be called "Apache"
+ * nor may "Apache" appear in their names without prior written
+ * permission of the Apache Group.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * .
+ */
+
+package org.apache.tools.ant;
+
+/**
+ * Base class for all tasks.
+ *
+ * Use Project.createTask to create a new task instance rather than
+ * using this class directly for construction.
+ *
+ * @see Project#createTask
+ */
+public abstract class OriginalAnt1Task extends ProjectComponent {
+ /** Target this task belongs to, if any. */
+ protected Target target = null;
+ /** Description of this task, if any. */
+ protected String description = null;
+ /** Location within the build file of this task definition. */
+ protected Location location = Location.UNKNOWN_LOCATION;
+ /**
+ * Name of this task to be used for logging purposes.
+ * This defaults to the same as the type, but may be
+ * overridden by the user. For instance, the name "java"
+ * isn't terribly descriptive for a task used within
+ * another task - the outer task code can probably
+ * provide a better one.
+ */
+ protected String taskName = null;
+ /** Type of this task. */
+ protected String taskType = null;
+ /** Wrapper for this object, used to configure it at runtime. */
+ protected RuntimeConfigurable wrapper;
+ /**
+ * Whether or not this task is invalid. A task becomes invalid
+ * if a conflicting class is specified as the implementation for
+ * its type.
+ */
+ private boolean invalid = false;
+
+ /** Sole constructor. */
+ public OriginalAnt1Task() {
+ }
+
+ /**
+ * Sets the target container of this task.
+ *
+ * @param target Target in whose scope this task belongs.
+ * May be null
, indicating a top-level task.
+ */
+ public void setOwningTarget(Target target) {
+ this.target = target;
+ }
+
+ /**
+ * Returns the container target of this task.
+ *
+ * @return The target containing this task, or null
if
+ * this task is a top-level task.
+ */
+ public Target getOwningTarget() {
+ return target;
+ }
+
+ /**
+ * Sets the name to use in logging messages.
+ *
+ * @param name The name to use in logging messages.
+ * Should not be null
.
+ */
+ public void setTaskName(String name) {
+ this.taskName = name;
+ }
+
+ /**
+ * Returns the name to use in logging messages.
+ *
+ * @return the name to use in logging messages.
+ */
+ public String getTaskName() {
+ return taskName;
+ }
+
+ /**
+ * Sets the name with which the task has been invoked.
+ *
+ * @param type The name the task has been invoked as.
+ * Should not be null
.
+ */
+ void setTaskType(String type) {
+ this.taskType = type;
+ }
+
+ /**
+ * Sets a description of the current action. This may be used for logging
+ * purposes.
+ *
+ * @param desc Description of the current action.
+ * May be null
, indicating that no description is
+ * available.
+ *
+ */
+ public void setDescription( String desc ) {
+ description = desc;
+ }
+
+ /**
+ * Returns the description of the current action.
+ *
+ * @return the description of the current action, or null
if
+ * no description is available.
+ */
+ public String getDescription() {
+ return description;
+ }
+
+ /**
+ * Called by the project to let the task initialize properly.
+ * The default implementation is a no-op.
+ *
+ * @exception BuildException if someting goes wrong with the build
+ */
+ public void init() throws BuildException {}
+
+ /**
+ * Called by the project to let the task do its work. This method may be
+ * called more than once, if the task is invoked more than once.
+ * For example,
+ * if target1 and target2 both depend on target3, then running
+ * "ant target1 target2" will run all tasks in target3 twice.
+ *
+ * @exception BuildException if something goes wrong with the build
+ */
+ public void execute() throws BuildException {}
+
+ /**
+ * Returns the file/location where this task was defined.
+ *
+ * @return the file/location where this task was defined.
+ * Should not return null
. Location.UNKNOWN_LOCATION
+ * is used for unknown locations.
+ *
+ * @see Location#UNKNOWN_LOCATION
+ */
+ public Location getLocation() {
+ return location;
+ }
+
+ /**
+ * Sets the file/location where this task was defined.
+ *
+ * @param location The file/location where this task was defined.
+ * Should not be null
- use
+ * Location.UNKNOWN_LOCATION if the location isn't known.
+ *
+ * @see Location#UNKNOWN_LOCATION
+ */
+ public void setLocation(Location location) {
+ this.location = location;
+ }
+
+ /**
+ * Returns the wrapper used for runtime configuration.
+ *
+ * @return the wrapper used for runtime configuration. This
+ * method will generate a new wrapper (and cache it)
+ * if one isn't set already.
+ */
+ public RuntimeConfigurable getRuntimeConfigurableWrapper() {
+ if (wrapper == null) {
+ wrapper = new RuntimeConfigurable(this, getTaskName());
+ }
+ return wrapper;
+ }
+
+ /**
+ * Sets the wrapper to be used for runtime configuration.
+ *
+ * @param wrapper The wrapper to be used for runtime configuration.
+ * May be null
, in which case the next call
+ * to getRuntimeConfigurableWrapper will generate a new
+ * wrapper.
+ */
+ protected void setRuntimeConfigurableWrapper(RuntimeConfigurable wrapper) {
+ this.wrapper = wrapper;
+ }
+
+ // XXX: (Jon Skeet) The comment "if it hasn't been done already" may
+ // not be strictly true. wrapper.maybeConfigure() won't configure the same
+ // attributes/text more than once, but it may well add the children again,
+ // unless I've missed something.
+ /**
+ * Configures this task - if it hasn't been done already.
+ * If the task has been invalidated, it is replaced with an
+ * UnknownElement task which uses the new definition in the project.
+ *
+ * @exception BuildException if the task cannot be configured.
+ */
+ public void maybeConfigure() throws BuildException {
+ if (!invalid) {
+ if (wrapper != null) {
+ wrapper.maybeConfigure(project);
+ }
+ } else {
+ getReplacement();
+ }
+ }
+
+ /**
+ * Handles a line of output by logging it with the INFO priority.
+ *
+ * @param line The line of output to log. Should not be null
.
+ */
+ protected void handleOutput(String line) {
+ log(line, Project.MSG_INFO);
+ }
+
+ /**
+ * Handles an error line by logging it with the INFO priority.
+ *
+ * @param line The error line to log. Should not be null
.
+ */
+ protected void handleErrorOutput(String line) {
+ log(line, Project.MSG_ERR);
+ }
+
+ /**
+ * Logs a message with the default (INFO) priority.
+ *
+ * @param msg The message to be logged. Should not be null
.
+ */
+ public void log(String msg) {
+ log(msg, Project.MSG_INFO);
+ }
+
+ /**
+ * Logs a mesage with the given priority. This delegates
+ * the actual logging to the project.
+ *
+ * @param msg The message to be logged. Should not be null
.
+ * @param msgLevel The message priority at which this message is to
+ * be logged.
+ */
+ public void log(String msg, int msgLevel) {
+ project.log(getThis(), msg, msgLevel);
+ }
+
+ /**
+ * Performs this task if it's still valid, or gets a replacement
+ * version and performs that otherwise.
+ *
+ * Performing a task consists of firing a task started event,
+ * configuring the task, executing it, and then firing task finished
+ * event. If a runtime exception is thrown, the task finished event
+ * is still fired, but with the exception as the cause.
+ */
+ public final void perform() {
+ if (!invalid) {
+ try {
+ project.fireTaskStarted(getThis());
+ maybeConfigure();
+ execute();
+ project.fireTaskFinished(getThis(), null);
+ }
+ catch (RuntimeException exc) {
+ if (exc instanceof BuildException) {
+ BuildException be = (BuildException) exc;
+ if (be.getLocation() == Location.UNKNOWN_LOCATION) {
+ be.setLocation(getLocation());
+ }
+ }
+ project.fireTaskFinished(getThis(), exc);
+ throw exc;
+ }
+ } else {
+ UnknownElement ue = getReplacement();
+ Task task = ue.getTask();
+ task.perform();
+ }
+ }
+
+ /**
+ * Marks this task as invalid. Any further use of this task
+ * will go through a replacement with the updated definition.
+ */
+ final void markInvalid() {
+ invalid = true;
+ }
+
+ /**
+ * Replacement element used if this task is invalidated.
+ */
+ private UnknownElement replacement;
+
+ /**
+ * Creates an UnknownElement that can be used to replace this task.
+ * Once this has been created once, it is cached and returned by
+ * future calls.
+ *
+ * @return the UnknownElement instance for the new definition of this task.
+ */
+ private UnknownElement getReplacement() {
+ if (replacement == null) {
+ replacement = new UnknownElement(taskType);
+ replacement.setProject(project);
+ replacement.setTaskType(taskType);
+ replacement.setTaskName(taskName);
+ replacement.setLocation(location);
+ replacement.setOwningTarget(target);
+ replacement.setRuntimeConfigurableWrapper(wrapper);
+ wrapper.setProxy(replacement);
+ target.replaceChild(getThis(), replacement);
+ replacement.maybeConfigure();
+ }
+ return replacement;
+ }
+
+ private Task getThis()
+ {
+ return (Task) this;
+ }
+
+}
diff --git a/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Task.java b/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Task.java
new file mode 100644
index 000000000..ac013cad0
--- /dev/null
+++ b/proposal/myrmidon/src/ant1compat/org/apache/tools/ant/Task.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE.txt file.
+ */
+package org.apache.tools.ant;
+
+import java.util.Locale;
+import org.apache.avalon.framework.configuration.Configurable;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.myrmidon.api.TaskContext;
+import org.apache.myrmidon.api.TaskException;
+
+/**
+ * Ant1 Task proxy for Myrmidon.
+ * Note that this class and OriginalAnt1Task (superclass) comprise a single logical
+ * class, but the code is kept separate for ease of development. OriginalAnt1Task
+ * is barely modified from the Ant1 original, whereas this class contains
+ * all of the Myrmidon-specific adaptations.
+ *
+ * @author Darrell DeBoer
+ * @version $Revision$ $Date$
+ */
+public class Task extends OriginalAnt1Task
+ implements org.apache.myrmidon.api.Task, Configurable
+{
+ private TaskContext m_context;
+
+ /**
+ * Specify the context in which the task operates in.
+ * The Task will use the TaskContext to receive information
+ * about it's environment.
+ */
+ public void contextualize( TaskContext context )
+ throws TaskException
+ {
+ m_context = context;
+
+ this.setTaskType( context.getName() );
+ this.setTaskName( context.getName() );
+
+ Project project = (Project)context.getProperty( "ant1.project" );
+ if( project == null )
+ {
+ project = createProject();
+ m_context.setProperty( "ant1.project", project );
+ }
+ this.setProject( project );
+ }
+
+ private Project createProject()
+ throws TaskException
+ {
+ Project project = new Ant1CompatProject( m_context );
+ project.init();
+ return project;
+ }
+
+ public void configure( Configuration configuration ) throws ConfigurationException
+ {
+ configure( this, configuration );
+ }
+
+ protected void configure( Object target, Configuration configuration ) throws ConfigurationException
+ {
+ IntrospectionHelper helper = IntrospectionHelper.getHelper( target.getClass() );
+
+ // Configure the id.
+ String id = configuration.getAttribute( "id", null );
+ if( id != null )
+ {
+ project.addReference( id, target );
+ }
+
+ // Configure the attributes.
+ final String[] attribs = configuration.getAttributeNames();
+ for( int i = 0; i < attribs.length; i++ )
+ {
+ final String name = attribs[ i ];
+ final String value =
+ project.replaceProperties( configuration.getAttribute( name ) );
+ try
+ {
+ helper.setAttribute( project, target,
+ name.toLowerCase( Locale.US ), value );
+ }
+ catch( BuildException be )
+ {
+ // id attribute must be set externally
+ if( !name.equals( "id" ) )
+ {
+ throw be;
+ }
+ }
+ }
+
+ // Configure the text content.
+ String text = configuration.getValue( null );
+ if( text != null )
+ {
+ helper.addText( project, target, text );
+ }
+
+ // Configure the nested elements
+ Configuration[] nestedConfigs = configuration.getChildren();
+ for( int i = 0; i < nestedConfigs.length; i++ )
+ {
+ Configuration nestedConfig = nestedConfigs[ i ];
+ String name = nestedConfig.getName();
+ Object nestedElement = helper.createElement( project, target, name );
+ configure( nestedElement, nestedConfig );
+ helper.storeElement( project, target, nestedElement, name );
+ }
+
+ /*
+ task.setLocation(new Location(helperImpl.buildFile.toString(), helperImpl.locator.getLineNumber(),
+ helperImpl.locator.getColumnNumber()));
+ String id = attr.getValue("id");
+ if (id != null) {
+ project.addReference(id, target);
+ }
+
+ // Top level tasks don't have associated targets
+ if (target != null) {
+ task.setOwningTarget(target);
+ container.addTask(task);
+ task.init();
+ wrapper = task.getRuntimeConfigurableWrapper();
+ wrapper.setAttributes(attrs);
+ if (parentWrapper != null) {
+ parentWrapper.addChild(wrapper);
+ }
+ } else {
+ task.init();
+ configure(task, attrs, helperImpl.project);
+ }
+ */
+
+ }
+
+ protected String getAnt1Name( String fullName )
+ {
+ return fullName.substring( Ant1CompatProject.ANT1_TASK_PREFIX.length() );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/DefaultProjectBuilder.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/DefaultProjectBuilder.java
index 566a6af38..3c34f2266 100644
--- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/DefaultProjectBuilder.java
+++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/DefaultProjectBuilder.java
@@ -36,7 +36,6 @@ import org.xml.sax.XMLReader;
* @author Peter Donald
* @version $Revision$ $Date$
*
- * @ant:type type="project-builder" name="xml"
* @ant:type type="project-builder" name="ant"
*/
public class DefaultProjectBuilder
@@ -105,7 +104,7 @@ public class DefaultProjectBuilder
/**
* Parses the project.
*/
- private Configuration parseProject( final String systemID )
+ protected Configuration parseProject( final String systemID )
throws ProjectException
{
try
diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/Resources.properties b/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/Resources.properties
index 9c73d7f79..c2c4365e4 100644
--- a/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/Resources.properties
+++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/Resources.properties
@@ -30,5 +30,8 @@ ant.version-missing.error=Project has no version attribute.
ant.bad-version.error=Incompatible build file version detected. Expected version {0} but found version {1}.
ant.no-project-file.error=Project file does not exist, or is not a file.
+ant.project-convert.notice=Applying compatibility stylesheet to project file.
+ant.project-convert.error=Error converting build file.
+
duplicate-project.error=Can not have two projects referenced in a file with the name {0}.
duplicate-target.error=Can not have two targets in a file with the name {0}.
diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/TransformingProjectBuilder.java b/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/TransformingProjectBuilder.java
new file mode 100644
index 000000000..d65bd9dd4
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/TransformingProjectBuilder.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) The Apache Software Foundation. All rights reserved.
+ *
+ * This software is published under the terms of the Apache Software License
+ * version 1.1, a copy of which has been included with this distribution in
+ * the LICENSE.txt file.
+ */
+package org.apache.myrmidon.components.builder;
+
+import java.io.InputStream;
+import javax.xml.transform.Result;
+import javax.xml.transform.Source;
+import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerConfigurationException;
+import javax.xml.transform.TransformerFactory;
+import javax.xml.transform.sax.SAXResult;
+import javax.xml.transform.stream.StreamSource;
+import org.apache.avalon.excalibur.i18n.ResourceManager;
+import org.apache.avalon.excalibur.i18n.Resources;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.SAXConfigurationHandler;
+import org.apache.myrmidon.interfaces.builder.ProjectException;
+
+/**
+ * A Project Builder which performs an XSL transformation on a project.
+ *
+ * @author Darrell DeBoer
+ * @version $Revision$ $Date$
+ *
+ * @ant:type type="project-builder" name="xml"
+ */
+public class TransformingProjectBuilder
+ extends DefaultProjectBuilder
+{
+ private final static Resources REZ =
+ ResourceManager.getPackageResources( TransformingProjectBuilder.class );
+
+ private final static String STYLESHEET = "ant1convert.xsl";
+ private Transformer m_transformer;
+
+ protected Configuration parseProject( String systemID )
+ throws ProjectException
+ {
+ if( getLogger().isDebugEnabled() )
+ {
+ final String message = REZ.getString( "ant.project-convert.notice" );
+ getLogger().debug( message );
+ }
+
+ try
+ {
+ // Create a XSLT source for the build file.
+ Source source = new StreamSource( systemID );
+
+ // Create a configuration handler for the output.
+ final SAXConfigurationHandler handler = new SAXConfigurationHandler();
+ Result result = new SAXResult( handler );
+
+ // Perform the transformation.
+ getTransformer().transform( source, result );
+
+ return handler.getConfiguration();
+ }
+ catch( Exception e )
+ {
+ throw new ProjectException( REZ.getString( "ant.project-convert.error" ),
+ e );
+ }
+ }
+
+ /**
+ * Lazy load a Transformer with the conversion stylesheet.
+ * @return the initialised Transformer
+ * @throws TransformerConfigurationException
+ */
+ private Transformer getTransformer()
+ throws TransformerConfigurationException
+ {
+ // Lazy loading of stylesheet source.
+ if( m_transformer == null )
+ {
+ InputStream stylesheet =
+ this.getClass().getResourceAsStream( STYLESHEET );
+ StreamSource stylesheetSource = new StreamSource( stylesheet );
+ TransformerFactory xformFactory = TransformerFactory.newInstance();
+ m_transformer = xformFactory.newTransformer( stylesheetSource );
+ }
+ return m_transformer;
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/ant1convert.xsl b/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/ant1convert.xsl
new file mode 100644
index 000000000..e6aba539f
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/myrmidon/components/builder/ant1convert.xsl
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+ Converted Project file.
+
+ 2.0
+
+
+
+
+
+
+
+
+ Copied Project file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+