diff --git a/proposal/myrmidon/.ant.properties b/proposal/myrmidon/.ant.properties
new file mode 100644
index 000000000..a1e728061
--- /dev/null
+++ b/proposal/myrmidon/.ant.properties
@@ -0,0 +1 @@
+build.compiler=jikes
\ No newline at end of file
diff --git a/proposal/myrmidon/build.bat b/proposal/myrmidon/build.bat
new file mode 100755
index 000000000..ce02ad3f1
--- /dev/null
+++ b/proposal/myrmidon/build.bat
@@ -0,0 +1,16 @@
+@echo off
+
+echo.
+echo Ant Build System
+echo ----------------
+
+set ANT_HOME=.
+
+set CLASSPATH=
+
+%ANT_HOME%\bin\ant.bat -emacs %1 %2 %3 %4 %5 %6 %7 %8
+goto cleanup
+
+:cleanup
+set ANT_HOME=
+set CLASSPATH=
diff --git a/proposal/myrmidon/build.sh b/proposal/myrmidon/build.sh
new file mode 100644
index 000000000..00b3cbf1c
--- /dev/null
+++ b/proposal/myrmidon/build.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+echo
+echo "Ant Build System"
+echo "----------------"
+
+chmod u+x $PWD/bin/antRun
+chmod u+x $PWD/bin/ant
+#export ANT_OPTS="-Djava.compiler="
+
+$PWD/bin/ant -emacs $@ | awk -f $PWD/bin/fixPath.awk
diff --git a/proposal/myrmidon/build.xml b/proposal/myrmidon/build.xml
new file mode 100644
index 000000000..f90cffa37
--- /dev/null
+++ b/proposal/myrmidon/build.xml
@@ -0,0 +1,260 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/proposal/myrmidon/emacs-jprj.el b/proposal/myrmidon/emacs-jprj.el
new file mode 100644
index 000000000..ddf0be918
--- /dev/null
+++ b/proposal/myrmidon/emacs-jprj.el
@@ -0,0 +1,7 @@
+(setq jprj-base-path (message "%s/" (expand-file-name (substitute-in-file-name "."))) )
+(setq jprj-src-path (message "%ssrc/java/" jprj-base-path) )
+(setq jprj-compile-command "./build.bat")
+(setq jprj-run-command "./build.bat&");; cd dist; bin/ant.bat -f ../src/make/sample.xmk&")
+;(setq tab-expansion-size 4)
+
+(load "update-prj")
diff --git a/proposal/myrmidon/lib/ant.jar b/proposal/myrmidon/lib/ant.jar
new file mode 100644
index 000000000..f74662db3
Binary files /dev/null and b/proposal/myrmidon/lib/ant.jar differ
diff --git a/proposal/myrmidon/lib/avalonapi.jar b/proposal/myrmidon/lib/avalonapi.jar
new file mode 100644
index 000000000..1238e40f0
Binary files /dev/null and b/proposal/myrmidon/lib/avalonapi.jar differ
diff --git a/proposal/myrmidon/lib/xerces.jar b/proposal/myrmidon/lib/xerces.jar
new file mode 100644
index 000000000..f4708953a
Binary files /dev/null and b/proposal/myrmidon/lib/xerces.jar differ
diff --git a/proposal/myrmidon/src/java/org/apache/ant/AntContextResources.java b/proposal/myrmidon/src/java/org/apache/ant/AntContextResources.java
new file mode 100644
index 000000000..fff46f0c9
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/AntContextResources.java
@@ -0,0 +1,29 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant;
+
+public interface AntContextResources
+{
+ // the directory of ant
+ String HOME_DIR = "ant.install.dir";
+
+ // the bin directory of ant
+ String BIN_DIR = "ant.install.bin";
+
+ // the lib directory of ant
+ String LIB_DIR = "ant.install.lib";
+
+ // the tasklib directory of ant
+ String TASKLIB_DIR = "ant.install.task-lib";
+
+ // the directory to look for per user ant information
+ String USER_DIR = "ant.user.dir";
+
+ // the directory to look for per project ant information
+ String PROJECT_DIR = "ant.project.dir";
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/AntException.java b/proposal/myrmidon/src/java/org/apache/ant/AntException.java
new file mode 100644
index 000000000..846b52c35
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/AntException.java
@@ -0,0 +1,25 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant;
+
+import org.apache.avalon.CascadingRuntimeException;
+
+public class AntException
+ extends CascadingRuntimeException
+{
+ public AntException( final String message )
+ {
+ this( message, null );
+ }
+
+ public AntException( final String message, final Throwable throwable )
+ {
+ super( message, throwable );
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/Main.java b/proposal/myrmidon/src/java/org/apache/ant/Main.java
new file mode 100644
index 000000000..f474e65d6
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/Main.java
@@ -0,0 +1,797 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Iterator;
+import org.apache.ant.project.DefaultProjectEngine;
+import org.apache.ant.project.Project;
+import org.apache.ant.project.ProjectBuilder;
+import org.apache.ant.project.ProjectEngine;
+import org.apache.ant.project.ProjectListener;
+import org.apache.ant.tasklet.JavaVersion;
+import org.apache.ant.tasklet.TaskletContext;
+import org.apache.avalon.Disposable;
+import org.apache.avalon.Initializable;
+import org.apache.avalon.camelot.Deployer;
+import org.apache.avalon.camelot.DeploymentException;
+import org.apache.avalon.util.StringUtil;
+import org.apache.avalon.util.cli.AbstractMain;
+import org.apache.avalon.util.cli.CLOption;
+import org.apache.avalon.util.cli.CLOptionDescriptor;
+import org.apache.avalon.util.io.ExtensionFileFilter;
+import org.apache.avalon.util.io.FileUtil;
+import org.apache.log.Category;
+import org.apache.log.LogKit;
+import org.apache.log.Logger;
+import org.apache.log.Priority;
+
+/**
+ * The class to kick the tires and light the fires.
+ * Starts ant, loads ProjectBuilder, builds project then uses ProjectEngine
+ * to run project.
+ *
+ * @author Peter Donald
+ */
+public class Main
+ extends AbstractMain
+{
+ public final static String BUILD_DATE = "@@DATE@@";
+ public final static String BUILD_VERSION = "@@VERSION@@";
+ public final static String VERSION =
+ "Ant " + BUILD_VERSION + " compiled on " + BUILD_DATE;
+
+ protected final static String DEFAULT_LOGLEVEL = "INFO";
+ protected final static String DEFAULT_LIB_DIRECTORY = ".." + File.separator + "lib";
+ protected final static String DEFAULT_TASKLIB_DIRECTORY = DEFAULT_LIB_DIRECTORY;
+ protected final static String DEFAULT_FILENAME = "build.xmk";
+ protected final static String DEFAULT_LISTENER =
+ "org.apache.ant.project.DefaultProjectListener";
+
+ protected final static String DEFAULT_BUILDER =
+ "org.apache.ant.project.DefaultProjectBuilder";
+
+ private static final int HELP_OPT = 'h';
+ private static final int QUIET_OPT = 'q';
+ private static final int VERBOSE_OPT = 'v';
+ private static final int FILE_OPT = 'f';
+ private static final int LOG_LEVEL_OPT = 'l';
+ private static final int DEFINE_OPT = 'D';
+ private static final int VERSION_OPT = 1;
+ private static final int LISTENER_OPT = 2;
+ private static final int BIN_DIR_OPT = 3;
+ private static final int LIB_DIR_OPT = 4;
+ private static final int TASKLIB_DIR_OPT = 5;
+ private static final int INCREMENTAL_OPT = 6;
+ private static final int HOME_DIR_OPT = 7;
+
+ //incompatable options for info options
+ private static final int INFO_OPT_INCOMPAT[] = new int[]
+ {
+ HELP_OPT, QUIET_OPT, VERBOSE_OPT, FILE_OPT,
+ LOG_LEVEL_OPT, VERSION_OPT, LISTENER_OPT,
+ DEFINE_OPT
+ //BIN_DIR_OPT, LIB_DIR_OPT, TASKLIB_DIR_OPT, HOME_DIR_OPT
+ };
+
+ //incompatable options for other logging options
+ private static final int LOG_OPT_INCOMPAT[] = new int[]
+ {
+ QUIET_OPT, VERBOSE_OPT, LOG_LEVEL_OPT
+ };
+
+ protected Logger m_logger;
+
+ protected File m_binDir;
+ protected File m_homeDir;
+ protected File m_libDir;
+ protected File m_taskLibDir;
+ protected File m_buildFile;
+ protected File m_userDir;
+
+ public static void main( final String args[] )
+ {
+ final Main main = new Main();
+
+ try { main.execute( args ); }
+ catch( final AntException ae )
+ {
+ main.m_logger.error( "Error: " + ae.getMessage() );
+ main.m_logger.debug( "Exception..." + StringUtil.printStackTrace( ae ) );
+ }
+ catch( final Throwable throwable )
+ {
+ main.m_logger.error( "Error: " + throwable );
+ main.m_logger.debug( "Exception..." + StringUtil.printStackTrace( throwable ) );
+ }
+ }
+
+ /**
+ * Initialise the options for command line parser.
+ */
+ protected CLOptionDescriptor[] createCLOptions()
+ {
+ //TODO: localise
+ final CLOptionDescriptor options[] = new CLOptionDescriptor[ 13 ];
+
+ options[0] =
+ new CLOptionDescriptor( "help",
+ CLOptionDescriptor.ARGUMENT_DISALLOWED,
+ HELP_OPT,
+ "display this help message",
+ INFO_OPT_INCOMPAT );
+
+ options[1] =
+ new CLOptionDescriptor( "file",
+ CLOptionDescriptor.ARGUMENT_REQUIRED,
+ FILE_OPT,
+ "the build file." );
+
+ options[2] =
+ new CLOptionDescriptor( "log-level",
+ CLOptionDescriptor.ARGUMENT_REQUIRED,
+ LOG_LEVEL_OPT,
+ "the verbosity level at which to log messages. " +
+ "(DEBUG|INFO|WARN|ERROR|FATAL_ERROR)",
+ LOG_OPT_INCOMPAT );
+
+ options[3] =
+ new CLOptionDescriptor( "quiet",
+ CLOptionDescriptor.ARGUMENT_DISALLOWED,
+ QUIET_OPT,
+ "equivelent to --log-level=FATAL_ERROR",
+ LOG_OPT_INCOMPAT );
+
+ options[4] =
+ new CLOptionDescriptor( "verbose",
+ CLOptionDescriptor.ARGUMENT_DISALLOWED,
+ VERBOSE_OPT,
+ "equivelent to --log-level=INFO",
+ LOG_OPT_INCOMPAT );
+
+ options[5] =
+ new CLOptionDescriptor( "listener",
+ CLOptionDescriptor.ARGUMENT_REQUIRED,
+ LISTENER_OPT,
+ "the listener for log events." );
+
+ options[6] =
+ new CLOptionDescriptor( "version",
+ CLOptionDescriptor.ARGUMENT_DISALLOWED,
+ VERSION_OPT,
+ "display version",
+ INFO_OPT_INCOMPAT );
+
+ options[7] =
+ new CLOptionDescriptor( "bin-dir",
+ CLOptionDescriptor.ARGUMENT_REQUIRED,
+ BIN_DIR_OPT,
+ "the listener for log events." );
+
+ options[8] =
+ new CLOptionDescriptor( "lib-dir",
+ CLOptionDescriptor.ARGUMENT_REQUIRED,
+ LIB_DIR_OPT,
+ "the lib directory to scan for jars/zip files." );
+
+ options[9] =
+ new CLOptionDescriptor( "task-lib-dir",
+ CLOptionDescriptor.ARGUMENT_REQUIRED,
+ TASKLIB_DIR_OPT,
+ "the task lib directory to scan for .tsk files." );
+ options[10] =
+ new CLOptionDescriptor( "incremental",
+ CLOptionDescriptor.ARGUMENT_DISALLOWED,
+ INCREMENTAL_OPT,
+ "Run in incremental mode" );
+ options[11] =
+ new CLOptionDescriptor( "ant-home",
+ CLOptionDescriptor.ARGUMENT_REQUIRED,
+ HOME_DIR_OPT,
+ "Specify ant home directory" );
+ options[12] =
+ new CLOptionDescriptor( "define",
+ CLOptionDescriptor.ARGUMENTS_REQUIRED_2,
+ DEFINE_OPT,
+ "Define a variable (ie -Dfoo=var)" );
+ return options;
+ }
+
+ /**
+ * Entry point for standard ant.
+ *
+ * @param clOptions the list of command line options
+ */
+ protected void execute( final List clOptions )
+ throws Throwable
+ {
+ final int size = clOptions.size();
+ final ArrayList targets = new ArrayList();
+ String filename = null;
+ String listenerName = null;
+ String builderName = null;
+ String logLevel = null;
+ String binDir = null;
+ String homeDir = null;
+ String libDir = null;
+ String taskLibDir = null;
+ boolean incremental = false;
+ HashMap defines = new HashMap();
+
+ for( int i = 0; i < size; i++ )
+ {
+ final CLOption option = (CLOption)clOptions.get( i );
+
+ switch( option.getId() )
+ {
+ case 0: targets.add( option.getArgument() ); break;
+ case HELP_OPT: usage(); return;
+ case VERSION_OPT: System.out.println( VERSION ); return;
+ case FILE_OPT: filename = option.getArgument(); break;
+ case BIN_DIR_OPT: binDir = option.getArgument(); break;
+ case LIB_DIR_OPT: libDir = option.getArgument(); break;
+ case HOME_DIR_OPT: homeDir = option.getArgument(); break;
+ case TASKLIB_DIR_OPT: taskLibDir = option.getArgument(); break;
+ case VERBOSE_OPT: logLevel = "INFO"; break;
+ case QUIET_OPT: logLevel = "ERROR"; break;
+ case LOG_LEVEL_OPT: logLevel = option.getArgument(); break;
+ case LISTENER_OPT: listenerName = option.getArgument(); break;
+ case INCREMENTAL_OPT: incremental = true; break;
+
+ case DEFINE_OPT:
+ defines.put( option.getArgument( 0 ), option.getArgument( 1 ) );
+ break;
+ }
+ }
+
+ if( null == logLevel ) logLevel = getDefaultLogLevel();
+ if( null == listenerName ) listenerName = getDefaultListener();
+ if( null == filename ) filename = getDefaultFilename();
+ if( null == libDir ) libDir = getDefaultLibDir();
+ if( null == taskLibDir ) taskLibDir = getDefaultTaskLibDir();
+ if( null == builderName ) builderName = getBuilderNameFor( filename );
+
+ setupLogger( logLevel ); //handle logging...
+ setupListener( listenerName ); //handle listener..
+ setupDefaultAntDirs();
+
+ if( null == binDir && null == homeDir )
+ {
+ m_homeDir = getDefaultHomeDir();
+ m_binDir = m_homeDir.getParentFile();
+ }
+ else if( null == binDir ) // && null != homeDir
+ {
+ m_homeDir = getHomeDir( homeDir );
+ m_binDir = new File( m_homeDir, "bin" );
+ }
+ else
+ {
+ m_binDir = getBinDir( binDir );
+ m_homeDir = m_binDir.getParentFile();
+ }
+
+ m_libDir = getLibDir( m_homeDir, libDir );
+ m_taskLibDir = getTaskLibDir( m_homeDir, taskLibDir );
+ m_buildFile = getFile( filename );
+
+ m_logger.info( "Ant Base Directory: " + m_homeDir );
+ m_logger.info( "Ant Bin Directory: " + m_binDir );
+ m_logger.info( "Ant Build File: " + m_buildFile );
+ m_logger.debug( "Ant Lib Directory: " + m_libDir );
+ m_logger.debug( "Ant Task Lib Directory: " + m_taskLibDir );
+
+ setupContextClassLoader( m_libDir );
+
+ final Project project = getProject( builderName, m_buildFile );
+ setupProjectContext( project, defines );
+
+ final ProjectEngine engine = getProjectEngine();
+
+ //make sure Engine is sweet...
+ if( engine instanceof Initializable )
+ {
+ ((Initializable)engine).init();
+ }
+
+ deployDefaultTaskLibs( engine, m_taskLibDir );
+
+ BufferedReader reader = null;
+
+ while( true )
+ {
+ doBuild( engine, project, targets );
+
+ if( !incremental ) break;
+
+ System.out.println( "Continue ? (Enter no to stop)" );
+
+ if( null == reader )
+ {
+ reader = new BufferedReader( new InputStreamReader( System.in ) );
+ }
+
+ String line = reader.readLine();
+
+ if( line.equalsIgnoreCase( "no" ) ) break;
+
+ }
+
+ if( engine instanceof Disposable )
+ {
+ ((Disposable)engine).dispose();
+ }
+ }
+
+ protected void deployDefaultTaskLibs( final ProjectEngine engine,
+ final File taskLibDirectory )
+
+ {
+ final ExtensionFileFilter filter =
+ new ExtensionFileFilter( new String[] { ".tsk" } );
+
+ final File files[] = taskLibDirectory.listFiles( filter );
+ final Deployer deployer = engine.getDeployer();
+
+ for( int i = 0; i < files.length; i++ )
+ {
+ final String name = files[ i ].getName();
+
+ try
+ {
+ deployer.deploy( name.substring( 0, name.length() - 4 ),
+ files[ i ].toURL() );
+ }
+ catch( final MalformedURLException mue ) {}
+ catch( final DeploymentException de )
+ {
+ throw new AntException( "Failed to deploy task library " + files[ i ],
+ de );
+ }
+ }
+ }
+
+ protected void doBuild( final ProjectEngine engine,
+ final Project project,
+ final ArrayList targets )
+ {
+ try
+ {
+ final int targetCount = targets.size();
+
+ if( 0 == targetCount )
+ {
+ engine.execute( project, project.getDefaultTargetName() );
+ }
+ else
+ {
+ for( int i = 0; i < targetCount; i++ )
+ {
+ engine.execute( project, (String)targets.get( i ) );
+ }
+ }
+ }
+ catch( final AntException ae )
+ {
+ m_logger.error( "BUILD FAILED" );
+ m_logger.error( "Reason:\n" + StringUtil.printStackTrace( ae, 5, true ) );
+ }
+ }
+
+ protected void setupLogger( final String logLevel )
+ {
+ m_logger = createLogger( logLevel );
+ }
+
+ protected void setupListener( final String listenerName )
+ {
+ final ProjectListener listener = createListener( listenerName );
+ m_logger.addLogTarget( listener );
+ }
+
+ protected void setupContextClassLoader( final File libDir )
+ {
+ final ClassLoader classLoader = createClassLoader( libDir );
+ Thread.currentThread().setContextClassLoader( classLoader );
+ }
+
+ protected ClassLoader createClassLoader( final File libDir )
+ {
+ final ExtensionFileFilter filter =
+ new ExtensionFileFilter( new String[] { ".jar", ".zip" } );
+
+ final ArrayList urlList = new ArrayList();
+ toURLS( urlList, libDir.listFiles( filter ) );
+
+ final URL urls[] = (URL[])urlList.toArray( new URL[0] );
+
+ return new URLClassLoader( urls, ClassLoader.getSystemClassLoader() );
+ }
+
+ protected void toURLS( final ArrayList urls, final File files[] )
+ {
+ for( int i = 0; i < files.length; i++ )
+ {
+ try { urls.add( files[ i ].toURL() ); }
+ catch( final MalformedURLException mue ) {}
+ }
+ }
+
+ protected Project getProject( final String builderName, final File file )
+ throws AntException, IOException
+ {
+ m_logger.debug( "Ant Project Builder: " + builderName );
+ final ProjectBuilder builder = createBuilder( builderName );
+ builder.setLogger( m_logger );
+
+ //create the project
+ final Project project = builder.build( file );
+
+ return project;
+ }
+
+ protected void setupProjectContext( final Project project, final HashMap defines )
+ throws AntException
+ {
+ final TaskletContext context = project.getContext();
+
+ final Iterator keys = defines.keySet().iterator();
+ //make sure these come before following so they get overidden if user tries to
+ //confuse the system
+ while( keys.hasNext() )
+ {
+ final String key = (String)keys.next();
+ final String value = (String)defines.get( key );
+ context.setProperty( key, value );
+ }
+
+ context.setProperty( AntContextResources.HOME_DIR, m_homeDir );
+ context.setProperty( AntContextResources.BIN_DIR, m_binDir );
+ context.setProperty( AntContextResources.LIB_DIR, m_libDir );
+ context.setProperty( AntContextResources.TASKLIB_DIR, m_taskLibDir );
+ //context.put( AntContextResources.USER_DIR, m_userDir );
+ context.setProperty( TaskletContext.LOGGER, m_logger );
+ context.setProperty( TaskletContext.JAVA_VERSION, getJavaVersion() );
+ }
+
+ protected JavaVersion getJavaVersion()
+ {
+ JavaVersion version = JavaVersion.JAVA1_0;
+
+ try
+ {
+ Class.forName( "java.lang.Void" );
+ version = JavaVersion.JAVA1_1;
+ Class.forName( "java.lang.ThreadLocal" );
+ version = JavaVersion.JAVA1_2;
+ Class.forName( "java.lang.StrictMath" );
+ version = JavaVersion.JAVA1_3;
+ }
+ catch( final ClassNotFoundException cnfe ) {}
+
+ return version;
+ }
+
+ protected ProjectEngine getProjectEngine()
+ {
+ final ProjectEngine engine = createProjectEngine();
+ engine.setLogger( m_logger );
+ return engine;
+ }
+
+ protected ProjectEngine createProjectEngine()
+ {
+ return new DefaultProjectEngine();
+ }
+
+ protected File getHomeDir( final String homeDir )
+ throws AntException
+ {
+ final File file = new File( homeDir );
+ checkDirectory( file, "ant-home" );
+ return file;
+ }
+
+ protected File getBinDir( final String binDir )
+ throws AntException
+ {
+ File file = (new File( binDir )).getAbsoluteFile();
+ if( !file.isDirectory() ) file = file.getParentFile();
+ checkDirectory( file, "bin-dir" );
+ return file;
+ }
+
+ protected File getLibDir( final File antHome, String libDir )
+ throws AntException
+ {
+ return resolveDirectory( antHome, libDir, "lib-dir" );
+ }
+
+ protected File getTaskLibDir( final File antHome, final String taskLibDir )
+ throws AntException
+ {
+ return resolveDirectory( antHome, taskLibDir, "task-lib-dir" );
+ }
+
+ protected File resolveDirectory( final File antHome, final String dir, final String name )
+ throws AntException
+ {
+ final File file = FileUtil.resolveFile( antHome, dir );
+ checkDirectory( file, name );
+ return file;
+ }
+
+ protected void checkDirectory( final File file, final String name )
+ {
+ if( !file.exists() )
+ {
+ throw new AntException( name + " (" + file + ") does not exist" );
+ }
+ else if( !file.isDirectory() )
+ {
+ throw new AntException( name + " (" + file + ") is not a directory" );
+ }
+ }
+
+ protected ProjectListener createListener( final String listenerName )
+ throws AntException
+ {
+ try { return (ProjectListener)createObject( listenerName, "listener" ); }
+ catch( final ClassCastException cce )
+ {
+ throw new AntException( "Aparently the listener named " + listenerName +
+ " does not implement the ProjectListener interface",
+ cce );
+ }
+ }
+
+ protected Logger createLogger( final String logLevel )
+ throws AntException
+ {
+ final String logLevelCapitalized = logLevel.toUpperCase();
+ final Priority.Enum priority = LogKit.getPriorityForName( logLevelCapitalized );
+
+ if( !priority.getName().equals( logLevelCapitalized ) )
+ {
+ throw new AntException( "Unknown log level - " + logLevel );
+ }
+
+ final Category category = LogKit.createCategory( "ant", priority );
+ return LogKit.createLogger( category );
+ }
+
+ protected void setupDefaultAntDirs()
+ {
+ final String os = System.getProperty( "os.name" );
+ final String userDir = System.getProperty( "user.home" );
+ m_userDir =
+ (new File( getUserLocationFor( os, userDir ) )).getAbsoluteFile();
+ }
+
+ /**
+ * Retrieve default bin-dir value if possible (Otherwise throw an exception).
+ *
+ * Lookup OS specific places for ant to be.
+ * /opt/ant on *BSD ?
+ * /usr/local/ant on linux ?
+ * /Program Files/Ant on Win32 ?
+ *
+ * @return bin directory
+ */
+ protected File getDefaultHomeDir()
+ throws AntException
+ {
+ if( null != m_userDir )
+ {
+ try
+ {
+ checkDirectory( m_userDir, null );
+ return m_userDir;
+ }
+ catch( final AntException ae ) {}
+ }
+
+ final String os = System.getProperty( "os.name" );
+ final File candidate =
+ (new File( getSystemLocationFor( os ) )).getAbsoluteFile();
+ checkDirectory( candidate, "ant-home" );
+ return candidate;
+ }
+
+ /**
+ * This determins a mapping from an OS specific place to ants home directory.
+ * In later versions the mapping should be read from configuration file.
+ *
+ * @param os the name of OS
+ * @return the location of directory
+ */
+ protected String getUserLocationFor( final String os, final String userDir )
+ {
+ if( os.startsWith( "Windows" ) )
+ {
+ return userDir + "\\Ant";
+ }
+ else if( '/' == File.separatorChar )
+ {
+ if( os.startsWith( "Linux" ) ) return userDir + "/ant";
+ else return userDir + "/opt/ant";
+ }
+ else
+ {
+ return userDir + File.separator + "ant";
+ }
+ }
+
+ /**
+ * This determins a mapping from an OS specific place to ants home directory.
+ * In later versions the mapping should be read from configuration file.
+ *
+ * @param os the name of OS
+ * @return the location of directory
+ */
+ protected String getSystemLocationFor( final String os )
+ {
+ if( os.startsWith( "Windows" ) )
+ {
+ return "\\Program Files\\Ant";
+ }
+ else if( '/' == File.separatorChar )
+ {
+ if( os.startsWith( "Linux" ) ) return "/usr/local/ant";
+ else return "/opt/ant";
+ }
+ else
+ {
+ return File.separator + "ant";
+ }
+ }
+
+ protected String getDefaultLibDir()
+ {
+ return DEFAULT_LIB_DIRECTORY;
+ }
+
+ protected String getDefaultTaskLibDir()
+ {
+ return DEFAULT_TASKLIB_DIRECTORY;
+ }
+
+ /**
+ * Retrieve default filename. Overide this in base classes to change default.
+ *
+ * @return the default filename
+ */
+ protected String getDefaultFilename()
+ {
+ return DEFAULT_FILENAME;
+ }
+
+ /**
+ * Retrieve default logelevel. Overide this in base classes to change default.
+ *
+ * @return the default loglevel
+ */
+ protected String getDefaultLogLevel()
+ {
+ return DEFAULT_LOGLEVEL;
+ }
+
+ /**
+ * Retrieve default listener. Overide this in base classes to change default.
+ *
+ * @return the default listener
+ */
+ protected String getDefaultListener()
+ {
+ return DEFAULT_LISTENER;
+ }
+
+ /**
+ * Get File object for filename.
+ * Check that file exists and is not a directory.
+ *
+ * @param filename the filename
+ * @return the file object
+ * @exception AntException if an error occurs
+ */
+ protected File getFile( final String filename )
+ throws AntException, IOException
+ {
+ final File file = (new File( filename )).getCanonicalFile();
+
+ if( !file.exists() )
+ {
+ throw new AntException( "File " + file + " does not exist." );
+ }
+
+ if( file.isDirectory() )
+ {
+ throw new AntException( "File " + file + " is a directory." );
+ }
+
+ return file;
+ }
+
+ /**
+ * Create instance of Builder based on classname.
+ *
+ * @param builderName builder class name
+ * @return the ProjectBuilder
+ * @exception AntException if an error occurs
+ */
+ protected ProjectBuilder createBuilder( final String builderName )
+ throws AntException
+ {
+ try { return (ProjectBuilder)createObject( builderName, "builder" ); }
+ catch( final ClassCastException cce )
+ {
+ throw new AntException( "Aparently the builder named " + builderName +
+ " does not implement the ProjectBuilder interface",
+ cce );
+ }
+ }
+
+ /**
+ * Helper method to create object and throw an apporpriate AntException if creation failed.
+ *
+ * @param objectName the classname of object
+ * @param type the type of object being created (ie builder|listener)
+ * @return the created object
+ * @exception AntException if an error occurs
+ */
+ protected Object createObject( final String objectName, final String type )
+ throws AntException
+ {
+ try
+ {
+ final Class clazz = Class.forName( objectName );
+ return clazz.newInstance();
+ }
+ catch( final IllegalAccessException iae )
+ {
+ throw new AntException( "Non-public constructor for " + type + " " + objectName,
+ iae );
+ }
+ catch( final InstantiationException ie )
+ {
+ throw new AntException( "Error instantiating class for " + type + " " + objectName,
+ ie );
+ }
+ catch( final ClassNotFoundException cnfe )
+ {
+ throw new AntException( "Could not find the class for " + type + " " + objectName,
+ cnfe );
+ }
+ }
+
+ /**
+ * Retrieve class name of builder for file.
+ * Eventually this will look in a registry of file extentions to BuilderNames.
+ *
+ * @param filename the filename
+ * @return the name of Class for Builder
+ * @exception AntException if an error occurs
+ */
+ protected String getBuilderNameFor( final String filename )
+ throws AntException
+ {
+ return DEFAULT_BUILDER;
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/configuration/Configurable.java b/proposal/myrmidon/src/java/org/apache/ant/configuration/Configurable.java
new file mode 100644
index 000000000..44903516e
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/configuration/Configurable.java
@@ -0,0 +1,39 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.configuration;
+
+import org.apache.avalon.ConfigurationException;
+
+/**
+ * This interface should be implemented by classes that need to be
+ * configured with custom parameters before initialization.
+ *
+ *
+ * The contract surrounding a Configurable
is that the
+ * instantiating entity must call the configure
+ * method before it is valid. The configure
method
+ * must be called after the constructor, and before any other method.
+ *
+ * @author Federico Barbieri
+ * @author Pierpaolo Fumagalli
+ * @author Stefano Mazzocchi
+ * @author Berin Loritsch
+ * @author Peter Donald
+ */
+public interface Configurable
+{
+ /**
+ * Pass the Configuration
to the Configurable
+ * class. This method must always be called after the constructor
+ * and before any other method.
+ *
+ * @param configuration the class configurations.
+ */
+ void configure( Configuration configuration )
+ throws ConfigurationException;
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/configuration/Configuration.java b/proposal/myrmidon/src/java/org/apache/ant/configuration/Configuration.java
new file mode 100644
index 000000000..c410857e3
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/configuration/Configuration.java
@@ -0,0 +1,31 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.configuration;
+
+import java.util.Iterator;
+
+/**
+ * Hostile fork till Avalon gets equivelent functionality ;)
+ */
+public interface Configuration
+ extends org.apache.avalon.Configuration
+{
+ /**
+ * Retrieve a list of all child names.
+ *
+ * @return the child names
+ */
+ Iterator getChildren();
+
+ /**
+ * Retrieve a list of all attribute names.
+ *
+ * @return the attribute names
+ */
+ Iterator getAttributeNames();
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/configuration/ConfigurationBuilder.java b/proposal/myrmidon/src/java/org/apache/ant/configuration/ConfigurationBuilder.java
new file mode 100644
index 000000000..a90c6c875
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/configuration/ConfigurationBuilder.java
@@ -0,0 +1,28 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.configuration;
+
+import org.xml.sax.SAXException;
+
+/**
+ * Hostile fork till Avalon gets equivelent functionality ;)
+ */
+public class ConfigurationBuilder
+ extends org.apache.avalon.DefaultConfigurationBuilder
+{
+ public ConfigurationBuilder()
+ throws SAXException
+ {
+ super();
+ }
+
+ protected org.apache.avalon.SAXConfigurationHandler getHandler()
+ {
+ return new SAXConfigurationHandler();
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/configuration/DefaultConfiguration.java b/proposal/myrmidon/src/java/org/apache/ant/configuration/DefaultConfiguration.java
new file mode 100644
index 000000000..916a5dc40
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/configuration/DefaultConfiguration.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 file.
+ */
+package org.apache.ant.configuration;
+
+import java.util.Iterator;
+
+/**
+ * Hostile fork till Avalon gets equivelent functionality ;)
+ */
+public class DefaultConfiguration
+ extends org.apache.avalon.DefaultConfiguration
+ implements Configuration
+{
+ public DefaultConfiguration( final String localname, final String location )
+ {
+ super( localname, location );
+ }
+
+ /**
+ * Retrieve a list of all child names.
+ *
+ * @return the child names
+ */
+ public Iterator getChildren()
+ {
+ if( null == m_children ) return EMPTY_ITERATOR;
+ else return m_children.iterator();
+ }
+
+ /**
+ * Retrieve a list of all attribute names.
+ *
+ * @return the attribute names
+ */
+ public Iterator getAttributeNames()
+ {
+ if( null == m_attributes ) return EMPTY_ITERATOR;
+ else return m_attributes.keySet().iterator();
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/configuration/SAXConfigurationHandler.java b/proposal/myrmidon/src/java/org/apache/ant/configuration/SAXConfigurationHandler.java
new file mode 100644
index 000000000..0a3569bea
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/configuration/SAXConfigurationHandler.java
@@ -0,0 +1,21 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.configuration;
+
+/**
+ * Hostile fork till Avalon gets equivelent functionality ;)
+ */
+public class SAXConfigurationHandler
+ extends org.apache.avalon.SAXConfigurationHandler
+{
+ protected org.apache.avalon.DefaultConfiguration
+ createConfiguration( final String localName, final String location )
+ {
+ return new DefaultConfiguration( localName, location );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/AbstractConverter.java b/proposal/myrmidon/src/java/org/apache/ant/convert/AbstractConverter.java
new file mode 100644
index 000000000..bdadecdea
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/AbstractConverter.java
@@ -0,0 +1,48 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+/**
+ * Instances of this interface are used to convert between different types.
+ *
+ * @author Peter Donald
+ */
+public abstract class AbstractConverter
+ implements Converter
+{
+ protected final Class m_source;
+ protected final Class m_destination;
+
+ public AbstractConverter( final Class source, final Class destination )
+ {
+ m_source = source;
+ m_destination = destination;
+ }
+
+ public Object convert( final Class destination, final Object original )
+ throws Exception
+ {
+ if( m_destination != destination )
+ {
+ throw new IllegalArgumentException( "Destination type " + destination.getName() +
+ " is not equal to " + m_destination );
+ }
+
+ if( !m_source.isInstance( original ) )
+ {
+ throw new IllegalArgumentException( "Object '" + original + "' is not an " +
+ "instance of " + m_source.getName() );
+ }
+
+ return convert( original );
+ }
+
+ protected abstract Object convert( Object original )
+ throws Exception;
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/Converter.java b/proposal/myrmidon/src/java/org/apache/ant/convert/Converter.java
new file mode 100644
index 000000000..a16b13a60
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/Converter.java
@@ -0,0 +1,19 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+/**
+ * Instances of this interface are used to convert between different types.
+ *
+ * @author Peter Donald
+ */
+public interface Converter
+{
+ Object convert( Class destination, Object original )
+ throws Exception;
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterEntry.java b/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterEntry.java
new file mode 100644
index 000000000..18c7b1ee3
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterEntry.java
@@ -0,0 +1,31 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+import org.apache.ant.convert.Converter;
+import org.apache.avalon.camelot.AbstractEntry;
+
+public class ConverterEntry
+ extends AbstractEntry
+{
+ public ConverterEntry( final ConverterInfo info, final Converter converter )
+ {
+ super( info, converter );
+ }
+
+ /**
+ * Retrieve instance of converter.
+ *
+ * @return the component instance
+ */
+ public Converter getConverter()
+ {
+ return (Converter)getInstance();
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterFactory.java b/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterFactory.java
new file mode 100644
index 000000000..504025622
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterFactory.java
@@ -0,0 +1,23 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+import org.apache.avalon.camelot.Factory;
+import org.apache.avalon.camelot.FactoryException;
+
+/**
+ * Facility used to load Converters.
+ *
+ * @author Peter Donald
+ */
+public interface ConverterFactory
+ extends Factory
+{
+ ConverterEntry create( ConverterInfo info )
+ throws FactoryException;
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterInfo.java b/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterInfo.java
new file mode 100644
index 000000000..f4937cd34
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterInfo.java
@@ -0,0 +1,51 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+import java.net.URL;
+import org.apache.avalon.camelot.Info;
+
+/**
+ * This info represents meta-information about a converter.
+ *
+ * @author Peter Donald
+ */
+public interface ConverterInfo
+ extends Info
+{
+ /**
+ * Retrieve the source type from which it can convert.
+ * NB: Should this be an array ????
+ *
+ * @return the classname from which object produced
+ */
+ String getSource();
+
+ /**
+ * Retrieve the type to which the converter converts.
+ * NB: Should this be an array ????
+ *
+ * @return the classname of the produced object
+ */
+ String getDestination();
+
+ /**
+ * Retrieve classname for concerter.
+ *
+ * @return the taskname
+ */
+ String getClassname();
+
+ /**
+ * Retrieve location of task library where task is contained.
+ *
+ * @return the location of task library
+ */
+ URL getLocation();
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterLoader.java b/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterLoader.java
new file mode 100644
index 000000000..13c975dae
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterLoader.java
@@ -0,0 +1,22 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+import org.apache.avalon.camelot.Loader;
+
+/**
+ * Class used to load converters et al from a source.
+ *
+ * @author Peter Donald
+ */
+public interface ConverterLoader
+ extends Loader
+{
+ Converter loadConverter( String converter )
+ throws Exception;
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterRegistry.java b/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterRegistry.java
new file mode 100644
index 000000000..8feccecf5
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/ConverterRegistry.java
@@ -0,0 +1,16 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+import org.apache.avalon.camelot.Registry;
+
+public interface ConverterRegistry
+ extends Registry
+{
+ ConverterInfo getConverterInfo( String source, String destination );
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterFactory.java b/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterFactory.java
new file mode 100644
index 000000000..256735905
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterFactory.java
@@ -0,0 +1,75 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashMap;
+import org.apache.ant.convert.Converter;
+import org.apache.avalon.camelot.Entry;
+import org.apache.avalon.camelot.Factory;
+import org.apache.avalon.camelot.Loader;
+import org.apache.avalon.camelot.FactoryException;
+import org.apache.avalon.camelot.Info;
+
+/**
+ * Facility used to load Converters.
+ *
+ * @author Peter Donald
+ */
+public class DefaultConverterFactory
+ implements ConverterFactory
+{
+ protected final HashMap m_loaders = new HashMap();
+
+ public Entry create( final Info info )
+ throws FactoryException
+ {
+ if( info.getClass().equals( ConverterInfo.class ) )
+ {
+ throw new IllegalArgumentException( "Passed incorrect Info type to factory" );
+ }
+ return create( (ConverterInfo)info );
+ }
+
+ public ConverterEntry create( final ConverterInfo info )
+ throws FactoryException
+ {
+ final ConverterLoader loader = getLoader( info.getLocation() );
+
+ Object object = null;
+
+ try { object = loader.load( info.getClassname() ); }
+ catch( final Exception e )
+ {
+ throw new FactoryException( "Failed loading converter from " + info.getLocation() +
+ " due to " + e, e );
+ }
+
+ return new ConverterEntry( info, (Converter)object );
+ }
+
+ protected ConverterLoader getLoader( final URL location )
+ {
+ ConverterLoader loader = (ConverterLoader)m_loaders.get( location );
+
+ if( null == loader )
+ {
+ loader = createLoader( location );
+ m_loaders.put( location, loader );
+ }
+
+ return loader;
+ }
+
+ protected ConverterLoader createLoader( final URL location )
+ {
+ if( null != location ) return new DefaultConverterLoader( location );
+ else return new DefaultConverterLoader();
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterInfo.java b/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterInfo.java
new file mode 100644
index 000000000..bfa3d663f
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterInfo.java
@@ -0,0 +1,78 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+import java.net.URL;
+
+/**
+ * This info represents meta-information about a converter.
+ *
+ * @author Peter Donald
+ */
+public class DefaultConverterInfo
+ implements ConverterInfo
+{
+ protected final String m_source;
+ protected final String m_destination;
+ protected final String m_classname;
+ protected final URL m_location;
+
+ public DefaultConverterInfo( final String source,
+ final String destination,
+ final String classname,
+ final URL location )
+ {
+ m_source = source;
+ m_destination = destination;
+ m_classname = classname;
+ m_location = location;
+ }
+
+ /**
+ * Retrieve the source type from which it can convert.
+ * NB: Should this be an array ????
+ *
+ * @return the classname from which object produced
+ */
+ public String getSource()
+ {
+ return m_source;
+ }
+
+ /**
+ * Retrieve the type to which the converter converts.
+ * NB: Should this be an array ????
+ *
+ * @return the classname of the produced object
+ */
+ public String getDestination()
+ {
+ return m_destination;
+ }
+
+ /**
+ * Retrieve classname for concerter.
+ *
+ * @return the taskname
+ */
+ public String getClassname()
+ {
+ return m_classname;
+ }
+
+ /**
+ * Retrieve location of task library where task is contained.
+ *
+ * @return the location of task library
+ */
+ public URL getLocation()
+ {
+ return m_location;
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterLoader.java b/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterLoader.java
new file mode 100644
index 000000000..f4c37f183
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterLoader.java
@@ -0,0 +1,39 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import org.apache.avalon.camelot.AbstractLoader;
+
+/**
+ * Class used to load converters et al from a source.
+ *
+ * @author Peter Donald
+ */
+public class DefaultConverterLoader
+ extends AbstractLoader
+ implements ConverterLoader
+{
+ public DefaultConverterLoader()
+ {
+ super( new URLClassLoader( new URL[0],
+ Thread.currentThread().getContextClassLoader() ) );
+ }
+
+ public DefaultConverterLoader( final URL location )
+ {
+ super( new URLClassLoader( new URL[] { location } ) );
+ }
+
+ public Converter loadConverter( final String converter )
+ throws Exception
+ {
+ return (Converter)load( converter );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterRegistry.java b/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterRegistry.java
new file mode 100644
index 000000000..54eaaecb3
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/DefaultConverterRegistry.java
@@ -0,0 +1,51 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert;
+
+import java.util.HashMap;
+import org.apache.avalon.camelot.AbstractRegistry;
+import org.apache.avalon.camelot.Info;
+import org.apache.avalon.camelot.RegistryException;
+
+public class DefaultConverterRegistry
+ extends AbstractRegistry
+ implements ConverterRegistry
+{
+ protected final HashMap m_mapping = new HashMap();
+
+ public ConverterInfo getConverterInfo( final String source, final String destination )
+ {
+ final HashMap map = (HashMap)m_mapping.get( source );
+ if( null == map ) return null;
+ return (ConverterInfo)map.get( destination );
+ }
+
+ protected void checkInfo( final String name, final Info info )
+ throws RegistryException
+ {
+ super.checkInfo( name, info );
+
+ final ConverterInfo converterInfo = (ConverterInfo)info;
+ final String source = converterInfo.getSource();
+ final String destination = converterInfo.getDestination();
+
+ HashMap map = (HashMap)m_mapping.get( source );
+ if( null == map )
+ {
+ map = new HashMap();
+ m_mapping.put( source, map );
+ }
+
+ map.put( destination, info );
+ }
+
+ protected Class getInfoClass()
+ {
+ return ConverterInfo.class;
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToByteConverter.java b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToByteConverter.java
new file mode 100644
index 000000000..64f009cb0
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToByteConverter.java
@@ -0,0 +1,31 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert.core;
+
+import org.apache.ant.convert.AbstractConverter;
+
+/**
+ * String to byte converter
+ *
+ * @author Peter Donald
+ */
+public class StringToByteConverter
+ extends AbstractConverter
+{
+ public StringToByteConverter()
+ {
+ super( String.class, Byte.class );
+ }
+
+ public Object convert( final Object original )
+ throws Exception
+ {
+ return new Byte( (String)original );
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToDoubleConverter.java b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToDoubleConverter.java
new file mode 100644
index 000000000..9fd30be83
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToDoubleConverter.java
@@ -0,0 +1,31 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert.core;
+
+import org.apache.ant.convert.AbstractConverter;
+
+/**
+ * String to double converter
+ *
+ * @author Peter Donald
+ */
+public class StringToDoubleConverter
+ extends AbstractConverter
+{
+ public StringToDoubleConverter()
+ {
+ super( String.class, Double.class );
+ }
+
+ public Object convert( final Object original )
+ throws Exception
+ {
+ return new Double( (String)original );
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToFloatConverter.java b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToFloatConverter.java
new file mode 100644
index 000000000..53405b1c1
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToFloatConverter.java
@@ -0,0 +1,31 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert.core;
+
+import org.apache.ant.convert.AbstractConverter;
+
+/**
+ * String to float converter
+ *
+ * @author Peter Donald
+ */
+public class StringToFloatConverter
+ extends AbstractConverter
+{
+ public StringToFloatConverter()
+ {
+ super( String.class, Float.class );
+ }
+
+ public Object convert( final Object original )
+ throws Exception
+ {
+ return new Float( (String)original );
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToIntegerConverter.java b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToIntegerConverter.java
new file mode 100644
index 000000000..7bc118077
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToIntegerConverter.java
@@ -0,0 +1,31 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert.core;
+
+import org.apache.ant.convert.AbstractConverter;
+
+/**
+ * String to integer converter.
+ *
+ * @author Peter Donald
+ */
+public class StringToIntegerConverter
+ extends AbstractConverter
+{
+ public StringToIntegerConverter()
+ {
+ super( String.class, Integer.class );
+ }
+
+ public Object convert( final Object original )
+ throws Exception
+ {
+ return new Integer( (String)original );
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToLongConverter.java b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToLongConverter.java
new file mode 100644
index 000000000..e70f29307
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToLongConverter.java
@@ -0,0 +1,31 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert.core;
+
+import org.apache.ant.convert.AbstractConverter;
+
+/**
+ * String to long converter
+ *
+ * @author Peter Donald
+ */
+public class StringToLongConverter
+ extends AbstractConverter
+{
+ public StringToLongConverter()
+ {
+ super( String.class, Long.class );
+ }
+
+ public Object convert( final Object original )
+ throws Exception
+ {
+ return new Long( (String)original );
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToShortConverter.java b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToShortConverter.java
new file mode 100644
index 000000000..d5f1e7848
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/core/StringToShortConverter.java
@@ -0,0 +1,31 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.convert.core;
+
+import org.apache.ant.convert.AbstractConverter;
+
+/**
+ * String to short converter
+ *
+ * @author Peter Donald
+ */
+public class StringToShortConverter
+ extends AbstractConverter
+{
+ public StringToShortConverter()
+ {
+ super( String.class, Short.class );
+ }
+
+ public Object convert( final Object original )
+ throws Exception
+ {
+ return new Short( (String)original );
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/convert/core/converters.properties b/proposal/myrmidon/src/java/org/apache/ant/convert/core/converters.properties
new file mode 100644
index 000000000..52c6bb2a5
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/convert/core/converters.properties
@@ -0,0 +1,6 @@
+org.apache.ant.convert.core.StringToLongConverter=java.lang.String, java.lang.Long
+org.apache.ant.convert.core.StringToIntegerConverter=java.lang.String, java.lang.Integer
+org.apache.ant.convert.core.StringToShortConverter=java.lang.String, java.lang.Short
+org.apache.ant.convert.core.StringToByteConverter=java.lang.String, java.lang.Byte
+org.apache.ant.convert.core.StringToDoubleConverter=java.lang.String, java.lang.Double
+org.apache.ant.convert.core.StringToFloatConverter=java.lang.String, java.lang.Float
diff --git a/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProject.java b/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProject.java
new file mode 100644
index 000000000..2fc70820f
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProject.java
@@ -0,0 +1,77 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.project;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import org.apache.ant.AntException;
+import org.apache.ant.tasklet.DefaultTaskletContext;
+import org.apache.ant.tasklet.TaskletContext;
+
+public class DefaultProject
+ implements Project
+{
+ protected final TaskletContext m_baseContext = new DefaultTaskletContext();
+ protected final HashMap m_targets = new HashMap();
+ protected Target m_implicitTarget;
+ protected String m_defaultTarget;
+
+ public Target getImplicitTarget()
+ {
+ return m_implicitTarget;
+ }
+
+ public void setImplicitTarget( final Target target )
+ {
+ m_implicitTarget = target;
+ }
+
+ public Target getTarget( final String targetName )
+ {
+ return (Target)m_targets.get( targetName );
+ }
+
+ public String getDefaultTargetName()
+ {
+ return m_defaultTarget;
+ }
+
+ public Iterator getTargetNames()
+ {
+ return m_targets.keySet().iterator();
+ }
+
+ public TaskletContext getContext()
+ {
+ return m_baseContext;
+ }
+
+ public void setDefaultTargetName( final String defaultTarget )
+ {
+ m_defaultTarget = defaultTarget;
+ }
+
+ public void addTarget( final String name, final Target target )
+ throws AntException
+ {
+ if( null != m_targets.get( name ) )
+ {
+ throw new AntException( "Can not have two targets in a file with the name " +
+ name );
+ }
+ else
+ {
+ m_targets.put( name, target );
+ }
+ }
+}
+
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProjectBuilder.java b/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProjectBuilder.java
new file mode 100644
index 000000000..ab760516a
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProjectBuilder.java
@@ -0,0 +1,215 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.project;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Iterator;
+import org.apache.ant.AntException;
+import org.apache.ant.configuration.Configuration;
+import org.apache.ant.configuration.ConfigurationBuilder;
+import org.apache.ant.tasklet.TaskletContext;
+import org.apache.avalon.ConfigurationException;
+import org.apache.log.Logger;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+public class DefaultProjectBuilder
+ implements ProjectBuilder
+{
+ protected final ConfigurationBuilder m_configurationBuilder;
+ protected Logger m_logger;
+
+ public DefaultProjectBuilder()
+ {
+ ConfigurationBuilder builder = null;
+ try { builder = new ConfigurationBuilder(); }
+ catch( final SAXException se ) {}
+
+ m_configurationBuilder = builder;
+ }
+
+ public void setLogger( final Logger logger )
+ {
+ m_logger = logger;
+ }
+
+ public Project build( final File projectFile )
+ throws IOException, AntException
+ {
+ try
+ {
+ final String location = projectFile.getCanonicalFile().toURL().toString();
+ final InputSource inputSource = new InputSource( location );
+ final Configuration configuration =
+ (Configuration)m_configurationBuilder.build( inputSource );
+ return build( projectFile, configuration );
+ }
+ catch( final SAXException se )
+ {
+ throw new AntException( "SAXEception: " + se.getMessage(), se );
+ }
+ catch( final ConfigurationException ce )
+ {
+ throw new AntException( "ConfigurationException: " + ce.getMessage(), ce );
+ }
+ }
+
+ protected Project build( final File file, final Configuration configuration )
+ throws IOException, AntException, ConfigurationException
+ {
+ if( !configuration.getName().equals("project") )
+ {
+ throw new AntException( "Project file must be enclosed in project element" );
+ }
+
+ final String baseDirectoryName = configuration.getAttribute( "basedir" );
+ final String defaultTarget = configuration.getAttribute( "default" );
+ final String projectName = configuration.getAttribute( "name" );
+
+ final DefaultProject project = new DefaultProject();
+ project.setDefaultTargetName( defaultTarget );
+
+ final File baseDirectory =
+ (new File( file.getParentFile(), baseDirectoryName )).getAbsoluteFile();
+
+ m_logger.debug( "Project " + projectName + " base directory: " + baseDirectory );
+
+ final TaskletContext context = project.getContext();
+ context.setProperty( TaskletContext.BASE_DIRECTORY, baseDirectory );
+ context.setProperty( Project.PROJECT_FILE, file );
+ context.setProperty( Project.PROJECT, projectName );
+
+ buildTopLevelProject( project, configuration );
+
+ return project;
+ }
+
+ protected void buildTopLevelProject( final DefaultProject project,
+ final Configuration configuration )
+ throws AntException
+ {
+ final Iterator elements = configuration.getChildren();
+
+ while( elements.hasNext() )
+ {
+ final Configuration element = (Configuration)elements.next();
+ final String name = element.getName();
+
+ if( name.equals( "target" ) ) buildTarget( project, element );
+ else if( name.equals( "property" ) ) buildProperty( project, element );
+ else
+ {
+ throw new AntException( "Unknown top-level element " + name +
+ " at " + element.getLocation() );
+ }
+ }
+ }
+
+ protected void buildTarget( final DefaultProject project,
+ final Configuration configuration )
+ {
+ final String name = configuration.getAttribute( "name", null );
+ final String depends = configuration.getAttribute( "depends", null );
+ final String ifCondition = configuration.getAttribute( "if", null );
+ final String unlessCondition = configuration.getAttribute( "unless", null );
+
+ if( null == name )
+ {
+ throw new AntException( "Discovered un-named target at " +
+ configuration.getLocation() );
+ }
+
+ m_logger.debug( "Parsing target: " + name );
+
+ if( null != ifCondition && null != unlessCondition )
+ {
+ throw new AntException( "Discovered invalid target that has both a if and " +
+ "unless condition at " + configuration.getLocation() );
+ }
+
+ final DefaultTarget target = new DefaultTarget();
+
+ if( null != ifCondition )
+ {
+ m_logger.debug( "Target if condition: " + ifCondition );
+ target.setIfCondition( true );
+ target.setCondition( ifCondition );
+ }
+ else if( null != unlessCondition )
+ {
+ m_logger.debug( "Target unless condition: " + unlessCondition );
+ target.setIfCondition( false );
+ target.setCondition( unlessCondition );
+ }
+
+ if( null != depends )
+ {
+ int start = 0;
+ int end = depends.indexOf( ',' );
+
+ while( -1 != end )
+ {
+ final String dependency =
+ parseDependency( configuration, depends.substring( start, end ) );
+
+ target.addDependency( dependency );
+ start = end++;
+ end = depends.indexOf( ',', start );
+ }
+
+ final String dependency =
+ parseDependency( configuration, depends.substring( start ) );
+
+ target.addDependency( dependency );
+ }
+
+ final Iterator tasks = configuration.getChildren();
+ while( tasks.hasNext() )
+ {
+ final Configuration task = (Configuration)tasks.next();
+ m_logger.debug( "Parsed task: " + task.getName() );
+ target.addTask( task );
+ }
+
+ project.addTarget( name, target );
+ }
+
+ protected String parseDependency( final Configuration configuration,
+ String dependency )
+ throws AntException
+ {
+ dependency = dependency.trim();
+
+ if( 0 == dependency.length() )
+ {
+ throw new AntException( "Discovered empty dependency in target " +
+ configuration.getName() + " at " +
+ configuration.getLocation() );
+ }
+
+ m_logger.debug( "Target dependency: " + dependency );
+
+ return dependency;
+ }
+
+ protected void buildProperty( final DefaultProject project,
+ final Configuration configuration )
+ {
+ DefaultTarget target = (DefaultTarget)project.getImplicitTarget();
+
+ if( null == target )
+ {
+ target = new DefaultTarget();
+ project.setImplicitTarget( target );
+ }
+
+ m_logger.debug( "Parsed implicit task: " + configuration.getName() );
+ target.addTask( configuration );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProjectEngine.java b/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProjectEngine.java
new file mode 100644
index 000000000..a9c2463d1
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProjectEngine.java
@@ -0,0 +1,183 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.project;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import org.apache.ant.AntException;
+import org.apache.ant.configuration.Configuration;
+import org.apache.ant.convert.ConverterRegistry;
+import org.apache.ant.convert.DefaultConverterRegistry;
+import org.apache.ant.tasklet.DefaultTaskletContext;
+import org.apache.ant.tasklet.TaskletContext;
+import org.apache.ant.tasklet.engine.DefaultTaskletEngine;
+import org.apache.ant.tasklet.engine.DefaultTaskletInfo;
+import org.apache.ant.tasklet.engine.DefaultTaskletRegistry;
+import org.apache.ant.tasklet.engine.TaskletEngine;
+import org.apache.ant.tasklet.engine.TaskletRegistry;
+import org.apache.ant.tasklet.engine.TskDeployer;
+import org.apache.avalon.DefaultComponentManager;
+import org.apache.avalon.Disposable;
+import org.apache.avalon.Initializable;
+import org.apache.avalon.camelot.Deployer;
+import org.apache.avalon.camelot.DeploymentException;
+import org.apache.avalon.camelot.RegistryException;
+import org.apache.log.Logger;
+
+public class DefaultProjectEngine
+ implements ProjectEngine, Initializable, Disposable
+{
+ protected Deployer m_deployer;
+ protected TaskletRegistry m_taskletRegistry;
+ protected ConverterRegistry m_converterRegistry;
+ protected TaskletEngine m_taskletEngine;
+ protected Logger m_logger;
+
+ public void setLogger( final Logger logger )
+ {
+ m_logger = logger;
+ }
+
+ public void init()
+ throws Exception
+ {
+ m_taskletEngine = createTaskletEngine();
+
+ m_taskletRegistry = createTaskletRegistry();
+ m_converterRegistry = createConverterRegistry();
+ m_deployer = createDeployer();
+
+ //final DefaultTaskletContext context = new DefaultTaskletContext();
+ //m_taskletEngine.contextualize( context );
+
+ final DefaultComponentManager componentManager = new DefaultComponentManager();
+ componentManager.put( "org.apache.ant.tasklet.engine.TaskletRegistry",
+ m_taskletRegistry );
+
+ componentManager.put( "org.apache.ant.convert.ConverterRegistry",
+ m_converterRegistry );
+
+ componentManager.put( "org.apache.avalon.camelot.Deployer", m_deployer );
+
+ m_taskletEngine.compose( componentManager );
+
+ if( m_taskletEngine instanceof Initializable )
+ {
+ ((Initializable)m_taskletEngine).init();
+ }
+ }
+
+ public void dispose()
+ throws Exception
+ {
+ if( m_taskletEngine instanceof Disposable )
+ {
+ ((Disposable)m_taskletEngine).dispose();
+ }
+ }
+
+ public Deployer getDeployer()
+ {
+ return m_deployer;
+ }
+
+ protected TaskletEngine createTaskletEngine()
+ {
+ return new DefaultTaskletEngine();
+ }
+
+ protected TaskletRegistry createTaskletRegistry()
+ {
+ return new DefaultTaskletRegistry();
+ }
+
+ protected ConverterRegistry createConverterRegistry()
+ {
+ return new DefaultConverterRegistry();
+ }
+
+ protected Deployer createDeployer()
+ {
+ final TskDeployer deployer =
+ new TskDeployer( m_taskletRegistry, m_converterRegistry );
+ deployer.setLogger( m_logger );
+ return deployer;
+ }
+
+ public void execute( final Project project, final String target )
+ throws AntException
+ {
+ m_taskletEngine.contextualize( project.getContext() );
+ executeTarget( "", project.getImplicitTarget() );
+
+ final ArrayList done = new ArrayList();
+ execute( project, target, done );
+ }
+
+ protected void execute( final Project project,
+ final String targetName,
+ final ArrayList done )
+ throws AntException
+ {
+ final Target target = project.getTarget( targetName );
+
+ if( null == target )
+ {
+ throw new AntException( "Unable to find target " + targetName );
+ }
+
+ done.add( targetName );
+
+ final Iterator dependencies = target.getDependencies();
+ while( dependencies.hasNext() )
+ {
+ final String dependency = (String)dependencies.next();
+ if( !done.contains( dependency ) )
+ {
+ execute( project, dependency, done );
+ }
+ }
+
+ final TaskletContext context = getContextFor( project, targetName );
+ m_taskletEngine.contextualize( context );
+ executeTarget( targetName, target );
+ }
+
+ protected TaskletContext getContextFor( final Project project, final String targetName )
+ {
+ final DefaultTaskletContext context =
+ new DefaultTaskletContext( project.getContext() );
+
+ context.setProperty( Project.TARGET, targetName );
+ context.put( TaskletContext.LOGGER, m_logger );
+
+ return context;
+ }
+
+ protected void executeTarget( final String targetName, final Target target )
+ throws AntException
+ {
+ m_logger.debug( "Executing target " + targetName );
+
+ final Iterator tasks = target.getTasks();
+ while( tasks.hasNext() )
+ {
+ final Configuration task = (Configuration)tasks.next();
+ executeTask( task );
+ }
+ }
+
+ protected void executeTask( final Configuration configuration )
+ throws AntException
+ {
+ final String name = configuration.getName();
+ m_logger.debug( "Executing task " + name );
+
+ m_taskletEngine.execute( configuration );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProjectListener.java b/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProjectListener.java
new file mode 100644
index 000000000..51f9e203d
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/project/DefaultProjectListener.java
@@ -0,0 +1,62 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.project;
+
+import org.apache.log.format.PatternFormatter;
+import org.apache.log.output.DefaultOutputLogTarget;
+
+public class DefaultProjectListener
+ extends DefaultOutputLogTarget
+ implements ProjectListener
+{
+ protected String m_prefix;
+
+ /**
+ * Initialize the default pattern.
+ */
+ protected void initPattern()
+ {
+ final PatternFormatter formatrer = new PatternFormatter();
+ formatrer.setFormat( "%{message}\\n%{throwable}" );
+ m_formatter = formatrer;
+ }
+
+ public void projectStarted( final String projectName )
+ {
+ output( "Starting project " + projectName + "\n" );
+ }
+
+ public void projectFinished()
+ {
+ }
+
+ public void targetStarted( final String targetName )
+ {
+ output( targetName + ":\n" );
+ }
+
+ public void targetFinished()
+ {
+ }
+
+ public void taskletStarted( final String taskletName )
+ {
+ m_prefix = taskletName;
+ }
+
+ public void taskletFinished()
+ {
+ m_prefix = null;
+ }
+
+ protected void output( final String data )
+ {
+ if( null != m_prefix ) super.output( "[" + m_prefix + "] " + data );
+ else super.output( data );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/project/DefaultTarget.java b/proposal/myrmidon/src/java/org/apache/ant/project/DefaultTarget.java
new file mode 100644
index 000000000..242062aa5
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/project/DefaultTarget.java
@@ -0,0 +1,61 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.project;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import org.apache.ant.configuration.Configuration;
+
+public class DefaultTarget
+ implements Target
+{
+ protected ArrayList m_dependencies = new ArrayList();
+ protected ArrayList m_tasks = new ArrayList();
+ protected String m_condition;
+ protected boolean m_isIfCondition;
+
+ public Iterator getDependencies()
+ {
+ return m_dependencies.iterator();
+ }
+
+ public Iterator getTasks()
+ {
+ return m_tasks.iterator();
+ }
+
+ public String getCondition()
+ {
+ return m_condition;
+ }
+
+ public void setCondition( final String condition )
+ {
+ m_condition = condition;
+ }
+
+ public boolean isIfCondition()
+ {
+ return m_isIfCondition;
+ }
+
+ public void setIfCondition( final boolean isIfCondition )
+ {
+ m_isIfCondition = isIfCondition;
+ }
+
+ public void addDependency( final String dependency )
+ {
+ m_dependencies.add( dependency );
+ }
+
+ public void addTask( final Configuration taskConfiguration )
+ {
+ m_tasks.add( taskConfiguration );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/project/Project.java b/proposal/myrmidon/src/java/org/apache/ant/project/Project.java
new file mode 100644
index 000000000..88bf83eff
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/project/Project.java
@@ -0,0 +1,30 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.project;
+
+import java.util.Iterator;
+import org.apache.ant.AntException;
+import org.apache.ant.tasklet.TaskletContext;
+
+public interface Project
+{
+ // the name of currently executing project
+ String PROJECT = "ant.project.name";
+
+ // the name of currently executing project
+ String PROJECT_FILE = "ant.project.file";
+
+ // the name of currently executing target
+ String TARGET = "ant.target.name";
+
+ String getDefaultTargetName();
+ Target getImplicitTarget();
+ Target getTarget( String name );
+ Iterator getTargetNames();
+ TaskletContext getContext();
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/project/ProjectBuilder.java b/proposal/myrmidon/src/java/org/apache/ant/project/ProjectBuilder.java
new file mode 100644
index 000000000..ac98b8097
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/project/ProjectBuilder.java
@@ -0,0 +1,23 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.project;
+
+import java.io.File;
+import java.io.IOException;
+import org.apache.ant.AntException;
+import org.apache.log.Logger;
+
+public interface ProjectBuilder
+{
+ void setLogger( Logger logger );
+
+ Project build( File projectFile )
+ throws IOException, AntException;
+}
+
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/project/ProjectEngine.java b/proposal/myrmidon/src/java/org/apache/ant/project/ProjectEngine.java
new file mode 100644
index 000000000..04d8b5a5a
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/project/ProjectEngine.java
@@ -0,0 +1,21 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.project;
+
+import org.apache.ant.AntException;
+import org.apache.avalon.camelot.Deployer;
+import org.apache.avalon.camelot.Registry;
+import org.apache.log.Logger;
+
+public interface ProjectEngine
+{
+ Deployer getDeployer();
+ void setLogger( Logger logger );
+ void execute( Project project, String target )
+ throws AntException;
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/project/ProjectListener.java b/proposal/myrmidon/src/java/org/apache/ant/project/ProjectListener.java
new file mode 100644
index 000000000..b3b92dea9
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/project/ProjectListener.java
@@ -0,0 +1,23 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.project;
+
+import org.apache.log.LogTarget;
+
+public interface ProjectListener
+ extends LogTarget
+{
+ void projectStarted( String projectName );
+ void projectFinished();
+
+ void targetStarted( String targetName );
+ void targetFinished();
+
+ void taskletStarted( String taskletName );
+ void taskletFinished();
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/project/Target.java b/proposal/myrmidon/src/java/org/apache/ant/project/Target.java
new file mode 100644
index 000000000..f86c5e901
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/project/Target.java
@@ -0,0 +1,20 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.project;
+
+import java.util.Iterator;
+
+public interface Target
+{
+ Iterator getDependencies();
+ Iterator getTasks();
+ String getCondition();
+ boolean isIfCondition();
+}
+
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/AbstractTasklet.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/AbstractTasklet.java
new file mode 100644
index 000000000..82f415300
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/AbstractTasklet.java
@@ -0,0 +1,82 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet;
+
+import org.apache.ant.AntException;
+import org.apache.avalon.Context;
+import org.apache.avalon.Initializable;
+import org.apache.log.Logger;
+
+/**
+ * This is abstract base class for tasklets.
+ *
+ * @author Peter Donald
+ */
+public abstract class AbstractTasklet
+ implements Tasklet, Initializable
+{
+ protected JavaVersion m_requiredJavaVersion;
+
+ private TaskletContext m_context;
+ private Logger m_logger;
+
+ /**
+ * Retrieve context from container.
+ *
+ * @param context the context
+ */
+ public void contextualize( final Context context )
+ {
+ m_context = (TaskletContext)context;
+ m_logger = (Logger)m_context.getLogger();
+ }
+
+ /**
+ * This will be called before run() method and checks any preconditions.
+ *
+ * Intially preconditions just include JVM version but in future it
+ * will automagically also check if all required parameters are present.
+ *
+ * @exception AntException if an error occurs
+ */
+ public void init()
+ throws AntException
+ {
+ if( null != m_requiredJavaVersion )
+ {
+ final JavaVersion suppliedVersion = m_context.getJavaVersion();
+
+ if( m_requiredJavaVersion.isLessThan( suppliedVersion ) )
+ {
+ throw new AntException( "Task requires a JavaVersion of at least " +
+ m_requiredJavaVersion + " but current version is " +
+ suppliedVersion );
+ }
+ }
+ }
+
+ /**
+ * Convenience method for sub-class to retrieve context.
+ *
+ * @return the context
+ */
+ protected TaskletContext getContext()
+ {
+ return m_context;
+ }
+
+ /**
+ * Convenience method for subclass to get logger.
+ *
+ * @return the Logger
+ */
+ protected Logger getLogger()
+ {
+ return m_logger;
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/DefaultTaskletContext.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/DefaultTaskletContext.java
new file mode 100644
index 000000000..34437ffcc
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/DefaultTaskletContext.java
@@ -0,0 +1,236 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet;
+
+import java.io.File;
+import org.apache.ant.AntException;
+import org.apache.avalon.DefaultContext;
+import org.apache.avalon.util.PropertyException;
+import org.apache.avalon.util.PropertyUtil;
+import org.apache.avalon.util.io.FileUtil;
+import org.apache.log.Logger;
+
+/**
+ * This represents the *Context* in which a task can be executed.
+ *
+ * @author Peter Donald
+ */
+public class DefaultTaskletContext
+ extends DefaultContext
+ implements TaskletContext
+{
+ protected File m_baseDirectory;
+
+ /**
+ * Constructor for Context with no parent contexts.
+ */
+ public DefaultTaskletContext()
+ {
+ this( null );
+ }
+ /**
+ * Constructor.
+ */
+ public DefaultTaskletContext( final TaskletContext parent )
+ {
+ super( parent );
+
+ if( null != parent )
+ {
+ m_baseDirectory = (File)parent.get( BASE_DIRECTORY );
+ }
+ }
+
+ /**
+ * Retrieve JavaVersion running under.
+ *
+ * @return the version of JVM
+ */
+ public JavaVersion getJavaVersion()
+ {
+ return (JavaVersion)get( JAVA_VERSION );
+ }
+
+
+ /**
+ * Retrieve Name of tasklet.
+ *
+ * @return the name
+ */
+ public String getName()
+ {
+ return (String)get( NAME );
+ }
+
+ /**
+ * Retrieve Logger associated with task.
+ *
+ * @return the logger
+ */
+ public Logger getLogger()
+ {
+ return (Logger)get( LOGGER );
+ }
+
+ /**
+ * Retrieve base directory.
+ *
+ * @return the base directory
+ */
+ public File getBaseDirectory()
+ {
+ return m_baseDirectory;
+ }
+
+ /**
+ * Resolve filename.
+ * This involves resolving it against baseDirectory and
+ * removing ../ and ./ references. It also means formatting
+ * it appropriately for the particular OS (ie different OS have
+ * different volumes, file conventions etc)
+ *
+ * @param filename the filename to resolve
+ * @return the resolved filename
+ */
+ public String resolveFilename( final String filename )
+ {
+ final File result = FileUtil.resolveFile( m_baseDirectory, filename );
+ if( null != result ) return result.toString();
+ else return null;
+ }
+
+ /**
+ * Resolve property.
+ * This evaluates all property substitutions based on current context.
+ *
+ * @param property the property to resolve
+ * @return the resolved property
+ */
+ public Object resolveValue( final String property )
+ {
+ try { return PropertyUtil.resolveProperty( property, this, false ); }
+ catch( final PropertyException pe )
+ {
+ throw new AntException( "Error resolving " + property + " due to " +pe.getMessage(),
+ pe );
+ }
+ }
+
+ /**
+ * Retrieve property for name.
+ *
+ * @param name the name of property
+ * @return the value of the property
+ */
+ public Object getProperty( final String name )
+ {
+ return get( name );
+ }
+
+ /**
+ * Set property value in current context.
+ *
+ * @param name the name of property
+ * @param value the value of property
+ */
+ public void setProperty( final String name, final Object value )
+ {
+ setProperty( name, value, CURRENT );
+ }
+
+ /**
+ * Set property value.
+ *
+ * @param property the property
+ */
+ public void setProperty( final String name, final Object value, final ScopeEnum scope )
+ {
+ checkPropertyValid( name, value );
+
+ if( CURRENT == scope ) put( name, value );
+ else if( PARENT == scope )
+ {
+ if( null == m_parent )
+ {
+ throw new AntException( "Can't set a property with parent scope when context " +
+ " has no parent" );
+ }
+ else
+ {
+ ((DefaultTaskletContext)m_parent).put( name, value );
+ }
+ }
+ else if( TOP_LEVEL == scope )
+ {
+ DefaultTaskletContext context = this;
+
+ while( null != context.m_parent )
+ {
+ context = (DefaultTaskletContext)context.m_parent;
+ }
+
+ context.put( name, value );
+ }
+ else
+ {
+ throw new AntException( "Can't set a property with an unknown " +
+ "property context! (" + scope + ")" );
+ }
+ }
+
+ public void put( final Object key, final Object value )
+ {
+ if( key.equals( BASE_DIRECTORY ) )
+ {
+ try { m_baseDirectory = (File)value; }
+ catch( final ClassCastException cce )
+ {
+ throw new AntException( "Can not set baseDirectory to a non-file value.",
+ cce );
+ }
+ }
+
+ super.put( key, value );
+ }
+
+ /**
+ * Make sure property is valid if it is one of the "magic" properties.
+ *
+ * @param name the name of property
+ * @param value the value of proeprty
+ * @exception AntException if an error occurs
+ */
+ protected void checkPropertyValid( final String name, final Object value )
+ throws AntException
+ {
+ if( LOGGER.equals( name ) && !( value instanceof Logger ) )
+ {
+ throw new AntException( "property " + LOGGER +
+ " must have a value of type " +
+ Logger.class.getName() );
+ }
+ else if( BASE_DIRECTORY.equals( name ) && !( value instanceof File ) )
+ {
+ throw new AntException( "Property " + BASE_DIRECTORY +
+ " must have a value of type " +
+ File.class.getName() );
+ }
+ else if( NAME.equals( name ) && !( value instanceof String ) )
+ {
+ throw new AntException( "Property " + NAME +
+ " must have a value of type " +
+ String.class.getName() );
+ }
+ else if( JAVA_VERSION.equals( name ) && !( value instanceof JavaVersion ) )
+ {
+ throw new AntException( "property " + JAVA_VERSION +
+ " must have a value of type " +
+ JavaVersion.class.getName() );
+ }
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/JavaVersion.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/JavaVersion.java
new file mode 100644
index 000000000..e702b1bd1
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/JavaVersion.java
@@ -0,0 +1,28 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet;
+
+import org.apache.avalon.util.ValuedEnum;
+
+/**
+ * Type safe wrapper class for Java Version enums.
+ */
+public final class JavaVersion
+ extends ValuedEnum
+{
+ //standard enums for version of JVM
+ public final static JavaVersion JAVA1_0 = new JavaVersion( "Java 1.0", 100 );
+ public final static JavaVersion JAVA1_1 = new JavaVersion( "Java 1.1", 110 );
+ public final static JavaVersion JAVA1_2 = new JavaVersion( "Java 1.2", 120 );
+ public final static JavaVersion JAVA1_3 = new JavaVersion( "Java 1.3", 130 );
+
+ private JavaVersion( final String name, final int value )
+ {
+ super( name, value );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/Tasklet.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/Tasklet.java
new file mode 100644
index 000000000..a19e92935
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/Tasklet.java
@@ -0,0 +1,21 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet;
+
+import org.apache.avalon.Component;
+import org.apache.avalon.Contextualizable;
+
+/**
+ * This represents the individual tasks.
+ *
+ * @author Peter Donald
+ */
+public interface Tasklet
+ extends Component, Contextualizable, Runnable
+{
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/TaskletContext.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/TaskletContext.java
new file mode 100644
index 000000000..3c089d7d0
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/TaskletContext.java
@@ -0,0 +1,122 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet;
+
+import java.io.File;
+import org.apache.avalon.Context;
+import org.apache.avalon.util.Enum;
+import org.apache.avalon.util.ValuedEnum;
+import org.apache.log.Logger;
+
+/**
+ * This represents the *Context* in which a task can be executed.
+ *
+ * @author Peter Donald
+ */
+public interface TaskletContext
+ extends Context
+{
+ //these values are used when setting properties to indicate the scope at
+ //which properties are set
+ ScopeEnum CURRENT = new ScopeEnum( "Current" );
+ ScopeEnum PARENT = new ScopeEnum( "Parent" );
+ ScopeEnum TOP_LEVEL = new ScopeEnum( "TopLevel" );
+
+ //these are the names of properties that every TaskContext must contain
+ String JAVA_VERSION = "ant.java.version";
+ String BASE_DIRECTORY = "ant.base.directory";
+ String LOGGER = "ant.logger";
+ String NAME = "ant.task.name";
+
+ /**
+ * Retrieve JavaVersion running under.
+ *
+ * @return the version of JVM
+ */
+ JavaVersion getJavaVersion();
+
+ /**
+ * Retrieve Name of tasklet.
+ *
+ * @return the name
+ */
+ String getName();
+
+ /**
+ * Retrieve Logger associated with task.
+ *
+ * @return the logger
+ */
+ Logger getLogger();
+
+ /**
+ * Retrieve base directory.
+ *
+ * @return the base directory
+ */
+ File getBaseDirectory();
+
+ /**
+ * Resolve filename.
+ * This involves resolving it against baseDirectory and
+ * removing ../ and ./ references. It also means formatting
+ * it appropriately for the particular OS (ie different OS have
+ * different volumes, file conventions etc)
+ *
+ * @param filename the filename to resolve
+ * @return the resolved filename
+ */
+ String resolveFilename( String filename );
+
+ /**
+ * Resolve property.
+ * This evaluates all property substitutions based on current context.
+ *
+ * @param property the property to resolve
+ * @return the resolved property
+ */
+ Object resolveValue( String property );
+
+ /**
+ * Retrieve property for name.
+ *
+ * @param name the name of property
+ * @return the value of property
+ */
+ Object getProperty( String name );
+
+ /**
+ * Set property value in current context.
+ *
+ * @param name the name of property
+ * @param value the value of property
+ */
+ void setProperty( String name, Object value );
+
+ /**
+ * Set property value.
+ *
+ * @param name the name of property
+ * @param value the value of property
+ * @param scope the scope at which to set property
+ */
+ void setProperty( String name, Object value, ScopeEnum scope );
+
+ /**
+ * Safe wrapper class for Scope enums.
+ */
+ final class ScopeEnum
+ extends Enum
+ {
+ ScopeEnum( final String name )
+ {
+ super( name );
+ }
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletConfigurer.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletConfigurer.java
new file mode 100644
index 000000000..1348abbbb
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletConfigurer.java
@@ -0,0 +1,438 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Iterator;
+import org.apache.ant.configuration.Configurable;
+import org.apache.ant.configuration.Configuration;
+import org.apache.ant.convert.Converter;
+import org.apache.ant.convert.ConverterEntry;
+import org.apache.ant.convert.ConverterFactory;
+import org.apache.ant.convert.ConverterInfo;
+import org.apache.ant.convert.ConverterRegistry;
+import org.apache.ant.tasklet.Tasklet;
+import org.apache.avalon.ComponentManager;
+import org.apache.avalon.ComponentNotAccessibleException;
+import org.apache.avalon.ComponentNotFoundException;
+import org.apache.avalon.Composer;
+import org.apache.avalon.ConfigurationException;
+import org.apache.avalon.Context;
+import org.apache.avalon.camelot.FactoryException;
+import org.apache.avalon.util.PropertyException;
+import org.apache.avalon.util.PropertyUtil;
+
+/**
+ * Class used to configure tasks.
+ *
+ * @author Peter Donald
+ */
+public class DefaultTaskletConfigurer
+ implements TaskletConfigurer, Composer
+{
+ protected final static String RESERVED_ATTRIBUTES[] =
+ {
+ "id"
+ };
+
+ protected final static String RESERVED_ELEMENTS[] =
+ {
+ "content"
+ };
+
+ protected ConverterRegistry m_converterRegistry;
+ protected ConverterFactory m_converterFactory;
+
+ public void compose( final ComponentManager componentManager )
+ throws ComponentNotFoundException, ComponentNotAccessibleException
+ {
+ m_converterRegistry = (ConverterRegistry)componentManager.
+ lookup( "org.apache.ant.convert.ConverterRegistry" );
+ m_converterFactory = (ConverterFactory)componentManager.
+ lookup( "org.apache.ant.convert.ConverterFactory" );
+ }
+
+ public void configure( final Tasklet tasklet,
+ final Configuration configuration,
+ final Context context )
+ throws ConfigurationException
+ {
+ configure( (Object)tasklet, configuration, context );
+ }
+
+ public void configure( final Object object,
+ final Configuration configuration,
+ final Context context )
+ throws ConfigurationException
+ {
+ if( object instanceof Configurable )
+ {
+ ((Configurable)object).configure( configuration );
+ }
+ else
+ {
+ final Iterator attributes = configuration.getAttributeNames();
+
+ while( attributes.hasNext() )
+ {
+ final String name = (String)attributes.next();
+ final String value = configuration.getAttribute( name );
+ configureAttribute( object, name, value, context );
+ }
+
+ final Iterator elements = configuration.getChildren();
+
+ while( elements.hasNext() )
+ {
+ final Configuration element = (Configuration)elements.next();
+ configureElement( object, element, context );
+ }
+
+ final String content = configuration.getValue( null );
+
+ if( null != content )
+ {
+ if( !content.trim().equals( "" ) )
+ {
+ configureContent( object, content, context );
+ }
+ }
+ }
+ }
+
+ protected void configureContent( final Object object,
+ final String content,
+ final Context context )
+ throws ConfigurationException
+ {
+ setValue( object, "addContent", content, context );
+ }
+
+ protected void configureAttribute( final Object object,
+ final String name,
+ final String value,
+ final Context context )
+ throws ConfigurationException
+ {
+ for( int i = 0; i < RESERVED_ATTRIBUTES.length; i++ )
+ {
+ if( RESERVED_ATTRIBUTES[ i ].equals( name ) ) return;
+ }
+
+ final String methodName = getMethodNameFor( name );
+ setValue( object, methodName, value, context );
+ }
+
+ protected void setValue( final Object object,
+ final String methodName,
+ final String value,
+ final Context context )
+ throws ConfigurationException
+ {
+ // OMFG the rest of this is soooooooooooooooooooooooooooooooo
+ // slow. Need to cache results per class etc.
+
+ final Class clazz = object.getClass();
+ final Method methods[] = getMethodsFor( clazz, methodName );
+
+ if( 0 == methods.length )
+ {
+ throw new ConfigurationException( "Unable to set attribute via " + methodName +
+ " due to not finding any appropriate " +
+ "accessor method" );
+ }
+
+ setValue( object, value, context, methods );
+ }
+
+ protected void setValue( final Object object,
+ final String value,
+ final Context context,
+ final Method methods[] )
+ throws ConfigurationException
+ {
+ try
+ {
+ final Object objectValue =
+ PropertyUtil.resolveProperty( value, context, false );
+
+ setValue( object, objectValue, methods );
+ }
+ catch( final PropertyException pe )
+ {
+ throw new ConfigurationException( "Error resolving property " + value,
+ pe );
+ }
+ }
+
+ protected void setValue( final Object object, Object value, final Method methods[] )
+ throws ConfigurationException
+ {
+ final Class sourceClass = value.getClass();
+ final String source = sourceClass.getName();
+
+ for( int i = 0; i < methods.length; i++ )
+ {
+ if( setValue( object, value, methods[ i ], sourceClass, source ) )
+ {
+ return;
+ }
+ }
+
+ throw new ConfigurationException( "Unable to set attribute via " +
+ methods[ 0 ].getName() + " as could not convert " +
+ source + " to a matching type" );
+ }
+
+ protected boolean setValue( final Object object,
+ Object value,
+ final Method method,
+ final Class sourceClass,
+ final String source )
+ throws ConfigurationException
+ {
+ Class parameterType = method.getParameterTypes()[ 0 ];
+ if( parameterType.isPrimitive() )
+ {
+ parameterType = getComplexTypeFor( parameterType );
+ }
+
+ if( !parameterType.isAssignableFrom( sourceClass ) )
+ {
+ final String destination = parameterType.getName();
+
+ try
+ {
+ final ConverterInfo info = m_converterRegistry.
+ getConverterInfo( source, destination );
+
+ if( null == info ) return false;
+
+ final ConverterEntry entry = m_converterFactory.create( info );
+ final Converter converter = entry.getConverter();
+ value = converter.convert( parameterType, value );
+ }
+ catch( final FactoryException fe )
+ {
+ throw new ConfigurationException( "Badly configured ConverterFactory ",
+ fe );
+ }
+ catch( final Exception e )
+ {
+ throw new ConfigurationException( "Error converting attribute for " +
+ method.getName(),
+ e );
+ }
+ }
+
+ try
+ {
+ method.invoke( object, new Object[] { value } );
+ }
+ catch( final IllegalAccessException iae )
+ {
+ //should never happen ....
+ throw new ConfigurationException( "Error retrieving methods with " +
+ "correct access specifiers",
+ iae );
+ }
+ catch( final InvocationTargetException ite )
+ {
+ throw new ConfigurationException( "Error calling method attribute " +
+ method.getName(),
+ ite );
+ }
+
+ return true;
+ }
+
+ protected Class getComplexTypeFor( final Class clazz )
+ {
+ if( String.class == clazz ) return String.class;
+ else if( Integer.TYPE.equals( clazz ) ) return Integer.class;
+ else if( Long.TYPE.equals( clazz ) ) return Long.class;
+ else if( Short.TYPE.equals( clazz ) ) return Short.class;
+ else if( Byte.TYPE.equals( clazz ) ) return Byte.class;
+ else if( Boolean.TYPE.equals( clazz ) ) return Boolean.class;
+ else if( Float.TYPE.equals( clazz ) ) return Float.class;
+ else if( Double.TYPE.equals( clazz ) ) return Double.class;
+ else
+ {
+ throw new IllegalArgumentException( "Can not get complex type for non-primitive " +
+ "type " + clazz.getName() );
+ }
+ }
+
+ protected Method[] getMethodsFor( final Class clazz, final String methodName )
+ {
+ final Method methods[] = clazz.getMethods();
+ final ArrayList matches = new ArrayList();
+
+ for( int i = 0; i < methods.length; i++ )
+ {
+ final Method method = methods[ i ];
+ if( methodName.equals( method.getName() ) &&
+ Method.PUBLIC == (method.getModifiers() & Method.PUBLIC) )
+ {
+ if( method.getReturnType().equals( Void.TYPE ) )
+ {
+ final Class parameters[] = method.getParameterTypes();
+ if( 1 == parameters.length )
+ {
+ matches.add( method );
+ }
+ }
+ }
+ }
+
+ return (Method[])matches.toArray( new Method[0] );
+ }
+
+ protected Method[] getCreateMethodsFor( final Class clazz, final String methodName )
+ {
+ final Method methods[] = clazz.getMethods();
+ final ArrayList matches = new ArrayList();
+
+ for( int i = 0; i < methods.length; i++ )
+ {
+ final Method method = methods[ i ];
+ if( methodName.equals( method.getName() ) &&
+ Method.PUBLIC == (method.getModifiers() & Method.PUBLIC) )
+ {
+ final Class returnType = method.getReturnType();
+ if( !returnType.equals( Void.TYPE ) &&
+ !returnType.isPrimitive() )
+ {
+ final Class parameters[] = method.getParameterTypes();
+ if( 0 == parameters.length )
+ {
+ matches.add( method );
+ }
+ }
+ }
+ }
+
+ return (Method[])matches.toArray( new Method[0] );
+ }
+
+ protected String getMethodNameFor( final String attribute )
+ {
+ return "set" + getJavaNameFor( attribute );
+ }
+
+ protected String getJavaNameFor( final String name )
+ {
+ final StringBuffer sb = new StringBuffer();
+
+ int index = name.indexOf( '-' );
+ int last = 0;
+
+ while( -1 != index )
+ {
+ final String word = name.substring( last, index ).toLowerCase();
+ sb.append( Character.toUpperCase( word.charAt( 0 ) ) );
+ sb.append( word.substring( 1, word.length() ) );
+ last = index + 1;
+ index = name.indexOf( '-', last );
+ }
+
+ index = name.length();
+ final String word = name.substring( last, index ).toLowerCase();
+ sb.append( Character.toUpperCase( word.charAt( 0 ) ) );
+ sb.append( word.substring( 1, word.length() ) );
+
+ return sb.toString();
+ }
+
+ protected void configureElement( final Object object,
+ final Configuration configuration,
+ final Context context )
+ throws ConfigurationException
+ {
+ final String name = configuration.getName();
+
+ for( int i = 0; i < RESERVED_ELEMENTS.length; i++ )
+ {
+ if( RESERVED_ATTRIBUTES[ i ].equals( name ) ) return;
+ }
+
+ final String javaName = getJavaNameFor( name );
+
+ // OMFG the rest of this is soooooooooooooooooooooooooooooooo
+ // slow. Need to cache results per class etc.
+ final Class clazz = object.getClass();
+ Method methods[] = getMethodsFor( clazz, "add" + javaName );
+
+ if( 0 != methods.length )
+ {
+ //guess it is first method ????
+ addElement( object, methods[ 0 ], configuration, context );
+ }
+ else
+ {
+ methods = getCreateMethodsFor( clazz, "create" + javaName );
+
+ if( 0 == methods.length )
+ {
+ throw new ConfigurationException( "Unable to set attribute " + javaName +
+ " due to not finding any appropriate " +
+ "accessor method" );
+ }
+
+ //guess it is first method ????
+ createElement( object, methods[ 0 ], configuration, context );
+ }
+ }
+
+ protected void createElement( final Object object,
+ final Method method,
+ final Configuration configuration,
+ final Context context )
+ throws ConfigurationException
+ {
+ try
+ {
+ final Object created = method.invoke( object, new Object[ 0 ] );
+ configure( created, configuration, context );
+ }
+ catch( final ConfigurationException ce )
+ {
+ throw ce;
+ }
+ catch( final Exception e )
+ {
+ throw new ConfigurationException( "Error creating sub-element", e );
+ }
+ }
+
+ protected void addElement( final Object object,
+ final Method method,
+ final Configuration configuration,
+ final Context context )
+ throws ConfigurationException
+ {
+ try
+ {
+ final Class clazz = method.getParameterTypes()[ 0 ];
+ final Object created = clazz.newInstance();
+
+ configure( created, configuration, context );
+ method.invoke( object, new Object[] { created } );
+ }
+ catch( final ConfigurationException ce )
+ {
+ throw ce;
+ }
+ catch( final Exception e )
+ {
+ throw new ConfigurationException( "Error creating sub-element", e );
+ }
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletEngine.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletEngine.java
new file mode 100644
index 000000000..9dc964946
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletEngine.java
@@ -0,0 +1,231 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import java.util.HashMap;
+import org.apache.ant.AntException;
+import org.apache.ant.configuration.Configurable;
+import org.apache.ant.configuration.Configuration;
+import org.apache.ant.convert.ConverterFactory;
+import org.apache.ant.convert.ConverterRegistry;
+import org.apache.ant.tasklet.Tasklet;
+import org.apache.ant.tasklet.TaskletContext;
+import org.apache.avalon.ComponentManager;
+import org.apache.avalon.ComponentNotAccessibleException;
+import org.apache.avalon.ComponentNotFoundException;
+import org.apache.avalon.Composer;
+import org.apache.avalon.Context;
+import org.apache.avalon.Contextualizable;
+import org.apache.avalon.DefaultComponentManager;
+import org.apache.avalon.Disposable;
+import org.apache.avalon.Initializable;
+import org.apache.avalon.camelot.FactoryException;
+import org.apache.avalon.camelot.RegistryException;
+import org.apache.log.Logger;
+
+public class DefaultTaskletEngine
+ implements TaskletEngine, Initializable
+{
+ protected ComponentManager m_componentManager;
+ protected TaskletContext m_context;
+ protected TaskletFactory m_taskletFactory;
+ protected ConverterFactory m_converterFactory;
+ protected TaskletRegistry m_taskletRegistry;
+ protected ConverterRegistry m_converterRegistry;
+ protected TaskletConfigurer m_configurer;
+ protected Logger m_logger;
+
+ public void contextualize( final Context context )
+ {
+ m_context = (TaskletContext)context;
+ m_logger = m_context.getLogger();
+ }
+
+ public void compose( final ComponentManager componentManager )
+ throws ComponentNotFoundException, ComponentNotAccessibleException
+ {
+ m_componentManager = componentManager;
+ m_taskletRegistry = (TaskletRegistry)componentManager.
+ lookup( "org.apache.ant.tasklet.engine.TaskletRegistry" );
+ m_converterRegistry = (ConverterRegistry)componentManager.
+ lookup( "org.apache.ant.convert.ConverterRegistry" );
+ }
+
+ public void init()
+ throws Exception
+ {
+ m_taskletFactory = createTaskletFactory();
+ m_converterFactory = createConverterFactory();
+ m_configurer = createTaskletConfigurer();
+
+ if( m_configurer instanceof Composer )
+ {
+ final DefaultComponentManager componentManager =
+ new DefaultComponentManager( m_componentManager );
+ componentManager.put( "org.apache.ant.convert.ConverterFactory",
+ m_converterFactory );
+
+ ((Composer)m_configurer).compose( componentManager );
+ }
+
+ if( m_configurer instanceof Initializable )
+ {
+ ((Initializable)m_configurer).init();
+ }
+ }
+
+ protected TaskletConfigurer createTaskletConfigurer()
+ {
+ return new DefaultTaskletConfigurer();
+ }
+
+ protected TaskletFactory createTaskletFactory()
+ {
+ return new DefaultTaskletFactory();
+ }
+
+ protected ConverterFactory createConverterFactory()
+ {
+ return (ConverterFactory)m_taskletFactory;
+ }
+
+ public void execute( final Configuration task )
+ throws AntException
+ {
+ final Tasklet tasklet = createTasklet( task );
+
+ final String name = task.getName();
+ m_logger.debug( "Created task " + name );
+
+ doContextualize( tasklet, task );
+ m_logger.debug( "Contextualized task " + name );
+
+ doCompose( tasklet, task );
+ m_logger.debug( "Composed task " + name );
+
+ doConfigure( tasklet, task );
+ m_logger.debug( "Configured task " + name );
+
+ doInitialize( tasklet, task );
+ m_logger.debug( "Initialize task " + name );
+
+ tasklet.run();
+ m_logger.debug( "Ran task " + name );
+
+ doDispose( tasklet, task );
+ m_logger.debug( "Dispose task " + name );
+ }
+
+ protected void doConfigure( final Tasklet tasklet, final Configuration task )
+ throws AntException
+ {
+ try { m_configurer.configure( tasklet, task, m_context ); }
+ catch( final Throwable throwable )
+ {
+ throw new AntException( "Error configuring task " + task.getName() + " at " +
+ task.getLocation() + "(Reason: " +
+ throwable.getMessage() + ")", throwable );
+ }
+ }
+
+ protected TaskletContext getContextFor( final String name )
+ {
+ //If we are single threaded we really don't need to have a new object
+ //for context ... if we are not single threaded then we need to create new
+ //context. Alternatively we could remove getName from TaskletContext
+
+ //final DefaultTaskletContext context = new DefaultTaskletContext( m_context );
+ m_context.setProperty( TaskletContext.NAME, name );
+ return m_context;
+ }
+
+ protected void doCompose( final Tasklet tasklet, final Configuration task )
+ throws AntException
+ {
+ if( tasklet instanceof Composer )
+ {
+ try { ((Composer)tasklet).compose( m_componentManager ); }
+ catch( final Throwable throwable )
+ {
+ throw new AntException( "Error composing task " + task.getName() + " at " +
+ task.getLocation() + "(Reason: " +
+ throwable.getMessage() + ")", throwable );
+ }
+ }
+ }
+
+ protected void doContextualize( final Tasklet tasklet, final Configuration task )
+ throws AntException
+ {
+ final TaskletContext context = getContextFor( task.getName() );
+
+ try { tasklet.contextualize( context ); }
+ catch( final Throwable throwable )
+ {
+ throw new AntException( "Error contextualizing task " + task.getName() + " at " +
+ task.getLocation() + "(Reason: " +
+ throwable.getMessage() + ")", throwable );
+ }
+ }
+
+ protected void doDispose( final Tasklet tasklet, final Configuration task )
+ throws AntException
+ {
+ if( tasklet instanceof Disposable )
+ {
+ try { ((Disposable)tasklet).dispose(); }
+ catch( final Throwable throwable )
+ {
+ throw new AntException( "Error disposing task " + task.getName() + " at " +
+ task.getLocation() + "(Reason: " +
+ throwable.getMessage() + ")", throwable );
+ }
+ }
+ }
+
+ protected void doInitialize( final Tasklet tasklet, final Configuration task )
+ throws AntException
+ {
+ if( tasklet instanceof Initializable )
+ {
+ try { ((Initializable)tasklet).init(); }
+ catch( final Throwable throwable )
+ {
+ throw new AntException( "Error initializing task " + task.getName() + " at " +
+ task.getLocation() + "(Reason: " +
+ throwable.getMessage() + ")", throwable );
+ }
+ }
+ }
+
+ protected Tasklet createTasklet( final Configuration configuration )
+ throws AntException
+ {
+ final String name = configuration.getName();
+ TaskletInfo info = null;
+
+ try { info = (TaskletInfo)m_taskletRegistry.getInfo( name ); }
+ catch( final RegistryException re )
+ {
+ throw new AntException( "Unable to locate task " + name, re );
+ }
+
+ TaskletEntry entry = null;
+
+ try { entry = m_taskletFactory.create( info ); }
+ catch( final FactoryException fe )
+ {
+ throw new AntException( "Unable to create task " + name +
+ " (of type " + info.getClassname() + " from " +
+ info.getLocation() + ")",
+ fe );
+ }
+
+ return entry.getTasklet();
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletFactory.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletFactory.java
new file mode 100644
index 000000000..553f8ea39
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletFactory.java
@@ -0,0 +1,65 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.HashMap;
+import org.apache.ant.tasklet.Tasklet;
+import org.apache.ant.convert.ConverterLoader;
+import org.apache.ant.convert.DefaultConverterFactory;
+import org.apache.avalon.camelot.Entry;
+import org.apache.avalon.camelot.Factory;
+import org.apache.avalon.camelot.FactoryException;
+import org.apache.avalon.camelot.Info;
+
+/**
+ * Facility used to load Tasklets.
+ *
+ * @author Peter Donald
+ */
+public class DefaultTaskletFactory
+ extends DefaultConverterFactory
+ implements TaskletFactory
+{
+ public Entry create( final Info info )
+ throws FactoryException
+ {
+ if( !info.getClass().equals( TaskletInfo.class ) )
+ {
+ return super.create( info );
+ }
+ else
+ {
+ return create( (TaskletInfo)info );
+ }
+ }
+
+ public TaskletEntry create( final TaskletInfo info )
+ throws FactoryException
+ {
+ final TaskletLoader loader = (TaskletLoader)getLoader( info.getLocation() );
+
+ Object object = null;
+
+ try { object = loader.load( info.getClassname() ); }
+ catch( final Exception e )
+ {
+ throw new FactoryException( "Failed loading tasklet from " + info.getLocation() +
+ " due to " + e, e );
+ }
+
+ return new TaskletEntry( info, (Tasklet)object );
+ }
+
+ protected ConverterLoader createLoader( final URL location )
+ {
+ if( null != location ) return new DefaultTaskletLoader( location );
+ else return new DefaultTaskletLoader();
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletInfo.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletInfo.java
new file mode 100644
index 000000000..d4abdd6ba
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletInfo.java
@@ -0,0 +1,54 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import java.net.URL;
+import org.apache.avalon.camelot.Info;
+
+/**
+ * This is default container of information about a task.
+ * A BeanInfo equivelent for a task. Eventually it will auto-magically
+ * generate a schema via reflection for Validator/Editor tools.
+ *
+ * @author Peter Donald
+ */
+public class DefaultTaskletInfo
+ implements TaskletInfo
+{
+ protected final String m_classname;
+ protected final URL m_location;
+
+ /**
+ * Constructor that takes classname and taskLibraryLocation.
+ */
+ public DefaultTaskletInfo( final String classname, final URL location )
+ {
+ m_location = location;
+ m_classname = classname;
+ }
+
+ /**
+ * Retrieve classname for task.
+ *
+ * @return the taskname
+ */
+ public String getClassname()
+ {
+ return m_classname;
+ }
+
+ /**
+ * Retrieve tasklib location from which task is loaded.
+ *
+ * @return the location
+ */
+ public URL getLocation()
+ {
+ return m_location;
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletLoader.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletLoader.java
new file mode 100644
index 000000000..87e9eac97
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletLoader.java
@@ -0,0 +1,37 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import java.net.URL;
+import org.apache.ant.tasklet.Tasklet;
+import org.apache.ant.convert.DefaultConverterLoader;
+
+/**
+ * Class used to load tasks et al from a source.
+ *
+ * @author Peter Donald
+ */
+public class DefaultTaskletLoader
+ extends DefaultConverterLoader
+ implements TaskletLoader
+{
+ public DefaultTaskletLoader()
+ {
+ }
+
+ public DefaultTaskletLoader( final URL location )
+ {
+ super( location );
+ }
+
+ public Tasklet loadTasklet( final String tasklet )
+ throws Exception
+ {
+ return (Tasklet)load( tasklet );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletRegistry.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletRegistry.java
new file mode 100644
index 000000000..04e95fab3
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/DefaultTaskletRegistry.java
@@ -0,0 +1,20 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import org.apache.avalon.camelot.AbstractRegistry;
+
+public class DefaultTaskletRegistry
+ extends AbstractRegistry
+ implements TaskletRegistry
+{
+ protected Class getInfoClass()
+ {
+ return TaskletInfo.class;
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletConfigurer.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletConfigurer.java
new file mode 100644
index 000000000..3e23680a0
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletConfigurer.java
@@ -0,0 +1,24 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import org.apache.ant.configuration.Configuration;
+import org.apache.ant.tasklet.Tasklet;
+import org.apache.avalon.ConfigurationException;
+import org.apache.avalon.Context;
+
+/**
+ * Class used to configure tasks.
+ *
+ * @author Peter Donald
+ */
+public interface TaskletConfigurer
+{
+ void configure( Tasklet tasklet, Configuration configuration, Context context )
+ throws ConfigurationException;
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletEngine.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletEngine.java
new file mode 100644
index 000000000..cf2d576ce
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletEngine.java
@@ -0,0 +1,20 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import org.apache.ant.AntException;
+import org.apache.ant.configuration.Configuration;
+import org.apache.avalon.Composer;
+import org.apache.avalon.Contextualizable;
+
+public interface TaskletEngine
+ extends Contextualizable, Composer
+{
+ void execute( final Configuration task )
+ throws AntException;
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletEntry.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletEntry.java
new file mode 100644
index 000000000..b8003b772
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletEntry.java
@@ -0,0 +1,31 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import org.apache.ant.tasklet.Tasklet;
+import org.apache.avalon.camelot.AbstractEntry;
+
+public class TaskletEntry
+ extends AbstractEntry
+{
+ public TaskletEntry( final TaskletInfo info, final Tasklet tasklet )
+ {
+ super( info, tasklet );
+ }
+
+ /**
+ * Retrieve instance of tasklet.
+ *
+ * @return the component instance
+ */
+ public Tasklet getTasklet()
+ {
+ return (Tasklet)getInstance();
+ }
+}
+
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletFactory.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletFactory.java
new file mode 100644
index 000000000..eaaf8be55
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletFactory.java
@@ -0,0 +1,23 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import org.apache.avalon.camelot.Factory;
+import org.apache.avalon.camelot.FactoryException;
+
+/**
+ * Facility used to load Tasklets.
+ *
+ * @author Peter Donald
+ */
+public interface TaskletFactory
+ extends Factory
+{
+ TaskletEntry create( TaskletInfo info )
+ throws FactoryException;
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletInfo.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletInfo.java
new file mode 100644
index 000000000..62496ae02
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletInfo.java
@@ -0,0 +1,36 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import java.net.URL;
+import org.apache.avalon.camelot.Info;
+
+/**
+ * This is information about a task.
+ * A BeanInfo equivelent for a task. Eventually it will auto-magically
+ * generate a schema via reflection for Validator/Editor tools.
+ *
+ * @author Peter Donald
+ */
+public interface TaskletInfo
+ extends Info
+{
+ /**
+ * Retrieve classname for task.
+ *
+ * @return the taskname
+ */
+ String getClassname();
+
+ /**
+ * Retrieve location of task library where task is contained.
+ *
+ * @return the location of task library
+ */
+ URL getLocation();
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletLoader.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletLoader.java
new file mode 100644
index 000000000..913469aff
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletLoader.java
@@ -0,0 +1,23 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import org.apache.ant.tasklet.Tasklet;
+import org.apache.avalon.camelot.Loader;
+
+/**
+ * Class used to load tasks et al from a source.
+ *
+ * @author Peter Donald
+ */
+public interface TaskletLoader
+ extends Loader
+{
+ Tasklet loadTasklet( String tasklet )
+ throws Exception;
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletRegistry.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletRegistry.java
new file mode 100644
index 000000000..59cf3a03c
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TaskletRegistry.java
@@ -0,0 +1,15 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import org.apache.avalon.camelot.Registry;
+
+public interface TaskletRegistry
+ extends Registry
+{
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TskDeployer.java b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TskDeployer.java
new file mode 100644
index 000000000..ed76ccb23
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasklet/engine/TskDeployer.java
@@ -0,0 +1,200 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasklet.engine;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Enumeration;
+import java.util.Properties;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipException;
+import java.util.zip.ZipFile;
+import org.apache.ant.tasklet.engine.DefaultTaskletInfo;
+import org.apache.ant.convert.ConverterRegistry;
+import org.apache.ant.convert.DefaultConverterInfo;
+import org.apache.avalon.Component;
+import org.apache.avalon.camelot.AbstractDeployer;
+import org.apache.avalon.camelot.DeploymentException;
+import org.apache.avalon.camelot.RegistryException;
+import org.apache.log.Logger;
+
+/**
+ * This class deploys a .tsk file into a registry.
+ *
+ * @author Peter Donald
+ */
+public class TskDeployer
+ extends AbstractDeployer
+{
+ protected final static String TASKDEF_FILE = "TASK-LIB/taskdefs.properties";
+ protected final static String CONVERTER_FILE = "TASK-LIB/converters.properties";
+
+ protected TaskletRegistry m_taskletRegistry;
+ protected ConverterRegistry m_converterRegistry;
+
+ public TskDeployer( final TaskletRegistry taskletRegistry,
+ final ConverterRegistry converterRegistry )
+ {
+ m_taskletRegistry = taskletRegistry;
+ m_converterRegistry = converterRegistry;
+ m_autoUndeploy = true;
+ m_type = "Tasklet";
+ }
+
+ public void setLogger( final Logger logger )
+ {
+ m_logger = logger;
+ }
+
+ protected boolean isValidLocation( final String location )
+ {
+ //TODO: Make sure it is valid JavaIdentifier
+ //that optionally has '-' embedded in it
+ return true;
+ }
+
+ /**
+ * Deploy Tasklets from a .tsk file.
+ * Eventually this should be cached for performance reasons.
+ *
+ * @param location the location
+ * @param file the file
+ * @exception DeploymentException if an error occurs
+ */
+ protected void deployFromFile( final String location, final File file )
+ throws DeploymentException
+ {
+ m_logger.info( "Deploying .tsk file (" + file + ") as " + location );
+
+ ZipFile zipFile = null;
+
+ try { zipFile = new ZipFile( file ); }
+ catch( final IOException ioe )
+ {
+ throw new DeploymentException( "Error opening " + file +
+ " due to " + ioe.getMessage(),
+ ioe );
+ }
+
+ try
+ {
+ final Properties taskdefs = loadProperties( zipFile, TASKDEF_FILE );
+ final Properties converters = loadProperties( zipFile, CONVERTER_FILE );
+
+ try { zipFile.close(); }
+ catch( final IOException ioe ) {}
+
+ URL url = null;
+
+ try { url = file.toURL(); }
+ catch( final MalformedURLException mue ) {}
+
+ handleTaskdefs( taskdefs, url );
+ handleConverters( converters, url );
+ }
+ catch( final DeploymentException de )
+ {
+ try { zipFile.close(); }
+ catch( final IOException ioe ) {}
+
+ throw de;
+ }
+ }
+
+ protected void handleConverters( final Properties properties, final URL url )
+ throws DeploymentException
+ {
+ final Enumeration enum = properties.propertyNames();
+
+ while( enum.hasMoreElements() )
+ {
+ final String key = (String)enum.nextElement();
+ final String value = (String)properties.get( key );
+ final int index = value.indexOf( ',' );
+
+ if( -1 == index )
+ {
+ throw new DeploymentException( "Malformed converter definition (" +
+ key + ")" );
+ }
+
+ final String source = value.substring( 0, index ).trim();
+ final String destination = value.substring( index + 1 ).trim();
+
+ final DefaultConverterInfo info =
+ new DefaultConverterInfo( source, destination, key, url );
+
+ try { m_converterRegistry.register( key, info ); }
+ catch( final RegistryException re )
+ {
+ throw new DeploymentException( "Error registering converter " +
+ key + " due to " + re,
+ re );
+ }
+ }
+ }
+
+ protected void handleTaskdefs( final Properties properties, final URL url )
+ throws DeploymentException
+ {
+ final Enumeration enum = properties.propertyNames();
+
+ while( enum.hasMoreElements() )
+ {
+ final String key = (String)enum.nextElement();
+ final String value = (String)properties.get( key );
+ final DefaultTaskletInfo info = new DefaultTaskletInfo( value, url );
+
+ try { m_taskletRegistry.register( key, info ); }
+ catch( final RegistryException re )
+ {
+ throw new DeploymentException( "Error registering " + key + " due to " + re,
+ re );
+ }
+ }
+ }
+
+ protected Properties loadProperties( final ZipFile zipFile, final String filename )
+ throws DeploymentException
+ {
+ final ZipEntry entry = zipFile.getEntry( filename );
+ if( null == entry )
+ {
+ throw new DeploymentException( "Unable to locate " + filename +
+ " in " + zipFile.getName() );
+ }
+
+ Properties properties = new Properties();
+
+ try
+ {
+ properties.load( zipFile.getInputStream( entry ) );
+ }
+ catch( final IOException ioe )
+ {
+ throw new DeploymentException( "Error reading " + filename +
+ " from " + zipFile.getName(),
+ ioe );
+ }
+
+ return properties;
+ }
+
+ protected boolean canUndeploy( final Component component )
+ throws DeploymentException
+ {
+ return true;
+ }
+
+ protected void shutdownDeployment( final Component component )
+ throws DeploymentException
+ {
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasks/core/ConfigurationTest.java b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/ConfigurationTest.java
new file mode 100644
index 000000000..5d18585ef
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/ConfigurationTest.java
@@ -0,0 +1,47 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasks.core;
+
+import org.apache.ant.AntException;
+import org.apache.ant.configuration.Configurable;
+import org.apache.ant.configuration.Configuration;
+import org.apache.ant.tasklet.AbstractTasklet;
+import org.apache.avalon.ConfigurationException;
+
+/**
+ * This is abstract base class for tasklets.
+ *
+ * @author Peter Donald
+ */
+public class ConfigurationTest
+ extends AbstractTasklet
+ implements Configurable
+{
+ protected String m_message;
+
+ public void configure( final Configuration configuration )
+ throws ConfigurationException
+ {
+ String message = configuration.getAttribute( "message" );
+ final Object object = getContext().resolveValue( message );
+ if( object instanceof String )
+ {
+ m_message = (String)object;
+ }
+ else
+ {
+ m_message = object.toString();
+ }
+ }
+
+ public void run()
+ throws AntException
+ {
+ getLogger().info( m_message );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasks/core/ContentTest.java b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/ContentTest.java
new file mode 100644
index 000000000..08faf803a
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/ContentTest.java
@@ -0,0 +1,37 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasks.core;
+
+import org.apache.ant.AntException;
+import org.apache.ant.tasklet.AbstractTasklet;
+
+/**
+ * This is abstract base class for tasklets.
+ *
+ * @author Peter Donald
+ */
+public class ContentTest
+ extends AbstractTasklet
+{
+ public void addContent( final Integer value )
+ {
+ getLogger().info( "Integer content: " + value );
+ }
+
+ /*
+ public void addContent( final String blah )
+ {
+ System.out.println( "String: " + blah );
+ }
+ */
+
+ public void run()
+ throws AntException
+ {
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasks/core/Echo.java b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/Echo.java
new file mode 100644
index 000000000..0d5d918e0
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/Echo.java
@@ -0,0 +1,33 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasks.core;
+
+import org.apache.ant.AntException;
+import org.apache.ant.tasklet.AbstractTasklet;
+
+/**
+ * This is abstract base class for tasklets.
+ *
+ * @author Peter Donald
+ */
+public class Echo
+ extends AbstractTasklet
+{
+ protected String m_message;
+
+ public void setMessage( final String message )
+ {
+ m_message = message;
+ }
+
+ public void run()
+ throws AntException
+ {
+ getLogger().info( m_message );
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasks/core/PrimitiveTypesTest.java b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/PrimitiveTypesTest.java
new file mode 100644
index 000000000..121e0f3ff
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/PrimitiveTypesTest.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 file.
+ */
+package org.apache.ant.tasks.core;
+
+import org.apache.ant.AntException;
+import org.apache.ant.tasklet.AbstractTasklet;
+
+/**
+ *
+ * @author Peter Donald
+ */
+public class PrimitiveTypesTest
+ extends AbstractTasklet
+{
+ public void setInteger( final Integer value )
+ {
+ getLogger().info( "setInteger( " + value + " );" );
+ }
+
+ public void setInteger2( final int value )
+ {
+ getLogger().info( "setInteger2( " + value + " );" );
+ }
+
+ public void setShort( final Short value )
+ {
+ getLogger().info( "setShort( " + value + " );" );
+ }
+
+ public void setShort2( final short value )
+ {
+ getLogger().info( "setShort2( " + value + " );" );
+ }
+
+ public void setByte( final Byte value )
+ {
+ getLogger().info( "setByte( " + value + " );" );
+ }
+
+ public void setByte2( final byte value )
+ {
+ getLogger().info( "setByte2( " + value + " );" );
+ }
+
+ public void setLong( final Long value )
+ {
+ getLogger().info( "setLong( " + value + " );" );
+ }
+
+ public void setLong2( final long value )
+ {
+ getLogger().info( "setLong2( " + value + " );" );
+ }
+
+ public void setFloat( final Float value )
+ {
+ getLogger().info( "setFloat( " + value + " );" );
+ }
+
+ public void setFloat2( final float value )
+ {
+ getLogger().info( "setFloat2( " + value + " );" );
+ }
+
+ public void setDouble( final Double value )
+ {
+ getLogger().info( "setDouble( " + value + " );" );
+ }
+
+ public void setDouble2( final double value )
+ {
+ getLogger().info( "setDouble2( " + value + " );" );
+ }
+
+ public void setString( final String value )
+ {
+ getLogger().info( "setString( " + value + " );" );
+ }
+
+ public void run()
+ throws AntException
+ {
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasks/core/Property.java b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/Property.java
new file mode 100644
index 000000000..dcf2a892d
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/Property.java
@@ -0,0 +1,64 @@
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasks.core;
+
+import org.apache.ant.AntException;
+import org.apache.ant.tasklet.AbstractTasklet;
+import org.apache.ant.tasklet.TaskletContext;
+
+/**
+ * @author Peter Donald
+ */
+public class Property
+ extends AbstractTasklet
+{
+ protected String m_name;
+ protected String m_value;
+ protected boolean m_localScope = true;
+
+ public void setName( final String name )
+ {
+ m_name = name;
+ }
+
+ public void setValue( final String value )
+ {
+ m_value = value;
+ }
+
+ public void setLocalScope( final boolean localScope )
+ {
+ m_localScope = localScope;
+ }
+
+ public void run()
+ throws AntException
+ {
+ if( null == m_name )
+ {
+ throw new AntException( "Name must be specified" );
+ }
+
+ if( null == m_value )
+ {
+ throw new AntException( "Value must be specified" );
+ }
+
+ final TaskletContext context = getContext();
+ final Object value = context.resolveValue( m_value );
+
+ if( m_localScope )
+ {
+ context.setProperty( m_name, value );
+ }
+ else
+ {
+ context.setProperty( m_name, value, TaskletContext.PARENT );
+ }
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasks/core/SubElementTest.java b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/SubElementTest.java
new file mode 100644
index 000000000..e580a35e4
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/SubElementTest.java
@@ -0,0 +1,43 @@
+
+/*
+ * 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 file.
+ */
+package org.apache.ant.tasks.core;
+
+import org.apache.ant.AntException;
+import org.apache.ant.tasklet.AbstractTasklet;
+
+/**
+ * @author Peter Donald
+ */
+public class SubElementTest
+ extends AbstractTasklet
+{
+ public static final class Beep
+ {
+ public void setMessage( final String string )
+ {
+ System.out.println( string );
+ }
+ }
+
+ public Beep createCreateBeep()
+ {
+ System.out.println( "createCreateBeep()" );
+ return new Beep();
+ }
+
+ public void addAddBeep( final Beep beep )
+ {
+ System.out.println( "addBeeper(" + beep + ");" );
+ }
+
+ public void run()
+ throws AntException
+ {
+ }
+}
diff --git a/proposal/myrmidon/src/java/org/apache/ant/tasks/core/taskdefs.properties b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/taskdefs.properties
new file mode 100644
index 000000000..f3476cfb9
--- /dev/null
+++ b/proposal/myrmidon/src/java/org/apache/ant/tasks/core/taskdefs.properties
@@ -0,0 +1,7 @@
+# TASK-LIB/taskdefs.properties
+echo=org.apache.ant.tasks.core.Echo
+prim-test=org.apache.ant.tasks.core.PrimitiveTypesTest
+sub-elements-test=org.apache.ant.tasks.core.SubElementTest
+conf-test=org.apache.ant.tasks.core.ConfigurationTest
+content-test=org.apache.ant.tasks.core.ContentTest
+property=org.apache.ant.tasks.core.Property
\ No newline at end of file
diff --git a/proposal/myrmidon/src/make/sample.xmk b/proposal/myrmidon/src/make/sample.xmk
new file mode 100644
index 000000000..db04135d8
--- /dev/null
+++ b/proposal/myrmidon/src/make/sample.xmk
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 123
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/proposal/myrmidon/src/script/ant b/proposal/myrmidon/src/script/ant
new file mode 100644
index 000000000..9667d7de9
--- /dev/null
+++ b/proposal/myrmidon/src/script/ant
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+if [ -f $HOME/.antrc ] ; then
+ . $HOME/.antrc
+fi
+
+# Cygwin support.
+if [ "$OSTYPE" == "cygwin32" ] || [ "$OSTYPE" = "cygwin" ]; then
+
+ if [ ! "$JAVA_HOME" = "" ]; then
+ JAVA_HOME=`cygpath --path --unix $JAVA_HOME`
+ fi
+fi
+
+## resolve links - $0 may be a link to ant's home
+PRG=$0
+progname=`basename $0`
+
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '.*/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG="`dirname $PRG`/$link"
+ fi
+done
+
+ANT_HOME=`dirname "$PRG"`/..
+
+# Allow .antrc to specifiy flags to java cmd
+if [ "$JAVACMD" = "" ] ; then
+ JAVACMD=java
+fi
+
+LOCALCLASSPATH=`echo $ANT_HOME/lib/*.jar | tr ' ' ':'`
+
+if [ "$CLASSPATH" != "" ] ; then
+ LOCALCLASSPATH=$CLASSPATH:$LOCALCLASSPATH
+fi
+
+if [ "$JAVA_HOME" != "" ] ; then
+ if test -f $JAVA_HOME/lib/tools.jar ; then
+ LOCALCLASSPATH=$LOCALCLASSPATH:$JAVA_HOME/lib/tools.jar
+ fi
+else
+ echo "Warning: JAVA_HOME environment variable is not set."
+ echo " If build fails because sun.* classes could not be found"
+ echo " you will need to set the JAVA_HOME environment variable"
+ echo " to the installation directory of java."
+fi
+
+# More Cygwin support
+if [ "$OSTYPE" == "cygwin32" ] || [ "$OSTYPE" = "cygwin" ] ; then
+ LOCALCLASSPATH=`cygpath --path --windows "$LOCALCLASSPATH"`
+fi
+
+$JAVACMD -classpath $LOCALCLASSPATH $ANT_OPTS org.apache.ant.Main --ant-home=${ANT_HOME} $@
\ No newline at end of file
diff --git a/proposal/myrmidon/src/script/ant.bat b/proposal/myrmidon/src/script/ant.bat
new file mode 100644
index 000000000..af3a91dc9
--- /dev/null
+++ b/proposal/myrmidon/src/script/ant.bat
@@ -0,0 +1,35 @@
+@echo off
+
+:checkJava
+if "%JAVACMD%" == "" set JAVACMD=%JAVA_HOME%\bin\java
+if not "%JAVA_HOME%" == "" goto setupClasspath
+
+echo.
+echo Warning: JAVA_HOME environment variable is not set.
+echo If build fails because sun.* classes could not be found
+echo you will need to set the JAVA_HOME environment variable
+echo to the installation directory of java.
+echo.
+goto end
+
+:setupClasspath
+set LOCALCLASSPATH=lib\xerces.jar;lib\ant.jar;lib\avalonapi.jar;%JAVA_HOME%\lib\tools.jar
+
+set THIS_FILE=%0
+
+set ANT_CMD_LINE_ARGS=
+
+rem Slurp all args...
+:setupArgs
+if "%0" == "" goto doneArgs
+set ANT_CMD_LINE_ARGS=%ANT_CMD_LINE_ARGS% %1
+shift
+goto setupArgs
+
+:doneArgs
+rem Mmmmmm tasty - finished slurping args
+
+%JAVACMD% -classpath "%LOCALCLASSPATH%" %ANT_OPTS% org.apache.ant.Main "--bin-dir=%THIS_FILE%" %ANT_CMD_LINE_ARGS%
+
+:end
+set LOCALCLASSPATH=
\ No newline at end of file
diff --git a/proposal/myrmidon/src/script/antRun b/proposal/myrmidon/src/script/antRun
new file mode 100644
index 000000000..f0a18f165
--- /dev/null
+++ b/proposal/myrmidon/src/script/antRun
@@ -0,0 +1,9 @@
+#! /bin/sh
+
+# Args: DIR command
+cd "$1"
+CMD="$2"
+shift
+shift
+
+exec $CMD "$@"
diff --git a/proposal/myrmidon/src/script/antRun.bat b/proposal/myrmidon/src/script/antRun.bat
new file mode 100644
index 000000000..fe0d1ceeb
--- /dev/null
+++ b/proposal/myrmidon/src/script/antRun.bat
@@ -0,0 +1,18 @@
+@echo off
+
+cd %1
+set ANT_RUN_CMD=%2
+shift
+shift
+
+set PARAMS=
+:loop
+if "%1" == "" goto runCommand
+set PARAMS=%PARAMS% %1
+shift
+goto loop
+
+:runCommand
+echo %ANT_RUN_CMD% %PARAMS%
+%ANT_RUN_CMD% %PARAMS%
+