Browse Source

Add in a clone of the main ant source tree so that it can undergo some heavy refactoring.

Initial stages involves just making the Ant1.x tasks implement Ant2 interface and refactoring BuildException such that it can be easily be replaced by TaskException etc.


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@270153 13f79535-47bb-0310-9956-ffa450edef68
master
Peter Donald 24 years ago
parent
commit
d1064deab4
100 changed files with 30760 additions and 0 deletions
  1. +1088
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/AntClassLoader.java
  2. +138
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/BuildEvent.java
  3. +103
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/BuildException.java
  4. +80
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/BuildListener.java
  5. +54
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/BuildLogger.java
  6. +17
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/Constants.java
  7. +220
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/DefaultLogger.java
  8. +127
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/DemuxOutputStream.java
  9. +96
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/DesirableFilter.java
  10. +1177
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/DirectoryScanner.java
  11. +39
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/ExitException.java
  12. +127
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/FileScanner.java
  13. +848
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/IntrospectionHelper.java
  14. +182
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/Launcher.java
  15. +80
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/Location.java
  16. +842
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/Main.java
  17. +50
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/NoBannerLogger.java
  18. +90
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/PathTokenizer.java
  19. +1575
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/Project.java
  20. +71
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/ProjectComponent.java
  21. +1061
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/ProjectHelper.java
  22. +159
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/RuntimeConfigurable.java
  23. +232
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/Target.java
  24. +281
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/Task.java
  25. +127
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/TaskAdapter.java
  26. +28
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/TaskContainer.java
  27. +256
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/UnknownElement.java
  28. +3
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/defaultManifest.mf
  29. +550
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Ant.java
  30. +394
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/AntStructure.java
  31. +418
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Available.java
  32. +114
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/BUnzip2.java
  33. +56
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/BZip2.java
  34. +165
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/CVSPass.java
  35. +125
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/CallTarget.java
  36. +489
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Checksum.java
  37. +193
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Chmod.java
  38. +73
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/CompileTask.java
  39. +76
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ConditionTask.java
  40. +526
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Copy.java
  41. +137
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Copydir.java
  42. +90
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Copyfile.java
  43. +338
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Cvs.java
  44. +220
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Definer.java
  45. +456
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Delete.java
  46. +103
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Deltree.java
  47. +308
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/DependSet.java
  48. +114
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Ear.java
  49. +159
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Echo.java
  50. +252
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Exec.java
  51. +456
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ExecTask.java
  52. +947
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Execute.java
  53. +122
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java
  54. +473
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java
  55. +62
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ExecuteStreamHandler.java
  56. +209
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java
  57. +83
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Exit.java
  58. +303
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Expand.java
  59. +74
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Filter.java
  60. +1159
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java
  61. +93
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/GUnzip.java
  62. +53
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/GZip.java
  63. +350
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/GenerateKey.java
  64. +410
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Get.java
  65. +153
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Input.java
  66. +397
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Jar.java
  67. +456
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Java.java
  68. +941
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Javac.java
  69. +95
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/JavacOutputStream.java
  70. +1473
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Javadoc.java
  71. +128
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Jikes.java
  72. +174
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/JikesOutputParser.java
  73. +202
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/KeySubst.java
  74. +116
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java
  75. +48
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java
  76. +950
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Manifest.java
  77. +29
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ManifestException.java
  78. +207
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java
  79. +55
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Mkdir.java
  80. +302
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Move.java
  81. +93
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Pack.java
  82. +170
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Parallel.java
  83. +157
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Patch.java
  84. +397
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/PathConvert.java
  85. +90
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java
  86. +394
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Property.java
  87. +130
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/PumpStreamHandler.java
  88. +239
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Recorder.java
  89. +208
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java
  90. +92
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Rename.java
  91. +591
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Replace.java
  92. +688
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Rmic.java
  93. +867
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/SQLExec.java
  94. +406
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/SendEmail.java
  95. +60
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Sequential.java
  96. +335
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/SignJar.java
  97. +183
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Sleep.java
  98. +68
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/StreamPumper.java
  99. +481
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Tar.java
  100. +84
    -0
      proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/TaskOutputStream.java

+ 1088
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/AntClassLoader.java
File diff suppressed because it is too large
View File


+ 138
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/BuildEvent.java View File

@@ -0,0 +1,138 @@
/*
* 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.tools.ant;
import java.util.EventObject;

public class BuildEvent extends EventObject
{
private int priority = Project.MSG_VERBOSE;
private Throwable exception;
private String message;
private Project project;
private Target target;
private Task task;

/**
* Construct a BuildEvent for a project level event
*
* @param project the project that emitted the event.
*/
public BuildEvent( Project project )
{
super( project );
this.project = project;
this.target = null;
this.task = null;
}

/**
* Construct a BuildEvent for a target level event
*
* @param target the target that emitted the event.
*/
public BuildEvent( Target target )
{
super( target );
this.project = target.getProject();
this.target = target;
this.task = null;
}

/**
* Construct a BuildEvent for a task level event
*
* @param task the task that emitted the event.
*/
public BuildEvent( Task task )
{
super( task );
this.project = task.getProject();
this.target = task.getOwningTarget();
this.task = task;
}

public void setException( Throwable exception )
{
this.exception = exception;
}

public void setMessage( String message, int priority )
{
this.message = message;
this.priority = priority;
}

/**
* Returns the exception that was thrown, if any. This field will only be
* set for "taskFinished", "targetFinished", and "buildFinished" events.
*
* @return The Exception value
* @see BuildListener#taskFinished(BuildEvent)
* @see BuildListener#targetFinished(BuildEvent)
* @see BuildListener#buildFinished(BuildEvent)
*/
public Throwable getException()
{
return exception;
}

/**
* Returns the logging message. This field will only be set for
* "messageLogged" events.
*
* @return The Message value
* @see BuildListener#messageLogged(BuildEvent)
*/
public String getMessage()
{
return message;
}

/**
* Returns the priority of the logging message. This field will only be set
* for "messageLogged" events.
*
* @return The Priority value
* @see BuildListener#messageLogged(BuildEvent)
*/
public int getPriority()
{
return priority;
}

/**
* Returns the project that fired this event.
*
* @return The Project value
*/
public Project getProject()
{
return project;
}

/**
* Returns the target that fired this event.
*
* @return The Target value
*/
public Target getTarget()
{

return target;
}

/**
* Returns the task that fired this event.
*
* @return The Task value
*/
public Task getTask()
{
return task;
}
}

+ 103
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/BuildException.java View File

@@ -0,0 +1,103 @@
/*
* 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.tools.ant;

import org.apache.myrmidon.api.TaskException;

/**
* Signals an error condition during a build.
*
* @author James Duncan Davidson
*/
public class BuildException
extends TaskException
{
/**
* Location in the build file where the exception occured
*/
private Location location = Location.UNKNOWN_LOCATION;

/**
* Constructs an exception with the given descriptive message.
*
* @param msg Description of or information about the exception.
*/
public BuildException( String msg )
{
super( msg );
}

/**
* Constructs an exception with the given message and exception as a root
* cause.
*
* @param msg Description of or information about the exception.
* @param cause Throwable that might have cause this one.
*/
public BuildException( String msg, Throwable cause )
{
super( msg, cause );
}

/**
* Constructs an exception with the given message and exception as a root
* cause and a location in a file.
*
* @param msg Description of or information about the exception.
* @param cause Exception that might have cause this one.
* @param location Location in the project file where the error occured.
*/
public BuildException( String msg, Throwable cause, Location location )
{
this( msg, cause );
this.location = location;
}

/**
* Constructs an exception with the given exception as a root cause.
*
* @param cause Exception that might have caused this one.
*/
public BuildException( Throwable cause )
{
super( cause.toString(), cause );
}

/**
* Constructs an exception with the given descriptive message and a location
* in a file.
*
* @param msg Description of or information about the exception.
* @param location Location in the project file where the error occured.
*/
public BuildException( String msg, Location location )
{
super( msg );
this.location = location;
}

/**
* Sets the file location where the error occured.
*
* @param location The new Location value
*/
public void setLocation( Location location )
{
this.location = location;
}

/**
* Returns the file location where the error occured.
*
* @return The Location value
*/
public Location getLocation()
{
return location;
}
}

+ 80
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/BuildListener.java View File

@@ -0,0 +1,80 @@
/*
* 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.tools.ant;
import java.util.EventListener;

/**
* Classes that implement this interface will be notified when things happend
* during a build.
*
* @author RT
* @see BuildEvent
* @see Project#addBuildListener(BuildListener)
*/
public interface BuildListener extends EventListener
{

/**
* Fired before any targets are started.
*
* @param event Description of Parameter
*/
void buildStarted( BuildEvent event );

/**
* Fired after the last target has finished. This event will still be thrown
* if an error occured during the build.
*
* @param event Description of Parameter
* @see BuildEvent#getException()
*/
void buildFinished( BuildEvent event );

/**
* Fired when a target is started.
*
* @param event Description of Parameter
* @see BuildEvent#getTarget()
*/
void targetStarted( BuildEvent event );

/**
* Fired when a target has finished. This event will still be thrown if an
* error occured during the build.
*
* @param event Description of Parameter
* @see BuildEvent#getException()
*/
void targetFinished( BuildEvent event );

/**
* Fired when a task is started.
*
* @param event Description of Parameter
* @see BuildEvent#getTask()
*/
void taskStarted( BuildEvent event );

/**
* Fired when a task has finished. This event will still be throw if an
* error occured during the build.
*
* @param event Description of Parameter
* @see BuildEvent#getException()
*/
void taskFinished( BuildEvent event );

/**
* Fired whenever a message is logged.
*
* @param event Description of Parameter
* @see BuildEvent#getMessage()
* @see BuildEvent#getPriority()
*/
void messageLogged( BuildEvent event );
}

+ 54
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/BuildLogger.java View File

@@ -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.tools.ant;
import java.io.PrintStream;

/**
* Interface used by Ant to log the build output. A build logger is a build
* listener which has the 'right' to send output to the ant log, which is
* usually System.out unles redirected by the -logfile option.
*
* @author Conor MacNeill
*/
public interface BuildLogger extends BuildListener
{
/**
* Set the msgOutputLevel this logger is to respond to. Only messages with a
* message level lower than or equal to the given level are output to the
* log. <P>
*
* Constants for the message levels are in Project.java. The order of the
* levels, from least to most verbose, is MSG_ERR, MSG_WARN, MSG_INFO,
* MSG_VERBOSE, MSG_DEBUG.
*
* @param level the logging level for the logger.
*/
void setMessageOutputLevel( int level );

/**
* Set the output stream to which this logger is to send its output.
*
* @param output the output stream for the logger.
*/
void setOutputPrintStream( PrintStream output );

/**
* Set this logger to produce emacs (and other editor) friendly output.
*
* @param emacsMode true if output is to be unadorned so that emacs and
* other editors can parse files names, etc.
*/
void setEmacsMode( boolean emacsMode );

/**
* Set the output stream to which this logger is to send error messages.
*
* @param err the error stream for the logger.
*/
void setErrorPrintStream( PrintStream err );
}

+ 17
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/Constants.java View File

@@ -0,0 +1,17 @@
/*
* 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.tools.ant;

/**
* Abstract interface to hold constants.
*
* @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
*/
interface Constants
{
}

+ 220
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/DefaultLogger.java View File

@@ -0,0 +1,220 @@
/*
* 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.tools.ant;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import org.apache.tools.ant.util.StringUtils;

/**
* Writes build event to a PrintStream. Currently, it only writes which targets
* are being executed, and any messages that get logged.
*
* @author RT
*/
public class DefaultLogger implements BuildLogger
{
private static int LEFT_COLUMN_SIZE = 12;
protected int msgOutputLevel = Project.MSG_ERR;
private long startTime = System.currentTimeMillis();

protected boolean emacsMode = false;
protected PrintStream err;

protected PrintStream out;

protected static String formatTime( long millis )
{
long seconds = millis / 1000;
long minutes = seconds / 60;

if( minutes > 0 )
{
return Long.toString( minutes ) + " minute"
+ ( minutes == 1 ? " " : "s " )
+ Long.toString( seconds % 60 ) + " second"
+ ( seconds % 60 == 1 ? "" : "s" );
}
else
{
return Long.toString( seconds ) + " second"
+ ( seconds % 60 == 1 ? "" : "s" );
}

}

/**
* Set this logger to produce emacs (and other editor) friendly output.
*
* @param emacsMode true if output is to be unadorned so that emacs and
* other editors can parse files names, etc.
*/
public void setEmacsMode( boolean emacsMode )
{
this.emacsMode = emacsMode;
}

/**
* Set the output stream to which this logger is to send error messages.
*
* @param err the error stream for the logger.
*/
public void setErrorPrintStream( PrintStream err )
{
this.err = new PrintStream( err, true );
}

/**
* Set the msgOutputLevel this logger is to respond to. Only messages with a
* message level lower than or equal to the given level are output to the
* log. <P>
*
* Constants for the message levels are in Project.java. The order of the
* levels, from least to most verbose, is MSG_ERR, MSG_WARN, MSG_INFO,
* MSG_VERBOSE, MSG_DEBUG. The default message level for DefaultLogger is
* Project.MSG_ERR.
*
* @param level the logging level for the logger.
*/
public void setMessageOutputLevel( int level )
{
this.msgOutputLevel = level;
}

/**
* Set the output stream to which this logger is to send its output.
*
* @param output the output stream for the logger.
*/
public void setOutputPrintStream( PrintStream output )
{
this.out = new PrintStream( output, true );
}

/**
* Prints whether the build succeeded or failed, and any errors the occured
* during the build.
*
* @param event Description of Parameter
*/
public void buildFinished( BuildEvent event )
{
Throwable error = event.getException();
StringBuffer message = new StringBuffer();

if( error == null )
{
message.append( StringUtils.LINE_SEP );
message.append( "BUILD SUCCESSFUL" );
}
else
{
message.append( StringUtils.LINE_SEP );
message.append( "BUILD FAILED" );
message.append( StringUtils.LINE_SEP );

if( Project.MSG_VERBOSE <= msgOutputLevel ||
!( error instanceof BuildException ) )
{
message.append( StringUtils.getStackTrace( error ) );
}
else
{
if( error instanceof BuildException )
{
message.append( error.toString() ).append( StringUtils.LINE_SEP );
}
else
{
message.append( error.getMessage() ).append( StringUtils.LINE_SEP );
}
}
}
message.append( StringUtils.LINE_SEP );
message.append( "Total time: "
+ formatTime( System.currentTimeMillis() - startTime ) );

String msg = message.toString();
if( error == null )
{
out.println( msg );
}
else
{
err.println( msg );
}
log( msg );
}

public void buildStarted( BuildEvent event )
{
startTime = System.currentTimeMillis();
}

public void messageLogged( BuildEvent event )
{
// Filter out messages based on priority
if( event.getPriority() <= msgOutputLevel )
{

StringBuffer message = new StringBuffer();
// Print out the name of the task if we're in one
if( event.getTask() != null )
{
String name = event.getTask().getTaskName();

if( !emacsMode )
{
String label = "[" + name + "] ";
for( int i = 0; i < ( LEFT_COLUMN_SIZE - label.length() ); i++ )
{
message.append( " " );
}
message.append( label );
}
}

message.append( event.getMessage() );
String msg = message.toString();
if( event.getPriority() != Project.MSG_ERR )
{
out.println( msg );
}
else
{
err.println( msg );
}
log( msg );
}
}

public void targetFinished( BuildEvent event ) { }

public void targetStarted( BuildEvent event )
{
if( Project.MSG_INFO <= msgOutputLevel )
{
String msg = StringUtils.LINE_SEP + event.getTarget().getName() + ":";
out.println( msg );
log( msg );
}
}

public void taskFinished( BuildEvent event ) { }

public void taskStarted( BuildEvent event ) { }

/**
* Empty implementation which allows subclasses to receive the same output
* that is generated here.
*
* @param message Description of Parameter
*/
protected void log( String message ) { }

}

+ 127
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/DemuxOutputStream.java View File

@@ -0,0 +1,127 @@
/*
* 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.tools.ant;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Hashtable;


/**
* Logs content written by a thread and forwards the buffers onto the project
* object which will forward the content to the appropriate task
*
* @author Conor MacNeill
*/
public class DemuxOutputStream extends OutputStream
{

private final static int MAX_SIZE = 1024;

private Hashtable buffers = new Hashtable();
// private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private boolean skip = false;
private boolean isErrorStream;
private Project project;

/**
* Creates a new instance of this class.
*
* @param project Description of Parameter
* @param isErrorStream Description of Parameter
*/
public DemuxOutputStream( Project project, boolean isErrorStream )
{
this.project = project;
this.isErrorStream = isErrorStream;
}

/**
* Writes all remaining
*
* @exception IOException Description of Exception
*/
public void close()
throws IOException
{
flush();
}

/**
* Writes all remaining
*
* @exception IOException Description of Exception
*/
public void flush()
throws IOException
{
if( getBuffer().size() > 0 )
{
processBuffer();
}
}

/**
* Write the data to the buffer and flush the buffer, if a line separator is
* detected.
*
* @param cc data to log (byte).
* @exception IOException Description of Exception
*/
public void write( int cc )
throws IOException
{
final byte c = ( byte )cc;
if( ( c == '\n' ) || ( c == '\r' ) )
{
if( !skip )
{
processBuffer();
}
}
else
{
ByteArrayOutputStream buffer = getBuffer();
buffer.write( cc );
if( buffer.size() > MAX_SIZE )
{
processBuffer();
}
}
skip = ( c == '\r' );
}


/**
* Converts the buffer to a string and sends it to <code>processLine</code>
*/
protected void processBuffer()
{
String output = getBuffer().toString();
project.demuxOutput( output, isErrorStream );
resetBuffer();
}

private ByteArrayOutputStream getBuffer()
{
Thread current = Thread.currentThread();
ByteArrayOutputStream buffer = ( ByteArrayOutputStream )buffers.get( current );
if( buffer == null )
{
buffer = new ByteArrayOutputStream();
buffers.put( current, buffer );
}
return buffer;
}

private void resetBuffer()
{
Thread current = Thread.currentThread();
buffers.remove( current );
}
}

+ 96
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/DesirableFilter.java View File

@@ -0,0 +1,96 @@
/*
* 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.tools.ant;
import java.io.File;
import java.io.FilenameFilter;


/**
* Filters filenames to determine whether or not the file is desirable.
*
* @author Jason Hunter [jhunter@servlets.com]
* @author james@x180.com
*/
public class DesirableFilter implements FilenameFilter
{

/**
* Test the given filename to determine whether or not it's desirable. This
* helps tasks filter temp files and files used by CVS.
*
* @param dir Description of Parameter
* @param name Description of Parameter
* @return Description of the Returned Value
*/

public boolean accept( File dir, String name )
{

// emacs save file
if( name.endsWith( "~" ) )
{
return false;
}

// emacs autosave file
if( name.startsWith( "#" ) && name.endsWith( "#" ) )
{
return false;
}

// openwindows text editor does this I think
if( name.startsWith( "%" ) && name.endsWith( "%" ) )
{
return false;
}

/*
* CVS stuff -- hopefully there won't be a case with
* an all cap file/dir named "CVS" that somebody wants
* to keep around...
*/
if( name.equals( "CVS" ) )
{
return false;
}

/*
* If we are going to ignore CVS might as well ignore
* this one as well...
*/
if( name.equals( ".cvsignore" ) )
{
return false;
}

// CVS merge autosaves.
if( name.startsWith( ".#" ) )
{
return false;
}

// SCCS/CSSC/TeamWare:
if( name.equals( "SCCS" ) )
{
return false;
}

// Visual Source Save
if( name.equals( "vssver.scc" ) )
{
return false;
}

// default
return true;
}
}





+ 1177
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/DirectoryScanner.java
File diff suppressed because it is too large
View File


+ 39
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/ExitException.java View File

@@ -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.tools.ant;

/**
* Used to report exit status of classes which call System.exit()
*
* @author Conor MacNeill
* @see NoExitSecurityManager
*/
public class ExitException extends SecurityException
{

private int status;

/**
* Constructs an exit exception.
*
* @param status the status code returned via System.exit()
*/
public ExitException( int status )
{
super( "ExitException: status " + status );
this.status = status;
}

/**
* @return the status code return via System.exit()
*/
public int getStatus()
{
return status;
}
}

+ 127
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/FileScanner.java View File

@@ -0,0 +1,127 @@
/*
* 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.tools.ant;
import java.io.File;

/**
* An interface used to describe the actions required by any type of directory
* scanner.
*
* @author RT
*/
public interface FileScanner
{
/**
* Adds an array with default exclusions to the current exclusions set.
*/
void addDefaultExcludes();

/**
* Gets the basedir that is used for scanning. This is the directory that is
* scanned recursively.
*
* @return the basedir that is used for scanning
*/
File getBasedir();

/**
* Get the names of the directories that matched at least one of the include
* patterns, an matched also at least one of the exclude patterns. The names
* are relative to the basedir.
*
* @return the names of the directories
*/
String[] getExcludedDirectories();

/**
* Get the names of the files that matched at least one of the include
* patterns, an matched also at least one of the exclude patterns. The names
* are relative to the basedir.
*
* @return the names of the files
*/
String[] getExcludedFiles();

/**
* Get the names of the directories that matched at least one of the include
* patterns, an matched none of the exclude patterns. The names are relative
* to the basedir.
*
* @return the names of the directories
*/
String[] getIncludedDirectories();

/**
* Get the names of the files that matched at least one of the include
* patterns, an matched none of the exclude patterns. The names are relative
* to the basedir.
*
* @return the names of the files
*/
String[] getIncludedFiles();

/**
* Get the names of the directories that matched at none of the include
* patterns. The names are relative to the basedir.
*
* @return the names of the directories
*/
String[] getNotIncludedDirectories();

/**
* Get the names of the files that matched at none of the include patterns.
* The names are relative to the basedir.
*
* @return the names of the files
*/
String[] getNotIncludedFiles();

/**
* Scans the base directory for files that match at least one include
* pattern, and don't match any exclude patterns.
*
*/
void scan();

/**
* Sets the basedir for scanning. This is the directory that is scanned
* recursively.
*
* @param basedir the (non-null) basedir for scanning
*/
void setBasedir( String basedir );

/**
* Sets the basedir for scanning. This is the directory that is scanned
* recursively.
*
* @param basedir the basedir for scanning
*/
void setBasedir( File basedir );

/**
* Sets the set of exclude patterns to use.
*
* @param excludes list of exclude patterns
*/
void setExcludes( String[] excludes );

/**
* Sets the set of include patterns to use.
*
* @param includes list of include patterns
*/
void setIncludes( String[] includes );

/**
* Sets the case sensitivity of the file system
*
* @param isCaseSensitive The new CaseSensitive value
*/
void setCaseSensitive( boolean isCaseSensitive );
}

+ 848
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/IntrospectionHelper.java View File

@@ -0,0 +1,848 @@
/*
* 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.tools.ant;
import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.Path;

/**
* Helper class that collects the methods a task or nested element holds to set
* attributes, create nested elements or hold PCDATA elements.
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/
public class IntrospectionHelper implements BuildListener
{

/**
* instances we've already created
*/
private static Hashtable helpers = new Hashtable();

/**
* The method to add PCDATA stuff.
*/
private Method addText = null;

/**
* holds the attribute setter methods.
*/
private Hashtable attributeSetters;

/**
* holds the types of the attributes that could be set.
*/
private Hashtable attributeTypes;

/**
* The Class that's been introspected.
*/
private Class bean;

/**
* Holds methods to create nested elements.
*/
private Hashtable nestedCreators;

/**
* Holds methods to store configured nested elements.
*/
private Hashtable nestedStorers;

/**
* Holds the types of nested elements that could be created.
*/
private Hashtable nestedTypes;

private IntrospectionHelper( final Class bean )
{
attributeTypes = new Hashtable();
attributeSetters = new Hashtable();
nestedTypes = new Hashtable();
nestedCreators = new Hashtable();
nestedStorers = new Hashtable();

this.bean = bean;

Method[] methods = bean.getMethods();
for( int i = 0; i < methods.length; i++ )
{
final Method m = methods[i];
final String name = m.getName();
Class returnType = m.getReturnType();
Class[] args = m.getParameterTypes();

// not really user settable properties on tasks
if( org.apache.tools.ant.Task.class.isAssignableFrom( bean )
&& args.length == 1 &&
(
(
"setLocation".equals( name ) && org.apache.tools.ant.Location.class.equals( args[0] )
) || (
"setTaskType".equals( name ) && java.lang.String.class.equals( args[0] )
)
) )
{
continue;
}

// hide addTask for TaskContainers
if( org.apache.tools.ant.TaskContainer.class.isAssignableFrom( bean )
&& args.length == 1 && "addTask".equals( name )
&& org.apache.tools.ant.Task.class.equals( args[0] ) )
{
continue;
}

if( "addText".equals( name )
&& java.lang.Void.TYPE.equals( returnType )
&& args.length == 1
&& java.lang.String.class.equals( args[0] ) )
{

addText = methods[i];

}
else if( name.startsWith( "set" )
&& java.lang.Void.TYPE.equals( returnType )
&& args.length == 1
&& !args[0].isArray() )
{

String propName = getPropertyName( name, "set" );
if( attributeSetters.get( propName ) != null )
{
if( java.lang.String.class.equals( args[0] ) )
{
/*
* Ignore method m, as there is an overloaded
* form of this method that takes in a
* non-string argument, which gains higher
* priority.
*/
continue;
}
/*
* If the argument is not a String, and if there
* is an overloaded form of this method already defined,
* we just override that with the new one.
* This mechanism does not guarantee any specific order
* in which the methods will be selected: so any code
* that depends on the order in which "set" methods have
* been defined, is not guaranteed to be selected in any
* particular order.
*/
}
AttributeSetter as = createAttributeSetter( m, args[0] );
if( as != null )
{
attributeTypes.put( propName, args[0] );
attributeSetters.put( propName, as );
}

}
else if( name.startsWith( "create" )
&& !returnType.isArray()
&& !returnType.isPrimitive()
&& args.length == 0 )
{

String propName = getPropertyName( name, "create" );
nestedTypes.put( propName, returnType );
nestedCreators.put( propName,
new NestedCreator()
{

public Object create( Object parent )
throws InvocationTargetException,
IllegalAccessException
{

return m.invoke( parent, new Object[]{} );
}

} );

}
else if( name.startsWith( "addConfigured" )
&& java.lang.Void.TYPE.equals( returnType )
&& args.length == 1
&& !java.lang.String.class.equals( args[0] )
&& !args[0].isArray()
&& !args[0].isPrimitive() )
{

try
{
final Constructor c =
args[0].getConstructor( new Class[]{} );
String propName = getPropertyName( name, "addConfigured" );
nestedTypes.put( propName, args[0] );
nestedCreators.put( propName,
new NestedCreator()
{

public Object create( Object parent )
throws InvocationTargetException, IllegalAccessException, InstantiationException
{

Object o = c.newInstance( new Object[]{} );
return o;
}

} );
nestedStorers.put( propName,
new NestedStorer()
{

public void store( Object parent, Object child )
throws InvocationTargetException, IllegalAccessException, InstantiationException
{

m.invoke( parent, new Object[]{child} );
}

} );
}
catch( NoSuchMethodException nse )
{
}
}
else if( name.startsWith( "add" )
&& java.lang.Void.TYPE.equals( returnType )
&& args.length == 1
&& !java.lang.String.class.equals( args[0] )
&& !args[0].isArray()
&& !args[0].isPrimitive() )
{

try
{
final Constructor c =
args[0].getConstructor( new Class[]{} );
String propName = getPropertyName( name, "add" );
nestedTypes.put( propName, args[0] );
nestedCreators.put( propName,
new NestedCreator()
{

public Object create( Object parent )
throws InvocationTargetException, IllegalAccessException, InstantiationException
{

Object o = c.newInstance( new Object[]{} );
m.invoke( parent, new Object[]{o} );
return o;
}

} );
}
catch( NoSuchMethodException nse )
{
}
}
}
}

/**
* Factory method for helper objects.
*
* @param c Description of Parameter
* @return The Helper value
*/
public static synchronized IntrospectionHelper getHelper( Class c )
{
IntrospectionHelper ih = ( IntrospectionHelper )helpers.get( c );
if( ih == null )
{
ih = new IntrospectionHelper( c );
helpers.put( c, ih );
}
return ih;
}

/**
* Sets the named attribute.
*
* @param p The new Attribute value
* @param element The new Attribute value
* @param attributeName The new Attribute value
* @param value The new Attribute value
* @exception BuildException Description of Exception
*/
public void setAttribute( Project p, Object element, String attributeName,
String value )
throws BuildException
{
AttributeSetter as = ( AttributeSetter )attributeSetters.get( attributeName );
if( as == null )
{
String msg = getElementName( p, element ) +
//String msg = "Class " + element.getClass().getName() +
" doesn't support the \"" + attributeName + "\" attribute.";
throw new BuildException( msg );
}
try
{
as.set( p, element, value );
}
catch( IllegalAccessException ie )
{
// impossible as getMethods should only return public methods
throw new BuildException( ie );
}
catch( InvocationTargetException ite )
{
Throwable t = ite.getTargetException();
if( t instanceof BuildException )
{
throw ( BuildException )t;
}
throw new BuildException( t );
}
}

/**
* returns the type of a named attribute.
*
* @param attributeName Description of Parameter
* @return The AttributeType value
* @exception BuildException Description of Exception
*/
public Class getAttributeType( String attributeName )
throws BuildException
{
Class at = ( Class )attributeTypes.get( attributeName );
if( at == null )
{
String msg = "Class " + bean.getName() +
" doesn't support the \"" + attributeName + "\" attribute.";
throw new BuildException( msg );
}
return at;
}

/**
* Return all attribues supported by the introspected class.
*
* @return The Attributes value
*/
public Enumeration getAttributes()
{
return attributeSetters.keys();
}

/**
* returns the type of a named nested element.
*
* @param elementName Description of Parameter
* @return The ElementType value
* @exception BuildException Description of Exception
*/
public Class getElementType( String elementName )
throws BuildException
{
Class nt = ( Class )nestedTypes.get( elementName );
if( nt == null )
{
String msg = "Class " + bean.getName() +
" doesn't support the nested \"" + elementName + "\" element.";
throw new BuildException( msg );
}
return nt;
}

/**
* Return all nested elements supported by the introspected class.
*
* @return The NestedElements value
*/
public Enumeration getNestedElements()
{
return nestedTypes.keys();
}

/**
* Adds PCDATA areas.
*
* @param project The feature to be added to the Text attribute
* @param element The feature to be added to the Text attribute
* @param text The feature to be added to the Text attribute
*/
public void addText( Project project, Object element, String text )
{
if( addText == null )
{
String msg = getElementName( project, element ) +
//String msg = "Class " + element.getClass().getName() +
" doesn't support nested text data.";
throw new BuildException( msg );
}
try
{
addText.invoke( element, new String[]{text} );
}
catch( IllegalAccessException ie )
{
// impossible as getMethods should only return public methods
throw new BuildException( ie );
}
catch( InvocationTargetException ite )
{
Throwable t = ite.getTargetException();
if( t instanceof BuildException )
{
throw ( BuildException )t;
}
throw new BuildException( t );
}
}

public void buildFinished( BuildEvent event )
{
attributeTypes.clear();
attributeSetters.clear();
nestedTypes.clear();
nestedCreators.clear();
addText = null;
helpers.clear();
}

public void buildStarted( BuildEvent event ) { }

/**
* Creates a named nested element.
*
* @param project Description of Parameter
* @param element Description of Parameter
* @param elementName Description of Parameter
* @return Description of the Returned Value
* @exception BuildException Description of Exception
*/
public Object createElement( Project project, Object element, String elementName )
throws BuildException
{
NestedCreator nc = ( NestedCreator )nestedCreators.get( elementName );
if( nc == null )
{
String msg = getElementName( project, element ) +
" doesn't support the nested \"" + elementName + "\" element.";
throw new BuildException( msg );
}
try
{
Object nestedElement = nc.create( element );
if( nestedElement instanceof ProjectComponent )
{
( ( ProjectComponent )nestedElement ).setProject( project );
}
return nestedElement;
}
catch( IllegalAccessException ie )
{
// impossible as getMethods should only return public methods
throw new BuildException( ie );
}
catch( InstantiationException ine )
{
// impossible as getMethods should only return public methods
throw new BuildException( ine );
}
catch( InvocationTargetException ite )
{
Throwable t = ite.getTargetException();
if( t instanceof BuildException )
{
throw ( BuildException )t;
}
throw new BuildException( t );
}
}

public void messageLogged( BuildEvent event ) { }

/**
* Creates a named nested element.
*
* @param project Description of Parameter
* @param element Description of Parameter
* @param child Description of Parameter
* @param elementName Description of Parameter
* @exception BuildException Description of Exception
*/
public void storeElement( Project project, Object element, Object child, String elementName )
throws BuildException
{
if( elementName == null )
{
return;
}
NestedStorer ns = ( NestedStorer )nestedStorers.get( elementName );
if( ns == null )
{
return;
}
try
{
ns.store( element, child );
}
catch( IllegalAccessException ie )
{
// impossible as getMethods should only return public methods
throw new BuildException( ie );
}
catch( InstantiationException ine )
{
// impossible as getMethods should only return public methods
throw new BuildException( ine );
}
catch( InvocationTargetException ite )
{
Throwable t = ite.getTargetException();
if( t instanceof BuildException )
{
throw ( BuildException )t;
}
throw new BuildException( t );
}
}

/**
* Does the introspected class support PCDATA?
*
* @return Description of the Returned Value
*/
public boolean supportsCharacters()
{
return addText != null;
}

public void targetFinished( BuildEvent event ) { }

public void targetStarted( BuildEvent event ) { }

public void taskFinished( BuildEvent event ) { }

public void taskStarted( BuildEvent event ) { }

protected String getElementName( Project project, Object element )
{
Hashtable elements = project.getTaskDefinitions();
String typeName = "task";
if( !elements.contains( element.getClass() ) )
{
elements = project.getDataTypeDefinitions();
typeName = "data type";
if( !elements.contains( element.getClass() ) )
{
elements = null;
}
}

if( elements != null )
{
Enumeration e = elements.keys();
while( e.hasMoreElements() )
{
String elementName = ( String )e.nextElement();
Class elementClass = ( Class )elements.get( elementName );
if( element.getClass().equals( elementClass ) )
{
return "The <" + elementName + "> " + typeName;
}
}
}

return "Class " + element.getClass().getName();
}

/**
* extract the name of a property from a method name - subtracting a given
* prefix.
*
* @param methodName Description of Parameter
* @param prefix Description of Parameter
* @return The PropertyName value
*/
private String getPropertyName( String methodName, String prefix )
{
int start = prefix.length();
return methodName.substring( start ).toLowerCase( Locale.US );
}

/**
* Create a proper implementation of AttributeSetter for the given attribute
* type.
*
* @param m Description of Parameter
* @param arg Description of Parameter
* @return Description of the Returned Value
*/
private AttributeSetter createAttributeSetter( final Method m,
final Class arg )
{

// simplest case - setAttribute expects String
if( java.lang.String.class.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent, new String[]{value} );
}
};
// now for the primitive types, use their wrappers
}
else if( java.lang.Character.class.equals( arg )
|| java.lang.Character.TYPE.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent, new Character[]{new Character( value.charAt( 0 ) )} );
}

};
}
else if( java.lang.Byte.TYPE.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent, new Byte[]{new Byte( value )} );
}

};
}
else if( java.lang.Short.TYPE.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent, new Short[]{new Short( value )} );
}

};
}
else if( java.lang.Integer.TYPE.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent, new Integer[]{new Integer( value )} );
}

};
}
else if( java.lang.Long.TYPE.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent, new Long[]{new Long( value )} );
}

};
}
else if( java.lang.Float.TYPE.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent, new Float[]{new Float( value )} );
}

};
}
else if( java.lang.Double.TYPE.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent, new Double[]{new Double( value )} );
}

};
// boolean gets an extra treatment, because we have a nice method
// in Project
}
else if( java.lang.Boolean.class.equals( arg )
|| java.lang.Boolean.TYPE.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent,
new Boolean[]{new Boolean( Project.toBoolean( value ) )} );
}

};
// Class doesn't have a String constructor but a decent factory method
}
else if( java.lang.Class.class.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException, BuildException
{
try
{
m.invoke( parent, new Class[]{Class.forName( value )} );
}
catch( ClassNotFoundException ce )
{
throw new BuildException( ce );
}
}
};
// resolve relative paths through Project
}
else if( java.io.File.class.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent, new File[]{p.resolveFile( value )} );
}

};
// resolve relative paths through Project
}
else if( org.apache.tools.ant.types.Path.class.equals( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException
{
m.invoke( parent, new Path[]{new Path( p, value )} );
}

};
// EnumeratedAttributes have their own helper class
}
else if( org.apache.tools.ant.types.EnumeratedAttribute.class.isAssignableFrom( arg ) )
{
return
new AttributeSetter()
{
public void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException, BuildException
{
try
{
org.apache.tools.ant.types.EnumeratedAttribute ea = ( org.apache.tools.ant.types.EnumeratedAttribute )arg.newInstance();
ea.setValue( value );
m.invoke( parent, new EnumeratedAttribute[]{ea} );
}
catch( InstantiationException ie )
{
throw new BuildException( ie );
}
}
};

// worst case. look for a public String constructor and use it
}
else
{

try
{
final Constructor c =
arg.getConstructor( new Class[]{java.lang.String.class} );

return
new AttributeSetter()
{
public void set( Project p, Object parent,
String value )
throws InvocationTargetException, IllegalAccessException, BuildException
{
try
{
Object attribute = c.newInstance( new String[]{value} );
if( attribute instanceof ProjectComponent )
{
( ( ProjectComponent )attribute ).setProject( p );
}
m.invoke( parent, new Object[]{attribute} );
}
catch( InstantiationException ie )
{
throw new BuildException( ie );
}
}
};
}
catch( NoSuchMethodException nme )
{
}
}

return null;
}

private interface AttributeSetter
{
void set( Project p, Object parent, String value )
throws InvocationTargetException, IllegalAccessException,
BuildException;
}

private interface NestedCreator
{
Object create( Object parent )
throws InvocationTargetException, IllegalAccessException, InstantiationException;
}

private interface NestedStorer
{
void store( Object parent, Object child )
throws InvocationTargetException, IllegalAccessException, InstantiationException;
}
}

+ 182
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/Launcher.java View File

@@ -0,0 +1,182 @@
/*
* 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.tools.ant;
import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;
import java.util.StringTokenizer;

/**
* This is the Ant command line front end to end. This front end works out where
* ant is installed and loads the ant libraries before starting Ant proper.
*
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
*/
public class Launcher
{

public static void main( String[] args )
{
File antHome = null;
ClassLoader systemClassLoader = Launcher.class.getClassLoader();
if( systemClassLoader == null )
{
antHome = determineAntHome11();
}
else
{
antHome = determineAntHome( systemClassLoader );
}
if( antHome == null )
{
System.err.println( "Unable to determine ANT_HOME" );
System.exit( 1 );
}

System.out.println( "ANT_HOME is " + antHome );

// We now create the class loader with which we are going to launch ant
AntClassLoader antLoader = new AntClassLoader( systemClassLoader, false );

// need to find tools.jar
addToolsJar( antLoader );

// add everything in the lib directory to this classloader
File libDir = new File( antHome, "lib" );
addDirJars( antLoader, libDir );

File optionalDir = new File( antHome, "lib/optional" );
addDirJars( antLoader, optionalDir );

Properties launchProperties = new Properties();
launchProperties.put( "ant.home", antHome.getAbsolutePath() );

try
{
Class mainClass = antLoader.loadClass( "org.apache.tools.ant.Main" );
antLoader.initializeClass( mainClass );

final Class[] param = {Class.forName( "[Ljava.lang.String;" ),
Properties.class, ClassLoader.class};
final Method startMethod = mainClass.getMethod( "start", param );
final Object[] argument = {args, launchProperties, systemClassLoader};
startMethod.invoke( null, argument );
}
catch( Exception e )
{
System.out.println( "Exception running Ant: " + e.getClass().getName() + ": " + e.getMessage() );
e.printStackTrace();
}
}

private static void addDirJars( AntClassLoader classLoader, File jarDir )
{
String[] fileList = jarDir.list(
new FilenameFilter()
{
public boolean accept( File dir, String name )
{
return name.endsWith( ".jar" );
}
} );

if( fileList != null )
{
for( int i = 0; i < fileList.length; ++i )
{
File jarFile = new File( jarDir, fileList[i] );
classLoader.addPathElement( jarFile.getAbsolutePath() );
}
}
}

private static void addToolsJar( AntClassLoader antLoader )
{
String javaHome = System.getProperty( "java.home" );
if( javaHome.endsWith( "jre" ) )
{
javaHome = javaHome.substring( 0, javaHome.length() - 4 );
}
System.out.println( "Java home is " + javaHome );
File toolsJar = new File( javaHome, "lib/tools.jar" );
if( !toolsJar.exists() )
{
System.out.println( "Unable to find tools.jar at " + toolsJar.getPath() );
}
else
{
antLoader.addPathElement( toolsJar.getAbsolutePath() );
}
}

private static File determineAntHome( ClassLoader systemClassLoader )
{
try
{
String className = Launcher.class.getName().replace( '.', '/' ) + ".class";
URL classResource = systemClassLoader.getResource( className );
String fileComponent = classResource.getFile();
if( classResource.getProtocol().equals( "file" ) )
{
// Class comes from a directory of class files rather than
// from a jar.
int classFileIndex = fileComponent.lastIndexOf( className );
if( classFileIndex != -1 )
{
fileComponent = fileComponent.substring( 0, classFileIndex );
}
File classFilesDir = new File( fileComponent );
File buildDir = new File( classFilesDir.getParent() );
File devAntHome = new File( buildDir.getParent() );
return devAntHome;
}
else if( classResource.getProtocol().equals( "jar" ) )
{
// Class is coming from a jar. The file component of the URL
// is actually the URL of the jar file
int classSeparatorIndex = fileComponent.lastIndexOf( "!" );
if( classSeparatorIndex != -1 )
{
fileComponent = fileComponent.substring( 0, classSeparatorIndex );
}
URL antJarURL = new URL( fileComponent );
File antJarFile = new File( antJarURL.getFile() );
File libDirectory = new File( antJarFile.getParent() );
File antHome = new File( libDirectory.getParent() );
return antHome;
}
}
catch( MalformedURLException e )
{
e.printStackTrace();
}
return null;
}

private static File determineAntHome11()
{
String classpath = System.getProperty( "java.class.path" );
StringTokenizer tokenizer = new StringTokenizer( classpath, System.getProperty( "path.separator" ) );
while( tokenizer.hasMoreTokens() )
{
String path = tokenizer.nextToken();
if( path.endsWith( "ant.jar" ) )
{
File antJarFile = new File( path );
File libDirectory = new File( antJarFile.getParent() );
File antHome = new File( libDirectory.getParent() );
return antHome;
}
}
return null;
}
}


+ 80
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/Location.java View File

@@ -0,0 +1,80 @@
/*
* 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.tools.ant;

/**
* Stores the file name and line number in a file.
*
* @author RT
*/
public class Location
{

public final static Location UNKNOWN_LOCATION = new Location();
private int columnNumber;
private String fileName;
private int lineNumber;

/**
* Creates a location consisting of a file name but no line number.
*
* @param fileName Description of Parameter
*/
public Location( String fileName )
{
this( fileName, 0, 0 );
}

/**
* Creates a location consisting of a file name and line number.
*
* @param fileName Description of Parameter
* @param lineNumber Description of Parameter
* @param columnNumber Description of Parameter
*/
public Location( String fileName, int lineNumber, int columnNumber )
{
this.fileName = fileName;
this.lineNumber = lineNumber;
this.columnNumber = columnNumber;
}

/**
* Creates an "unknown" location.
*/
private Location()
{
this( null, 0, 0 );
}

/**
* Returns the file name, line number and a trailing space. An error message
* can be appended easily. For unknown locations, returns an empty string.
*
* @return Description of the Returned Value
*/
public String toString()
{
StringBuffer buf = new StringBuffer();

if( fileName != null )
{
buf.append( fileName );

if( lineNumber != 0 )
{
buf.append( ":" );
buf.append( lineNumber );
}

buf.append( ": " );
}

return buf.toString();
}
}

+ 842
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/Main.java View File

@@ -0,0 +1,842 @@
/*
* 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.tools.ant;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;

/**
* Command line entry point into Ant. This class is entered via the cannonical
* `public static void main` entry point and reads the command line arguments.
* It then assembles and executes an Ant project. <p>
*
* If you integrating Ant into some other tool, this is not the class to use as
* an entry point. Please see the source code of this class to see how it
* manipulates the Ant project classes.
*
* @author duncan@x180.com
*/
public class Main
{

/**
* The default build file name
*/
public final static String DEFAULT_BUILD_FILENAME = "build.xml";

private static String antVersion = null;

/**
* Our current message output status. Follows Project.MSG_XXX
*/
private int msgOutputLevel = Project.MSG_INFO;
/**
* null
*/

/**
* Stream that we are using for logging
*/
private PrintStream out = System.out;

/**
* Stream that we are using for logging error messages
*/
private PrintStream err = System.err;

/**
* The build targets
*/
private Vector targets = new Vector( 5 );

/**
* Set of properties that can be used by tasks
*/
private Properties definedProps = new Properties();

/**
* Names of classes to add as listeners to project
*/
private Vector listeners = new Vector( 5 );

/**
* The Ant logger class. There may be only one logger. It will have the
* right to use the 'out' PrintStream. The class must implements the
* BuildLogger interface
*/
private String loggerClassname = null;

/**
* Indicates whether output to the log is to be unadorned.
*/
private boolean emacsMode = false;

/**
* Indicates if this ant should be run.
*/
private boolean readyToRun = false;

/**
* Indicates we should only parse and display the project help information
*/
private boolean projectHelp = false;

/**
* File that we are using for configuration
*/
private File buildFile;

protected Main( String[] args )
throws BuildException
{

String searchForThis = null;

// cycle through given args

for( int i = 0; i < args.length; i++ )
{
String arg = args[i];

if( arg.equals( "-help" ) )
{
printUsage();
return;
}
else if( arg.equals( "-version" ) )
{
printVersion();
return;
}
else if( arg.equals( "-quiet" ) || arg.equals( "-q" ) )
{
msgOutputLevel = Project.MSG_WARN;
}
else if( arg.equals( "-verbose" ) || arg.equals( "-v" ) )
{
printVersion();
msgOutputLevel = Project.MSG_VERBOSE;
}
else if( arg.equals( "-debug" ) )
{
printVersion();
msgOutputLevel = Project.MSG_DEBUG;
}
else if( arg.equals( "-logfile" ) || arg.equals( "-l" ) )
{
try
{
File logFile = new File( args[i + 1] );
i++;
out = new PrintStream( new FileOutputStream( logFile ) );
err = out;
System.setOut( out );
System.setErr( out );
}
catch( IOException ioe )
{
String msg = "Cannot write on the specified log file. " +
"Make sure the path exists and you have write permissions.";
System.out.println( msg );
return;
}
catch( ArrayIndexOutOfBoundsException aioobe )
{
String msg = "You must specify a log file when " +
"using the -log argument";
System.out.println( msg );
return;
}
}
else if( arg.equals( "-buildfile" ) || arg.equals( "-file" ) || arg.equals( "-f" ) )
{
try
{
buildFile = new File( args[i + 1] );
i++;
}
catch( ArrayIndexOutOfBoundsException aioobe )
{
String msg = "You must specify a buildfile when " +
"using the -buildfile argument";
System.out.println( msg );
return;
}
}
else if( arg.equals( "-listener" ) )
{
try
{
listeners.addElement( args[i + 1] );
i++;
}
catch( ArrayIndexOutOfBoundsException aioobe )
{
String msg = "You must specify a classname when " +
"using the -listener argument";
System.out.println( msg );
return;
}
}
else if( arg.startsWith( "-D" ) )
{

/*
* Interestingly enough, we get to here when a user
* uses -Dname=value. However, in some cases, the JDK
* goes ahead * and parses this out to args
* {"-Dname", "value"}
* so instead of parsing on "=", we just make the "-D"
* characters go away and skip one argument forward.
*
* I don't know how to predict when the JDK is going
* to help or not, so we simply look for the equals sign.
*/
String name = arg.substring( 2, arg.length() );
String value = null;
int posEq = name.indexOf( "=" );
if( posEq > 0 )
{
value = name.substring( posEq + 1 );
name = name.substring( 0, posEq );
}
else if( i < args.length - 1 )
value = args[++i];

definedProps.put( name, value );
}
else if( arg.equals( "-logger" ) )
{
if( loggerClassname != null )
{
System.out.println( "Only one logger class may be specified." );
return;
}
try
{
loggerClassname = args[++i];
}
catch( ArrayIndexOutOfBoundsException aioobe )
{
System.out.println( "You must specify a classname when " +
"using the -logger argument" );
return;
}
}
else if( arg.equals( "-emacs" ) )
{
emacsMode = true;
}
else if( arg.equals( "-projecthelp" ) )
{
// set the flag to display the targets and quit
projectHelp = true;
}
else if( arg.equals( "-find" ) )
{
// eat up next arg if present, default to build.xml
if( i < args.length - 1 )
{
searchForThis = args[++i];
}
else
{
searchForThis = DEFAULT_BUILD_FILENAME;
}
}
else if( arg.startsWith( "-" ) )
{
// we don't have any more args to recognize!
String msg = "Unknown argument: " + arg;
System.out.println( msg );
printUsage();
return;
}
else
{
// if it's no other arg, it may be the target
targets.addElement( arg );
}

}

// if buildFile was not specified on the command line,
if( buildFile == null )
{
// but -find then search for it
if( searchForThis != null )
{
buildFile = findBuildFile( System.getProperty( "user.dir" ),
searchForThis );
}
else
{
buildFile = new File( DEFAULT_BUILD_FILENAME );
}
}

// make sure buildfile exists
if( !buildFile.exists() )
{
System.out.println( "Buildfile: " + buildFile + " does not exist!" );
throw new BuildException( "Build failed" );
}

// make sure it's not a directory (this falls into the ultra
// paranoid lets check everything catagory

if( buildFile.isDirectory() )
{
System.out.println( "What? Buildfile: " + buildFile + " is a dir!" );
throw new BuildException( "Build failed" );
}

readyToRun = true;
}

public static synchronized String getAntVersion()
throws BuildException
{
if( antVersion == null )
{
try
{
Properties props = new Properties();
InputStream in =
Main.class.getResourceAsStream( "/org/apache/tools/ant/version.txt" );
props.load( in );
in.close();

String lSep = System.getProperty( "line.separator" );
StringBuffer msg = new StringBuffer();
msg.append( "Apache Ant version " );
msg.append( props.getProperty( "VERSION" ) );
msg.append( " compiled on " );
msg.append( props.getProperty( "DATE" ) );
antVersion = msg.toString();
}
catch( IOException ioe )
{
throw new BuildException( "Could not load the version information:"
+ ioe.getMessage() );
}
catch( NullPointerException npe )
{
throw new BuildException( "Could not load the version information." );
}
}
return antVersion;
}


/**
* Command line entry point. This method kicks off the building of a project
* object and executes a build using either a given target or the default
* target.
*
* @param args Command line args.
*/
public static void main( String[] args )
{
start( args, null, null );
}

/**
* Entry point method.
*
* @param args Description of Parameter
* @param additionalUserProperties Description of Parameter
* @param coreLoader Description of Parameter
*/
public static void start( String[] args, Properties additionalUserProperties,
ClassLoader coreLoader )
{
Main m = null;

try
{
m = new Main( args );
}
catch( Throwable exc )
{
printMessage( exc );
System.exit( 1 );
}

if( additionalUserProperties != null )
{
for( Enumeration e = additionalUserProperties.keys(); e.hasMoreElements(); )
{
String key = ( String )e.nextElement();
String property = additionalUserProperties.getProperty( key );
m.definedProps.put( key, property );
}
}

try
{
m.runBuild( coreLoader );
System.exit( 0 );
}
catch( BuildException be )
{
if( m.err != System.err )
{
printMessage( be );
}
System.exit( 1 );
}
catch( Throwable exc )
{
exc.printStackTrace();
printMessage( exc );
System.exit( 1 );
}
}

/**
* Search for the insert position to keep names a sorted list of Strings
*
* @param names Description of Parameter
* @param name Description of Parameter
* @return Description of the Returned Value
*/
private static int findTargetPosition( Vector names, String name )
{
int res = names.size();
for( int i = 0; i < names.size() && res == names.size(); i++ )
{
if( name.compareTo( ( String )names.elementAt( i ) ) < 0 )
{
res = i;
}
}
return res;
}

/**
* Print the project description, if any
*
* @param project Description of Parameter
*/
private static void printDescription( Project project )
{
if( project.getDescription() != null )
{
System.out.println( project.getDescription() );
}
}

/**
* Prints the message of the Throwable if it's not null.
*
* @param t Description of Parameter
*/
private static void printMessage( Throwable t )
{
String message = t.getMessage();
if( message != null )
{
System.err.println( message );
}
}

/**
* Print out a list of all targets in the current buildfile
*
* @param project Description of Parameter
* @param printSubTargets Description of Parameter
*/
private static void printTargets( Project project, boolean printSubTargets )
{
// find the target with the longest name
int maxLength = 0;
Enumeration ptargets = project.getTargets().elements();
String targetName;
String targetDescription;
Target currentTarget;
// split the targets in top-level and sub-targets depending
// on the presence of a description
Vector topNames = new Vector();
Vector topDescriptions = new Vector();
Vector subNames = new Vector();

while( ptargets.hasMoreElements() )
{
currentTarget = ( Target )ptargets.nextElement();
targetName = currentTarget.getName();
targetDescription = currentTarget.getDescription();
// maintain a sorted list of targets
if( targetDescription == null )
{
int pos = findTargetPosition( subNames, targetName );
subNames.insertElementAt( targetName, pos );
}
else
{
int pos = findTargetPosition( topNames, targetName );
topNames.insertElementAt( targetName, pos );
topDescriptions.insertElementAt( targetDescription, pos );
if( targetName.length() > maxLength )
{
maxLength = targetName.length();
}
}
}

printTargets( topNames, topDescriptions, "Main targets:", maxLength );

if( printSubTargets )
{
printTargets( subNames, null, "Subtargets:", 0 );
}

String defaultTarget = project.getDefaultTarget();
if( defaultTarget != null && !"".equals( defaultTarget ) )
{// shouldn't need to check but...
System.out.println( "Default target: " + defaultTarget );
}
}

/**
* Output a formatted list of target names with an optional description
*
* @param names Description of Parameter
* @param descriptions Description of Parameter
* @param heading Description of Parameter
* @param maxlen Description of Parameter
*/
private static void printTargets( Vector names, Vector descriptions, String heading, int maxlen )
{
// now, start printing the targets and their descriptions
String lSep = System.getProperty( "line.separator" );
// got a bit annoyed that I couldn't find a pad function
String spaces = " ";
while( spaces.length() < maxlen )
{
spaces += spaces;
}
StringBuffer msg = new StringBuffer();
msg.append( heading + lSep + lSep );
for( int i = 0; i < names.size(); i++ )
{
msg.append( " " );
msg.append( names.elementAt( i ) );
if( descriptions != null )
{
msg.append( spaces.substring( 0, maxlen - ( ( String )names.elementAt( i ) ).length() + 2 ) );
msg.append( descriptions.elementAt( i ) );
}
msg.append( lSep );
}
System.out.println( msg.toString() );
}

/**
* Prints the usage of how to use this class to System.out
*/
private static void printUsage()
{
String lSep = System.getProperty( "line.separator" );
StringBuffer msg = new StringBuffer();
msg.append( "ant [options] [target [target2 [target3] ...]]" + lSep );
msg.append( "Options: " + lSep );
msg.append( " -help print this message" + lSep );
msg.append( " -projecthelp print project help information" + lSep );
msg.append( " -version print the version information and exit" + lSep );
msg.append( " -quiet be extra quiet" + lSep );
msg.append( " -verbose be extra verbose" + lSep );
msg.append( " -debug print debugging information" + lSep );
msg.append( " -emacs produce logging information without adornments" + lSep );
msg.append( " -logfile <file> use given file for log" + lSep );
msg.append( " -logger <classname> the class which is to perform logging" + lSep );
msg.append( " -listener <classname> add an instance of class as a project listener" + lSep );
msg.append( " -buildfile <file> use given buildfile" + lSep );
msg.append( " -D<property>=<value> use value for given property" + lSep );
msg.append( " -find <file> search for buildfile towards the root of the" + lSep );
msg.append( " filesystem and use it" + lSep );
System.out.println( msg.toString() );
}

private static void printVersion()
throws BuildException
{
System.out.println( getAntVersion() );
}

protected void addBuildListeners( Project project )
{

// Add the default listener
project.addBuildListener( createLogger() );

for( int i = 0; i < listeners.size(); i++ )
{
String className = ( String )listeners.elementAt( i );
try
{
BuildListener listener =
( BuildListener )Class.forName( className ).newInstance();
project.addBuildListener( listener );
}
catch( Throwable exc )
{
throw new BuildException( "Unable to instantiate listener " + className, exc );
}
}
}

/**
* Helper to get the parent file for a given file. <P>
*
* Added to simulate File.getParentFile() from JDK 1.2.
*
* @param file File
* @return Parent file or null if none
*/
private File getParentFile( File file )
{
String filename = file.getAbsolutePath();
file = new File( filename );
filename = file.getParent();

if( filename != null && msgOutputLevel >= Project.MSG_VERBOSE )
{
System.out.println( "Searching in " + filename );
}

return ( filename == null ) ? null : new File( filename );
}

/**
* Creates the default build logger for sending build events to the ant log.
*
* @return Description of the Returned Value
*/
private BuildLogger createLogger()
{
BuildLogger logger = null;
if( loggerClassname != null )
{
try
{
logger = ( BuildLogger )( Class.forName( loggerClassname ).newInstance() );
}
catch( ClassCastException e )
{
System.err.println( "The specified logger class " + loggerClassname +
" does not implement the BuildLogger interface" );
throw new RuntimeException();
}
catch( Exception e )
{
System.err.println( "Unable to instantiate specified logger class " +
loggerClassname + " : " + e.getClass().getName() );
throw new RuntimeException();
}
}
else
{
logger = new DefaultLogger();
}

logger.setMessageOutputLevel( msgOutputLevel );
logger.setOutputPrintStream( out );
logger.setErrorPrintStream( err );
logger.setEmacsMode( emacsMode );

return logger;
}

/**
* Search parent directories for the build file. <P>
*
* Takes the given target as a suffix to append to each parent directory in
* seach of a build file. Once the root of the file-system has been reached
* an exception is thrown.
*
* @param suffix Suffix filename to look for in parents.
* @param start Description of Parameter
* @return A handle to the build file
* @exception BuildException Failed to locate a build file
*/
private File findBuildFile( String start, String suffix )
throws BuildException
{
if( msgOutputLevel >= Project.MSG_INFO )
{
System.out.println( "Searching for " + suffix + " ..." );
}

File parent = new File( new File( start ).getAbsolutePath() );
File file = new File( parent, suffix );

// check if the target file exists in the current directory
while( !file.exists() )
{
// change to parent directory
parent = getParentFile( parent );

// if parent is null, then we are at the root of the fs,
// complain that we can't find the build file.
if( parent == null )
{
throw new BuildException( "Could not locate a build file!" );
}

// refresh our file handle
file = new File( parent, suffix );
}

return file;
}

/**
* Executes the build.
*
* @param coreLoader Description of Parameter
* @exception BuildException Description of Exception
*/
private void runBuild( ClassLoader coreLoader )
throws BuildException
{

if( !readyToRun )
{
return;
}

// track when we started

if( msgOutputLevel >= Project.MSG_INFO )
{
System.out.println( "Buildfile: " + buildFile );
}

final Project project = new Project();
project.setCoreLoader( coreLoader );

Throwable error = null;

try
{
addBuildListeners( project );

PrintStream err = System.err;
PrintStream out = System.out;

// use a system manager that prevents from System.exit()
// only in JDK > 1.1
SecurityManager oldsm = null;
if( !Project.JAVA_1_0.equals( Project.getJavaVersion() ) &&
!Project.JAVA_1_1.equals( Project.getJavaVersion() ) )
{
oldsm = System.getSecurityManager();

//SecurityManager can not be installed here for backwards
//compatability reasons (PD). Needs to be loaded prior to
//ant class if we are going to implement it.
//System.setSecurityManager(new NoExitSecurityManager());
}
try
{
System.setOut( new PrintStream( new DemuxOutputStream( project, false ) ) );
System.setErr( new PrintStream( new DemuxOutputStream( project, true ) ) );

if( !projectHelp )
{
project.fireBuildStarted();
}
project.init();
project.setUserProperty( "ant.version", getAntVersion() );

// set user-define properties
Enumeration e = definedProps.keys();
while( e.hasMoreElements() )
{
String arg = ( String )e.nextElement();
String value = ( String )definedProps.get( arg );
project.setUserProperty( arg, value );
}

project.setUserProperty( "ant.file", buildFile.getAbsolutePath() );

// first use the ProjectHelper to create the project object
// from the given build file.
String noParserMessage =
"No JAXP compliant XML parser found. Please visit http://xml.apache.org for a suitable parser";
try
{
Class.forName( "javax.xml.parsers.SAXParserFactory" );
ProjectHelper.configureProject( project, buildFile );
}
catch( NoClassDefFoundError ncdfe )
{
throw new BuildException( noParserMessage, ncdfe );
}
catch( ClassNotFoundException cnfe )
{
throw new BuildException( noParserMessage, cnfe );
}
catch( NullPointerException npe )
{
throw new BuildException( noParserMessage, npe );
}

if( projectHelp )
{
printDescription( project );
printTargets( project, msgOutputLevel > Project.MSG_INFO );
return;
}

// make sure that we have a target to execute
if( targets.size() == 0 )
{
targets.addElement( project.getDefaultTarget() );
}

project.executeTargets( targets );
}
finally
{
// put back the original security manager
//The following will never eval to true. (PD)
if( oldsm != null )
{
System.setSecurityManager( oldsm );
}

System.setOut( out );
System.setErr( err );
}
}
catch( RuntimeException exc )
{
error = exc;
throw exc;
}
catch( Error err )
{
error = err;
throw err;
}
finally
{
if( !projectHelp )
{
project.fireBuildFinished( error );
}
}
}
}

+ 50
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/NoBannerLogger.java View File

@@ -0,0 +1,50 @@
/*
* 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.tools.ant;

/**
* Extends DefaultLogger to strip out empty targets.
*
* @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
*/
public class NoBannerLogger extends DefaultLogger
{

private final static String lSep = System.getProperty( "line.separator" );

protected String targetName;

public void messageLogged( BuildEvent event )
{

if( event.getPriority() > msgOutputLevel ||
null == event.getMessage() ||
"".equals( event.getMessage().trim() ) )
{
return;
}

if( null != targetName )
{
out.println( lSep + targetName + ":" );
targetName = null;
}

super.messageLogged( event );
}

public void targetFinished( BuildEvent event )
{
targetName = null;
}

public void targetStarted( BuildEvent event )
{
targetName = event.getTarget().getName();
}
}

+ 90
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/PathTokenizer.java View File

@@ -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.tools.ant;
import java.io.File;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;

/**
* A Path tokenizer takes a path and returns the components that make up that
* path. The path can use path separators of either ':' or ';' and file
* separators of either '/' or '\'
*
* @author Conor MacNeill (conor@ieee.org)
*/
public class PathTokenizer
{

/**
* A String which stores any path components which have been read ahead.
*/
private String lookahead = null;

/**
* Flag to indicate whether we are running on a platform with a DOS style
* filesystem
*/
private boolean dosStyleFilesystem;
/**
* A tokenizer to break the string up based on the ':' or ';' separators.
*/
private StringTokenizer tokenizer;

public PathTokenizer( String path )
{
tokenizer = new StringTokenizer( path, ":;", false );
dosStyleFilesystem = File.pathSeparatorChar == ';';
}

public boolean hasMoreTokens()
{
if( lookahead != null )
{
return true;
}

return tokenizer.hasMoreTokens();
}

public String nextToken()
throws NoSuchElementException
{
String token = null;
if( lookahead != null )
{
token = lookahead;
lookahead = null;
}
else
{
token = tokenizer.nextToken().trim();
}

if( token.length() == 1 && Character.isLetter( token.charAt( 0 ) )
&& dosStyleFilesystem
&& tokenizer.hasMoreTokens() )
{
// we are on a dos style system so this path could be a drive
// spec. We look at the next token
String nextToken = tokenizer.nextToken().trim();
if( nextToken.startsWith( "\\" ) || nextToken.startsWith( "/" ) )
{
// we know we are on a DOS style platform and the next path starts with a
// slash or backslash, so we know this is a drive spec
token += ":" + nextToken;
}
else
{
// store the token just read for next time
lookahead = nextToken;
}
}

return token;
}
}

+ 1575
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/Project.java
File diff suppressed because it is too large
View File


+ 71
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/ProjectComponent.java View File

@@ -0,0 +1,71 @@
/*
* 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.tools.ant;

import org.apache.myrmidon.api.AbstractTask;

/**
* Base class for components of a project, including tasks and data types.
* Provides common facilities.
*
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
*/

public abstract class ProjectComponent
extends AbstractTask
{
protected Project project = null;

/**
* Sets the project object of this component. This method is used by project
* when a component is added to it so that the component has access to the
* functions of the project. It should not be used for any other purpose.
*
* @param project Project in whose scope this component belongs.
*/
public void setProject( Project project )
{
this.project = project;
}

/**
* Get the Project to which this component belongs
*
* @return the components's project.
*/
public Project getProject()
{
return project;
}

/**
* Log a message with the default (INFO) priority.
*
* @param msg Description of Parameter
*/
public void log( String msg )
{
log( msg, Project.MSG_INFO );
}

/**
* Log a mesage with the give priority.
*
* @param msgLevel the message priority at which this message is to be
* logged.
* @param msg Description of Parameter
*/
public void log( String msg, int msgLevel )
{
if( project != null )
{
project.log( msg, msgLevel );
}
}
}


+ 1061
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/ProjectHelper.java
File diff suppressed because it is too large
View File


+ 159
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/RuntimeConfigurable.java View File

@@ -0,0 +1,159 @@
/*
* 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.tools.ant;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Vector;
import org.xml.sax.AttributeList;
import org.xml.sax.helpers.AttributeListImpl;

/**
* Wrapper class that holds the attributes of a Task (or elements nested below
* that level) and takes care of configuring that element at runtime.
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/
public class RuntimeConfigurable
{

private String elementTag = null;
private Vector children = new Vector();
private Object wrappedObject = null;
private StringBuffer characters = new StringBuffer();
private AttributeList attributes;

/**
* @param proxy The element to wrap.
* @param elementTag Description of Parameter
*/
public RuntimeConfigurable( Object proxy, String elementTag )
{
wrappedObject = proxy;
this.elementTag = elementTag;
}

/**
* Set's the attributes for the wrapped element.
*
* @param attributes The new Attributes value
*/
public void setAttributes( AttributeList attributes )
{
this.attributes = new AttributeListImpl( attributes );
}

/**
* Returns the AttributeList of the wrapped element.
*
* @return The Attributes value
*/
public AttributeList getAttributes()
{
return attributes;
}

public String getElementTag()
{
return elementTag;
}

/**
* Adds child elements to the wrapped element.
*
* @param child The feature to be added to the Child attribute
*/
public void addChild( RuntimeConfigurable child )
{
children.addElement( child );
}

/**
* Add characters from #PCDATA areas to the wrapped element.
*
* @param data The feature to be added to the Text attribute
*/
public void addText( String data )
{
characters.append( data );
}

/**
* Add characters from #PCDATA areas to the wrapped element.
*
* @param buf The feature to be added to the Text attribute
* @param start The feature to be added to the Text attribute
* @param end The feature to be added to the Text attribute
*/
public void addText( char[] buf, int start, int end )
{
addText( new String( buf, start, end ) );
}


/**
* Configure the wrapped element and all children.
*
* @param p Description of Parameter
* @exception BuildException Description of Exception
*/
public void maybeConfigure( Project p )
throws BuildException
{
String id = null;

if( attributes != null )
{
ProjectHelper.configure( wrappedObject, attributes, p );
id = attributes.getValue( "id" );
attributes = null;
}
if( characters.length() != 0 )
{
ProjectHelper.addText( p, wrappedObject, characters.toString() );
characters.setLength( 0 );
}
Enumeration enum = children.elements();
while( enum.hasMoreElements() )
{
RuntimeConfigurable child = ( RuntimeConfigurable )enum.nextElement();
if( child.wrappedObject instanceof Task )
{
Task childTask = ( Task )child.wrappedObject;
childTask.setRuntimeConfigurableWrapper( child );
childTask.maybeConfigure();
}
else
{
child.maybeConfigure( p );
}
ProjectHelper.storeChild( p, wrappedObject, child.wrappedObject, child.getElementTag().toLowerCase( Locale.US ) );
}

if( id != null )
{
p.addReference( id, wrappedObject );
}
}

void setProxy( Object proxy )
{
wrappedObject = proxy;
}

/**
* Returns the child with index <code>index</code>.
*
* @param index Description of Parameter
* @return The Child value
*/
RuntimeConfigurable getChild( int index )
{
return ( RuntimeConfigurable )children.elementAt( index );
}

}

+ 232
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/Target.java View File

@@ -0,0 +1,232 @@
/*
* 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.tools.ant;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;

/**
* This class implements a target object with required parameters.
*
* @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a>
*/

public class Target implements TaskContainer
{
private String ifCondition = "";
private String unlessCondition = "";
private Vector dependencies = new Vector( 2 );
private Vector children = new Vector( 5 );
private String description = null;

private String name;
private Project project;

public void setDepends( String depS )
{
if( depS.length() > 0 )
{
StringTokenizer tok =
new StringTokenizer( depS, ",", true );
while( tok.hasMoreTokens() )
{
String token = tok.nextToken().trim();

//Make sure the dependency is not empty string
if( token.equals( "" ) || token.equals( "," ) )
{
throw new BuildException( "Syntax Error: Depend attribute " +
"for target \"" + getName() +
"\" has an empty string for dependency." );
}

addDependency( token );

//Make sure that depends attribute does not
//end in a ,
if( tok.hasMoreTokens() )
{
token = tok.nextToken();
if( !tok.hasMoreTokens() || !token.equals( "," ) )
{
throw new BuildException( "Syntax Error: Depend attribute " +
"for target \"" + getName() +
"\" ends with a , character" );
}
}
}
}
}

public void setDescription( String description )
{
this.description = description;
}

public void setIf( String property )
{
this.ifCondition = ( property == null ) ? "" : property;
}

public void setName( String name )
{
this.name = name;
}

public void setProject( Project project )
{
this.project = project;
}

public void setUnless( String property )
{
this.unlessCondition = ( property == null ) ? "" : property;
}

public Enumeration getDependencies()
{
return dependencies.elements();
}

public String getDescription()
{
return description;
}

public String getName()
{
return name;
}

public Project getProject()
{
return project;
}

/**
* Get the current set of tasks to be executed by this target.
*
* @return The current set of tasks.
*/
public Task[] getTasks()
{
Vector tasks = new Vector( children.size() );
Enumeration enum = children.elements();
while( enum.hasMoreElements() )
{
Object o = enum.nextElement();
if( o instanceof Task )
{
tasks.addElement( o );
}
}

Task[] retval = new Task[tasks.size()];
tasks.copyInto( retval );
return retval;
}

public final void performTasks()
{
try
{
project.fireTargetStarted( this );
execute();
project.fireTargetFinished( this, null );
}
catch( RuntimeException exc )
{
project.fireTargetFinished( this, exc );
throw exc;
}
}

public void addDataType( RuntimeConfigurable r )
{
children.addElement( r );
}

public void addDependency( String dependency )
{
dependencies.addElement( dependency );
}

public void addTask( Task task )
{
children.addElement( task );
}

public void execute()
throws BuildException
{
if( testIfCondition() && testUnlessCondition() )
{
Enumeration enum = children.elements();
while( enum.hasMoreElements() )
{
Object o = enum.nextElement();
if( o instanceof Task )
{
Task task = ( Task )o;
task.perform();
}
else
{
RuntimeConfigurable r = ( RuntimeConfigurable )o;
r.maybeConfigure( project );
}
}
}
else if( !testIfCondition() )
{
project.log( this, "Skipped because property '" + this.ifCondition + "' not set.",
Project.MSG_VERBOSE );
}
else
{
project.log( this, "Skipped because property '" + this.unlessCondition + "' set.",
Project.MSG_VERBOSE );
}
}

public String toString()
{
return name;
}

void replaceChild( Task el, Object o )
{
int index = -1;
while( ( index = children.indexOf( el ) ) >= 0 )
{
children.setElementAt( o, index );
}
}

private boolean testIfCondition()
{
if( "".equals( ifCondition ) )
{
return true;
}

String test = project.replaceProperties( ifCondition );
return project.getProperty( test ) != null;
}

private boolean testUnlessCondition()
{
if( "".equals( unlessCondition ) )
{
return true;
}
String test = project.replaceProperties( unlessCondition );
return project.getProperty( test ) == null;
}

}

+ 281
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/Task.java View File

@@ -0,0 +1,281 @@
/*
* 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.tools.ant;

import org.apache.myrmidon.api.TaskException;

public abstract class Task
extends ProjectComponent
implements org.apache.myrmidon.api.Task
{
protected Target target;
protected String description;
protected Location location = Location.UNKNOWN_LOCATION;
protected String taskName;
protected String taskType;
private boolean invalid;
protected RuntimeConfigurable wrapper;

private UnknownElement replacement;

/**
* Sets a description of the current action. It will be usefull in
* commenting what we are doing.
*
* @param desc The new Description value
*/
public void setDescription( String desc )
{
description = desc;
}

/**
* Sets the file location where this task was defined.
*
* @param location The new Location value
*/
public void setLocation( Location location )
{
this.location = location;
}

/**
* Sets the target object of this task.
*
* @param target Target in whose scope this task belongs.
*/
public void setOwningTarget( Target target )
{
this.target = target;
}

/**
* Set the name to use in logging messages.
*
* @param name the name to use in logging messages.
*/
public void setTaskName( String name )
{
this.taskName = name;
}

public String getDescription()
{
return description;
}

/**
* Returns the file location where this task was defined.
*
* @return The Location value
*/
public Location getLocation()
{
return location;
}

/**
* Get the Target to which this task belongs
*
* @return the task's target.
*/
public Target getOwningTarget()
{
return target;
}

/**
* Returns the wrapper class for runtime configuration.
*
* @return The RuntimeConfigurableWrapper value
*/
public RuntimeConfigurable getRuntimeConfigurableWrapper()
{
if( wrapper == null )
{
wrapper = new RuntimeConfigurable( this, getTaskName() );
}
return wrapper;
}

/**
* Get the name to use in logging messages.
*
* @return the name to use in logging messages.
*/
public String getTaskName()
{
return taskName;
}

/**
* Perform this task
*/
public final void perform()
throws TaskException
{
if( !invalid )
{
try
{
project.fireTaskStarted( this );
maybeConfigure();
execute();
project.fireTaskFinished( this, null );
}
catch( TaskException te )
{
if( te instanceof BuildException )
{
BuildException be = (BuildException)te;
if( be.getLocation() == Location.UNKNOWN_LOCATION )
{
be.setLocation( getLocation() );
}
}
project.fireTaskFinished( this, te );
throw te;
}
catch( RuntimeException re )
{
project.fireTaskFinished( this, re );
throw re;
}
}
else
{
UnknownElement ue = getReplacement();
Task task = ue.getTask();
task.perform();
}
}

/**
* Called by the project to let the task do it's work. This method may be
* called more than once, if the task is invoked more than once. For
* example, if target1 and target2 both depend on target3, then running "ant
* target1 target2" will run all tasks in target3 twice.
*
* @throws BuildException if someting goes wrong with the build
*/
public void execute()
throws TaskException
{
}

/**
* Called by the project to let the task initialize properly.
*
* @throws BuildException if someting goes wrong with the build
*/
public void init()
throws TaskException
{
}

/**
* Log a message with the default (INFO) priority.
*
* @param msg Description of Parameter
*/
public void log( String msg )
{
log( msg, Project.MSG_INFO );
}

/**
* Log a mesage with the give priority.
*
* @param msgLevel the message priority at which this message is to be
* logged.
* @param msg Description of Parameter
*/
public void log( String msg, int msgLevel )
{
project.log( this, msg, msgLevel );
}

/**
* Configure this task - if it hasn't been done already.
*
* @exception BuildException Description of Exception
*/
public void maybeConfigure()
throws TaskException
{
if( !invalid )
{
if( wrapper != null )
{
wrapper.maybeConfigure( project );
}
}
else
{
getReplacement();
}
}

protected void setRuntimeConfigurableWrapper( RuntimeConfigurable wrapper )
{
this.wrapper = wrapper;
}

protected void handleErrorOutput( String line )
{
log( line, Project.MSG_ERR );
}

protected void handleOutput( String line )
{
log( line, Project.MSG_INFO );
}

/**
* Set the name with which the task has been invoked.
*
* @param type the name the task has been invoked as.
*/
void setTaskType( String type )
{
this.taskType = type;
}

/**
* Mark this task as invalid.
*/
final void markInvalid()
{
invalid = true;
}

/**
* Create an UnknownElement that can be used to replace this task.
*
* @return The Replacement value
*/
private UnknownElement getReplacement()
throws TaskException
{
if( replacement == null )
{
replacement = new UnknownElement( taskType );
replacement.setProject( project );
replacement.setTaskType( taskType );
replacement.setTaskName( taskName );
replacement.setLocation( location );
replacement.setOwningTarget( target );
replacement.setRuntimeConfigurableWrapper( wrapper );
wrapper.setProxy( replacement );
target.replaceChild( this, replacement );
replacement.maybeConfigure();
}
return replacement;
}
}


+ 127
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/TaskAdapter.java View File

@@ -0,0 +1,127 @@
/*
* 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.tools.ant;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;


/**
* Use introspection to "adapt" an arbitrary Bean ( not extending Task, but with
* similar patterns).
*
* @author costin@dnt.ro
*/
public class TaskAdapter extends Task
{

Object proxy;

/**
* Checks a class, whether it is suitable to be adapted by TaskAdapter.
* Checks conditions only, which are additionally required for a tasks
* adapted by TaskAdapter. Thus, this method should be called by {@link
* Project#checkTaskClass}. Throws a BuildException and logs as
* Project.MSG_ERR for conditions, that will cause the task execution to
* fail. Logs other suspicious conditions with Project.MSG_WARN.
*
* @param taskClass Description of Parameter
* @param project Description of Parameter
*/
public static void checkTaskClass( final Class taskClass, final Project project )
{
// don't have to check for interface, since then
// taskClass would be abstract too.
try
{
final Method executeM = taskClass.getMethod( "execute", null );
// don't have to check for public, since
// getMethod finds public method only.
// don't have to check for abstract, since then
// taskClass would be abstract too.
if( !Void.TYPE.equals( executeM.getReturnType() ) )
{
final String message = "return type of execute() should be void but was \"" + executeM.getReturnType() + "\" in " + taskClass;
project.log( message, Project.MSG_WARN );
}
}
catch( NoSuchMethodException e )
{
final String message = "No public execute() in " + taskClass;
project.log( message, Project.MSG_ERR );
throw new BuildException( message );
}
}

/**
* Set the target object class
*
* @param o The new Proxy value
*/
public void setProxy( Object o )
{
this.proxy = o;
}

public Object getProxy()
{
return this.proxy;
}

/**
* Do the execution.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
Method setProjectM = null;
try
{
Class c = proxy.getClass();
setProjectM =
c.getMethod( "setProject", new Class[]{Project.class} );
if( setProjectM != null )
{
setProjectM.invoke( proxy, new Object[]{project} );
}
}
catch( NoSuchMethodException e )
{
// ignore this if the class being used as a task does not have
// a set project method.
}
catch( Exception ex )
{
log( "Error setting project in " + proxy.getClass(),
Project.MSG_ERR );
throw new BuildException( ex );
}

Method executeM = null;
try
{
Class c = proxy.getClass();
executeM = c.getMethod( "execute", new Class[0] );
if( executeM == null )
{
log( "No public execute() in " + proxy.getClass(), Project.MSG_ERR );
throw new BuildException( "No public execute() in " + proxy.getClass() );
}
executeM.invoke( proxy, null );
return;
}
catch( Exception ex )
{
log( "Error in " + proxy.getClass(), Project.MSG_ERR );
throw new BuildException( ex );
}

}

}

+ 28
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/TaskContainer.java View File

@@ -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.tools.ant;

/**
* Interface for objects which can contain tasks <p>
*
* It is recommended that implementations call {@link Task#perform perform}
* instead of {@link Task#execute execute} for the tasks they contain, as this
* method ensures that {@link BuildEvent BuildEvents} will be generated.</p>
*
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
*/
public interface TaskContainer
{
/**
* Add a task to this task container
*
* @param task the task to be added to this container
*/
void addTask( Task task );
}


+ 256
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/UnknownElement.java View File

@@ -0,0 +1,256 @@
/*
* 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.tools.ant;
import java.util.Vector;

/**
* Wrapper class that holds all information necessary to create a task or data
* type that did not exist when Ant started.
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/
public class UnknownElement extends Task
{

/**
* Childelements, holds UnknownElement instances.
*/
private Vector children = new Vector();

/**
* Holds the name of the task/type or nested child element of a task/type
* that hasn't been defined at parser time.
*/
private String elementName;

/**
* The real object after it has been loaded.
*/
private Object realThing;

public UnknownElement( String elementName )
{
this.elementName = elementName;
}

/**
* return the corresponding XML element name.
*
* @return The Tag value
*/
public String getTag()
{
return elementName;
}

/**
* Return the task instance after it has been created (and if it is a task.
*
* @return The Task value
*/
public Task getTask()
{
if( realThing != null && realThing instanceof Task )
{
return ( Task )realThing;
}
return null;
}

/**
* Get the name to use in logging messages.
*
* @return the name to use in logging messages.
*/
public String getTaskName()
{
return realThing == null || !( realThing instanceof Task ) ?
super.getTaskName() : ( ( Task )realThing ).getTaskName();
}

/**
* Adds a child element to this element.
*
* @param child The feature to be added to the Child attribute
*/
public void addChild( UnknownElement child )
{
children.addElement( child );
}

/**
* Called when the real task has been configured for the first time.
*/
public void execute()
{
if( realThing == null )
{
// plain impossible to get here, maybeConfigure should
// have thrown an exception.
throw new BuildException( "Could not create task of type: "
+ elementName, location );
}

if( realThing instanceof Task )
{
( ( Task )realThing ).perform();
}
}

/**
* creates the real object instance, creates child elements, configures the
* attributes of the real object.
*
* @exception BuildException Description of Exception
*/
public void maybeConfigure()
throws BuildException
{
realThing = makeObject( this, wrapper );

wrapper.setProxy( realThing );
if( realThing instanceof Task )
{
( ( Task )realThing ).setRuntimeConfigurableWrapper( wrapper );
}

handleChildren( realThing, wrapper );

wrapper.maybeConfigure( project );
if( realThing instanceof Task )
{
target.replaceChild( this, realThing );
}
else
{
target.replaceChild( this, wrapper );
}
}

protected BuildException getNotFoundException( String what,
String elementName )
{
String lSep = System.getProperty( "line.separator" );
String msg = "Could not create " + what + " of type: " + elementName
+ "." + lSep
+ "Ant could not find the task or a class this" + lSep
+ "task relies upon." + lSep
+ "Common solutions are to use taskdef to declare" + lSep
+ "your task, or, if this is an optional task," + lSep
+ "to put the optional.jar and all required libraries of" + lSep
+ "this task in the lib directory of" + lSep
+ "your ant installation (ANT_HOME)." + lSep
+ "There is also the possibility that your build file " + lSep
+ "is written to work with a more recent version of ant " + lSep
+ "than the one you are using, in which case you have to " + lSep
+ "upgrade.";
return new BuildException( msg, location );
}

/**
* Creates child elements, creates children of the children, sets attributes
* of the child elements.
*
* @param parent Description of Parameter
* @param parentWrapper Description of Parameter
* @exception BuildException Description of Exception
*/
protected void handleChildren( Object parent,
RuntimeConfigurable parentWrapper )
throws BuildException
{

if( parent instanceof TaskAdapter )
{
parent = ( ( TaskAdapter )parent ).getProxy();
}

Class parentClass = parent.getClass();
IntrospectionHelper ih = IntrospectionHelper.getHelper( parentClass );

for( int i = 0; i < children.size(); i++ )
{
RuntimeConfigurable childWrapper = parentWrapper.getChild( i );
UnknownElement child = ( UnknownElement )children.elementAt( i );
Object realChild = null;

if( parent instanceof TaskContainer )
{
realChild = makeTask( child, childWrapper, false );
( ( TaskContainer )parent ).addTask( ( Task )realChild );
}
else
{
realChild = ih.createElement( project, parent, child.getTag() );
}

childWrapper.setProxy( realChild );
if( parent instanceof TaskContainer )
{
( ( Task )realChild ).setRuntimeConfigurableWrapper( childWrapper );
}

child.handleChildren( realChild, childWrapper );

if( parent instanceof TaskContainer )
{
( ( Task )realChild ).maybeConfigure();
}
}
}

/**
* Creates a named task or data type - if it is a task, configure it up to
* the init() stage.
*
* @param ue Description of Parameter
* @param w Description of Parameter
* @return Description of the Returned Value
*/
protected Object makeObject( UnknownElement ue, RuntimeConfigurable w )
{
Object o = makeTask( ue, w, true );
if( o == null )
{
o = project.createDataType( ue.getTag() );
}
if( o == null )
{
throw getNotFoundException( "task or type", ue.getTag() );
}
return o;
}

/**
* Create a named task and configure it up to the init() stage.
*
* @param ue Description of Parameter
* @param w Description of Parameter
* @param onTopLevel Description of Parameter
* @return Description of the Returned Value
*/
protected Task makeTask( UnknownElement ue, RuntimeConfigurable w,
boolean onTopLevel )
{
Task task = project.createTask( ue.getTag() );
if( task == null && !onTopLevel )
{
throw getNotFoundException( "task", ue.getTag() );
}

if( task != null )
{
task.setLocation( getLocation() );
// UnknownElement always has an associated target
task.setOwningTarget( target );
task.init();
}
return task;
}

}// UnknownElement

+ 3
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/defaultManifest.mf View File

@@ -0,0 +1,3 @@
Manifest-Version: 1.0
Created-By: Apache Ant @VERSION@


+ 550
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Ant.java View File

@@ -0,0 +1,550 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.BuildListener;
import org.apache.tools.ant.DefaultLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectComponent;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.util.FileUtils;

/**
* Call Ant in a sub-project <pre>
* &lt;target name=&quot;foo&quot; depends=&quot;init&quot;&gt;
* &lt;ant antfile=&quot;build.xml&quot; target=&quot;bar&quot; &gt;
* &lt;property name=&quot;property1&quot; value=&quot;aaaaa&quot; /&gt;
* &lt;property name=&quot;foo&quot; value=&quot;baz&quot; /&gt;
* &lt;/ant&gt;</SPAN> &lt;/target&gt;</SPAN> &lt;target name=&quot;bar&quot;
* depends=&quot;init&quot;&gt; &lt;echo message=&quot;prop is ${property1}
* ${foo}&quot; /&gt; &lt;/target&gt; </pre>
*
* @author costin@dnt.ro
*/
public class Ant extends Task
{

/**
* the basedir where is executed the build file
*/
private File dir = null;

/**
* the build.xml file (can be absolute) in this case dir will be ignored
*/
private String antFile = null;

/**
* the target to call if any
*/
private String target = null;

/**
* the output
*/
private String output = null;

/**
* should we inherit properties from the parent ?
*/
private boolean inheritAll = true;

/**
* should we inherit references from the parent ?
*/
private boolean inheritRefs = false;

/**
* the properties to pass to the new project
*/
private Vector properties = new Vector();

/**
* the references to pass to the new project
*/
private Vector references = new Vector();

/**
* the temporary project created to run the build file
*/
private Project newProject;

/**
* set the build file, it can be either absolute or relative. If it is
* absolute, <tt>dir</tt> will be ignored, if it is relative it will be
* resolved relative to <tt>dir</tt> .
*
* @param s The new Antfile value
*/
public void setAntfile( String s )
{
// @note: it is a string and not a file to handle relative/absolute
// otherwise a relative file will be resolved based on the current
// basedir.
this.antFile = s;
}

/**
* ...
*
* @param d The new Dir value
*/
public void setDir( File d )
{
this.dir = d;
}

/**
* If true, inherit all properties from parent Project If false, inherit
* only userProperties and those defined inside the ant call itself
*
* @param value The new InheritAll value
*/
public void setInheritAll( boolean value )
{
inheritAll = value;
}

/**
* If true, inherit all references from parent Project If false, inherit
* only those defined inside the ant call itself
*
* @param value The new InheritRefs value
*/
public void setInheritRefs( boolean value )
{
inheritRefs = value;
}

public void setOutput( String s )
{
this.output = s;
}

/**
* set the target to execute. If none is defined it will execute the default
* target of the build file
*
* @param s The new Target value
*/
public void setTarget( String s )
{
this.target = s;
}

/**
* create a reference element that identifies a data type that should be
* carried over to the new project.
*
* @param r The feature to be added to the Reference attribute
*/
public void addReference( Reference r )
{
references.addElement( r );
}

/**
* create a property to pass to the new project as a 'user property'
*
* @return Description of the Returned Value
*/
public Property createProperty()
{
if( newProject == null )
{
reinit();
}
Property p = new Property( true );
p.setProject( newProject );
p.setTaskName( "property" );
properties.addElement( p );
return p;
}

/**
* Do the execution.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
try
{
if( newProject == null )
{
reinit();
}

if( ( dir == null ) && ( inheritAll == true ) )
{
dir = project.getBaseDir();
}

initializeProject();

if( dir != null )
{
newProject.setBaseDir( dir );
newProject.setUserProperty( "basedir", dir.getAbsolutePath() );
}
else
{
dir = project.getBaseDir();
}

overrideProperties();

if( antFile == null )
{
antFile = "build.xml";
}

File file = FileUtils.newFileUtils().resolveFile( dir, antFile );
antFile = file.getAbsolutePath();

newProject.setUserProperty( "ant.file", antFile );
ProjectHelper.configureProject( newProject, new File( antFile ) );

if( target == null )
{
target = newProject.getDefaultTarget();
}

addReferences();

// Are we trying to call the target in which we are defined?
if( newProject.getBaseDir().equals( project.getBaseDir() ) &&
newProject.getProperty( "ant.file" ).equals( project.getProperty( "ant.file" ) ) &&
getOwningTarget() != null &&
target.equals( this.getOwningTarget().getName() ) )
{

throw new BuildException( "ant task calling its own parent target" );
}

newProject.executeTarget( target );
}
finally
{
// help the gc
newProject = null;
}
}

public void init()
{
newProject = new Project();
newProject.setJavaVersionProperty();
newProject.addTaskDefinition( "property",
( Class )project.getTaskDefinitions().get( "property" ) );
}

protected void handleErrorOutput( String line )
{
if( newProject != null )
{
newProject.demuxOutput( line, true );
}
else
{
super.handleErrorOutput( line );
}
}

protected void handleOutput( String line )
{
if( newProject != null )
{
newProject.demuxOutput( line, false );
}
else
{
super.handleOutput( line );
}
}

/**
* Add the references explicitly defined as nested elements to the new
* project. Also copy over all references that don't override existing
* references in the new project if inheritall has been requested.
*
* @exception BuildException Description of Exception
*/
private void addReferences()
throws BuildException
{
Hashtable thisReferences = ( Hashtable )project.getReferences().clone();
Hashtable newReferences = newProject.getReferences();
Enumeration e;
if( references.size() > 0 )
{
for( e = references.elements(); e.hasMoreElements(); )
{
Reference ref = ( Reference )e.nextElement();
String refid = ref.getRefId();
if( refid == null )
{
throw new BuildException( "the refid attribute is required for reference elements" );
}
if( !thisReferences.containsKey( refid ) )
{
log( "Parent project doesn't contain any reference '"
+ refid + "'",
Project.MSG_WARN );
continue;
}

Object o = thisReferences.remove( refid );
String toRefid = ref.getToRefid();
if( toRefid == null )
{
toRefid = refid;
}
copyReference( refid, toRefid );
}
}

// Now add all references that are not defined in the
// subproject, if inheritRefs is true
if( inheritRefs )
{
for( e = thisReferences.keys(); e.hasMoreElements(); )
{
String key = ( String )e.nextElement();
if( newReferences.containsKey( key ) )
{
continue;
}
copyReference( key, key );
}
}
}

/**
* Try to clone and reconfigure the object referenced by oldkey in the
* parent project and add it to the new project with the key newkey. <p>
*
* If we cannot clone it, copy the referenced object itself and keep our
* fingers crossed.</p>
*
* @param oldKey Description of Parameter
* @param newKey Description of Parameter
*/
private void copyReference( String oldKey, String newKey )
{
Object orig = project.getReference( oldKey );
Class c = orig.getClass();
Object copy = orig;
try
{
Method cloneM = c.getMethod( "clone", new Class[0] );
if( cloneM != null )
{
copy = cloneM.invoke( orig, new Object[0] );
}
}
catch( Exception e )
{
// not Clonable
}

if( copy instanceof ProjectComponent )
{
( ( ProjectComponent )copy ).setProject( newProject );
}
else
{
try
{
Method setProjectM =
c.getMethod( "setProject", new Class[]{Project.class} );
if( setProjectM != null )
{
setProjectM.invoke( copy, new Object[]{newProject} );
}
}
catch( NoSuchMethodException e )
{
// ignore this if the class being referenced does not have
// a set project method.
}
catch( Exception e2 )
{
String msg = "Error setting new project instance for reference with id "
+ oldKey;
throw new BuildException( msg, e2, location );
}
}
newProject.addReference( newKey, copy );
}

private void initializeProject()
{
Vector listeners = project.getBuildListeners();
for( int i = 0; i < listeners.size(); i++ )
{
newProject.addBuildListener( ( BuildListener )listeners.elementAt( i ) );
}

if( output != null )
{
try
{
PrintStream out = new PrintStream( new FileOutputStream( output ) );
DefaultLogger logger = new DefaultLogger();
logger.setMessageOutputLevel( Project.MSG_INFO );
logger.setOutputPrintStream( out );
logger.setErrorPrintStream( out );
newProject.addBuildListener( logger );
}
catch( IOException ex )
{
log( "Ant: Can't set output to " + output );
}
}

Hashtable taskdefs = project.getTaskDefinitions();
Enumeration et = taskdefs.keys();
while( et.hasMoreElements() )
{
String taskName = ( String )et.nextElement();
if( taskName.equals( "property" ) )
{
// we have already added this taskdef in #init
continue;
}
Class taskClass = ( Class )taskdefs.get( taskName );
newProject.addTaskDefinition( taskName, taskClass );
}

Hashtable typedefs = project.getDataTypeDefinitions();
Enumeration e = typedefs.keys();
while( e.hasMoreElements() )
{
String typeName = ( String )e.nextElement();
Class typeClass = ( Class )typedefs.get( typeName );
newProject.addDataTypeDefinition( typeName, typeClass );
}

// set user-defined or all properties from calling project
Hashtable prop1;
if( inheritAll == true )
{
prop1 = project.getProperties();
}
else
{
prop1 = project.getUserProperties();

// set Java built-in properties separately,
// b/c we won't inherit them.
newProject.setSystemProperties();
}

e = prop1.keys();
while( e.hasMoreElements() )
{
String arg = ( String )e.nextElement();
if( "basedir".equals( arg ) || "ant.file".equals( arg ) )
{
// basedir and ant.file get special treatment in execute()
continue;
}

String value = ( String )prop1.get( arg );
if( inheritAll == true )
{
newProject.setProperty( arg, value );
}
else
{
newProject.setUserProperty( arg, value );
}
}
}

/**
* Override the properties in the new project with the one explicitly
* defined as nested elements here.
*
* @exception BuildException Description of Exception
*/
private void overrideProperties()
throws BuildException
{
Enumeration e = properties.elements();
while( e.hasMoreElements() )
{
Property p = ( Property )e.nextElement();
p.setProject( newProject );
p.execute();
}
}

private void reinit()
{
init();
for( int i = 0; i < properties.size(); i++ )
{
Property p = ( Property )properties.elementAt( i );
Property newP = ( Property )newProject.createTask( "property" );
newP.setName( p.getName() );
if( p.getValue() != null )
{
newP.setValue( p.getValue() );
}
if( p.getFile() != null )
{
newP.setFile( p.getFile() );
}
if( p.getResource() != null )
{
newP.setResource( p.getResource() );
}
properties.setElementAt( newP, i );
}
}

/**
* Helper class that implements the nested &lt;reference&gt; element of
* &lt;ant&gt; and &lt;antcall&gt;.
*
* @author RT
*/
public static class Reference
extends org.apache.tools.ant.types.Reference
{

private String targetid = null;

public Reference()
{
super();
}

public void setToRefid( String targetid )
{
this.targetid = targetid;
}

public String getToRefid()
{
return targetid;
}
}
}

+ 394
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/AntStructure.java View File

@@ -0,0 +1,394 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.IntrospectionHelper;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskContainer;
import org.apache.tools.ant.types.EnumeratedAttribute;

/**
* Creates a partial DTD for Ant from the currently known tasks.
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @version $Revision$
*/

public class AntStructure extends Task
{

private final String lSep = System.getProperty( "line.separator" );

private final String BOOLEAN = "%boolean;";
private final String TASKS = "%tasks;";
private final String TYPES = "%types;";

private Hashtable visited = new Hashtable();

private File output;

/**
* The output file.
*
* @param output The new Output value
*/
public void setOutput( File output )
{
this.output = output;
}

public void execute()
throws BuildException
{

if( output == null )
{
throw new BuildException( "output attribute is required", location );
}

PrintWriter out = null;
try
{
try
{
out = new PrintWriter( new OutputStreamWriter( new FileOutputStream( output ), "UTF8" ) );
}
catch( UnsupportedEncodingException ue )
{
/*
* Plain impossible with UTF8, see
* http://java.sun.com/products/jdk/1.2/docs/guide/internat/encoding.doc.html
*
* fallback to platform specific anyway.
*/
out = new PrintWriter( new FileWriter( output ) );
}

printHead( out, project.getTaskDefinitions().keys(),
project.getDataTypeDefinitions().keys() );

printTargetDecl( out );

Enumeration dataTypes = project.getDataTypeDefinitions().keys();
while( dataTypes.hasMoreElements() )
{
String typeName = ( String )dataTypes.nextElement();
printElementDecl( out, typeName,
( Class )project.getDataTypeDefinitions().get( typeName ) );
}

Enumeration tasks = project.getTaskDefinitions().keys();
while( tasks.hasMoreElements() )
{
String taskName = ( String )tasks.nextElement();
printElementDecl( out, taskName,
( Class )project.getTaskDefinitions().get( taskName ) );
}

printTail( out );

}
catch( IOException ioe )
{
throw new BuildException( "Error writing " + output.getAbsolutePath(),
ioe, location );
}
finally
{
if( out != null )
{
out.close();
}
}
}

/**
* Does this String match the XML-NMTOKEN production?
*
* @param s Description of Parameter
* @return The Nmtoken value
*/
protected boolean isNmtoken( String s )
{
for( int i = 0; i < s.length(); i++ )
{
char c = s.charAt( i );
// XXX - we are ommitting CombiningChar and Extender here
if( !Character.isLetterOrDigit( c ) &&
c != '.' && c != '-' &&
c != '_' && c != ':' )
{
return false;
}
}
return true;
}

/**
* Do the Strings all match the XML-NMTOKEN production? <p>
*
* Otherwise they are not suitable as an enumerated attribute, for example.
* </p>
*
* @param s Description of Parameter
* @return Description of the Returned Value
*/
protected boolean areNmtokens( String[] s )
{
for( int i = 0; i < s.length; i++ )
{
if( !isNmtoken( s[i] ) )
{
return false;
}
}
return true;
}

private void printElementDecl( PrintWriter out, String name, Class element )
throws BuildException
{

if( visited.containsKey( name ) )
{
return;
}
visited.put( name, "" );

IntrospectionHelper ih = null;
try
{
ih = IntrospectionHelper.getHelper( element );
}
catch( Throwable t )
{
/*
* XXX - failed to load the class properly.
*
* should we print a warning here?
*/
return;
}

StringBuffer sb = new StringBuffer( "<!ELEMENT " );
sb.append( name ).append( " " );

if( org.apache.tools.ant.types.Reference.class.equals( element ) )
{
sb.append( "EMPTY>" ).append( lSep );
sb.append( "<!ATTLIST " ).append( name );
sb.append( lSep ).append( " id ID #IMPLIED" );
sb.append( lSep ).append( " refid IDREF #IMPLIED" );
sb.append( ">" ).append( lSep );
out.println( sb );
return;
}

Vector v = new Vector();
if( ih.supportsCharacters() )
{
v.addElement( "#PCDATA" );
}

if( TaskContainer.class.isAssignableFrom( element ) )
{
v.addElement( TASKS );
}

Enumeration enum = ih.getNestedElements();
while( enum.hasMoreElements() )
{
v.addElement( ( String )enum.nextElement() );
}

if( v.isEmpty() )
{
sb.append( "EMPTY" );
}
else
{
sb.append( "(" );
for( int i = 0; i < v.size(); i++ )
{
if( i != 0 )
{
sb.append( " | " );
}
sb.append( v.elementAt( i ) );
}
sb.append( ")" );
if( v.size() > 1 || !v.elementAt( 0 ).equals( "#PCDATA" ) )
{
sb.append( "*" );
}
}
sb.append( ">" );
out.println( sb );

sb.setLength( 0 );
sb.append( "<!ATTLIST " ).append( name );
sb.append( lSep ).append( " id ID #IMPLIED" );

enum = ih.getAttributes();
while( enum.hasMoreElements() )
{
String attrName = ( String )enum.nextElement();
if( "id".equals( attrName ) )
continue;

sb.append( lSep ).append( " " ).append( attrName ).append( " " );
Class type = ih.getAttributeType( attrName );
if( type.equals( java.lang.Boolean.class ) ||
type.equals( java.lang.Boolean.TYPE ) )
{
sb.append( BOOLEAN ).append( " " );
}
else if( org.apache.tools.ant.types.Reference.class.isAssignableFrom( type ) )
{
sb.append( "IDREF " );
}
else if( org.apache.tools.ant.types.EnumeratedAttribute.class.isAssignableFrom( type ) )
{
try
{
EnumeratedAttribute ea =
( EnumeratedAttribute )type.newInstance();
String[] values = ea.getValues();
if( values == null
|| values.length == 0
|| !areNmtokens( values ) )
{
sb.append( "CDATA " );
}
else
{
sb.append( "(" );
for( int i = 0; i < values.length; i++ )
{
if( i != 0 )
{
sb.append( " | " );
}
sb.append( values[i] );
}
sb.append( ") " );
}
}
catch( InstantiationException ie )
{
sb.append( "CDATA " );
}
catch( IllegalAccessException ie )
{
sb.append( "CDATA " );
}
}
else
{
sb.append( "CDATA " );
}
sb.append( "#IMPLIED" );
}
sb.append( ">" ).append( lSep );
out.println( sb );

for( int i = 0; i < v.size(); i++ )
{
String nestedName = ( String )v.elementAt( i );
if( !"#PCDATA".equals( nestedName ) &&
!TASKS.equals( nestedName ) &&
!TYPES.equals( nestedName )
)
{
printElementDecl( out, nestedName, ih.getElementType( nestedName ) );
}
}
}

private void printHead( PrintWriter out, Enumeration tasks,
Enumeration types )
{
out.println( "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" );
out.println( "<!ENTITY % boolean \"(true|false|on|off|yes|no)\">" );
out.print( "<!ENTITY % tasks \"" );
boolean first = true;
while( tasks.hasMoreElements() )
{
String taskName = ( String )tasks.nextElement();
if( !first )
{
out.print( " | " );
}
else
{
first = false;
}
out.print( taskName );
}
out.println( "\">" );
out.print( "<!ENTITY % types \"" );
first = true;
while( types.hasMoreElements() )
{
String typeName = ( String )types.nextElement();
if( !first )
{
out.print( " | " );
}
else
{
first = false;
}
out.print( typeName );
}
out.println( "\">" );

out.println( "" );

out.print( "<!ELEMENT project (target | property | taskdef | " );
out.print( TYPES );
out.println( ")*>" );
out.println( "<!ATTLIST project" );
out.println( " name CDATA #REQUIRED" );
out.println( " default CDATA #REQUIRED" );
out.println( " basedir CDATA #IMPLIED>" );
out.println( "" );
}

private void printTail( PrintWriter out ) { }

private void printTargetDecl( PrintWriter out )
{
out.print( "<!ELEMENT target (" );
out.print( TASKS );
out.print( " | " );
out.print( TYPES );
out.println( ")*>" );
out.println( "" );

out.println( "<!ATTLIST target" );
out.println( " id ID #IMPLIED" );
out.println( " name CDATA #REQUIRED" );
out.println( " if CDATA #IMPLIED" );
out.println( " unless CDATA #IMPLIED" );
out.println( " depends CDATA #IMPLIED" );
out.println( " description CDATA #IMPLIED>" );
out.println( "" );
}

}

+ 418
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Available.java View File

@@ -0,0 +1,418 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.condition.Condition;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.FileUtils;

/**
* Will set the given property if the requested resource is available at
* runtime.
*
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">
* stefano@apache.org</a>
* @author <a href="mailto:umagesh@apache.org">Magesh Umasankar</a>
*/

public class Available extends Task implements Condition
{
private String value = "true";
private String classname;
private Path classpath;
private String file;
private Path filepath;
private AntClassLoader loader;

private String property;
private String resource;
private FileDir type;

public void setClassname( String classname )
{
if( !"".equals( classname ) )
{
this.classname = classname;
}
}

public void setClasspath( Path classpath )
{
createClasspath().append( classpath );
}

public void setClasspathRef( Reference r )
{
createClasspath().setRefid( r );
}

public void setFile( String file )
{
this.file = file;
}

public void setFilepath( Path filepath )
{
createFilepath().append( filepath );
}

public void setProperty( String property )
{
this.property = property;
}

public void setResource( String resource )
{
this.resource = resource;
}

/**
* @param type The new Type value
* @deprecated setType(String) is deprecated and is replaced with
* setType(Available.FileDir) to make Ant's Introspection mechanism do
* the work and also to encapsulate operations on the type in its own
* class.
*/
public void setType( String type )
{
log( "DEPRECATED - The setType(String) method has been deprecated."
+ " Use setType(Available.FileDir) instead." );
this.type = new FileDir();
this.type.setValue( type );
}

public void setType( FileDir type )
{
this.type = type;
}

public void setValue( String value )
{
this.value = value;
}

public Path createClasspath()
{
if( this.classpath == null )
{
this.classpath = new Path( project );
}
return this.classpath.createPath();
}

public Path createFilepath()
{
if( this.filepath == null )
{
this.filepath = new Path( project );
}
return this.filepath.createPath();
}

public boolean eval()
throws BuildException
{
if( classname == null && file == null && resource == null )
{
throw new BuildException( "At least one of (classname|file|resource) is required", location );
}

if( type != null )
{
if( file == null )
{
throw new BuildException( "The type attribute is only valid when specifying the file attribute." );
}
}

if( classpath != null )
{
classpath.setProject( project );
this.loader = new AntClassLoader( project, classpath );
}

if( ( classname != null ) && !checkClass( classname ) )
{
log( "Unable to load class " + classname + " to set property " + property, Project.MSG_VERBOSE );
return false;
}

if( ( file != null ) && !checkFile() )
{
if( type != null )
{
log( "Unable to find " + type + " " + file + " to set property " + property, Project.MSG_VERBOSE );
}
else
{
log( "Unable to find " + file + " to set property " + property, Project.MSG_VERBOSE );
}
return false;
}

if( ( resource != null ) && !checkResource( resource ) )
{
log( "Unable to load resource " + resource + " to set property " + property, Project.MSG_VERBOSE );
return false;
}

if( loader != null )
{
loader.cleanup();
}

return true;
}

public void execute()
throws BuildException
{
if( property == null )
{
throw new BuildException( "property attribute is required", location );
}

if( eval() )
{
String lSep = System.getProperty( "line.separator" );
if( null != project.getProperty( property ) )
{
log( "DEPRECATED - <available> used to overide an existing property. "
+ lSep
+ " Build writer should not reuse the same property name for "
+ lSep + "different values." );
}
this.project.setProperty( property, value );
}
}

private boolean checkClass( String classname )
{
try
{
if( loader != null )
{
loader.loadClass( classname );
}
else
{
ClassLoader l = this.getClass().getClassLoader();
// Can return null to represent the bootstrap class loader.
// see API docs of Class.getClassLoader.
if( l != null )
{
l.loadClass( classname );
}
else
{
Class.forName( classname );
}
}
return true;
}
catch( ClassNotFoundException e )
{
return false;
}
catch( NoClassDefFoundError e )
{
return false;
}
}

private boolean checkFile()
{
if( filepath == null )
{
return checkFile( project.resolveFile( file ), file );
}
else
{
String[] paths = filepath.list();
for( int i = 0; i < paths.length; ++i )
{
log( "Searching " + paths[i], Project.MSG_DEBUG );
/*
* filepath can be a list of directory and/or
* file names (gen'd via <fileset>)
*
* look for:
* full-pathname specified == path in list
* full-pathname specified == parent dir of path in list
* simple name specified == path in list
* simple name specified == path in list + name
* simple name specified == parent dir + name
* simple name specified == parent of parent dir + name
*
*/
File path = new File( paths[i] );

// ** full-pathname specified == path in list
// ** simple name specified == path in list
if( path.exists() && file.equals( paths[i] ) )
{
if( type == null )
{
log( "Found: " + path, Project.MSG_VERBOSE );
return true;
}
else if( type.isDir()
&& path.isDirectory() )
{
log( "Found directory: " + path, Project.MSG_VERBOSE );
return true;
}
else if( type.isFile()
&& path.isFile() )
{
log( "Found file: " + path, Project.MSG_VERBOSE );
return true;
}
// not the requested type
return false;
}

FileUtils fileUtils = FileUtils.newFileUtils();
File parent = fileUtils.getParentFile( path );
// ** full-pathname specified == parent dir of path in list
if( parent != null && parent.exists()
&& file.equals( parent.getAbsolutePath() ) )
{
if( type == null )
{
log( "Found: " + parent, Project.MSG_VERBOSE );
return true;
}
else if( type.isDir() )
{
log( "Found directory: " + parent, Project.MSG_VERBOSE );
return true;
}
// not the requested type
return false;
}

// ** simple name specified == path in list + name
if( path.exists() && path.isDirectory() )
{
if( checkFile( new File( path, file ),
file + " in " + path ) )
{
return true;
}
}

// ** simple name specified == parent dir + name
if( parent != null && parent.exists() )
{
if( checkFile( new File( parent, file ),
file + " in " + parent ) )
{
return true;
}
}

// ** simple name specified == parent of parent dir + name
if( parent != null )
{
File grandParent = fileUtils.getParentFile( parent );
if( grandParent != null && grandParent.exists() )
{
if( checkFile( new File( grandParent, file ),
file + " in " + grandParent ) )
{
return true;
}
}
}
}
}
return false;
}

private boolean checkFile( File f, String text )
{
if( type != null )
{
if( type.isDir() )
{
if( f.isDirectory() )
{
log( "Found directory: " + text, Project.MSG_VERBOSE );
}
return f.isDirectory();
}
else if( type.isFile() )
{
if( f.isFile() )
{
log( "Found file: " + text, Project.MSG_VERBOSE );
}
return f.isFile();
}
}
if( f.exists() )
{
log( "Found: " + text, Project.MSG_VERBOSE );
}
return f.exists();
}

private boolean checkResource( String resource )
{
if( loader != null )
{
return ( loader.getResourceAsStream( resource ) != null );
}
else
{
ClassLoader cL = this.getClass().getClassLoader();
if( cL != null )
{
return ( cL.getResourceAsStream( resource ) != null );
}
else
{
return
( ClassLoader.getSystemResourceAsStream( resource ) != null );
}
}
}

public static class FileDir extends EnumeratedAttribute
{

private final static String[] values = {"file", "dir"};

public String[] getValues()
{
return values;
}

public boolean isDir()
{
return "dir".equalsIgnoreCase( getValue() );
}

public boolean isFile()
{
return "file".equalsIgnoreCase( getValue() );
}

public String toString()
{
return getValue();
}
}
}

+ 114
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/BUnzip2.java View File

@@ -0,0 +1,114 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.bzip2.CBZip2InputStream;

/**
* Expands a file that has been compressed with the BZIP2 algorithm. Normally
* used to compress non-compressed archives such as TAR files.
*
* @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
*/

public class BUnzip2 extends Unpack
{

private final static String DEFAULT_EXTENSION = ".bz2";

protected String getDefaultExtension()
{
return DEFAULT_EXTENSION;
}

protected void extract()
{
if( source.lastModified() > dest.lastModified() )
{
log( "Expanding " + source.getAbsolutePath() + " to "
+ dest.getAbsolutePath() );

FileOutputStream out = null;
CBZip2InputStream zIn = null;
FileInputStream fis = null;
BufferedInputStream bis = null;
try
{
out = new FileOutputStream( dest );
fis = new FileInputStream( source );
bis = new BufferedInputStream( fis );
int b = bis.read();
if( b != 'B' )
{
throw new BuildException( "Invalid bz2 file.", location );
}
b = bis.read();
if( b != 'Z' )
{
throw new BuildException( "Invalid bz2 file.", location );
}
zIn = new CBZip2InputStream( bis );
byte[] buffer = new byte[8 * 1024];
int count = 0;
do
{
out.write( buffer, 0, count );
count = zIn.read( buffer, 0, buffer.length );
}while ( count != -1 );
}
catch( IOException ioe )
{
String msg = "Problem expanding bzip2 " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}
finally
{
if( bis != null )
{
try
{
bis.close();
}
catch( IOException ioex )
{}
}
if( fis != null )
{
try
{
fis.close();
}
catch( IOException ioex )
{}
}
if( out != null )
{
try
{
out.close();
}
catch( IOException ioex )
{}
}
if( zIn != null )
{
try
{
zIn.close();
}
catch( IOException ioex )
{}
}
}
}
}
}

+ 56
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/BZip2.java View File

@@ -0,0 +1,56 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.Pack;
import org.apache.tools.bzip2.CBZip2OutputStream;

/**
* Compresses a file with the BZip2 algorithm. Normally used to compress
* non-compressed archives such as TAR files.
*
* @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
*/

public class BZip2 extends Pack
{
protected void pack()
{
CBZip2OutputStream zOut = null;
try
{
BufferedOutputStream bos =
new BufferedOutputStream( new FileOutputStream( zipFile ) );
bos.write( 'B' );
bos.write( 'Z' );
zOut = new CBZip2OutputStream( bos );
zipFile( source, zOut );
}
catch( IOException ioe )
{
String msg = "Problem creating bzip2 " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}
finally
{
if( zOut != null )
{
try
{
// close up
zOut.close();
}
catch( IOException e )
{}
}
}
}
}

+ 165
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/CVSPass.java View File

@@ -0,0 +1,165 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

/**
* CVSLogin Adds an new entry to a CVS password file
*
* @author <a href="jeff@custommonkey.org">Jeff Martin</a>
* @version $Revision$
*/
public class CVSPass extends Task
{
/**
* CVS Root
*/
private String cvsRoot = null;
/**
* Password file to add password to
*/
private File passFile = null;
/**
* Password to add to file
*/
private String password = null;
/**
* End of line character
*/
private final String EOL = System.getProperty( "line.separator" );

/**
* Array contain char conversion data
*/
private final char shifts[] = {
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
114, 120, 53, 79, 96, 109, 72, 108, 70, 64, 76, 67, 116, 74, 68, 87,
111, 52, 75, 119, 49, 34, 82, 81, 95, 65, 112, 86, 118, 110, 122, 105,
41, 57, 83, 43, 46, 102, 40, 89, 38, 103, 45, 50, 42, 123, 91, 35,
125, 55, 54, 66, 124, 126, 59, 47, 92, 71, 115, 78, 88, 107, 106, 56,
36, 121, 117, 104, 101, 100, 69, 73, 99, 63, 94, 93, 39, 37, 61, 48,
58, 113, 32, 90, 44, 98, 60, 51, 33, 97, 62, 77, 84, 80, 85, 223,
225, 216, 187, 166, 229, 189, 222, 188, 141, 249, 148, 200, 184, 136, 248, 190,
199, 170, 181, 204, 138, 232, 218, 183, 255, 234, 220, 247, 213, 203, 226, 193,
174, 172, 228, 252, 217, 201, 131, 230, 197, 211, 145, 238, 161, 179, 160, 212,
207, 221, 254, 173, 202, 146, 224, 151, 140, 196, 205, 130, 135, 133, 143, 246,
192, 159, 244, 239, 185, 168, 215, 144, 139, 165, 180, 157, 147, 186, 214, 176,
227, 231, 219, 169, 175, 156, 206, 198, 129, 164, 150, 210, 154, 177, 134, 127,
182, 128, 158, 208, 162, 132, 167, 209, 149, 241, 153, 251, 237, 236, 171, 195,
243, 233, 253, 240, 194, 250, 191, 155, 142, 137, 245, 235, 163, 242, 178, 152};

public CVSPass()
{
passFile = new File( System.getProperty( "user.home" ) + "/.cvspass" );
}

/**
* Sets cvs root to be added to the password file
*
* @param cvsRoot The new Cvsroot value
*/
public void setCvsroot( String cvsRoot )
{
this.cvsRoot = cvsRoot;
}

/**
* Sets the password file attribute.
*
* @param passFile The new Passfile value
*/
public void setPassfile( File passFile )
{
this.passFile = passFile;
}

/**
* Sets the password attribute.
*
* @param password The new Password value
*/
public void setPassword( String password )
{
this.password = password;
}

/**
* Does the work.
*
* @exception BuildException if someting goes wrong with the build
*/
public final void execute()
throws BuildException
{
if( cvsRoot == null )
throw new BuildException( "cvsroot is required" );
if( password == null )
throw new BuildException( "password is required" );

log( "cvsRoot: " + cvsRoot, project.MSG_DEBUG );
log( "password: " + password, project.MSG_DEBUG );
log( "passFile: " + passFile, project.MSG_DEBUG );

try
{
StringBuffer buf = new StringBuffer();

if( passFile.exists() )
{
BufferedReader reader =
new BufferedReader( new FileReader( passFile ) );

String line = null;

while( ( line = reader.readLine() ) != null )
{
if( !line.startsWith( cvsRoot ) )
{
buf.append( line + EOL );
}
}

reader.close();
}

String pwdfile = buf.toString() + cvsRoot + " A" + mangle( password );

log( "Writing -> " + pwdfile, project.MSG_DEBUG );

PrintWriter writer = new PrintWriter( new FileWriter( passFile ) );

writer.println( pwdfile );

writer.close();
}
catch( IOException e )
{
throw new BuildException( e );
}

}

private final String mangle( String password )
{
StringBuffer buf = new StringBuffer();
for( int i = 0; i < password.length(); i++ )
{
buf.append( shifts[password.charAt( i )] );
}
return buf.toString();
}

}

+ 125
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/CallTarget.java View File

@@ -0,0 +1,125 @@
/*
* 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.tools.ant.taskdefs;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

/**
* Call another target in the same project. <pre>
* &lt;target name="foo"&gt;
* &lt;antcall target="bar"&gt;
* &lt;param name="property1" value="aaaaa" /&gt;
* &lt;param name="foo" value="baz" /&gt;
* &lt;/antcall&gt;
* &lt;/target&gt;
*
* &lt;target name="bar" depends="init"&gt;
* &lt;echo message="prop is ${property1} ${foo}" /&gt;
* &lt;/target&gt;
* </pre> <p>
*
* This only works as expected if neither property1 nor foo are defined in the
* project itself.
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/
public class CallTarget extends Task
{
private boolean initialized = false;
private boolean inheritAll = true;

private Ant callee;
private String subTarget;

/**
* If true, inherit all properties from parent Project If false, inherit
* only userProperties and those defined inside the antcall call itself
*
* @param inherit The new InheritAll value
*/
public void setInheritAll( boolean inherit )
{
inheritAll = inherit;
}

public void setTarget( String target )
{
subTarget = target;
}

/**
* create a reference element that identifies a data type that should be
* carried over to the new project.
*
* @param r The feature to be added to the Reference attribute
*/
public void addReference( Ant.Reference r )
{
callee.addReference( r );
}

public Property createParam()
{
return callee.createProperty();
}

public void execute()
{
if( !initialized )
{
init();
}

if( subTarget == null )
{
throw new BuildException( "Attribute target is required.",
location );
}

callee.setDir( project.getBaseDir() );
callee.setAntfile( project.getProperty( "ant.file" ) );
callee.setTarget( subTarget );
callee.setInheritAll( inheritAll );
callee.execute();
}//-- setInheritAll

public void init()
{
callee = ( Ant )project.createTask( "ant" );
callee.setOwningTarget( target );
callee.setTaskName( getTaskName() );
callee.setLocation( location );
callee.init();
initialized = true;
}

protected void handleErrorOutput( String line )
{
if( callee != null )
{
callee.handleErrorOutput( line );
}
else
{
super.handleErrorOutput( line );
}
}

protected void handleOutput( String line )
{
if( callee != null )
{
callee.handleOutput( line );
}
else
{
super.handleOutput( line );
}
}

}

+ 489
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Checksum.java View File

@@ -0,0 +1,489 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.taskdefs.condition.Condition;
import org.apache.tools.ant.types.FileSet;

/**
* This task can be used to create checksums for files. It can also be used to
* verify checksums.
*
* @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
*/
public class Checksum extends MatchingTask implements Condition
{
/**
* File for which checksum is to be calculated.
*/
private File file = null;
/**
* MessageDigest algorithm to be used.
*/
private String algorithm = "MD5";
/**
* MessageDigest Algorithm provider
*/
private String provider = null;
/**
* Vector to hold source file sets.
*/
private Vector filesets = new Vector();
/**
* Stores SourceFile, DestFile pairs and SourceFile, Property String pairs.
*/
private Hashtable includeFileMap = new Hashtable();
/**
* File Extension that is be to used to create or identify destination file
*/
private String fileext;
/**
* Create new destination file? Defaults to false.
*/
private boolean forceOverwrite;
/**
* is this task being used as a nested condition element?
*/
private boolean isCondition;
/**
* Message Digest instance
*/
private MessageDigest messageDigest;
/**
* Holds generated checksum and gets set as a Project Property.
*/
private String property;
/**
* Contains the result of a checksum verification. ("true" or "false")
*/
private String verifyProperty;

/**
* Sets the MessageDigest algorithm to be used to calculate the checksum.
*
* @param algorithm The new Algorithm value
*/
public void setAlgorithm( String algorithm )
{
this.algorithm = algorithm;
}

/**
* Sets the file for which the checksum is to be calculated.
*
* @param file The new File value
*/
public void setFile( File file )
{
this.file = file;
}

/**
* Sets the File Extension that is be to used to create or identify
* destination file
*
* @param fileext The new Fileext value
*/
public void setFileext( String fileext )
{
this.fileext = fileext;
}

/**
* Overwrite existing file irrespective of whether it is newer than the
* source file? Defaults to false.
*
* @param forceOverwrite The new ForceOverwrite value
*/
public void setForceOverwrite( boolean forceOverwrite )
{
this.forceOverwrite = forceOverwrite;
}

/**
* Sets the property to hold the generated checksum
*
* @param property The new Property value
*/
public void setProperty( String property )
{
this.property = property;
}

/**
* Sets the MessageDigest algorithm provider to be used to calculate the
* checksum.
*
* @param provider The new Provider value
*/
public void setProvider( String provider )
{
this.provider = provider;
}

/**
* Sets verify property. This project property holds the result of a
* checksum verification - "true" or "false"
*
* @param verifyProperty The new Verifyproperty value
*/
public void setVerifyproperty( String verifyProperty )
{
this.verifyProperty = verifyProperty;
}

/**
* Adds a set of files (nested fileset attribute).
*
* @param set The feature to be added to the Fileset attribute
*/
public void addFileset( FileSet set )
{
filesets.addElement( set );
}

/**
* Calculate the checksum(s)
*
* @return Returns true if the checksum verification test passed, false
* otherwise.
* @exception BuildException Description of Exception
*/
public boolean eval()
throws BuildException
{
isCondition = true;
return validateAndExecute();
}

/**
* Calculate the checksum(s).
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
boolean value = validateAndExecute();
if( verifyProperty != null )
{
project.setNewProperty( verifyProperty,
new Boolean( value ).toString() );
}
}

/**
* Add key-value pair to the hashtable upon which to later operate upon.
*
* @param file The feature to be added to the ToIncludeFileMap attribute
* @exception BuildException Description of Exception
*/
private void addToIncludeFileMap( File file )
throws BuildException
{
if( file != null )
{
if( file.exists() )
{
if( property == null )
{
File dest = new File( file.getParent(), file.getName() + fileext );
if( forceOverwrite || isCondition ||
( file.lastModified() > dest.lastModified() ) )
{
includeFileMap.put( file, dest );
}
else
{
log( file + " omitted as " + dest + " is up to date.",
Project.MSG_VERBOSE );
}
}
else
{
includeFileMap.put( file, property );
}
}
else
{
String message = "Could not find file "
+ file.getAbsolutePath()
+ " to generate checksum for.";
log( message );
throw new BuildException( message, location );
}
}
}

/**
* Generate checksum(s) using the message digest created earlier.
*
* @return Description of the Returned Value
* @exception BuildException Description of Exception
*/
private boolean generateChecksums()
throws BuildException
{
boolean checksumMatches = true;
FileInputStream fis = null;
FileOutputStream fos = null;
try
{
for( Enumeration e = includeFileMap.keys(); e.hasMoreElements(); )
{
messageDigest.reset();
File src = ( File )e.nextElement();
if( !isCondition )
{
log( "Calculating " + algorithm + " checksum for " + src );
}
fis = new FileInputStream( src );
DigestInputStream dis = new DigestInputStream( fis,
messageDigest );
while( dis.read() != -1 )
;
dis.close();
fis.close();
fis = null;
byte[] fileDigest = messageDigest.digest();
String checksum = "";
for( int i = 0; i < fileDigest.length; i++ )
{
String hexStr = Integer.toHexString( 0x00ff & fileDigest[i] );
if( hexStr.length() < 2 )
{
checksum += "0";
}
checksum += hexStr;
}
//can either be a property name string or a file
Object destination = includeFileMap.get( src );
if( destination instanceof java.lang.String )
{
String prop = ( String )destination;
if( isCondition )
{
checksumMatches = checksum.equals( property );
}
else
{
project.setProperty( prop, checksum );
}
}
else if( destination instanceof java.io.File )
{
if( isCondition )
{
File existingFile = ( File )destination;
if( existingFile.exists() &&
existingFile.length() == checksum.length() )
{
fis = new FileInputStream( existingFile );
InputStreamReader isr = new InputStreamReader( fis );
BufferedReader br = new BufferedReader( isr );
String suppliedChecksum = br.readLine();
fis.close();
fis = null;
br.close();
isr.close();
checksumMatches =
checksum.equals( suppliedChecksum );
}
else
{
checksumMatches = false;
}
}
else
{
File dest = ( File )destination;
fos = new FileOutputStream( dest );
fos.write( checksum.getBytes() );
fos.close();
fos = null;
}
}
}
}
catch( Exception e )
{
throw new BuildException( e );
}
finally
{
if( fis != null )
{
try
{
fis.close();
}
catch( IOException e )
{}
}
if( fos != null )
{
try
{
fos.close();
}
catch( IOException e )
{}
}
}
return checksumMatches;
}

/**
* Validate attributes and get down to business.
*
* @return Description of the Returned Value
* @exception BuildException Description of Exception
*/
private boolean validateAndExecute()
throws BuildException
{

if( file == null && filesets.size() == 0 )
{
throw new BuildException(
"Specify at least one source - a file or a fileset." );
}

if( file != null && file.exists() && file.isDirectory() )
{
throw new BuildException(
"Checksum cannot be generated for directories" );
}

if( property != null && fileext != null )
{
throw new BuildException(
"Property and FileExt cannot co-exist." );
}

if( property != null )
{
if( forceOverwrite )
{
throw new BuildException(
"ForceOverwrite cannot be used when Property is specified" );
}

if( file != null )
{
if( filesets.size() > 0 )
{
throw new BuildException(
"Multiple files cannot be used when Property is specified" );
}
}
else
{
if( filesets.size() > 1 )
{
throw new BuildException(
"Multiple files cannot be used when Property is specified" );
}
}
}

if( verifyProperty != null )
{
isCondition = true;
}

if( verifyProperty != null && forceOverwrite )
{
throw new BuildException(
"VerifyProperty and ForceOverwrite cannot co-exist." );
}

if( isCondition && forceOverwrite )
{
throw new BuildException(
"ForceOverwrite cannot be used when conditions are being used." );
}

if( fileext == null )
{
fileext = "." + algorithm;
}
else if( fileext.trim().length() == 0 )
{
throw new BuildException(
"File extension when specified must not be an empty string" );
}

messageDigest = null;
if( provider != null )
{
try
{
messageDigest = MessageDigest.getInstance( algorithm, provider );
}
catch( NoSuchAlgorithmException noalgo )
{
throw new BuildException( noalgo );
}
catch( NoSuchProviderException noprovider )
{
throw new BuildException( noprovider );
}
}
else
{
try
{
messageDigest = MessageDigest.getInstance( algorithm );
}
catch( NoSuchAlgorithmException noalgo )
{
throw new BuildException( noalgo );
}
}

if( messageDigest == null )
{
throw new BuildException( "Unable to create Message Digest",
location );
}

addToIncludeFileMap( file );

int sizeofFileSet = filesets.size();
for( int i = 0; i < sizeofFileSet; i++ )
{
FileSet fs = ( FileSet )filesets.elementAt( i );
DirectoryScanner ds = fs.getDirectoryScanner( project );
String[] srcFiles = ds.getIncludedFiles();
for( int j = 0; j < srcFiles.length; j++ )
{
File src = new File( fs.getDir( project ), srcFiles[j] );
addToIncludeFileMap( src );
}
}

return generateChecksums();
}
}

+ 193
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Chmod.java View File

@@ -0,0 +1,193 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import org.apache.tools.ant.BuildException;
import org.apache.myrmidon.framework.Os;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.PatternSet;


/**
* Chmod equivalent for unix-like environments.
*
* @author costin@eng.sun.com
* @author Mariusz Nowostawski (Marni) <a
* href="mailto:mnowostawski@infoscience.otago.ac.nz">
* mnowostawski@infoscience.otago.ac.nz</a>
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/

public class Chmod extends ExecuteOn
{

private FileSet defaultSet = new FileSet();
private boolean defaultSetDefined = false;
private boolean havePerm = false;

public Chmod()
{
super.setExecutable( "chmod" );
super.setParallel( true );
super.setSkipEmptyFilesets( true );
}

public void setCommand( String e )
{
throw new BuildException( taskType + " doesn\'t support the command attribute", location );
}

/**
* Sets whether default exclusions should be used or not.
*
* @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
* should be used, "false"|"off"|"no" when they shouldn't be used.
*/
public void setDefaultexcludes( boolean useDefaultExcludes )
{
defaultSetDefined = true;
defaultSet.setDefaultexcludes( useDefaultExcludes );
}

public void setDir( File src )
{
defaultSet.setDir( src );
}

/**
* Sets the set of exclude patterns. Patterns may be separated by a comma or
* a space.
*
* @param excludes the string containing the exclude patterns
*/
public void setExcludes( String excludes )
{
defaultSetDefined = true;
defaultSet.setExcludes( excludes );
}


public void setExecutable( String e )
{
throw new BuildException( taskType + " doesn\'t support the executable attribute", location );
}

public void setFile( File src )
{
FileSet fs = new FileSet();
fs.setDir( new File( src.getParent() ) );
fs.createInclude().setName( src.getName() );
addFileset( fs );
}

/**
* Sets the set of include patterns. Patterns may be separated by a comma or
* a space.
*
* @param includes the string containing the include patterns
*/
public void setIncludes( String includes )
{
defaultSetDefined = true;
defaultSet.setIncludes( includes );
}

public void setPerm( String perm )
{
createArg().setValue( perm );
havePerm = true;
}

public void setSkipEmptyFilesets( boolean skip )
{
throw new BuildException( taskType + " doesn\'t support the skipemptyfileset attribute", location );
}

/**
* add a name entry on the exclude list
*
* @return Description of the Returned Value
*/
public PatternSet.NameEntry createExclude()
{
defaultSetDefined = true;
return defaultSet.createExclude();
}

/**
* add a name entry on the include list
*
* @return Description of the Returned Value
*/
public PatternSet.NameEntry createInclude()
{
defaultSetDefined = true;
return defaultSet.createInclude();
}

/**
* add a set of patterns
*
* @return Description of the Returned Value
*/
public PatternSet createPatternSet()
{
defaultSetDefined = true;
return defaultSet.createPatternSet();
}

public void execute()
throws BuildException
{
if( defaultSetDefined || defaultSet.getDir( project ) == null )
{
super.execute();
}
else if( isValidOs() )
{
// we are chmodding the given directory
createArg().setValue( defaultSet.getDir( project ).getPath() );
Execute execute = prepareExec();
try
{
execute.setCommandline( cmdl.getCommandline() );
runExecute( execute );
}
catch( IOException e )
{
throw new BuildException( "Execute failed: " + e, e, location );
}
finally
{
// close the output file if required
logFlush();
}
}
}

protected boolean isValidOs()
{
return Os.isFamily( "unix" ) && super.isValidOs();
}

protected void checkConfiguration()
{
if( !havePerm )
{
throw new BuildException( "Required attribute perm not set in chmod",
location );
}

if( defaultSetDefined && defaultSet.getDir( project ) != null )
{
addFileset( defaultSet );
}
super.checkConfiguration();
}
}

+ 73
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/CompileTask.java View File

@@ -0,0 +1,73 @@
/*
* 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.tools.ant.taskdefs;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.tools.ant.types.PatternSet;

/**
* This task will compile and load a new taskdef all in one step. At times, this
* is useful for eliminating ordering dependencies which otherwise would require
* multiple executions of Ant.
*
* @author Sam Ruby <a href="mailto:rubys@us.ibm.com">rubys@us.ibm.com</a>
* @deprecated use &lt;taskdef&gt; elements nested into &lt;target&gt;s instead
*/

public class CompileTask extends Javac
{

protected Vector taskList = new Vector();

/**
* add a new task entry on the task list
*
* @return Description of the Returned Value
*/
public Taskdef createTaskdef()
{
Taskdef task = new Taskdef();
taskList.addElement( task );
return task;
}

/**
* have execute do nothing
*/
public void execute() { }

/**
* do all the real work in init
*/
public void init()
{
log( "!! CompileTask is deprecated. !!" );
log( "Use <taskdef> elements nested into <target>s instead" );

// create all the include entries from the task defs
for( Enumeration e = taskList.elements(); e.hasMoreElements(); )
{
Taskdef task = ( Taskdef )e.nextElement();
String source = task.getClassname().replace( '.', '/' ) + ".java";
PatternSet.NameEntry include = super.createInclude();
include.setName( "**/" + source );
}

// execute Javac
super.init();
super.execute();

// now define all the new tasks
for( Enumeration e = taskList.elements(); e.hasMoreElements(); )
{
Taskdef task = ( Taskdef )e.nextElement();
task.init();
}

}
}

+ 76
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ConditionTask.java View File

@@ -0,0 +1,76 @@
/*
* 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.tools.ant.taskdefs;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.condition.Condition;
import org.apache.tools.ant.taskdefs.condition.ConditionBase;

/**
* &lt;condition&gt; task as a generalization of &lt;available&gt; and
* &lt;uptodate&gt; <p>
*
* This task supports boolean logic as well as pluggable conditions to decide,
* whether a property should be set.</p> <p>
*
* This task does not extend Task to take advantage of ConditionBase.</p>
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @version $Revision$
*/
public class ConditionTask extends ConditionBase
{
private String value = "true";

private String property;

/**
* The name of the property to set. Required.
*
* @param p The new Property value
* @since 1.1
*/
public void setProperty( String p )
{
property = p;
}

/**
* The value for the property to set. Defaults to "true".
*
* @param v The new Value value
* @since 1.1
*/
public void setValue( String v )
{
value = v;
}

/**
* See whether our nested condition holds and set the property.
*
* @exception BuildException Description of Exception
* @since 1.1
*/
public void execute()
throws BuildException
{
if( countConditions() > 1 )
{
throw new BuildException( "You must not nest more than one condition into <condition>" );
}
if( countConditions() < 1 )
{
throw new BuildException( "You must nest a condition into <condition>" );
}
Condition c = ( Condition )getConditions().nextElement();
if( c.eval() )
{
getProject().setNewProperty( property, value );
}
}
}

+ 526
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Copy.java View File

@@ -0,0 +1,526 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.FilterSet;
import org.apache.tools.ant.types.FilterSetCollection;
import org.apache.tools.ant.types.Mapper;
import org.apache.tools.ant.util.FileNameMapper;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.util.FlatFileNameMapper;
import org.apache.tools.ant.util.IdentityMapper;
import org.apache.tools.ant.util.SourceFileScanner;

/**
* A consolidated copy task. Copies a file or directory to a new file or
* directory. Files are only copied if the source file is newer than the
* destination file, or when the destination file does not exist. It is possible
* to explicitly overwrite existing files.</p> <p>
*
* This implementation is based on Arnout Kuiper's initial design document, the
* following mailing list discussions, and the copyfile/copydir tasks.</p>
*
* @author Glenn McAllister <a href="mailto:glennm@ca.ibm.com">glennm@ca.ibm.com
* </a>
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @author <A href="gholam@xtra.co.nz">Michael McCallum</A>
* @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
*/
public class Copy extends Task
{
protected File file = null;// the source file
protected File destFile = null;// the destination file
protected File destDir = null;// the destination directory
protected Vector filesets = new Vector();

protected boolean filtering = false;
protected boolean preserveLastModified = false;
protected boolean forceOverwrite = false;
protected boolean flatten = false;
protected int verbosity = Project.MSG_VERBOSE;
protected boolean includeEmpty = true;

protected Hashtable fileCopyMap = new Hashtable();
protected Hashtable dirCopyMap = new Hashtable();
protected Hashtable completeDirMap = new Hashtable();

protected Mapper mapperElement = null;
private Vector filterSets = new Vector();
private FileUtils fileUtils;

public Copy()
{
fileUtils = FileUtils.newFileUtils();
}

/**
* Sets a single source file to copy.
*
* @param file The new File value
*/
public void setFile( File file )
{
this.file = file;
}

/**
* Sets filtering.
*
* @param filtering The new Filtering value
*/
public void setFiltering( boolean filtering )
{
this.filtering = filtering;
}

/**
* When copying directory trees, the files can be "flattened" into a single
* directory. If there are multiple files with the same name in the source
* directory tree, only the first file will be copied into the "flattened"
* directory, unless the forceoverwrite attribute is true.
*
* @param flatten The new Flatten value
*/
public void setFlatten( boolean flatten )
{
this.flatten = flatten;
}

/**
* Used to copy empty directories.
*
* @param includeEmpty The new IncludeEmptyDirs value
*/
public void setIncludeEmptyDirs( boolean includeEmpty )
{
this.includeEmpty = includeEmpty;
}

/**
* Overwrite any existing destination file(s).
*
* @param overwrite The new Overwrite value
*/
public void setOverwrite( boolean overwrite )
{
this.forceOverwrite = overwrite;
}

/**
* Give the copied files the same last modified time as the original files.
*
* @param preserve The new PreserveLastModified value
* @deprecated setPreserveLastModified(String) has been deprecated and
* replaced with setPreserveLastModified(boolean) to consistently let
* the Introspection mechanism work.
*/
public void setPreserveLastModified( String preserve )
{
setPreserveLastModified( Project.toBoolean( preserve ) );
}

/**
* Give the copied files the same last modified time as the original files.
*
* @param preserve The new PreserveLastModified value
*/
public void setPreserveLastModified( boolean preserve )
{
preserveLastModified = preserve;
}

/**
* Sets the destination directory.
*
* @param destDir The new Todir value
*/
public void setTodir( File destDir )
{
this.destDir = destDir;
}

/**
* Sets the destination file.
*
* @param destFile The new Tofile value
*/
public void setTofile( File destFile )
{
this.destFile = destFile;
}

/**
* Used to force listing of all names of copied files.
*
* @param verbose The new Verbose value
*/
public void setVerbose( boolean verbose )
{
if( verbose )
{
this.verbosity = Project.MSG_INFO;
}
else
{
this.verbosity = Project.MSG_VERBOSE;
}
}

/**
* Adds a set of files (nested fileset attribute).
*
* @param set The feature to be added to the Fileset attribute
*/
public void addFileset( FileSet set )
{
filesets.addElement( set );
}

/**
* Create a nested filterset
*
* @return Description of the Returned Value
*/
public FilterSet createFilterSet()
{
FilterSet filterSet = new FilterSet();
filterSets.addElement( filterSet );
return filterSet;
}

/**
* Defines the FileNameMapper to use (nested mapper element).
*
* @return Description of the Returned Value
* @exception BuildException Description of Exception
*/
public Mapper createMapper()
throws BuildException
{
if( mapperElement != null )
{
throw new BuildException( "Cannot define more than one mapper",
location );
}
mapperElement = new Mapper( project );
return mapperElement;
}

/**
* Performs the copy operation.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
// make sure we don't have an illegal set of options
validateAttributes();

// deal with the single file
if( file != null )
{
if( file.exists() )
{
if( destFile == null )
{
destFile = new File( destDir, file.getName() );
}

if( forceOverwrite ||
( file.lastModified() > destFile.lastModified() ) )
{
fileCopyMap.put( file.getAbsolutePath(), destFile.getAbsolutePath() );
}
else
{
log( file + " omitted as " + destFile + " is up to date.",
Project.MSG_VERBOSE );
}
}
else
{
String message = "Could not find file "
+ file.getAbsolutePath() + " to copy.";
log( message );
throw new BuildException( message );
}
}

// deal with the filesets
for( int i = 0; i < filesets.size(); i++ )
{
FileSet fs = ( FileSet )filesets.elementAt( i );
DirectoryScanner ds = fs.getDirectoryScanner( project );
File fromDir = fs.getDir( project );

String[] srcFiles = ds.getIncludedFiles();
String[] srcDirs = ds.getIncludedDirectories();
boolean isEverythingIncluded = ds.isEverythingIncluded();
if( isEverythingIncluded
&& !flatten && mapperElement == null )
{
completeDirMap.put( fromDir, destDir );
}
scan( fromDir, destDir, srcFiles, srcDirs );
}

// do all the copy operations now...
doFileOperations();

// clean up destDir again - so this instance can be used a second
// time without throwing an exception
if( destFile != null )
{
destDir = null;
}
}

protected FileUtils getFileUtils()
{
return fileUtils;
}

/**
* Get the filtersets being applied to this operation.
*
* @return a vector of FilterSet objects
*/
protected Vector getFilterSets()
{
return filterSets;
}

protected void buildMap( File fromDir, File toDir, String[] names,
FileNameMapper mapper, Hashtable map )
{

String[] toCopy = null;
if( forceOverwrite )
{
Vector v = new Vector();
for( int i = 0; i < names.length; i++ )
{
if( mapper.mapFileName( names[i] ) != null )
{
v.addElement( names[i] );
}
}
toCopy = new String[v.size()];
v.copyInto( toCopy );
}
else
{
SourceFileScanner ds = new SourceFileScanner( this );
toCopy = ds.restrict( names, fromDir, toDir, mapper );
}

for( int i = 0; i < toCopy.length; i++ )
{
File src = new File( fromDir, toCopy[i] );
File dest = new File( toDir, mapper.mapFileName( toCopy[i] )[0] );
map.put( src.getAbsolutePath(), dest.getAbsolutePath() );
}
}

/**
* Actually does the file (and possibly empty directory) copies. This is a
* good method for subclasses to override.
*/
protected void doFileOperations()
{
if( fileCopyMap.size() > 0 )
{
log( "Copying " + fileCopyMap.size() +
" file" + ( fileCopyMap.size() == 1 ? "" : "s" ) +
" to " + destDir.getAbsolutePath() );

Enumeration e = fileCopyMap.keys();
while( e.hasMoreElements() )
{
String fromFile = ( String )e.nextElement();
String toFile = ( String )fileCopyMap.get( fromFile );

if( fromFile.equals( toFile ) )
{
log( "Skipping self-copy of " + fromFile, verbosity );
continue;
}

try
{
log( "Copying " + fromFile + " to " + toFile, verbosity );

FilterSetCollection executionFilters = new FilterSetCollection();
if( filtering )
{
executionFilters.addFilterSet( project.getGlobalFilterSet() );
}
for( Enumeration filterEnum = filterSets.elements(); filterEnum.hasMoreElements(); )
{
executionFilters.addFilterSet( ( FilterSet )filterEnum.nextElement() );
}
fileUtils.copyFile( fromFile, toFile, executionFilters,
forceOverwrite, preserveLastModified );
}
catch( IOException ioe )
{
String msg = "Failed to copy " + fromFile + " to " + toFile
+ " due to " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}
}
}

if( includeEmpty )
{
Enumeration e = dirCopyMap.elements();
int count = 0;
while( e.hasMoreElements() )
{
File d = new File( ( String )e.nextElement() );
if( !d.exists() )
{
if( !d.mkdirs() )
{
log( "Unable to create directory " + d.getAbsolutePath(), Project.MSG_ERR );
}
else
{
count++;
}
}
}

if( count > 0 )
{
log( "Copied " + count +
" empty director" +
( count == 1 ? "y" : "ies" ) +
" to " + destDir.getAbsolutePath() );
}
}
}

/**
* Compares source files to destination files to see if they should be
* copied.
*
* @param fromDir Description of Parameter
* @param toDir Description of Parameter
* @param files Description of Parameter
* @param dirs Description of Parameter
*/
protected void scan( File fromDir, File toDir, String[] files, String[] dirs )
{
FileNameMapper mapper = null;
if( mapperElement != null )
{
mapper = mapperElement.getImplementation();
}
else if( flatten )
{
mapper = new FlatFileNameMapper();
}
else
{
mapper = new IdentityMapper();
}

buildMap( fromDir, toDir, files, mapper, fileCopyMap );

if( includeEmpty )
{
buildMap( fromDir, toDir, dirs, mapper, dirCopyMap );
}
}

//************************************************************************
// protected and private methods
//************************************************************************

/**
* Ensure we have a consistent and legal set of attributes, and set any
* internal flags necessary based on different combinations of attributes.
*
* @exception BuildException Description of Exception
*/
protected void validateAttributes()
throws BuildException
{
if( file == null && filesets.size() == 0 )
{
throw new BuildException( "Specify at least one source - a file or a fileset." );
}

if( destFile != null && destDir != null )
{
throw new BuildException( "Only one of tofile and todir may be set." );
}

if( destFile == null && destDir == null )
{
throw new BuildException( "One of tofile or todir must be set." );
}

if( file != null && file.exists() && file.isDirectory() )
{
throw new BuildException( "Use a fileset to copy directories." );
}

if( destFile != null && filesets.size() > 0 )
{
if( filesets.size() > 1 )
{
throw new BuildException(
"Cannot concatenate multiple files into a single file." );
}
else
{
FileSet fs = ( FileSet )filesets.elementAt( 0 );
DirectoryScanner ds = fs.getDirectoryScanner( project );
String[] srcFiles = ds.getIncludedFiles();

if( srcFiles.length > 0 )
{
if( file == null )
{
file = new File( srcFiles[0] );
filesets.removeElementAt( 0 );
}
else
{
throw new BuildException(
"Cannot concatenate multiple files into a single file." );
}
}
else
{
throw new BuildException(
"Cannot perform operation from directory to file." );
}
}
}

if( destFile != null )
{
destDir = new File( destFile.getParent() );// be 1.1 friendly
}

}

}

+ 137
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Copydir.java View File

@@ -0,0 +1,137 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;

/**
* Copies a directory.
*
* @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a>
* @deprecated The copydir task is deprecated. Use copy instead.
*/

public class Copydir extends MatchingTask
{
private boolean filtering = false;
private boolean flatten = false;
private boolean forceOverwrite = false;
private Hashtable filecopyList = new Hashtable();
private File destDir;

private File srcDir;

public void setDest( File dest )
{
destDir = dest;
}

public void setFiltering( boolean filter )
{
filtering = filter;
}

public void setFlatten( boolean flatten )
{
this.flatten = flatten;
}

public void setForceoverwrite( boolean force )
{
forceOverwrite = force;
}

public void setSrc( File src )
{
srcDir = src;
}

public void execute()
throws BuildException
{
log( "DEPRECATED - The copydir task is deprecated. Use copy instead." );

if( srcDir == null )
{
throw new BuildException( "src attribute must be set!",
location );
}

if( !srcDir.exists() )
{
throw new BuildException( "srcdir " + srcDir.toString()
+ " does not exist!", location );
}

if( destDir == null )
{
throw new BuildException( "The dest attribute must be set.", location );
}

if( srcDir.equals( destDir ) )
{
log( "Warning: src == dest" );
}

DirectoryScanner ds = super.getDirectoryScanner( srcDir );

String[] files = ds.getIncludedFiles();
scanDir( srcDir, destDir, files );
if( filecopyList.size() > 0 )
{
log( "Copying " + filecopyList.size() + " file"
+ ( filecopyList.size() == 1 ? "" : "s" )
+ " to " + destDir.getAbsolutePath() );
Enumeration enum = filecopyList.keys();
while( enum.hasMoreElements() )
{
String fromFile = ( String )enum.nextElement();
String toFile = ( String )filecopyList.get( fromFile );
try
{
project.copyFile( fromFile, toFile, filtering,
forceOverwrite );
}
catch( IOException ioe )
{
String msg = "Failed to copy " + fromFile + " to " + toFile
+ " due to " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}
}
}
}

private void scanDir( File from, File to, String[] files )
{
for( int i = 0; i < files.length; i++ )
{
String filename = files[i];
File srcFile = new File( from, filename );
File destFile;
if( flatten )
{
destFile = new File( to, new File( filename ).getName() );
}
else
{
destFile = new File( to, filename );
}
if( forceOverwrite ||
( srcFile.lastModified() > destFile.lastModified() ) )
{
filecopyList.put( srcFile.getAbsolutePath(),
destFile.getAbsolutePath() );
}
}
}
}

+ 90
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Copyfile.java View File

@@ -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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;

/**
* Copies a file.
*
* @author duncan@x180.com
* @deprecated The copyfile task is deprecated. Use copy instead.
*/

public class Copyfile extends Task
{
private boolean filtering = false;
private boolean forceOverwrite = false;
private File destFile;

private File srcFile;

public void setDest( File dest )
{
destFile = dest;
}

public void setFiltering( String filter )
{
filtering = Project.toBoolean( filter );
}

public void setForceoverwrite( boolean force )
{
forceOverwrite = force;
}

public void setSrc( File src )
{
srcFile = src;
}

public void execute()
throws BuildException
{
log( "DEPRECATED - The copyfile task is deprecated. Use copy instead." );

if( srcFile == null )
{
throw new BuildException( "The src attribute must be present.", location );
}

if( !srcFile.exists() )
{
throw new BuildException( "src " + srcFile.toString()
+ " does not exist.", location );
}

if( destFile == null )
{
throw new BuildException( "The dest attribute must be present.", location );
}

if( srcFile.equals( destFile ) )
{
log( "Warning: src == dest" );
}

if( forceOverwrite || srcFile.lastModified() > destFile.lastModified() )
{
try
{
project.copyFile( srcFile, destFile, filtering, forceOverwrite );
}
catch( IOException ioe )
{
String msg = "Error copying file: " + srcFile.getAbsolutePath()
+ " due to " + ioe.getMessage();
throw new BuildException( msg );
}
}
}
}

+ 338
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Cvs.java View File

@@ -0,0 +1,338 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.Environment;

/**
* @author costin@dnt.ro
* @author stefano@apache.org
* @author Wolfgang Werner <a href="mailto:wwerner@picturesafe.de">
* wwerner@picturesafe.de</a>
*/

public class Cvs extends Task
{

private Commandline cmd = new Commandline();

/**
* the CVS command to execute.
*/
private String command = "checkout";

/**
* suppress information messages.
*/
private boolean quiet = false;

/**
* report only, don't change any files.
*/
private boolean noexec = false;

/**
* CVS port
*/
private int port = 0;

/**
* CVS password file
*/
private File passFile = null;

/**
* If true it will stop the build if cvs exits with error. Default is false.
* (Iulian)
*/
private boolean failOnError = false;

/**
* the CVSROOT variable.
*/
private String cvsRoot;

/**
* the CVS_RSH variable.
*/
private String cvsRsh;

/**
* the directory where the checked out files should be placed.
*/
private File dest;

/**
* the file to direct standard error from the command.
*/
private File error;

/**
* the file to direct standard output from the command.
*/
private File output;

/**
* the package/module to check out.
*/
private String pack;

public void setCommand( String c )
{
this.command = c;
}

public void setCvsRoot( String root )
{
// Check if not real cvsroot => set it to null
if( root != null )
{
if( root.trim().equals( "" ) )
root = null;
}

this.cvsRoot = root;
}

public void setCvsRsh( String rsh )
{
// Check if not real cvsrsh => set it to null
if( rsh != null )
{
if( rsh.trim().equals( "" ) )
rsh = null;
}

this.cvsRsh = rsh;
}


public void setDate( String p )
{
if( p != null && p.trim().length() > 0 )
{
cmd.createArgument().setValue( "-D" );
cmd.createArgument().setValue( p );
}
}

public void setDest( File dest )
{
this.dest = dest;
}

public void setError( File error )
{
this.error = error;
}

public void setFailOnError( boolean failOnError )
{
this.failOnError = failOnError;
}

public void setNoexec( boolean ne )
{
noexec = ne;
}

public void setOutput( File output )
{
this.output = output;
}

public void setPackage( String p )
{
this.pack = p;
}

public void setPassfile( File passFile )
{
this.passFile = passFile;
}

public void setPort( int port )
{
this.port = port;
}

public void setQuiet( boolean q )
{
quiet = q;
}

public void setTag( String p )
{
// Check if not real tag => set it to null
if( p != null && p.trim().length() > 0 )
{
cmd.createArgument().setValue( "-r" );
cmd.createArgument().setValue( p );
}
}


public void execute()
throws BuildException
{

// XXX: we should use JCVS (www.ice.com/JCVS) instead of command line
// execution so that we don't rely on having native CVS stuff around (SM)

// We can't do it ourselves as jCVS is GPLed, a third party task
// outside of jakarta repositories would be possible though (SB).

Commandline toExecute = new Commandline();

toExecute.setExecutable( "cvs" );
if( cvsRoot != null )
{
toExecute.createArgument().setValue( "-d" );
toExecute.createArgument().setValue( cvsRoot );
}
if( noexec )
{
toExecute.createArgument().setValue( "-n" );
}
if( quiet )
{
toExecute.createArgument().setValue( "-q" );
}
toExecute.createArgument().setLine( command );
toExecute.addArguments( cmd.getCommandline() );

if( pack != null )
{
toExecute.createArgument().setLine( pack );
}

Environment env = new Environment();

if( port > 0 )
{
Environment.Variable var = new Environment.Variable();
var.setKey( "CVS_CLIENT_PORT" );
var.setValue( String.valueOf( port ) );
env.addVariable( var );
}

if( passFile != null )
{
Environment.Variable var = new Environment.Variable();
var.setKey( "CVS_PASSFILE" );
var.setValue( String.valueOf( passFile ) );
env.addVariable( var );
}

if( cvsRsh != null )
{
Environment.Variable var = new Environment.Variable();
var.setKey( "CVS_RSH" );
var.setValue( String.valueOf( cvsRsh ) );
env.addVariable( var );
}

ExecuteStreamHandler streamhandler = null;
OutputStream outputstream = null;
OutputStream errorstream = null;
if( error == null && output == null )
{
streamhandler = new LogStreamHandler( this, Project.MSG_INFO,
Project.MSG_WARN );
}
else
{
if( output != null )
{
try
{
outputstream = new PrintStream( new BufferedOutputStream( new FileOutputStream( output ) ) );
}
catch( IOException e )
{
throw new BuildException( e );
}
}
else
{
outputstream = new LogOutputStream( this, Project.MSG_INFO );
}
if( error != null )
{
try
{
errorstream = new PrintStream( new BufferedOutputStream( new FileOutputStream( error ) ) );
}
catch( IOException e )
{
throw new BuildException( e );
}
}
else
{
errorstream = new LogOutputStream( this, Project.MSG_WARN );
}
streamhandler = new PumpStreamHandler( outputstream, errorstream );
}

Execute exe = new Execute( streamhandler,
null );

exe.setAntRun( project );
if( dest == null )
dest = project.getBaseDir();
exe.setWorkingDirectory( dest );

exe.setCommandline( toExecute.getCommandline() );
exe.setEnvironment( env.getVariables() );
try
{
int retCode = exe.execute();
/*
* Throw an exception if cvs exited with error. (Iulian)
*/
if( failOnError && retCode != 0 )
throw new BuildException( "cvs exited with error code " + retCode );
}
catch( IOException e )
{
throw new BuildException( e );
}
finally
{
if( output != null )
{
try
{
outputstream.close();
}
catch( IOException e )
{}
}
if( error != null )
{
try
{
errorstream.close();
}
catch( IOException e )
{}
}
}
}
}


+ 220
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Definer.java View File

@@ -0,0 +1,220 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Properties;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;

/**
* Base class for Taskdef and Typedef - does all the classpath handling and and
* class loading.
*
* @author Costin Manolache
* @author <a href="stefan.bodewig@epost.de">Stefan Bodewig</a>
*/
public abstract class Definer extends Task
{
private boolean reverseLoader = false;
private Path classpath;
private File file;
private String name;
private String resource;
private String value;

public void setClassname( String v )
{
value = v;
}

public void setClasspath( Path classpath )
{
if( this.classpath == null )
{
this.classpath = classpath;
}
else
{
this.classpath.append( classpath );
}
}

public void setClasspathRef( Reference r )
{
createClasspath().setRefid( r );
}

public void setFile( File file )
{
this.file = file;
}

public void setName( String name )
{
this.name = name;
}

public void setResource( String res )
{
this.resource = res;
}

public void setReverseLoader( boolean reverseLoader )
{
this.reverseLoader = reverseLoader;
log( "The reverseloader attribute is DEPRECATED. It will be removed", Project.MSG_WARN );
}

public String getClassname()
{
return value;
}

public Path createClasspath()
{
if( this.classpath == null )
{
this.classpath = new Path( project );
}
return this.classpath.createPath();
}

public void execute()
throws BuildException
{
AntClassLoader al = createLoader();

if( file == null && resource == null )
{

// simple case - one definition
if( name == null || value == null )
{
String msg = "name or classname attributes of "
+ getTaskName() + " element "
+ "are undefined";
throw new BuildException( msg );
}
addDefinition( al, name, value );

}
else
{

try
{
if( name != null || value != null )
{
String msg = "You must not specify name or value "
+ "together with file or resource.";
throw new BuildException( msg, location );
}

if( file != null && resource != null )
{
String msg = "You must not specify both, file and resource.";
throw new BuildException( msg, location );
}

Properties props = new Properties();
InputStream is = null;
if( file != null )
{
log( "Loading definitions from file " + file,
Project.MSG_VERBOSE );
is = new FileInputStream( file );
if( is == null )
{
log( "Could not load definitions from file " + file
+ ". It doesn\'t exist.", Project.MSG_WARN );
}
}
if( resource != null )
{
log( "Loading definitions from resource " + resource,
Project.MSG_VERBOSE );
is = al.getResourceAsStream( resource );
if( is == null )
{
log( "Could not load definitions from resource "
+ resource + ". It could not be found.",
Project.MSG_WARN );
}
}

if( is != null )
{
props.load( is );
Enumeration keys = props.keys();
while( keys.hasMoreElements() )
{
String n = ( String )keys.nextElement();
String v = props.getProperty( n );
addDefinition( al, n, v );
}
}
}
catch( IOException ex )
{
throw new BuildException( ex);
}
}
}

protected abstract void addDefinition( String name, Class c );

private void addDefinition( ClassLoader al, String name, String value )
throws BuildException
{
try
{
Class c = al.loadClass( value );
AntClassLoader.initializeClass( c );
addDefinition( name, c );
}
catch( ClassNotFoundException cnfe )
{
String msg = getTaskName() + " class " + value +
" cannot be found";
throw new BuildException( msg, cnfe, location );
}
catch( NoClassDefFoundError ncdfe )
{
String msg = getTaskName() + " class " + value +
" cannot be found";
throw new BuildException( msg, ncdfe, location );
}
}


private AntClassLoader createLoader()
{
AntClassLoader al = null;
if( classpath != null )
{
al = new AntClassLoader( project, classpath, !reverseLoader );
}
else
{
al = new AntClassLoader( project, Path.systemClasspath, !reverseLoader );
}
// need to load Task via system classloader or the new
// task we want to define will never be a Task but always
// be wrapped into a TaskAdapter.
al.addSystemPackageRoot( "org.apache.tools.ant" );
return al;
}
}

+ 456
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Delete.java View File

@@ -0,0 +1,456 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.PatternSet;

/**
* Deletes a file or directory, or set of files defined by a fileset. The
* original delete task would delete a file, or a set of files using the
* include/exclude syntax. The deltree task would delete a directory tree. This
* task combines the functionality of these two originally distinct tasks. <p>
*
* Currently Delete extends MatchingTask. This is intend <i>only</i> to provide
* backwards compatibility for a release. The future position is to use nested
* filesets exclusively.</p>
*
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">
* stefano@apache.org</a>
* @author Tom Dimock <a href="mailto:tad1@cornell.edu">tad1@cornell.edu</a>
* @author Glenn McAllister <a href="mailto:glennm@ca.ibm.com">glennm@ca.ibm.com
* </a>
* @author Jon S. Stevens <a href="mailto:jon@latchkey.com">jon@latchkey.com</a>
*/
public class Delete extends MatchingTask
{
protected File file = null;
protected File dir = null;
protected Vector filesets = new Vector();
protected boolean usedMatchingTask = false;
protected boolean includeEmpty = false;// by default, remove matching empty dirs

private int verbosity = Project.MSG_VERBOSE;
private boolean quiet = false;
private boolean failonerror = true;

/**
* Sets whether default exclusions should be used or not.
*
* @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
* should be used, "false"|"off"|"no" when they shouldn't be used.
*/
public void setDefaultexcludes( boolean useDefaultExcludes )
{
usedMatchingTask = true;
super.setDefaultexcludes( useDefaultExcludes );
}

/**
* Set the directory from which files are to be deleted
*
* @param dir the directory path.
*/
public void setDir( File dir )
{
this.dir = dir;
}

/**
* Sets the set of exclude patterns. Patterns may be separated by a comma or
* a space.
*
* @param excludes the string containing the exclude patterns
*/
public void setExcludes( String excludes )
{
usedMatchingTask = true;
super.setExcludes( excludes );
}

/**
* Sets the name of the file containing the includes patterns.
*
* @param excludesfile A string containing the filename to fetch the include
* patterns from.
*/
public void setExcludesfile( File excludesfile )
{
usedMatchingTask = true;
super.setExcludesfile( excludesfile );
}

/**
* this flag means 'note errors to the output, but keep going'
*
* @param failonerror true or false
*/
public void setFailOnError( boolean failonerror )
{
this.failonerror = failonerror;
}

/**
* Set the name of a single file to be removed.
*
* @param file the file to be deleted
*/
public void setFile( File file )
{
this.file = file;
}


/**
* Used to delete empty directories.
*
* @param includeEmpty The new IncludeEmptyDirs value
*/
public void setIncludeEmptyDirs( boolean includeEmpty )
{
this.includeEmpty = includeEmpty;
}

/**
* Sets the set of include patterns. Patterns may be separated by a comma or
* a space.
*
* @param includes the string containing the include patterns
*/
public void setIncludes( String includes )
{
usedMatchingTask = true;
super.setIncludes( includes );
}

/**
* Sets the name of the file containing the includes patterns.
*
* @param includesfile A string containing the filename to fetch the include
* patterns from.
*/
public void setIncludesfile( File includesfile )
{
usedMatchingTask = true;
super.setIncludesfile( includesfile );
}

/**
* If the file does not exist, do not display a diagnostic message or modify
* the exit status to reflect an error. This means that if a file or
* directory cannot be deleted, then no error is reported. This setting
* emulates the -f option to the Unix &quot;rm&quot; command. Default is
* false meaning things are &quot;noisy&quot;
*
* @param quiet "true" or "on"
*/
public void setQuiet( boolean quiet )
{
this.quiet = quiet;
if( quiet )
{
this.failonerror = false;
}
}

/**
* Used to force listing of all names of deleted files.
*
* @param verbose "true" or "on"
*/
public void setVerbose( boolean verbose )
{
if( verbose )
{
this.verbosity = Project.MSG_INFO;
}
else
{
this.verbosity = Project.MSG_VERBOSE;
}
}

/**
* Adds a set of files (nested fileset attribute).
*
* @param set The feature to be added to the Fileset attribute
*/
public void addFileset( FileSet set )
{
filesets.addElement( set );
}

/**
* add a name entry on the exclude list
*
* @return Description of the Returned Value
*/
public PatternSet.NameEntry createExclude()
{
usedMatchingTask = true;
return super.createExclude();
}

/**
* add a name entry on the include list
*
* @return Description of the Returned Value
*/
public PatternSet.NameEntry createInclude()
{
usedMatchingTask = true;
return super.createInclude();
}

/**
* add a set of patterns
*
* @return Description of the Returned Value
*/
public PatternSet createPatternSet()
{
usedMatchingTask = true;
return super.createPatternSet();
}

/**
* Delete the file(s).
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
if( usedMatchingTask )
{
log( "DEPRECATED - Use of the implicit FileSet is deprecated. Use a nested fileset element instead." );
}

if( file == null && dir == null && filesets.size() == 0 )
{
throw new BuildException( "At least one of the file or dir attributes, or a fileset element, must be set." );
}

if( quiet && failonerror )
{
throw new BuildException( "quiet and failonerror cannot both be set to true",
location );
}

// delete the single file
if( file != null )
{
if( file.exists() )
{
if( file.isDirectory() )
{
log( "Directory " + file.getAbsolutePath() + " cannot be removed using the file attribute. Use dir instead." );
}
else
{
log( "Deleting: " + file.getAbsolutePath() );

if( !file.delete() )
{
String message = "Unable to delete file " + file.getAbsolutePath();
if( failonerror )
throw new BuildException( message );
else
log( message,
quiet ? Project.MSG_VERBOSE : Project.MSG_WARN );
}
}
}
else
{
log( "Could not find file " + file.getAbsolutePath() + " to delete.",
Project.MSG_VERBOSE );
}
}

// delete the directory
if( dir != null && dir.exists() && dir.isDirectory() && !usedMatchingTask )
{
/*
* If verbosity is MSG_VERBOSE, that mean we are doing regular logging
* (backwards as that sounds). In that case, we want to print one
* message about deleting the top of the directory tree. Otherwise,
* the removeDir method will handle messages for _all_ directories.
*/
if( verbosity == Project.MSG_VERBOSE )
{
log( "Deleting directory " + dir.getAbsolutePath() );
}
removeDir( dir );
}

// delete the files in the filesets
for( int i = 0; i < filesets.size(); i++ )
{
FileSet fs = ( FileSet )filesets.elementAt( i );
try
{
DirectoryScanner ds = fs.getDirectoryScanner( project );
String[] files = ds.getIncludedFiles();
String[] dirs = ds.getIncludedDirectories();
removeFiles( fs.getDir( project ), files, dirs );
}
catch( BuildException be )
{
// directory doesn't exist or is not readable
if( failonerror )
{
throw be;
}
else
{
log( be.getMessage(),
quiet ? Project.MSG_VERBOSE : Project.MSG_WARN );
}
}
}

// delete the files from the default fileset
if( usedMatchingTask && dir != null )
{
try
{
DirectoryScanner ds = super.getDirectoryScanner( dir );
String[] files = ds.getIncludedFiles();
String[] dirs = ds.getIncludedDirectories();
removeFiles( dir, files, dirs );
}
catch( BuildException be )
{
// directory doesn't exist or is not readable
if( failonerror )
{
throw be;
}
else
{
log( be.getMessage(),
quiet ? Project.MSG_VERBOSE : Project.MSG_WARN );
}
}
}
}

//************************************************************************
// protected and private methods
//************************************************************************

protected void removeDir( File d )
{
String[] list = d.list();
if( list == null )
list = new String[0];
for( int i = 0; i < list.length; i++ )
{
String s = list[i];
File f = new File( d, s );
if( f.isDirectory() )
{
removeDir( f );
}
else
{
log( "Deleting " + f.getAbsolutePath(), verbosity );
if( !f.delete() )
{
String message = "Unable to delete file " + f.getAbsolutePath();
if( failonerror )
throw new BuildException( message );
else
log( message,
quiet ? Project.MSG_VERBOSE : Project.MSG_WARN );
}
}
}
log( "Deleting directory " + d.getAbsolutePath(), verbosity );
if( !d.delete() )
{
String message = "Unable to delete directory " + dir.getAbsolutePath();
if( failonerror )
throw new BuildException( message );
else
log( message,
quiet ? Project.MSG_VERBOSE : Project.MSG_WARN );
}
}

/**
* remove an array of files in a directory, and a list of subdirectories
* which will only be deleted if 'includeEmpty' is true
*
* @param d directory to work from
* @param files array of files to delete; can be of zero length
* @param dirs array of directories to delete; can of zero length
*/
protected void removeFiles( File d, String[] files, String[] dirs )
{
if( files.length > 0 )
{
log( "Deleting " + files.length + " files from " + d.getAbsolutePath() );
for( int j = 0; j < files.length; j++ )
{
File f = new File( d, files[j] );
log( "Deleting " + f.getAbsolutePath(), verbosity );
if( !f.delete() )
{
String message = "Unable to delete file " + f.getAbsolutePath();
if( failonerror )
throw new BuildException( message );
else
log( message,
quiet ? Project.MSG_VERBOSE : Project.MSG_WARN );
}
}
}

if( dirs.length > 0 && includeEmpty )
{
int dirCount = 0;
for( int j = dirs.length - 1; j >= 0; j-- )
{
File dir = new File( d, dirs[j] );
String[] dirFiles = dir.list();
if( dirFiles == null || dirFiles.length == 0 )
{
log( "Deleting " + dir.getAbsolutePath(), verbosity );
if( !dir.delete() )
{
String message = "Unable to delete directory "
+ dir.getAbsolutePath();
if( failonerror )
throw new BuildException( message );
else
log( message,
quiet ? Project.MSG_VERBOSE : Project.MSG_WARN );
}
else
{
dirCount++;
}
}
}

if( dirCount > 0 )
{
log( "Deleted " + dirCount + " director" +
( dirCount == 1 ? "y" : "ies" ) +
" from " + d.getAbsolutePath() );
}
}
}
}


+ 103
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Deltree.java View File

@@ -0,0 +1,103 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

/**
* @author duncan@x180.com
* @deprecated The deltree task is deprecated. Use delete instead.
*/

public class Deltree extends Task
{

private File dir;

public void setDir( File dir )
{
this.dir = dir;
}

public void execute()
throws BuildException
{
log( "DEPRECATED - The deltree task is deprecated. Use delete instead." );

if( dir == null )
{
throw new BuildException( "dir attribute must be set!", location );
}

if( dir.exists() )
{
if( !dir.isDirectory() )
{
if( !dir.delete() )
{
throw new BuildException( "Unable to delete directory "
+ dir.getAbsolutePath(),
location );
}
return;
// String msg = "Given dir: " + dir.getAbsolutePath() +
// " is not a dir";
// throw new BuildException(msg);
}

log( "Deleting: " + dir.getAbsolutePath() );

try
{
removeDir( dir );
}
catch( IOException ioe )
{
String msg = "Unable to delete " + dir.getAbsolutePath();
throw new BuildException( msg, location );
}
}
}

private void removeDir( File dir )
throws IOException
{

// check to make sure that the given dir isn't a symlink
// the comparison of absolute path and canonical path
// catches this

// if (dir.getCanonicalPath().equals(dir.getAbsolutePath())) {
// (costin) It will not work if /home/costin is symlink to /da0/home/costin ( taz
// for example )
String[] list = dir.list();
for( int i = 0; i < list.length; i++ )
{
String s = list[i];
File f = new File( dir, s );
if( f.isDirectory() )
{
removeDir( f );
}
else
{
if( !f.delete() )
{
throw new BuildException( "Unable to delete file " + f.getAbsolutePath() );
}
}
}
if( !dir.delete() )
{
throw new BuildException( "Unable to delete directory " + dir.getAbsolutePath() );
}
}
}


+ 308
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/DependSet.java View File

@@ -0,0 +1,308 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.util.Date;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.myrmidon.framework.Os;
import org.apache.tools.ant.types.FileList;
import org.apache.tools.ant.types.FileSet;

/**
* A Task to record explicit dependencies. If any of the target files are out of
* date with respect to any of the source files, all target files are removed.
* This is useful where dependencies cannot be computed (for example,
* dynamically interpreted parameters or files that need to stay in synch but
* are not directly linked) or where the ant task in question could compute them
* but does not (for example, the linked DTD for an XML file using the style
* task). nested arguments:
* <ul>
* <li> srcfileset (fileset describing the source files to examine)
* <li> srcfilelist (filelist describing the source files to examine)
* <li> targetfileset (fileset describing the target files to examine)
* <li> targetfilelist (filelist describing the target files to examine)
* </ul>
* At least one instance of either a fileset or filelist for both source and
* target are required. <p>
*
* This task will examine each of the source files against each of the target
* files. If any target files are out of date with respect to any of the source
* files, all targets are removed. If any files named in a (src or target)
* filelist do not exist, all targets are removed. Hint: If missing files should
* be ignored, specify them as include patterns in filesets, rather than using
* filelists. </p> <p>
*
* This task attempts to optimize speed of dependency checking. It will stop
* after the first out of date file is found and remove all targets, rather than
* exhaustively checking every source vs target combination unnecessarily. </p>
* <p>
*
* Example uses:
* <ul>
* <li> Record the fact that an XML file must be up to date with respect to
* its XSD (Schema file), even though the XML file itself includes no
* reference to its XSD. </li>
* <li> Record the fact that an XSL stylesheet includes other sub-stylesheets
* </li>
* <li> Record the fact that java files must be recompiled if the ant build
* file changes </li>
* </ul>
*
*
* @author <a href="mailto:cstrong@arielpartners.com">Craeg Strong</a>
* @version $Revision$ $Date$
*/
public class DependSet extends MatchingTask
{

private Vector sourceFileSets = new Vector();
private Vector sourceFileLists = new Vector();
private Vector targetFileSets = new Vector();
private Vector targetFileLists = new Vector();

/**
* Creates a new DependSet Task.
*/
public DependSet() { }

/**
* Nested &lt;srcfilelist&gt; element.
*
* @param fl The feature to be added to the Srcfilelist attribute
*/
public void addSrcfilelist( FileList fl )
{
sourceFileLists.addElement( fl );
}//-- DependSet

/**
* Nested &lt;srcfileset&gt; element.
*
* @param fs The feature to be added to the Srcfileset attribute
*/
public void addSrcfileset( FileSet fs )
{
sourceFileSets.addElement( fs );
}

/**
* Nested &lt;targetfilelist&gt; element.
*
* @param fl The feature to be added to the Targetfilelist attribute
*/
public void addTargetfilelist( FileList fl )
{
targetFileLists.addElement( fl );
}

/**
* Nested &lt;targetfileset&gt; element.
*
* @param fs The feature to be added to the Targetfileset attribute
*/
public void addTargetfileset( FileSet fs )
{
targetFileSets.addElement( fs );
}

/**
* Executes the task.
*
* @exception BuildException Description of Exception
*/

public void execute()
throws BuildException
{

if( ( sourceFileSets.size() == 0 ) && ( sourceFileLists.size() == 0 ) )
{
throw new BuildException( "At least one <srcfileset> or <srcfilelist> element must be set" );
}

if( ( targetFileSets.size() == 0 ) && ( targetFileLists.size() == 0 ) )
{
throw new BuildException( "At least one <targetfileset> or <targetfilelist> element must be set" );
}

long now = ( new Date() ).getTime();
/*
* If we're on Windows, we have to munge the time up to 2 secs to
* be able to check file modification times.
* (Windows has a max resolution of two secs for modification times)
*/
if( Os.isFamily( "windows" ) )
{
now += 2000;
}

//
// Grab all the target files specified via filesets
//
Vector allTargets = new Vector();
Enumeration enumTargetSets = targetFileSets.elements();
while( enumTargetSets.hasMoreElements() )
{

FileSet targetFS = ( FileSet )enumTargetSets.nextElement();
DirectoryScanner targetDS = targetFS.getDirectoryScanner( project );
String[] targetFiles = targetDS.getIncludedFiles();

for( int i = 0; i < targetFiles.length; i++ )
{

File dest = new File( targetFS.getDir( project ), targetFiles[i] );
allTargets.addElement( dest );

if( dest.lastModified() > now )
{
log( "Warning: " + targetFiles[i] + " modified in the future.",
Project.MSG_WARN );
}
}
}

//
// Grab all the target files specified via filelists
//
boolean upToDate = true;
Enumeration enumTargetLists = targetFileLists.elements();
while( enumTargetLists.hasMoreElements() )
{

FileList targetFL = ( FileList )enumTargetLists.nextElement();
String[] targetFiles = targetFL.getFiles( project );

for( int i = 0; i < targetFiles.length; i++ )
{

File dest = new File( targetFL.getDir( project ), targetFiles[i] );
if( !dest.exists() )
{
log( targetFiles[i] + " does not exist.", Project.MSG_VERBOSE );
upToDate = false;
continue;
}
else
{
allTargets.addElement( dest );
}
if( dest.lastModified() > now )
{
log( "Warning: " + targetFiles[i] + " modified in the future.",
Project.MSG_WARN );
}
}
}

//
// Check targets vs source files specified via filesets
//
if( upToDate )
{
Enumeration enumSourceSets = sourceFileSets.elements();
while( upToDate && enumSourceSets.hasMoreElements() )
{

FileSet sourceFS = ( FileSet )enumSourceSets.nextElement();
DirectoryScanner sourceDS = sourceFS.getDirectoryScanner( project );
String[] sourceFiles = sourceDS.getIncludedFiles();

for( int i = 0; upToDate && i < sourceFiles.length; i++ )
{
File src = new File( sourceFS.getDir( project ), sourceFiles[i] );

if( src.lastModified() > now )
{
log( "Warning: " + sourceFiles[i] + " modified in the future.",
Project.MSG_WARN );
}

Enumeration enumTargets = allTargets.elements();
while( upToDate && enumTargets.hasMoreElements() )
{

File dest = ( File )enumTargets.nextElement();
if( src.lastModified() > dest.lastModified() )
{
log( dest.getPath() + " is out of date with respect to " +
sourceFiles[i], Project.MSG_VERBOSE );
upToDate = false;

}
}
}
}
}

//
// Check targets vs source files specified via filelists
//
if( upToDate )
{
Enumeration enumSourceLists = sourceFileLists.elements();
while( upToDate && enumSourceLists.hasMoreElements() )
{

FileList sourceFL = ( FileList )enumSourceLists.nextElement();
String[] sourceFiles = sourceFL.getFiles( project );

int i = 0;
do
{
File src = new File( sourceFL.getDir( project ), sourceFiles[i] );

if( src.lastModified() > now )
{
log( "Warning: " + sourceFiles[i] + " modified in the future.",
Project.MSG_WARN );
}

if( !src.exists() )
{
log( sourceFiles[i] + " does not exist.", Project.MSG_VERBOSE );
upToDate = false;
break;
}

Enumeration enumTargets = allTargets.elements();
while( upToDate && enumTargets.hasMoreElements() )
{

File dest = ( File )enumTargets.nextElement();

if( src.lastModified() > dest.lastModified() )
{
log( dest.getPath() + " is out of date with respect to " +
sourceFiles[i], Project.MSG_VERBOSE );
upToDate = false;

}
}
}while ( upToDate && ( ++i < sourceFiles.length ) );
}
}

if( !upToDate )
{
log( "Deleting all target files. ", Project.MSG_VERBOSE );
for( Enumeration e = allTargets.elements(); e.hasMoreElements(); )
{
File fileToRemove = ( File )e.nextElement();
log( "Deleting file " + fileToRemove.getAbsolutePath(), Project.MSG_VERBOSE );
fileToRemove.delete();
}
}

}//-- execute

}//-- DependSet.java

+ 114
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Ear.java View File

@@ -0,0 +1,114 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.ZipFileSet;
import org.apache.tools.zip.ZipOutputStream;


/**
* Creates a EAR archive. Based on WAR task
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @author <a href="mailto:leslie.hughes@rubus.com">Les Hughes</a>
*/
public class Ear extends Jar
{

private File deploymentDescriptor;
private boolean descriptorAdded;

public Ear()
{
super();
archiveType = "ear";
emptyBehavior = "create";
}

public void setAppxml( File descr )
{
deploymentDescriptor = descr;
if( !deploymentDescriptor.exists() )
throw new BuildException( "Deployment descriptor: " + deploymentDescriptor + " does not exist." );

// Create a ZipFileSet for this file, and pass it up.
ZipFileSet fs = new ZipFileSet();
fs.setDir( new File( deploymentDescriptor.getParent() ) );
fs.setIncludes( deploymentDescriptor.getName() );
fs.setFullpath( "META-INF/application.xml" );
super.addFileset( fs );
}

public void setEarfile( File earFile )
{
log( "DEPRECATED - The earfile attribute is deprecated. Use file attribute instead." );
setFile( earFile );
}


public void addArchives( ZipFileSet fs )
{
// We just set the prefix for this fileset, and pass it up.
// Do we need to do this? LH
log( "addArchives called", Project.MSG_DEBUG );
fs.setPrefix( "/" );
super.addFileset( fs );
}

/**
* Make sure we don't think we already have a web.xml next time this task
* gets executed.
*/
protected void cleanUp()
{
descriptorAdded = false;
super.cleanUp();
}


protected void initZipOutputStream( ZipOutputStream zOut )
throws IOException, BuildException
{
// If no webxml file is specified, it's an error.
if( deploymentDescriptor == null && !isInUpdateMode() )
{
throw new BuildException( "appxml attribute is required", location );
}

super.initZipOutputStream( zOut );
}

protected void zipFile( File file, ZipOutputStream zOut, String vPath )
throws IOException
{
// If the file being added is WEB-INF/web.xml, we warn if it's not the
// one specified in the "webxml" attribute - or if it's being added twice,
// meaning the same file is specified by the "webxml" attribute and in
// a <fileset> element.
if( vPath.equalsIgnoreCase( "META-INF/aplication.xml" ) )
{
if( deploymentDescriptor == null || !deploymentDescriptor.equals( file ) || descriptorAdded )
{
log( "Warning: selected " + archiveType + " files include a META-INF/application.xml which will be ignored " +
"(please use appxml attribute to " + archiveType + " task)", Project.MSG_WARN );
}
else
{
super.zipFile( file, zOut, vPath );
descriptorAdded = true;
}
}
else
{
super.zipFile( file, zOut, vPath );
}
}
}

+ 159
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Echo.java View File

@@ -0,0 +1,159 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.EnumeratedAttribute;

/**
* Echo
*
* @author costin@dnt.ro
*/
public class Echo extends Task
{
protected String message = "";// required
protected File file = null;
protected boolean append = false;

// by default, messages are always displayed
protected int logLevel = Project.MSG_WARN;

/**
* Shall we append to an existing file?
*
* @param append The new Append value
*/
public void setAppend( boolean append )
{
this.append = append;
}

/**
* Sets the file attribute.
*
* @param file The new File value
*/
public void setFile( File file )
{
this.file = file;
}

/**
* Set the logging level to one of
* <ul>
* <li> error</li>
* <li> warning</li>
* <li> info</li>
* <li> verbose</li>
* <li> debug</li>
* <ul><p>
*
* The default is &quot;warning&quot; to ensure that messages are
* displayed by default when using the -quiet command line option.</p>
*
* @param echoLevel The new Level value
*/
public void setLevel( EchoLevel echoLevel )
{
String option = echoLevel.getValue();
if( option.equals( "error" ) )
{
logLevel = Project.MSG_ERR;
}
else if( option.equals( "warning" ) )
{
logLevel = Project.MSG_WARN;
}
else if( option.equals( "info" ) )
{
logLevel = Project.MSG_INFO;
}
else if( option.equals( "verbose" ) )
{
logLevel = Project.MSG_VERBOSE;
}
else
{
// must be "debug"
logLevel = Project.MSG_DEBUG;
}
}

/**
* Sets the message variable.
*
* @param msg Sets the value for the message variable.
*/
public void setMessage( String msg )
{
this.message = msg;
}

/**
* Set a multiline message.
*
* @param msg The feature to be added to the Text attribute
*/
public void addText( String msg )
{
message += project.replaceProperties( msg );
}

/**
* Does the work.
*
* @exception BuildException if someting goes wrong with the build
*/
public void execute()
throws BuildException
{
if( file == null )
{
log( message, logLevel );
}
else
{
FileWriter out = null;
try
{
out = new FileWriter( file.getAbsolutePath(), append );
out.write( message, 0, message.length() );
}
catch( IOException ioe )
{
throw new BuildException( ioe);
}
finally
{
if( out != null )
{
try
{
out.close();
}
catch( IOException ioex )
{}
}
}
}
}

public static class EchoLevel extends EnumeratedAttribute
{
public String[] getValues()
{
return new String[]{"error", "warning", "info", "verbose", "debug"};
}
}
}

+ 252
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Exec.java View File

@@ -0,0 +1,252 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;

/**
* Executes a given command if the os platform is appropriate.
*
* @author duncan@x180.com
* @author rubys@us.ibm.com
* @deprecated Instead of using this class, please extend ExecTask or delegate
* to Execute.
*/
public class Exec extends Task
{

private final static int BUFFER_SIZE = 512;
protected PrintWriter fos = null;
private boolean failOnError = false;
private String command;
private File dir;
private String os;
private String out;

public void setCommand( String command )
{
this.command = command;
}

public void setDir( String d )
{
this.dir = project.resolveFile( d );
}

public void setFailonerror( boolean fail )
{
failOnError = fail;
}

public void setOs( String os )
{
this.os = os;
}

public void setOutput( String out )
{
this.out = out;
}

public void execute()
throws BuildException
{
run( command );
}

protected void logFlush()
{
if( fos != null )
fos.close();
}

protected void outputLog( String line, int messageLevel )
{
if( fos == null )
{
log( line, messageLevel );
}
else
{
fos.println( line );
}
}

protected int run( String command )
throws BuildException
{

int err = -1;// assume the worst

// test if os match
String myos = System.getProperty( "os.name" );
log( "Myos = " + myos, Project.MSG_VERBOSE );
if( ( os != null ) && ( os.indexOf( myos ) < 0 ) )
{
// this command will be executed only on the specified OS
log( "Not found in " + os, Project.MSG_VERBOSE );
return 0;
}

// default directory to the project's base directory
if( dir == null )
dir = project.getBaseDir();

if( myos.toLowerCase().indexOf( "windows" ) >= 0 )
{
if( !dir.equals( project.resolveFile( "." ) ) )
{
if( myos.toLowerCase().indexOf( "nt" ) >= 0 )
{
command = "cmd /c cd " + dir + " && " + command;
}
else
{
String ant = project.getProperty( "ant.home" );
if( ant == null )
{
throw new BuildException( "Property 'ant.home' not found", location );
}

String antRun = project.resolveFile( ant + "/bin/antRun.bat" ).toString();
command = antRun + " " + dir + " " + command;
}
}
}
else
{
String ant = project.getProperty( "ant.home" );
if( ant == null )
throw new BuildException( "Property 'ant.home' not found", location );
String antRun = project.resolveFile( ant + "/bin/antRun" ).toString();

command = antRun + " " + dir + " " + command;
}

try
{
// show the command
log( command, Project.MSG_VERBOSE );

// exec command on system runtime
Process proc = Runtime.getRuntime().exec( command );

if( out != null )
{
fos = new PrintWriter( new FileWriter( out ) );
log( "Output redirected to " + out, Project.MSG_VERBOSE );
}

// copy input and error to the output stream
StreamPumper inputPumper =
new StreamPumper( proc.getInputStream(), Project.MSG_INFO, this );
StreamPumper errorPumper =
new StreamPumper( proc.getErrorStream(), Project.MSG_WARN, this );

// starts pumping away the generated output/error
inputPumper.start();
errorPumper.start();

// Wait for everything to finish
proc.waitFor();
inputPumper.join();
errorPumper.join();
proc.destroy();

// close the output file if required
logFlush();

// check its exit value
err = proc.exitValue();
if( err != 0 )
{
if( failOnError )
{
throw new BuildException( "Exec returned: " + err, location );
}
else
{
log( "Result: " + err, Project.MSG_ERR );
}
}
}
catch( IOException ioe )
{
throw new BuildException( "Error exec: " + command, ioe, location );
}
catch( InterruptedException ex )
{}

return err;
}

// Inner class for continually pumping the input stream during
// Process's runtime.
class StreamPumper extends Thread
{
private boolean endOfStream = false;
private int SLEEP_TIME = 5;
private BufferedReader din;
private int messageLevel;
private Exec parent;

public StreamPumper( InputStream is, int messageLevel, Exec parent )
{
this.din = new BufferedReader( new InputStreamReader( is ) );
this.messageLevel = messageLevel;
this.parent = parent;
}

public void pumpStream()
throws IOException
{
byte[] buf = new byte[BUFFER_SIZE];
if( !endOfStream )
{
String line = din.readLine();

if( line != null )
{
outputLog( line, messageLevel );
}
else
{
endOfStream = true;
}
}
}

public void run()
{
try
{
try
{
while( !endOfStream )
{
pumpStream();
sleep( SLEEP_TIME );
}
}
catch( InterruptedException ie )
{}
din.close();
}
catch( IOException ioe )
{}
}
}
}

+ 456
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ExecTask.java View File

@@ -0,0 +1,456 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.Environment;

/**
* Executes a given command if the os platform is appropriate.
*
* @author duncan@x180.com
* @author rubys@us.ibm.com
* @author thomas.haas@softwired-inc.com
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @author <a href="mailto:mariusz@rakiura.org">Mariusz Nowostawski</a>
*/
public class ExecTask extends Task
{

private static String lSep = System.getProperty( "line.separator" );
protected boolean failOnError = false;
protected boolean newEnvironment = false;
private Integer timeout = null;
private Environment env = new Environment();
protected Commandline cmdl = new Commandline();
private FileOutputStream fos = null;
private ByteArrayOutputStream baos = null;
private boolean failIfExecFails = true;

/**
* Controls whether the VM (1.3 and above) is used to execute the command
*/
private boolean vmLauncher = true;
private File dir;

private String os;
private File out;
private String outputprop;
private String resultProperty;

/**
* The full commandline to execute, executable + arguments.
*
* @param cmdl The new Command value
*/
public void setCommand( Commandline cmdl )
{
log( "The command attribute is deprecated. " +
"Please use the executable attribute and nested arg elements.",
Project.MSG_WARN );
this.cmdl = cmdl;
}

/**
* The working directory of the process
*
* @param d The new Dir value
*/
public void setDir( File d )
{
this.dir = d;
}

/**
* The command to execute.
*
* @param value The new Executable value
*/
public void setExecutable( String value )
{
cmdl.setExecutable( value );
}

/**
* ant attribute
*
* @param flag The new FailIfExecutionFails value
*/
public void setFailIfExecutionFails( boolean flag )
{
failIfExecFails = flag;
}

/**
* Throw a BuildException if process returns non 0.
*
* @param fail The new Failonerror value
*/
public void setFailonerror( boolean fail )
{
failOnError = fail;
}

/**
* Use a completely new environment
*
* @param newenv The new Newenvironment value
*/
public void setNewenvironment( boolean newenv )
{
newEnvironment = newenv;
}

/**
* Only execute the process if <code>os.name</code> is included in this
* string.
*
* @param os The new Os value
*/
public void setOs( String os )
{
this.os = os;
}

/**
* File the output of the process is redirected to.
*
* @param out The new Output value
*/
public void setOutput( File out )
{
this.out = out;
}

/**
* Property name whose value should be set to the output of the process
*
* @param outputprop The new Outputproperty value
*/
public void setOutputproperty( String outputprop )
{
this.outputprop = outputprop;
}

/**
* fill a property in with a result. when no property is defined: failure to
* execute
*
* @param resultProperty The new ResultProperty value
* @since 1.5
*/
public void setResultProperty( String resultProperty )
{
this.resultProperty = resultProperty;
}

/**
* Timeout in milliseconds after which the process will be killed.
*
* @param value The new Timeout value
*/
public void setTimeout( Integer value )
{
timeout = value;
}

/**
* Control whether the VM is used to launch the new process or whether the
* OS's shell is used.
*
* @param vmLauncher The new VMLauncher value
*/
public void setVMLauncher( boolean vmLauncher )
{
this.vmLauncher = vmLauncher;
}

/**
* Add a nested env element - an environment variable.
*
* @param var The feature to be added to the Env attribute
*/
public void addEnv( Environment.Variable var )
{
env.addVariable( var );
}

/**
* Add a nested arg element - a command line argument.
*
* @return Description of the Returned Value
*/
public Commandline.Argument createArg()
{
return cmdl.createArgument();
}

/**
* Do the work.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
checkConfiguration();
if( isValidOs() )
{
runExec( prepareExec() );
}
}

/**
* Is this the OS the user wanted?
*
* @return The ValidOs value
*/
protected boolean isValidOs()
{
// test if os match
String myos = System.getProperty( "os.name" );
log( "Current OS is " + myos, Project.MSG_VERBOSE );
if( ( os != null ) && ( os.indexOf( myos ) < 0 ) )
{
// this command will be executed only on the specified OS
log( "This OS, " + myos + " was not found in the specified list of valid OSes: " + os, Project.MSG_VERBOSE );
return false;
}
return true;
}

/**
* A Utility method for this classes and subclasses to run an Execute
* instance (an external command).
*
* @param exe Description of Parameter
* @exception IOException Description of Exception
*/
protected final void runExecute( Execute exe )
throws IOException
{
int err = -1;// assume the worst

err = exe.execute();
//test for and handle a forced process death
if( exe.killedProcess() )
{
log( "Timeout: killed the sub-process", Project.MSG_WARN );
}
maybeSetResultPropertyValue( err );
if( err != 0 )
{
if( failOnError )
{
throw new BuildException( taskType + " returned: " + err, location );
}
else
{
log( "Result: " + err, Project.MSG_ERR );
}
}
if( baos != null )
{
BufferedReader in =
new BufferedReader( new StringReader( baos.toString() ) );
String line = null;
StringBuffer val = new StringBuffer();
while( ( line = in.readLine() ) != null )
{
if( val.length() != 0 )
{
val.append( lSep );
}
val.append( line );
}
project.setNewProperty( outputprop, val.toString() );
}
}

/**
* Has the user set all necessary attributes?
*
* @exception BuildException Description of Exception
*/
protected void checkConfiguration()
throws BuildException
{
if( cmdl.getExecutable() == null )
{
throw new BuildException( "no executable specified", location );
}
if( dir != null && !dir.exists() )
{
throw new BuildException( "The directory you specified does not exist" );
}
if( dir != null && !dir.isDirectory() )
{
throw new BuildException( "The directory you specified is not a directory" );
}
}

/**
* Create the StreamHandler to use with our Execute instance.
*
* @return Description of the Returned Value
* @exception BuildException Description of Exception
*/
protected ExecuteStreamHandler createHandler()
throws BuildException
{
if( out != null )
{
try
{
fos = new FileOutputStream( out );
log( "Output redirected to " + out, Project.MSG_VERBOSE );
return new PumpStreamHandler( fos );
}
catch( FileNotFoundException fne )
{
throw new BuildException( "Cannot write to " + out, fne, location );
}
catch( IOException ioe )
{
throw new BuildException( "Cannot write to " + out, ioe, location );
}
}
else if( outputprop != null )
{
baos = new ByteArrayOutputStream();
log( "Output redirected to ByteArray", Project.MSG_VERBOSE );
return new PumpStreamHandler( baos );
}
else
{
return new LogStreamHandler( this,
Project.MSG_INFO, Project.MSG_WARN );
}
}

/**
* Create the Watchdog to kill a runaway process.
*
* @return Description of the Returned Value
* @exception BuildException Description of Exception
*/
protected ExecuteWatchdog createWatchdog()
throws BuildException
{
if( timeout == null )
return null;
return new ExecuteWatchdog( timeout.intValue() );
}

/**
* Flush the output stream - if there is one.
*/
protected void logFlush()
{
try
{
if( fos != null )
fos.close();
if( baos != null )
baos.close();
}
catch( IOException io )
{}
}

/**
* helper method to set result property to the passed in value if
* appropriate
*
* @param result Description of Parameter
*/
protected void maybeSetResultPropertyValue( int result )
{
String res = Integer.toString( result );
if( resultProperty != null )
{
project.setNewProperty( resultProperty, res );
}
}

/**
* Create an Execute instance with the correct working directory set.
*
* @return Description of the Returned Value
* @exception BuildException Description of Exception
*/
protected Execute prepareExec()
throws BuildException
{
// default directory to the project's base directory
if( dir == null )
dir = project.getBaseDir();
// show the command
log( cmdl.toString(), Project.MSG_VERBOSE );

Execute exe = new Execute( createHandler(), createWatchdog() );
exe.setAntRun( project );
exe.setWorkingDirectory( dir );
exe.setVMLauncher( vmLauncher );
String[] environment = env.getVariables();
if( environment != null )
{
for( int i = 0; i < environment.length; i++ )
{
log( "Setting environment variable: " + environment[i],
Project.MSG_VERBOSE );
}
}
exe.setNewenvironment( newEnvironment );
exe.setEnvironment( environment );
return exe;
}

/**
* Run the command using the given Execute instance. This may be overidden
* by subclasses
*
* @param exe Description of Parameter
* @exception BuildException Description of Exception
*/
protected void runExec( Execute exe )
throws BuildException
{
exe.setCommandline( cmdl.getCommandline() );
try
{
runExecute( exe );
}
catch( IOException e )
{
if( failIfExecFails )
{
throw new BuildException( "Execute failed: " + e.toString(), e, location );
}
else
{
log( "Execute failed: " + e.toString(), Project.MSG_ERR );
}
}
finally
{
// close the output file if required
logFlush();
}
}

}

+ 947
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Execute.java View File

@@ -0,0 +1,947 @@
/*
* 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.tools.ant.taskdefs;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.myrmidon.framework.Os;
import org.apache.tools.ant.types.Commandline;

/**
* Runs an external program.
*
* @author thomas.haas@softwired-inc.com
*/
public class Execute
{
/**
* Invalid exit code.
*/
public final static int INVALID = Integer.MAX_VALUE;

private static String antWorkingDirectory = System.getProperty( "user.dir" );
private static CommandLauncher vmLauncher;
private static CommandLauncher shellLauncher;
private static Vector procEnvironment;

/**
* Used to destroy processes when the VM exits.
*/
private static ProcessDestroyer processDestroyer = new ProcessDestroyer();

private String[] cmdl = null;
private String[] env = null;
private int exitValue = INVALID;
private File workingDirectory = null;
private Project project = null;
private boolean newEnvironment = false;

/**
* Controls whether the VM is used to launch commands, where possible
*/
private boolean useVMLauncher = true;
private ExecuteStreamHandler streamHandler;
private ExecuteWatchdog watchdog;

/**
* Builds a command launcher for the OS and JVM we are running under
*/
static
{
// Try using a JDK 1.3 launcher
try
{
vmLauncher = new Java13CommandLauncher();
}
catch( NoSuchMethodException exc )
{
// Ignore and keep try
}

if( Os.isFamily( "mac" ) )
{
// Mac
shellLauncher = new MacCommandLauncher( new CommandLauncher() );
}
else if( Os.isFamily( "os/2" ) )
{
// OS/2 - use same mechanism as Windows 2000
shellLauncher = new WinNTCommandLauncher( new CommandLauncher() );
}
else if( Os.isFamily( "windows" ) )
{
// Windows. Need to determine which JDK we're running in

CommandLauncher baseLauncher;
if( System.getProperty( "java.version" ).startsWith( "1.1" ) )
{
// JDK 1.1
baseLauncher = new Java11CommandLauncher();
}
else
{
// JDK 1.2
baseLauncher = new CommandLauncher();
}

// Determine if we're running under 2000/NT or 98/95
String osname =
System.getProperty( "os.name" ).toLowerCase( Locale.US );

if( osname.indexOf( "nt" ) >= 0 || osname.indexOf( "2000" ) >= 0 )
{
// Windows 2000/NT
shellLauncher = new WinNTCommandLauncher( baseLauncher );
}
else
{
// Windows 98/95 - need to use an auxiliary script
shellLauncher = new ScriptCommandLauncher( "bin/antRun.bat", baseLauncher );
}
}
else if( ( new Os( "netware" ) ).eval() )
{
// NetWare. Need to determine which JDK we're running in
CommandLauncher baseLauncher;
if( System.getProperty( "java.version" ).startsWith( "1.1" ) )
{
// JDK 1.1
baseLauncher = new Java11CommandLauncher();
}
else
{
// JDK 1.2
baseLauncher = new CommandLauncher();
}

shellLauncher = new PerlScriptCommandLauncher( "bin/antRun.pl", baseLauncher );
}
else
{
// Generic
shellLauncher = new ScriptCommandLauncher( "bin/antRun", new CommandLauncher() );
}
}

/**
* Creates a new execute object using <code>PumpStreamHandler</code> for
* stream handling.
*/
public Execute()
{
this( new PumpStreamHandler(), null );
}

/**
* Creates a new execute object.
*
* @param streamHandler the stream handler used to handle the input and
* output streams of the subprocess.
*/
public Execute( ExecuteStreamHandler streamHandler )
{
this( streamHandler, null );
}

/**
* Creates a new execute object.
*
* @param streamHandler the stream handler used to handle the input and
* output streams of the subprocess.
* @param watchdog a watchdog for the subprocess or <code>null</code> to to
* disable a timeout for the subprocess.
*/
public Execute( ExecuteStreamHandler streamHandler, ExecuteWatchdog watchdog )
{
this.streamHandler = streamHandler;
this.watchdog = watchdog;
}

/**
* Find the list of environment variables for this process.
*
* @return The ProcEnvironment value
*/
public static synchronized Vector getProcEnvironment()
{
if( procEnvironment != null )
return procEnvironment;

procEnvironment = new Vector();
try
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
Execute exe = new Execute( new PumpStreamHandler( out ) );
exe.setCommandline( getProcEnvCommand() );
// Make sure we do not recurse forever
exe.setNewenvironment( true );
int retval = exe.execute();
if( retval != 0 )
{
// Just try to use what we got
}

BufferedReader in =
new BufferedReader( new StringReader( out.toString() ) );
String var = null;
String line;
String lineSep = System.getProperty( "line.separator" );
while( ( line = in.readLine() ) != null )
{
if( line.indexOf( '=' ) == -1 )
{
// Chunk part of previous env var (UNIX env vars can
// contain embedded new lines).
if( var == null )
{
var = lineSep + line;
}
else
{
var += lineSep + line;
}
}
else
{
// New env var...append the previous one if we have it.
if( var != null )
{
procEnvironment.addElement( var );
}
var = line;
}
}
// Since we "look ahead" before adding, there's one last env var.
procEnvironment.addElement( var );
}
catch( java.io.IOException exc )
{
exc.printStackTrace();
// Just try to see how much we got
}
return procEnvironment;
}

/**
* A utility method that runs an external command. Writes the output and
* error streams of the command to the project log.
*
* @param task The task that the command is part of. Used for logging
* @param cmdline The command to execute.
* @throws BuildException if the command does not return 0.
*/
public static void runCommand( Task task, String[] cmdline )
throws BuildException
{
try
{
task.log( Commandline.toString( cmdline ), Project.MSG_VERBOSE );
Execute exe = new Execute( new LogStreamHandler( task,
Project.MSG_INFO,
Project.MSG_ERR ) );
exe.setAntRun( task.getProject() );
exe.setCommandline( cmdline );
int retval = exe.execute();
if( retval != 0 )
{
throw new BuildException( cmdline[ 0 ] + " failed with return code " + retval, task.getLocation() );
}
}
catch( java.io.IOException exc )
{
throw new BuildException( "Could not launch " + cmdline[ 0 ] + ": " + exc, task.getLocation() );
}
}

private static String[] getProcEnvCommand()
{
if( Os.isFamily( "os/2" ) )
{
// OS/2 - use same mechanism as Windows 2000
// Not sure
String[] cmd = {"cmd", "/c", "set"};
return cmd;
}
else if( Os.isFamily( "windows" ) )
{
String osname =
System.getProperty( "os.name" ).toLowerCase( Locale.US );
// Determine if we're running under 2000/NT or 98/95
if( osname.indexOf( "nt" ) >= 0 || osname.indexOf( "2000" ) >= 0 )
{
// Windows 2000/NT
String[] cmd = {"cmd", "/c", "set"};
return cmd;
}
else
{
// Windows 98/95 - need to use an auxiliary script
String[] cmd = {"command.com", "/c", "set"};
return cmd;
}
}
else if( Os.isFamily( "unix" ) )
{
// Generic UNIX
// Alternatively one could use: /bin/sh -c env
String[] cmd = {"/usr/bin/env"};
return cmd;
}
else if( Os.isFamily( "netware" ) )
{
String[] cmd = {"env"};
return cmd;
}
else
{
// MAC OS 9 and previous
// TODO: I have no idea how to get it, someone must fix it
String[] cmd = null;
return cmd;
}
}

/**
* Set the name of the antRun script using the project's value.
*
* @param project the current project.
* @exception BuildException Description of Exception
*/
public void setAntRun( Project project )
throws BuildException
{
this.project = project;
}

/**
* Sets the commandline of the subprocess to launch.
*
* @param commandline the commandline of the subprocess to launch
*/
public void setCommandline( String[] commandline )
{
cmdl = commandline;
}

/**
* Sets the environment variables for the subprocess to launch.
*
* @param env The new Environment value
*/
public void setEnvironment( String[] env )
{
this.env = env;
}

/**
* Set whether to propagate the default environment or not.
*
* @param newenv whether to propagate the process environment.
*/
public void setNewenvironment( boolean newenv )
{
newEnvironment = newenv;
}

/**
* Launch this execution through the VM, where possible, rather than through
* the OS's shell. In some cases and operating systems using the shell will
* allow the shell to perform additional processing such as associating an
* executable with a script, etc
*
* @param useVMLauncher The new VMLauncher value
*/
public void setVMLauncher( boolean useVMLauncher )
{
this.useVMLauncher = useVMLauncher;
}

/**
* Sets the working directory of the process to execute. <p>
*
* This is emulated using the antRun scripts unless the OS is Windows NT in
* which case a cmd.exe is spawned, or MRJ and setting user.dir works, or
* JDK 1.3 and there is official support in java.lang.Runtime.
*
* @param wd the working directory of the process.
*/
public void setWorkingDirectory( File wd )
{
if( wd == null || wd.getAbsolutePath().equals( antWorkingDirectory ) )
workingDirectory = null;
else
workingDirectory = wd;
}

/**
* Returns the commandline used to create a subprocess.
*
* @return the commandline used to create a subprocess
*/
public String[] getCommandline()
{
return cmdl;
}

/**
* Returns the environment used to create a subprocess.
*
* @return the environment used to create a subprocess
*/
public String[] getEnvironment()
{
if( env == null || newEnvironment )
return env;
return patchEnvironment();
}

/**
* query the exit value of the process.
*
* @return the exit value, 1 if the process was killed, or Project.INVALID
* if no exit value has been received
*/
public int getExitValue()
{
return exitValue;
}

/**
* Runs a process defined by the command line and returns its exit status.
*
* @return the exit status of the subprocess or <code>INVALID</code>
* @exception IOException Description of Exception
*/
public int execute()
throws IOException
{
CommandLauncher launcher = vmLauncher != null ? vmLauncher : shellLauncher;
if( !useVMLauncher )
{
launcher = shellLauncher;
}

final Process process = launcher.exec( project, getCommandline(), getEnvironment(), workingDirectory );
try
{
streamHandler.setProcessInputStream( process.getOutputStream() );
streamHandler.setProcessOutputStream( process.getInputStream() );
streamHandler.setProcessErrorStream( process.getErrorStream() );
}
catch( IOException e )
{
process.destroy();
throw e;
}
streamHandler.start();

// add the process to the list of those to destroy if the VM exits
//
processDestroyer.add( process );

if( watchdog != null )
watchdog.start( process );
waitFor( process );

// remove the process to the list of those to destroy if the VM exits
//
processDestroyer.remove( process );

if( watchdog != null )
watchdog.stop();
streamHandler.stop();
if( watchdog != null )
watchdog.checkException();
return getExitValue();
}

/**
* test for an untimely death of the process
*
* @return true iff a watchdog had to kill the process
* @since 1.5
*/
public boolean killedProcess()
{
return watchdog != null && watchdog.killedProcess();
}

protected void setExitValue( int value )
{
exitValue = value;
}

protected void waitFor( Process process )
{
try
{
process.waitFor();
setExitValue( process.exitValue() );
}
catch( InterruptedException e )
{
}
}

/**
* Patch the current environment with the new values from the user.
*
* @return the patched environment
*/
private String[] patchEnvironment()
{
Vector osEnv = (Vector)getProcEnvironment().clone();
for( int i = 0; i < env.length; i++ )
{
int pos = env[ i ].indexOf( '=' );
// Get key including "="
String key = env[ i ].substring( 0, pos + 1 );
int size = osEnv.size();
for( int j = 0; j < size; j++ )
{
if( ( (String)osEnv.elementAt( j ) ).startsWith( key ) )
{
osEnv.removeElementAt( j );
break;
}
}
osEnv.addElement( env[ i ] );
}
String[] result = new String[ osEnv.size() ];
osEnv.copyInto( result );
return result;
}

/**
* A command launcher for a particular JVM/OS platform. This class is a
* general purpose command launcher which can only launch commands in the
* current working directory.
*
* @author RT
*/
private static class CommandLauncher
{
/**
* Launches the given command in a new process.
*
* @param project The project that the command is part of
* @param cmd The command to execute
* @param env The environment for the new process. If null, the
* environment of the current proccess is used.
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env )
throws IOException
{
if( project != null )
{
project.log( "Execute:CommandLauncher: " +
Commandline.toString( cmd ), Project.MSG_DEBUG );
}
return Runtime.getRuntime().exec( cmd, env );
}

/**
* Launches the given command in a new process, in the given working
* directory.
*
* @param project The project that the command is part of
* @param cmd The command to execute
* @param env The environment for the new process. If null, the
* environment of the current proccess is used.
* @param workingDir The directory to start the command in. If null, the
* current directory is used
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env, File workingDir )
throws IOException
{
if( workingDir == null )
{
return exec( project, cmd, env );
}
throw new IOException( "Cannot execute a process in different directory under this JVM" );
}
}

/**
* A command launcher that proxies another command launcher. Sub-classes
* override exec(args, env, workdir)
*
* @author RT
*/
private static class CommandLauncherProxy extends CommandLauncher
{

private CommandLauncher _launcher;

CommandLauncherProxy( CommandLauncher launcher )
{
_launcher = launcher;
}

/**
* Launches the given command in a new process. Delegates this method to
* the proxied launcher
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env )
throws IOException
{
return _launcher.exec( project, cmd, env );
}
}

/**
* A command launcher for JDK/JRE 1.1 under Windows. Fixes quoting problems
* in Runtime.exec(). Can only launch commands in the current working
* directory
*
* @author RT
*/
private static class Java11CommandLauncher extends CommandLauncher
{
/**
* Launches the given command in a new process. Needs to quote arguments
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env )
throws IOException
{
// Need to quote arguments with spaces, and to escape quote characters
String[] newcmd = new String[ cmd.length ];
for( int i = 0; i < cmd.length; i++ )
{
newcmd[ i ] = Commandline.quoteArgument( cmd[ i ] );
}
if( project != null )
{
project.log( "Execute:Java11CommandLauncher: " +
Commandline.toString( newcmd ), Project.MSG_DEBUG );
}
return Runtime.getRuntime().exec( newcmd, env );
}
}

/**
* A command launcher for JDK/JRE 1.3 (and higher). Uses the built-in
* Runtime.exec() command
*
* @author RT
*/
private static class Java13CommandLauncher extends CommandLauncher
{

private Method _execWithCWD;

public Java13CommandLauncher()
throws NoSuchMethodException
{
// Locate method Runtime.exec(String[] cmdarray, String[] envp, File dir)
_execWithCWD = Runtime.class.getMethod( "exec", new Class[]{String[].class, String[].class, File.class} );
}

/**
* Launches the given command in a new process, in the given working
* directory
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @param workingDir Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env, File workingDir )
throws IOException
{
try
{
if( project != null )
{
project.log( "Execute:Java13CommandLauncher: " +
Commandline.toString( cmd ), Project.MSG_DEBUG );
}
Object[] arguments = {cmd, env, workingDir};
return (Process)_execWithCWD.invoke( Runtime.getRuntime(), arguments );
}
catch( InvocationTargetException exc )
{
Throwable realexc = exc.getTargetException();
if( realexc instanceof ThreadDeath )
{
throw (ThreadDeath)realexc;
}
else if( realexc instanceof IOException )
{
throw (IOException)realexc;
}
else
{
throw new BuildException( "Unable to execute command", realexc );
}
}
catch( Exception exc )
{
// IllegalAccess, IllegalArgument, ClassCast
throw new BuildException( "Unable to execute command", exc );
}
}
}

/**
* A command launcher for Mac that uses a dodgy mechanism to change working
* directory before launching commands.
*
* @author RT
*/
private static class MacCommandLauncher extends CommandLauncherProxy
{
MacCommandLauncher( CommandLauncher launcher )
{
super( launcher );
}

/**
* Launches the given command in a new process, in the given working
* directory
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @param workingDir Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env, File workingDir )
throws IOException
{
if( workingDir == null )
{
return exec( project, cmd, env );
}

System.getProperties().put( "user.dir", workingDir.getAbsolutePath() );
try
{
return exec( project, cmd, env );
}
finally
{
System.getProperties().put( "user.dir", antWorkingDirectory );
}
}
}

/**
* A command launcher that uses an auxiliary perl script to launch commands
* in directories other than the current working directory.
*
* @author RT
*/
private static class PerlScriptCommandLauncher extends CommandLauncherProxy
{

private String _script;

PerlScriptCommandLauncher( String script, CommandLauncher launcher )
{
super( launcher );
_script = script;
}

/**
* Launches the given command in a new process, in the given working
* directory
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @param workingDir Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env, File workingDir )
throws IOException
{
if( project == null )
{
if( workingDir == null )
{
return exec( project, cmd, env );
}
throw new IOException( "Cannot locate antRun script: No project provided" );
}

// Locate the auxiliary script
String antHome = project.getProperty( "ant.home" );
if( antHome == null )
{
throw new IOException( "Cannot locate antRun script: Property 'ant.home' not found" );
}
String antRun = project.resolveFile( antHome + File.separator + _script ).toString();

// Build the command
File commandDir = workingDir;
if( workingDir == null && project != null )
{
commandDir = project.getBaseDir();
}

String[] newcmd = new String[ cmd.length + 3 ];
newcmd[ 0 ] = "perl";
newcmd[ 1 ] = antRun;
newcmd[ 2 ] = commandDir.getAbsolutePath();
System.arraycopy( cmd, 0, newcmd, 3, cmd.length );

return exec( project, newcmd, env );
}
}

/**
* A command launcher that uses an auxiliary script to launch commands in
* directories other than the current working directory.
*
* @author RT
*/
private static class ScriptCommandLauncher extends CommandLauncherProxy
{

private String _script;

ScriptCommandLauncher( String script, CommandLauncher launcher )
{
super( launcher );
_script = script;
}

/**
* Launches the given command in a new process, in the given working
* directory
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @param workingDir Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env, File workingDir )
throws IOException
{
if( project == null )
{
if( workingDir == null )
{
return exec( project, cmd, env );
}
throw new IOException( "Cannot locate antRun script: No project provided" );
}

// Locate the auxiliary script
String antHome = project.getProperty( "ant.home" );
if( antHome == null )
{
throw new IOException( "Cannot locate antRun script: Property 'ant.home' not found" );
}
String antRun = project.resolveFile( antHome + File.separator + _script ).toString();

// Build the command
File commandDir = workingDir;
if( workingDir == null && project != null )
{
commandDir = project.getBaseDir();
}

String[] newcmd = new String[ cmd.length + 2 ];
newcmd[ 0 ] = antRun;
newcmd[ 1 ] = commandDir.getAbsolutePath();
System.arraycopy( cmd, 0, newcmd, 2, cmd.length );

return exec( project, newcmd, env );
}
}

/**
* A command launcher for Windows 2000/NT that uses 'cmd.exe' when launching
* commands in directories other than the current working directory.
*
* @author RT
*/
private static class WinNTCommandLauncher extends CommandLauncherProxy
{
WinNTCommandLauncher( CommandLauncher launcher )
{
super( launcher );
}

/**
* Launches the given command in a new process, in the given working
* directory.
*
* @param project Description of Parameter
* @param cmd Description of Parameter
* @param env Description of Parameter
* @param workingDir Description of Parameter
* @return Description of the Returned Value
* @exception IOException Description of Exception
*/
public Process exec( Project project, String[] cmd, String[] env, File workingDir )
throws IOException
{
File commandDir = workingDir;
if( workingDir == null )
{
if( project != null )
{
commandDir = project.getBaseDir();
}
else
{
return exec( project, cmd, env );
}
}

// Use cmd.exe to change to the specified directory before running
// the command
final int preCmdLength = 6;
String[] newcmd = new String[ cmd.length + preCmdLength ];
newcmd[ 0 ] = "cmd";
newcmd[ 1 ] = "/c";
newcmd[ 2 ] = "cd";
newcmd[ 3 ] = "/d";
newcmd[ 4 ] = commandDir.getAbsolutePath();
newcmd[ 5 ] = "&&";
System.arraycopy( cmd, 0, newcmd, preCmdLength, cmd.length );

return exec( project, newcmd, env );
}
}
}

+ 122
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ExecuteJava.java View File

@@ -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.tools.ant.taskdefs;
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.CommandlineJava;
import org.apache.tools.ant.types.Path;

/*
* @author thomas.haas@softwired-inc.com
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/
public class ExecuteJava
{

private Commandline javaCommand = null;
private Path classpath = null;
private CommandlineJava.SysProperties sysProperties = null;

public void setClasspath( Path p )
{
classpath = p;
}

public void setJavaCommand( Commandline javaCommand )
{
this.javaCommand = javaCommand;
}

/**
* All output (System.out as well as System.err) will be written to this
* Stream.
*
* @param out The new Output value
* @deprecated manage output at the task level
*/
public void setOutput( PrintStream out ) { }

public void setSystemProperties( CommandlineJava.SysProperties s )
{
sysProperties = s;
}

public void execute( Project project )
throws BuildException
{
final String classname = javaCommand.getExecutable();
final Object[] argument = {javaCommand.getArguments()};

AntClassLoader loader = null;
try
{
if( sysProperties != null )
{
sysProperties.setSystem();
}

final Class[] param = {Class.forName( "[Ljava.lang.String;" )};
Class target = null;
if( classpath == null )
{
target = Class.forName( classname );
}
else
{
loader = new AntClassLoader( project.getCoreLoader(), project, classpath, false );
loader.setIsolated( true );
loader.setThreadContextLoader();
target = loader.forceLoadClass( classname );
AntClassLoader.initializeClass( target );
}
final Method main = target.getMethod( "main", param );
main.invoke( null, argument );
}
catch( NullPointerException e )
{
throw new BuildException( "Could not find main() method in " + classname );
}
catch( ClassNotFoundException e )
{
throw new BuildException( "Could not find " + classname + ". Make sure you have it in your classpath" );
}
catch( InvocationTargetException e )
{
Throwable t = e.getTargetException();
if( !( t instanceof SecurityException ) )
{
throw new BuildException( t );
}
else
{
throw ( SecurityException )t;
}
}
catch( Exception e )
{
throw new BuildException( e );
}
finally
{
if( loader != null )
{
loader.resetThreadContextLoader();
loader.cleanup();
}
if( sysProperties != null )
{
sysProperties.restoreSystem();
}
}
}
}

+ 473
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ExecuteOn.java View File

@@ -0,0 +1,473 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Mapper;
import org.apache.tools.ant.util.FileNameMapper;
import org.apache.tools.ant.util.SourceFileScanner;

/**
* Executes a given command, supplying a set of files as arguments.
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @author <a href="mailto:mariusz@rakiura.org">Mariusz Nowostawski</a>
*/
public class ExecuteOn extends ExecTask
{

protected Vector filesets = new Vector();
private boolean relative = false;
private boolean parallel = false;
protected String type = "file";
protected Commandline.Marker srcFilePos = null;
private boolean skipEmpty = false;
protected Commandline.Marker targetFilePos = null;
protected Mapper mapperElement = null;
protected FileNameMapper mapper = null;
protected File destDir = null;

/**
* Has &lt;srcfile&gt; been specified before &lt;targetfile&gt;
*/
protected boolean srcIsFirst = true;

/**
* Set the destination directory.
*
* @param destDir The new Dest value
*/
public void setDest( File destDir )
{
this.destDir = destDir;
}


/**
* Shall the command work on all specified files in parallel?
*
* @param parallel The new Parallel value
*/
public void setParallel( boolean parallel )
{
this.parallel = parallel;
}

/**
* Should filenames be returned as relative path names?
*
* @param relative The new Relative value
*/
public void setRelative( boolean relative )
{
this.relative = relative;
}

/**
* Should empty filesets be ignored?
*
* @param skip The new SkipEmptyFilesets value
*/
public void setSkipEmptyFilesets( boolean skip )
{
skipEmpty = skip;
}

/**
* Shall the command work only on files, directories or both?
*
* @param type The new Type value
*/
public void setType( FileDirBoth type )
{
this.type = type.getValue();
}

/**
* Adds a set of files (nested fileset attribute).
*
* @param set The feature to be added to the Fileset attribute
*/
public void addFileset( FileSet set )
{
filesets.addElement( set );
}

/**
* Defines the FileNameMapper to use (nested mapper element).
*
* @return Description of the Returned Value
* @exception BuildException Description of Exception
*/
public Mapper createMapper()
throws BuildException
{
if( mapperElement != null )
{
throw new BuildException( "Cannot define more than one mapper",
location );
}
mapperElement = new Mapper( project );
return mapperElement;
}

/**
* Marker that indicates where the name of the source file should be put on
* the command line.
*
* @return Description of the Returned Value
*/
public Commandline.Marker createSrcfile()
{
if( srcFilePos != null )
{
throw new BuildException( taskType + " doesn\'t support multiple srcfile elements.",
location );
}
srcFilePos = cmdl.createMarker();
return srcFilePos;
}

/**
* Marker that indicates where the name of the target file should be put on
* the command line.
*
* @return Description of the Returned Value
*/
public Commandline.Marker createTargetfile()
{
if( targetFilePos != null )
{
throw new BuildException( taskType + " doesn\'t support multiple targetfile elements.",
location );
}
targetFilePos = cmdl.createMarker();
srcIsFirst = ( srcFilePos != null );
return targetFilePos;
}

/**
* Construct the command line for parallel execution.
*
* @param srcFiles The filenames to add to the commandline
* @param baseDirs Description of Parameter
* @return The Commandline value
*/
protected String[] getCommandline( String[] srcFiles, File[] baseDirs )
{
Vector targets = new Vector();
if( targetFilePos != null )
{
Hashtable addedFiles = new Hashtable();
for( int i = 0; i < srcFiles.length; i++ )
{
String[] subTargets = mapper.mapFileName( srcFiles[i] );
if( subTargets != null )
{
for( int j = 0; j < subTargets.length; j++ )
{
String name = null;
if( !relative )
{
name =
( new File( destDir, subTargets[j] ) ).getAbsolutePath();
}
else
{
name = subTargets[j];
}
if( !addedFiles.contains( name ) )
{
targets.addElement( name );
addedFiles.put( name, name );
}
}
}
}
}
String[] targetFiles = new String[targets.size()];
targets.copyInto( targetFiles );

String[] orig = cmdl.getCommandline();
String[] result = new String[orig.length + srcFiles.length + targetFiles.length];

int srcIndex = orig.length;
if( srcFilePos != null )
{
srcIndex = srcFilePos.getPosition();
}

if( targetFilePos != null )
{
int targetIndex = targetFilePos.getPosition();

if( srcIndex < targetIndex
|| ( srcIndex == targetIndex && srcIsFirst ) )
{

// 0 --> srcIndex
System.arraycopy( orig, 0, result, 0, srcIndex );

// srcIndex --> targetIndex
System.arraycopy( orig, srcIndex, result,
srcIndex + srcFiles.length,
targetIndex - srcIndex );

// targets are already absolute file names
System.arraycopy( targetFiles, 0, result,
targetIndex + srcFiles.length,
targetFiles.length );

// targetIndex --> end
System.arraycopy( orig, targetIndex, result,
targetIndex + srcFiles.length + targetFiles.length,
orig.length - targetIndex );
}
else
{
// 0 --> targetIndex
System.arraycopy( orig, 0, result, 0, targetIndex );

// targets are already absolute file names
System.arraycopy( targetFiles, 0, result,
targetIndex,
targetFiles.length );

// targetIndex --> srcIndex
System.arraycopy( orig, targetIndex, result,
targetIndex + targetFiles.length,
srcIndex - targetIndex );

// srcIndex --> end
System.arraycopy( orig, srcIndex, result,
srcIndex + srcFiles.length + targetFiles.length,
orig.length - srcIndex );
srcIndex += targetFiles.length;
}

}
else
{// no targetFilePos

// 0 --> srcIndex
System.arraycopy( orig, 0, result, 0, srcIndex );
// srcIndex --> end
System.arraycopy( orig, srcIndex, result,
srcIndex + srcFiles.length,
orig.length - srcIndex );

}

// fill in source file names
for( int i = 0; i < srcFiles.length; i++ )
{
if( !relative )
{
result[srcIndex + i] =
( new File( baseDirs[i], srcFiles[i] ) ).getAbsolutePath();
}
else
{
result[srcIndex + i] = srcFiles[i];
}
}
return result;
}

/**
* Construct the command line for serial execution.
*
* @param srcFile The filename to add to the commandline
* @param baseDir filename is relative to this dir
* @return The Commandline value
*/
protected String[] getCommandline( String srcFile, File baseDir )
{
return getCommandline( new String[]{srcFile}, new File[]{baseDir} );
}

/**
* Return the list of Directories from this DirectoryScanner that should be
* included on the command line.
*
* @param baseDir Description of Parameter
* @param ds Description of Parameter
* @return The Dirs value
*/
protected String[] getDirs( File baseDir, DirectoryScanner ds )
{
if( mapper != null )
{
SourceFileScanner sfs = new SourceFileScanner( this );
return sfs.restrict( ds.getIncludedDirectories(), baseDir, destDir,
mapper );
}
else
{
return ds.getIncludedDirectories();
}
}

/**
* Return the list of files from this DirectoryScanner that should be
* included on the command line.
*
* @param baseDir Description of Parameter
* @param ds Description of Parameter
* @return The Files value
*/
protected String[] getFiles( File baseDir, DirectoryScanner ds )
{
if( mapper != null )
{
SourceFileScanner sfs = new SourceFileScanner( this );
return sfs.restrict( ds.getIncludedFiles(), baseDir, destDir,
mapper );
}
else
{
return ds.getIncludedFiles();
}
}

protected void checkConfiguration()
{
if( "execon".equals( taskName ) )
{
log( "!! execon is deprecated. Use apply instead. !!" );
}

super.checkConfiguration();
if( filesets.size() == 0 )
{
throw new BuildException( "no filesets specified", location );
}

if( targetFilePos != null || mapperElement != null
|| destDir != null )
{

if( mapperElement == null )
{
throw new BuildException( "no mapper specified", location );
}
if( mapperElement == null )
{
throw new BuildException( "no dest attribute specified",
location );
}
mapper = mapperElement.getImplementation();
}
}

protected void runExec( Execute exe )
throws BuildException
{
try
{

Vector fileNames = new Vector();
Vector baseDirs = new Vector();
for( int i = 0; i < filesets.size(); i++ )
{
FileSet fs = ( FileSet )filesets.elementAt( i );
File base = fs.getDir( project );
DirectoryScanner ds = fs.getDirectoryScanner( project );

if( !"dir".equals( type ) )
{
String[] s = getFiles( base, ds );
for( int j = 0; j < s.length; j++ )
{
fileNames.addElement( s[j] );
baseDirs.addElement( base );
}
}

if( !"file".equals( type ) )
{
String[] s = getDirs( base, ds );
;
for( int j = 0; j < s.length; j++ )
{
fileNames.addElement( s[j] );
baseDirs.addElement( base );
}
}

if( fileNames.size() == 0 && skipEmpty )
{
log( "Skipping fileset for directory "
+ base + ". It is empty.", Project.MSG_INFO );
continue;
}

if( !parallel )
{
String[] s = new String[fileNames.size()];
fileNames.copyInto( s );
for( int j = 0; j < s.length; j++ )
{
String[] command = getCommandline( s[j], base );
log( "Executing " + Commandline.toString( command ),
Project.MSG_VERBOSE );
exe.setCommandline( command );
runExecute( exe );
}
fileNames.removeAllElements();
baseDirs.removeAllElements();
}
}

if( parallel && ( fileNames.size() > 0 || !skipEmpty ) )
{
String[] s = new String[fileNames.size()];
fileNames.copyInto( s );
File[] b = new File[baseDirs.size()];
baseDirs.copyInto( b );
String[] command = getCommandline( s, b );
log( "Executing " + Commandline.toString( command ),
Project.MSG_VERBOSE );
exe.setCommandline( command );
runExecute( exe );
}

}
catch( IOException e )
{
throw new BuildException( "Execute failed: " + e, e, location );
}
finally
{
// close the output file if required
logFlush();
}
}

/**
* Enumerated attribute with the values "file", "dir" and "both" for the
* type attribute.
*
* @author RT
*/
public static class FileDirBoth extends EnumeratedAttribute
{
public String[] getValues()
{
return new String[]{"file", "dir", "both"};
}
}
}

+ 62
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ExecuteStreamHandler.java View File

@@ -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.tools.ant.taskdefs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* Used by <code>Execute</code> to handle input and output stream of
* subprocesses.
*
* @author thomas.haas@softwired-inc.com
*/
public interface ExecuteStreamHandler
{

/**
* Install a handler for the input stream of the subprocess.
*
* @param os output stream to write to the standard input stream of the
* subprocess
* @exception IOException Description of Exception
*/
void setProcessInputStream( OutputStream os )
throws IOException;

/**
* Install a handler for the error stream of the subprocess.
*
* @param is input stream to read from the error stream from the subprocess
* @exception IOException Description of Exception
*/
void setProcessErrorStream( InputStream is )
throws IOException;

/**
* Install a handler for the output stream of the subprocess.
*
* @param is input stream to read from the error stream from the subprocess
* @exception IOException Description of Exception
*/
void setProcessOutputStream( InputStream is )
throws IOException;

/**
* Start handling of the streams.
*
* @exception IOException Description of Exception
*/
void start()
throws IOException;

/**
* Stop handling of the streams - will not be restarted.
*/
void stop();
}

+ 209
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ExecuteWatchdog.java View File

@@ -0,0 +1,209 @@
/*
* 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.tools.ant.taskdefs;
import org.apache.tools.ant.BuildException;

/**
* Destroys a process running for too long. For example: <pre>
* ExecuteWatchdog watchdog = new ExecuteWatchdog(30000);
* Execute exec = new Execute(myloghandler, watchdog);
* exec.setCommandLine(mycmdline);
* int exitvalue = exec.execute();
* if (exitvalue != SUCCESS && watchdog.killedProcess()){
* // it was killed on purpose by the watchdog
* }
* </pre>
*
* @author thomas.haas@softwired-inc.com
* @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a>
* @see Execute
*/
public class ExecuteWatchdog implements Runnable
{

/**
* say whether or not the watchog is currently monitoring a process
*/
private boolean watch = false;

/**
* exception that might be thrown during the process execution
*/
private Exception caught = null;

/**
* say whether or not the process was killed due to running overtime
*/
private boolean killedProcess = false;

/**
* the process to execute and watch for duration
*/
private Process process;

/**
* timeout duration. Once the process running time exceeds this it should be
* killed
*/
private int timeout;

/**
* Creates a new watchdog with a given timeout.
*
* @param timeout the timeout for the process in milliseconds. It must be
* greather than 0.
*/
public ExecuteWatchdog( int timeout )
{
if( timeout < 1 )
{
throw new IllegalArgumentException( "timeout lesser than 1." );
}
this.timeout = timeout;
}

/**
* Indicates whether or not the watchdog is still monitoring the process.
*
* @return <tt>true</tt> if the process is still running, otherwise <tt>
* false</tt> .
*/
public boolean isWatching()
{
return watch;
}

/**
* This method will rethrow the exception that was possibly caught during
* the run of the process. It will only remains valid once the process has
* been terminated either by 'error', timeout or manual intervention.
* Information will be discarded once a new process is ran.
*
* @throws BuildException a wrapped exception over the one that was silently
* swallowed and stored during the process run.
*/
public void checkException()
throws BuildException
{
if( caught != null )
{
throw new BuildException( "Exception in ExecuteWatchdog.run: "
+ caught.getMessage(), caught );
}
}

/**
* Indicates whether the last process run was killed on timeout or not.
*
* @return <tt>true</tt> if the process was killed otherwise <tt>false</tt>
* .
*/
public boolean killedProcess()
{
return killedProcess;
}


/**
* Watches the process and terminates it, if it runs for to long.
*/
public synchronized void run()
{
try
{
// This isn't a Task, don't have a Project object to log.
// project.log("ExecuteWatchdog: timeout = "+timeout+" msec", Project.MSG_VERBOSE);
final long until = System.currentTimeMillis() + timeout;
long now;
while( watch && until > ( now = System.currentTimeMillis() ) )
{
try
{
wait( until - now );
}
catch( InterruptedException e )
{}
}

// if we are here, either someone stopped the watchdog,
// we are on timeout and the process must be killed, or
// we are on timeout and the process has already stopped.
try
{
// We must check if the process was not stopped
// before being here
process.exitValue();
}
catch( IllegalThreadStateException e )
{
// the process is not terminated, if this is really
// a timeout and not a manual stop then kill it.
if( watch )
{
killedProcess = true;
process.destroy();
}
}
}
catch( Exception e )
{
caught = e;
}
finally
{
cleanUp();
}
}

/**
* Watches the given process and terminates it, if it runs for too long. All
* information from the previous run are reset.
*
* @param process the process to monitor. It cannot be <tt>null</tt>
* @throws IllegalStateException thrown if a process is still being
* monitored.
*/
public synchronized void start( Process process )
{
if( process == null )
{
throw new NullPointerException( "process is null." );
}
if( this.process != null )
{
throw new IllegalStateException( "Already running." );
}
this.caught = null;
this.killedProcess = false;
this.watch = true;
this.process = process;
final Thread thread = new Thread( this, "WATCHDOG" );
thread.setDaemon( true );
thread.start();
}

/**
* Stops the watcher. It will notify all threads possibly waiting on this
* object.
*/
public synchronized void stop()
{
watch = false;
notifyAll();
}

/**
* reset the monitor flag and the process.
*/
protected void cleanUp()
{
watch = false;
process = null;
}
}


+ 83
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Exit.java View File

@@ -0,0 +1,83 @@
/*
* 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.tools.ant.taskdefs;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Task;

/**
* Just exit the active build, giving an additional message if available.
*
* @author <a href="mailto:nico@seessle.de">Nico Seessle</a>
*/
public class Exit extends Task
{
private String ifCondition, unlessCondition;
private String message;

public void setIf( String c )
{
ifCondition = c;
}

public void setMessage( String value )
{
this.message = value;
}

public void setUnless( String c )
{
unlessCondition = c;
}

/**
* Set a multiline message.
*
* @param msg The feature to be added to the Text attribute
*/
public void addText( String msg )
{
message += project.replaceProperties( msg );
}

public void execute()
throws BuildException
{
if( testIfCondition() && testUnlessCondition() )
{
if( message != null && message.length() > 0 )
{
throw new BuildException( message );
}
else
{
throw new BuildException( "No message" );
}
}
}

private boolean testIfCondition()
{
if( ifCondition == null || "".equals( ifCondition ) )
{
return true;
}

return project.getProperty( ifCondition ) != null;
}

private boolean testUnlessCondition()
{
if( unlessCondition == null || "".equals( unlessCondition ) )
{
return true;
}
return project.getProperty( unlessCondition ) == null;
}

}

+ 303
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Expand.java View File

@@ -0,0 +1,303 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Date;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.PatternSet;
import org.apache.tools.ant.util.FileUtils;

/**
* Unzip a file.
*
* @author costin@dnt.ro
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
*/
public class Expand extends MatchingTask
{// req
private boolean overwrite = true;
private Vector patternsets = new Vector();
private Vector filesets = new Vector();
private File dest;//req
private File source;

/**
* Set the destination directory. File will be unzipped into the destination
* directory.
*
* @param d Path to the directory.
*/
public void setDest( File d )
{
this.dest = d;
}

/**
* Should we overwrite files in dest, even if they are newer than the
* corresponding entries in the archive?
*
* @param b The new Overwrite value
*/
public void setOverwrite( boolean b )
{
overwrite = b;
}

/**
* Set the path to zip-file.
*
* @param s Path to zip-file.
*/
public void setSrc( File s )
{
this.source = s;
}

/**
* Add a fileset
*
* @param set The feature to be added to the Fileset attribute
*/
public void addFileset( FileSet set )
{
filesets.addElement( set );
}

/**
* Add a patternset
*
* @param set The feature to be added to the Patternset attribute
*/
public void addPatternset( PatternSet set )
{
patternsets.addElement( set );
}

/**
* Do the work.
*
* @exception BuildException Thrown in unrecoverable error.
*/
public void execute()
throws BuildException
{
if( "expand".equals( taskType ) )
{
log( "!! expand is deprecated. Use unzip instead. !!" );
}

if( source == null && filesets.size() == 0 )
{
throw new BuildException( "src attribute and/or filesets must be specified" );
}

if( dest == null )
{
throw new BuildException(
"Dest attribute must be specified" );
}

if( dest.exists() && !dest.isDirectory() )
{
throw new BuildException( "Dest must be a directory.", location );
}

FileUtils fileUtils = FileUtils.newFileUtils();

if( source != null )
{
if( source.isDirectory() )
{
throw new BuildException( "Src must not be a directory." +
" Use nested filesets instead.", location );
}
else
{
expandFile( fileUtils, source, dest );
}
}
if( filesets.size() > 0 )
{
for( int j = 0; j < filesets.size(); j++ )
{
FileSet fs = ( FileSet )filesets.elementAt( j );
DirectoryScanner ds = fs.getDirectoryScanner( project );
File fromDir = fs.getDir( project );

String[] files = ds.getIncludedFiles();
for( int i = 0; i < files.length; ++i )
{
File file = new File( fromDir, files[i] );
expandFile( fileUtils, file, dest );
}
}
}
}

/*
* This method is to be overridden by extending unarchival tasks.
*/
protected void expandFile( FileUtils fileUtils, File srcF, File dir )
{
ZipInputStream zis = null;
try
{
// code from WarExpand
zis = new ZipInputStream( new FileInputStream( srcF ) );
ZipEntry ze = null;

while( ( ze = zis.getNextEntry() ) != null )
{
extractFile( fileUtils, srcF, dir, zis,
ze.getName(),
new Date( ze.getTime() ),
ze.isDirectory() );
}

log( "expand complete", Project.MSG_VERBOSE );
}
catch( IOException ioe )
{
throw new BuildException( "Error while expanding " + srcF.getPath(), ioe );
}
finally
{
if( zis != null )
{
try
{
zis.close();
}
catch( IOException e )
{}
}
}
}

protected void extractFile( FileUtils fileUtils, File srcF, File dir,
InputStream compressedInputStream,
String entryName,
Date entryDate, boolean isDirectory )
throws IOException
{

if( patternsets != null && patternsets.size() > 0 )
{
String name = entryName;
boolean included = false;
for( int v = 0; v < patternsets.size(); v++ )
{
PatternSet p = ( PatternSet )patternsets.elementAt( v );
String[] incls = p.getIncludePatterns( project );
if( incls != null )
{
for( int w = 0; w < incls.length; w++ )
{
boolean isIncl = DirectoryScanner.match( incls[w], name );
if( isIncl )
{
included = true;
break;
}
}
}
String[] excls = p.getExcludePatterns( project );
if( excls != null )
{
for( int w = 0; w < excls.length; w++ )
{
boolean isExcl = DirectoryScanner.match( excls[w], name );
if( isExcl )
{
included = false;
break;
}
}
}
}
if( !included )
{
//Do not process this file
return;
}
}

File f = fileUtils.resolveFile( dir, entryName );
try
{
if( !overwrite && f.exists()
&& f.lastModified() >= entryDate.getTime() )
{
log( "Skipping " + f + " as it is up-to-date",
Project.MSG_DEBUG );
return;
}

log( "expanding " + entryName + " to " + f,
Project.MSG_VERBOSE );
// create intermediary directories - sometimes zip don't add them
File dirF = fileUtils.getParentFile( f );
dirF.mkdirs();

if( isDirectory )
{
f.mkdirs();
}
else
{
byte[] buffer = new byte[1024];
int length = 0;
FileOutputStream fos = null;
try
{
fos = new FileOutputStream( f );

while( ( length =
compressedInputStream.read( buffer ) ) >= 0 )
{
fos.write( buffer, 0, length );
}

fos.close();
fos = null;
}
finally
{
if( fos != null )
{
try
{
fos.close();
}
catch( IOException e )
{}
}
}
}

fileUtils.setFileLastModified( f, entryDate.getTime() );
}
catch( FileNotFoundException ex )
{
log( "Unable to expand to file " + f.getPath(), Project.MSG_WARN );
}

}
}

+ 74
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Filter.java View File

@@ -0,0 +1,74 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;

/**
* This task sets a token filter that is used by the file copy methods of the
* project to do token substitution, or sets mutiple tokens by reading these
* from a file.
*
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">
* stefano@apache.org</a>
* @author Gero Vermaas <a href="mailto:gero@xs4all.nl">gero@xs4all.nl</a>
* @author <A href="gholam@xtra.co.nz">Michael McCallum</A>
*/
public class Filter extends Task
{
private File filtersFile;

private String token;
private String value;

public void setFiltersfile( File filtersFile )
{
this.filtersFile = filtersFile;
}

public void setToken( String token )
{
this.token = token;
}

public void setValue( String value )
{
this.value = value;
}

public void execute()
throws BuildException
{
boolean isFiltersFromFile = filtersFile != null && token == null && value == null;
boolean isSingleFilter = filtersFile == null && token != null && value != null;

if( !isFiltersFromFile && !isSingleFilter )
{
throw new BuildException( "both token and value parameters, or only a filtersFile parameter is required", location );
}

if( isSingleFilter )
{
project.getGlobalFilterSet().addFilter( token, value );
}

if( isFiltersFromFile )
{
readFilters();
}
}

protected void readFilters()
throws BuildException
{
log( "Reading filters from " + filtersFile, Project.MSG_VERBOSE );
project.getGlobalFilterSet().readFiltersFromFile( filtersFile );
}
}

+ 1159
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/FixCRLF.java
File diff suppressed because it is too large
View File


+ 93
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/GUnzip.java View File

@@ -0,0 +1,93 @@
/*
* 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.tools.ant.taskdefs;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import org.apache.tools.ant.BuildException;

/**
* Expands a file that has been compressed with the GZIP algorithm. Normally
* used to compress non-compressed archives such as TAR files.
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
*/

public class GUnzip extends Unpack
{

private final static String DEFAULT_EXTENSION = ".gz";

protected String getDefaultExtension()
{
return DEFAULT_EXTENSION;
}

protected void extract()
{
if( source.lastModified() > dest.lastModified() )
{
log( "Expanding " + source.getAbsolutePath() + " to "
+ dest.getAbsolutePath() );

FileOutputStream out = null;
GZIPInputStream zIn = null;
FileInputStream fis = null;
try
{
out = new FileOutputStream( dest );
fis = new FileInputStream( source );
zIn = new GZIPInputStream( fis );
byte[] buffer = new byte[8 * 1024];
int count = 0;
do
{
out.write( buffer, 0, count );
count = zIn.read( buffer, 0, buffer.length );
}while ( count != -1 );
}
catch( IOException ioe )
{
String msg = "Problem expanding gzip " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}
finally
{
if( fis != null )
{
try
{
fis.close();
}
catch( IOException ioex )
{}
}
if( out != null )
{
try
{
out.close();
}
catch( IOException ioex )
{}
}
if( zIn != null )
{
try
{
zIn.close();
}
catch( IOException ioex )
{}
}
}
}
}
}

+ 53
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/GZip.java View File

@@ -0,0 +1,53 @@
/*
* 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.tools.ant.taskdefs;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.taskdefs.Pack;

/**
* Compresses a file with the GZIP algorithm. Normally used to compress
* non-compressed archives such as TAR files.
*
* @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a>
* @author Jon S. Stevens <a href="mailto:jon@clearink.com">jon@clearink.com</a>
* @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
*/

public class GZip extends Pack
{
protected void pack()
{
GZIPOutputStream zOut = null;
try
{
zOut = new GZIPOutputStream( new FileOutputStream( zipFile ) );
zipFile( source, zOut );
}
catch( IOException ioe )
{
String msg = "Problem creating gzip " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}
finally
{
if( zOut != null )
{
try
{
// close up
zOut.close();
}
catch( IOException e )
{}
}
}
}
}

+ 350
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/GenerateKey.java View File

@@ -0,0 +1,350 @@
/*
* 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.tools.ant.taskdefs;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Commandline;

/**
* Generates a key.
*
* @author <a href="mailto:donaldp@apache.org">Peter Donald</a>
*/
public class GenerateKey extends Task
{

/**
* The alias of signer.
*/
protected String alias;
protected String dname;
protected DistinguishedName expandedDname;
protected String keyalg;
protected String keypass;
protected int keysize;

/**
* The name of keystore file.
*/
protected String keystore;

protected String sigalg;
protected String storepass;
protected String storetype;
protected int validity;
protected boolean verbose;

public void setAlias( final String alias )
{
this.alias = alias;
}

public void setDname( final String dname )
{
if( null != expandedDname )
{
throw new BuildException( "It is not possible to specify dname both " +
"as attribute and element." );
}
this.dname = dname;
}

public void setKeyalg( final String keyalg )
{
this.keyalg = keyalg;
}

public void setKeypass( final String keypass )
{
this.keypass = keypass;
}

public void setKeysize( final String keysize )
throws BuildException
{
try
{
this.keysize = Integer.parseInt( keysize );
}
catch( final NumberFormatException nfe )
{
throw new BuildException( "KeySize attribute should be a integer" );
}
}

public void setKeystore( final String keystore )
{
this.keystore = keystore;
}

public void setSigalg( final String sigalg )
{
this.sigalg = sigalg;
}

public void setStorepass( final String storepass )
{
this.storepass = storepass;
}

public void setStoretype( final String storetype )
{
this.storetype = storetype;
}

public void setValidity( final String validity )
throws BuildException
{
try
{
this.validity = Integer.parseInt( validity );
}
catch( final NumberFormatException nfe )
{
throw new BuildException( "Validity attribute should be a integer" );
}
}

public void setVerbose( final boolean verbose )
{
this.verbose = verbose;
}

public DistinguishedName createDname()
throws BuildException
{
if( null != expandedDname )
{
throw new BuildException( "DName sub-element can only be specified once." );
}
if( null != dname )
{
throw new BuildException( "It is not possible to specify dname both " +
"as attribute and element." );
}
expandedDname = new DistinguishedName();
return expandedDname;
}

public void execute()
throws BuildException
{
if( project.getJavaVersion().equals( Project.JAVA_1_1 ) )
{
throw new BuildException( "The genkey task is only available on JDK" +
" versions 1.2 or greater" );
}

if( null == alias )
{
throw new BuildException( "alias attribute must be set" );
}

if( null == storepass )
{
throw new BuildException( "storepass attribute must be set" );
}

if( null == dname && null == expandedDname )
{
throw new BuildException( "dname must be set" );
}

final StringBuffer sb = new StringBuffer();

sb.append( "keytool -genkey " );

if( verbose )
{
sb.append( "-v " );
}

sb.append( "-alias \"" );
sb.append( alias );
sb.append( "\" " );

if( null != dname )
{
sb.append( "-dname \"" );
sb.append( dname );
sb.append( "\" " );
}

if( null != expandedDname )
{
sb.append( "-dname \"" );
sb.append( expandedDname );
sb.append( "\" " );
}

if( null != keystore )
{
sb.append( "-keystore \"" );
sb.append( keystore );
sb.append( "\" " );
}

if( null != storepass )
{
sb.append( "-storepass \"" );
sb.append( storepass );
sb.append( "\" " );
}

if( null != storetype )
{
sb.append( "-storetype \"" );
sb.append( storetype );
sb.append( "\" " );
}

sb.append( "-keypass \"" );
if( null != keypass )
{
sb.append( keypass );
}
else
{
sb.append( storepass );
}
sb.append( "\" " );

if( null != sigalg )
{
sb.append( "-sigalg \"" );
sb.append( sigalg );
sb.append( "\" " );
}

if( null != keyalg )
{
sb.append( "-keyalg \"" );
sb.append( keyalg );
sb.append( "\" " );
}

if( 0 < keysize )
{
sb.append( "-keysize \"" );
sb.append( keysize );
sb.append( "\" " );
}

if( 0 < validity )
{
sb.append( "-validity \"" );
sb.append( validity );
sb.append( "\" " );
}

log( "Generating Key for " + alias );
final ExecTask cmd = ( ExecTask )project.createTask( "exec" );
cmd.setCommand( new Commandline( sb.toString() ) );
cmd.setFailonerror( true );
cmd.setTaskName( getTaskName() );
cmd.execute();
}

public static class DistinguishedName
{

private Vector params = new Vector();
private String name;
private String path;

public Enumeration getParams()
{
return params.elements();
}

public Object createParam()
{
DnameParam param = new DnameParam();
params.addElement( param );

return param;
}

public String encode( final String string )
{
int end = string.indexOf( ',' );

if( -1 == end )
return string;

final StringBuffer sb = new StringBuffer();

int start = 0;

while( -1 != end )
{
sb.append( string.substring( start, end ) );
sb.append( "\\," );
start = end + 1;
end = string.indexOf( ',', start );
}

sb.append( string.substring( start ) );

return sb.toString();
}

public String toString()
{
final int size = params.size();
final StringBuffer sb = new StringBuffer();
boolean firstPass = true;

for( int i = 0; i < size; i++ )
{
if( !firstPass )
{
sb.append( " ," );
}
firstPass = false;

final DnameParam param = ( DnameParam )params.elementAt( i );
sb.append( encode( param.getName() ) );
sb.append( '=' );
sb.append( encode( param.getValue() ) );
}

return sb.toString();
}
}

public static class DnameParam
{
private String name;
private String value;

public void setName( String name )
{
this.name = name;
}

public void setValue( String value )
{
this.value = value;
}

public String getName()
{
return name;
}

public String getValue()
{
return value;
}
}
}


+ 410
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Get.java View File

@@ -0,0 +1,410 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.Date;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;

/**
* Get a particular file from a URL source. Options include verbose reporting,
* timestamp based fetches and controlling actions on failures. NB: access
* through a firewall only works if the whole Java runtime is correctly
* configured.
*
* @author costin@dnt.ro
* @author gg@grtmail.com (Added Java 1.1 style HTTP basic auth)
*/
public class Get extends Task
{// required
private boolean verbose = false;
private boolean useTimestamp = false;//off by default
private boolean ignoreErrors = false;
private String uname = null;
private String pword = null;// required
private File dest;
private URL source;

/**
* Where to copy the source file.
*
* @param dest Path to file.
*/
public void setDest( File dest )
{
this.dest = dest;
}

/**
* Don't stop if get fails if set to "<CODE>true</CODE>".
*
* @param v if "true" then don't report download errors up to ant
*/
public void setIgnoreErrors( boolean v )
{
ignoreErrors = v;
}

/**
* password for the basic auth.
*
* @param p password for authentication
*/
public void setPassword( String p )
{
this.pword = p;
}

/**
* Set the URL.
*
* @param u URL for the file.
*/
public void setSrc( URL u )
{
this.source = u;
}

/**
* Use timestamps, if set to "<CODE>true</CODE>". <p>
*
* In this situation, the if-modified-since header is set so that the file
* is only fetched if it is newer than the local file (or there is no local
* file) This flag is only valid on HTTP connections, it is ignored in other
* cases. When the flag is set, the local copy of the downloaded file will
* also have its timestamp set to the remote file time. <br>
* Note that remote files of date 1/1/1970 (GMT) are treated as 'no
* timestamp', and web servers often serve files with a timestamp in the
* future by replacing their timestamp with that of the current time. Also,
* inter-computer clock differences can cause no end of grief.
*
* @param v "true" to enable file time fetching
*/
public void setUseTimestamp( boolean v )
{
if( project.getJavaVersion() != Project.JAVA_1_1 )
{
useTimestamp = v;
}
}


/**
* Username for basic auth.
*
* @param u username for authentication
*/
public void setUsername( String u )
{
this.uname = u;
}

/**
* Be verbose, if set to "<CODE>true</CODE>".
*
* @param v if "true" then be verbose
*/
public void setVerbose( boolean v )
{
verbose = v;
}


/**
* Does the work.
*
* @exception BuildException Thrown in unrecoverable error.
*/
public void execute()
throws BuildException
{
if( source == null )
{
throw new BuildException( "src attribute is required", location );
}

if( dest == null )
{
throw new BuildException( "dest attribute is required", location );
}

if( dest.exists() && dest.isDirectory() )
{
throw new BuildException( "The specified destination is a directory",
location );
}

if( dest.exists() && !dest.canWrite() )
{
throw new BuildException( "Can't write to " + dest.getAbsolutePath(),
location );
}

try
{

log( "Getting: " + source );

//set the timestamp to the file date.
long timestamp = 0;

boolean hasTimestamp = false;
if( useTimestamp && dest.exists() )
{
timestamp = dest.lastModified();
if( verbose )
{
Date t = new Date( timestamp );
log( "local file date : " + t.toString() );
}

hasTimestamp = true;
}

//set up the URL connection
URLConnection connection = source.openConnection();
//modify the headers
//NB: things like user authentication could go in here too.
if( useTimestamp && hasTimestamp )
{
connection.setIfModifiedSince( timestamp );
}
// prepare Java 1.1 style credentials
if( uname != null || pword != null )
{
String up = uname + ":" + pword;
String encoding;
// check to see if sun's Base64 encoder is available.
try
{
sun.misc.BASE64Encoder encoder =
( sun.misc.BASE64Encoder )Class.forName( "sun.misc.BASE64Encoder" ).newInstance();
encoding = encoder.encode( up.getBytes() );

}
catch( Exception ex )
{// sun's base64 encoder isn't available
Base64Converter encoder = new Base64Converter();
encoding = encoder.encode( up.getBytes() );
}
connection.setRequestProperty( "Authorization", "Basic " + encoding );
}

//connect to the remote site (may take some time)
connection.connect();
//next test for a 304 result (HTTP only)
if( connection instanceof HttpURLConnection )
{
HttpURLConnection httpConnection = ( HttpURLConnection )connection;
if( httpConnection.getResponseCode() == HttpURLConnection.HTTP_NOT_MODIFIED )
{
//not modified so no file download. just return instead
//and trace out something so the user doesn't think that the
//download happened when it didnt
log( "Not modified - so not downloaded" );
return;
}
// test for 401 result (HTTP only)
if( httpConnection.getResponseCode() == HttpURLConnection.HTTP_UNAUTHORIZED )
{
log( "Not authorized - check " + dest + " for details" );
return;
}

}

//REVISIT: at this point even non HTTP connections may support the if-modified-since
//behaviour -we just check the date of the content and skip the write if it is not
//newer. Some protocols (FTP) dont include dates, of course.

FileOutputStream fos = new FileOutputStream( dest );

InputStream is = null;
for( int i = 0; i < 3; i++ )
{
try
{
is = connection.getInputStream();
break;
}
catch( IOException ex )
{
log( "Error opening connection " + ex );
}
}
if( is == null )
{
log( "Can't get " + source + " to " + dest );
if( ignoreErrors )
return;
throw new BuildException( "Can't get " + source + " to " + dest,
location );
}

byte[] buffer = new byte[100 * 1024];
int length;

while( ( length = is.read( buffer ) ) >= 0 )
{
fos.write( buffer, 0, length );
if( verbose )
System.out.print( "." );
}
if( verbose )
System.out.println();
fos.close();
is.close();

//if (and only if) the use file time option is set, then the
//saved file now has its timestamp set to that of the downloaded file
if( useTimestamp )
{
long remoteTimestamp = connection.getLastModified();
if( verbose )
{
Date t = new Date( remoteTimestamp );
log( "last modified = " + t.toString()
+ ( ( remoteTimestamp == 0 ) ? " - using current time instead" : "" ) );
}
if( remoteTimestamp != 0 )
touchFile( dest, remoteTimestamp );
}
}
catch( IOException ioe )
{
log( "Error getting " + source + " to " + dest );
if( ignoreErrors )
return;
throw new BuildException( ioe);
}
}

/**
* set the timestamp of a named file to a specified time.
*
* @param file Description of Parameter
* @param timemillis Description of Parameter
* @return true if it succeeded. False means that this is a java1.1 system
* and that file times can not be set
* @exception BuildException Thrown in unrecoverable error. Likely this
* comes from file access failures.
*/
protected boolean touchFile( File file, long timemillis )
throws BuildException
{

if( project.getJavaVersion() != Project.JAVA_1_1 )
{
Touch touch = ( Touch )project.createTask( "touch" );
touch.setOwningTarget( target );
touch.setTaskName( getTaskName() );
touch.setLocation( getLocation() );
touch.setFile( file );
touch.setMillis( timemillis );
touch.touch();
return true;
}
else
{
return false;
}
}

/**
* BASE 64 encoding of a String or an array of bytes. Based on RFC 1421.
*
* @author Unknown
* @author <a HREF="gg@grtmail.com">Gautam Guliani</a>
*/

class Base64Converter
{

public final char[] alphabet = {
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', // 0 to 7
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 8 to 15
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 16 to 23
'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 24 to 31
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 32 to 39
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 40 to 47
'w', 'x', 'y', 'z', '0', '1', '2', '3', // 48 to 55
'4', '5', '6', '7', '8', '9', '+', '/'};// 56 to 63


public String encode( String s )
{
return encode( s.getBytes() );
}

public String encode( byte[] octetString )
{
int bits24;
int bits6;

char[] out
= new char[( ( octetString.length - 1 ) / 3 + 1 ) * 4];

int outIndex = 0;
int i = 0;

while( ( i + 3 ) <= octetString.length )
{
// store the octets
bits24 = ( octetString[i++] & 0xFF ) << 16;
bits24 |= ( octetString[i++] & 0xFF ) << 8;

bits6 = ( bits24 & 0x00FC0000 ) >> 18;
out[outIndex++] = alphabet[bits6];
bits6 = ( bits24 & 0x0003F000 ) >> 12;
out[outIndex++] = alphabet[bits6];
bits6 = ( bits24 & 0x00000FC0 ) >> 6;
out[outIndex++] = alphabet[bits6];
bits6 = ( bits24 & 0x0000003F );
out[outIndex++] = alphabet[bits6];
}

if( octetString.length - i == 2 )
{
// store the octets
bits24 = ( octetString[i] & 0xFF ) << 16;
bits24 |= ( octetString[i + 1] & 0xFF ) << 8;
bits6 = ( bits24 & 0x00FC0000 ) >> 18;
out[outIndex++] = alphabet[bits6];
bits6 = ( bits24 & 0x0003F000 ) >> 12;
out[outIndex++] = alphabet[bits6];
bits6 = ( bits24 & 0x00000FC0 ) >> 6;
out[outIndex++] = alphabet[bits6];

// padding
out[outIndex++] = '=';
}
else if( octetString.length - i == 1 )
{
// store the octets
bits24 = ( octetString[i] & 0xFF ) << 16;
bits6 = ( bits24 & 0x00FC0000 ) >> 18;
out[outIndex++] = alphabet[bits6];
bits6 = ( bits24 & 0x0003F000 ) >> 12;
out[outIndex++] = alphabet[bits6];

// padding
out[outIndex++] = '=';
out[outIndex++] = '=';
}

return new String( out );
}
}
}

+ 153
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Input.java View File

@@ -0,0 +1,153 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import java.util.Vector;
import org.apache.tools.ant.*;


/**
* Ant task to read input line from console.
*
* @author Ulrich Schmidt <usch@usch.net>
*/
public class Input extends Task
{
private String validargs = null;
private String message = "";
private String addproperty = null;
private String input = null;

/**
* No arg constructor.
*/
public Input() { }

/**
* Defines the name of a property to be created from input. Behaviour is
* according to property task which means that existing properties cannot be
* overriden.
*
* @param addproperty Name for the property to be created from input
*/
public void setAddproperty( String addproperty )
{
this.addproperty = addproperty;
}

/**
* Sets the Message which gets displayed to the user during the build run.
*
* @param message The message to be displayed.
*/
public void setMessage( String message )
{
this.message = message;
}

/**
* Sets surrogate input to allow automated testing.
*
* @param testinput The new Testinput value
*/
public void setTestinput( String testinput )
{
this.input = testinput;
}

/**
* Defines valid input parameters as comma separated String. If set, input
* task will reject any input not defined as accepted and requires the user
* to reenter it. Validargs are case sensitive. If you want 'a' and 'A' to
* be accepted you need to define both values as accepted arguments.
*
* @param validargs A comma separated String defining valid input args.
*/
public void setValidargs( String validargs )
{
this.validargs = validargs;
}

// copied n' pasted from org.apache.tools.ant.taskdefs.Exit
/**
* Set a multiline message.
*
* @param msg The feature to be added to the Text attribute
*/
public void addText( String msg )
{
message += project.replaceProperties( msg );
}

/**
* Actual test method executed by jakarta-ant.
*
* @exception BuildException
*/
public void execute()
throws BuildException
{
Vector accept = null;
if( validargs != null )
{
accept = new Vector();
StringTokenizer stok = new StringTokenizer( validargs, ",", false );
while( stok.hasMoreTokens() )
{
accept.addElement( stok.nextToken() );
}
}
log( message, Project.MSG_WARN );
if( input == null )
{
try
{
BufferedReader in = new BufferedReader( new InputStreamReader( System.in ) );
input = in.readLine();
if( accept != null )
{
while( !accept.contains( input ) )
{
log( message, Project.MSG_WARN );
input = in.readLine();
}
}
}
catch( IOException e )
{
throw new BuildException( "Failed to read input from Console.", e );
}
}
// not quite the original intention of this task but for the sake
// of testing ;-)
else
{
if( accept != null && ( !accept.contains( input ) ) )
{
throw new BuildException( "Invalid input please reenter." );
}
}
// adopted from org.apache.tools.ant.taskdefs.Property
if( addproperty != null )
{
if( project.getProperty( addproperty ) == null )
{
project.setProperty( addproperty, input );
}
else
{
log( "Override ignored for " + addproperty, Project.MSG_VERBOSE );
}
}
}
}



+ 397
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Jar.java View File

@@ -0,0 +1,397 @@
/*
* 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.tools.ant.taskdefs;
import java.io.*;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.FileScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.ZipFileSet;
import org.apache.tools.zip.ZipOutputStream;

/**
* Creates a JAR archive.
*
* @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a>
*/
public class Jar extends Zip
{
/**
* The index file name.
*/
private final static String INDEX_NAME = "META-INF/INDEX.LIST";

/**
* true if a manifest has been specified in the task
*/
private boolean buildFileManifest = false;

/**
* jar index is JDK 1.3+ only
*/
private boolean index = false;
private Manifest execManifest;
private Manifest manifest;

private File manifestFile;

/**
* constructor
*/
public Jar()
{
super();
archiveType = "jar";
emptyBehavior = "create";
setEncoding( "UTF8" );
}

/**
* Set whether or not to create an index list for classes to speed up
* classloading.
*
* @param flag The new Index value
*/
public void setIndex( boolean flag )
{
index = flag;
}

/**
* @param jarFile The new Jarfile value
* @deprecated use setFile(File) instead.
*/
public void setJarfile( File jarFile )
{
log( "DEPRECATED - The jarfile attribute is deprecated. Use file attribute instead." );
setFile( jarFile );
}

public void setManifest( File manifestFile )
{
if( !manifestFile.exists() )
{
throw new BuildException( "Manifest file: " + manifestFile + " does not exist.",
getLocation() );
}

this.manifestFile = manifestFile;

Reader r = null;
try
{
r = new FileReader( manifestFile );
Manifest newManifest = new Manifest( r );
if( manifest == null )
{
manifest = Manifest.getDefaultManifest();
}
manifest.merge( newManifest );
}
catch( ManifestException e )
{
log( "Manifest is invalid: " + e.getMessage(), Project.MSG_ERR );
throw new BuildException( "Invalid Manifest: " + manifestFile, e, getLocation() );
}
catch( IOException e )
{
throw new BuildException( "Unable to read manifest file: " + manifestFile, e );
}
finally
{
if( r != null )
{
try
{
r.close();
}
catch( IOException e )
{
// do nothing
}
}
}
}

public void setWhenempty( WhenEmpty we )
{
log( "JARs are never empty, they contain at least a manifest file",
Project.MSG_WARN );
}

public void addConfiguredManifest( Manifest newManifest )
throws ManifestException
{
if( manifest == null )
{
manifest = Manifest.getDefaultManifest();
}
manifest.merge( newManifest );
buildFileManifest = true;
}

public void addMetainf( ZipFileSet fs )
{
// We just set the prefix for this fileset, and pass it up.
fs.setPrefix( "META-INF/" );
super.addFileset( fs );
}

/**
* Check whether the archive is up-to-date;
*
* @param scanners list of prepared scanners containing files to archive
* @param zipFile intended archive file (may or may not exist)
* @return true if nothing need be done (may have done something already);
* false if archive creation should proceed
* @exception BuildException if it likes
*/
protected boolean isUpToDate( FileScanner[] scanners, File zipFile )
throws BuildException
{
// need to handle manifest as a special check
if( buildFileManifest || manifestFile == null )
{
java.util.zip.ZipFile theZipFile = null;
try
{
theZipFile = new java.util.zip.ZipFile( zipFile );
java.util.zip.ZipEntry entry = theZipFile.getEntry( "META-INF/MANIFEST.MF" );
if( entry == null )
{
log( "Updating jar since the current jar has no manifest", Project.MSG_VERBOSE );
return false;
}
Manifest currentManifest = new Manifest( new InputStreamReader( theZipFile.getInputStream( entry ) ) );
if( manifest == null )
{
manifest = Manifest.getDefaultManifest();
}
if( !currentManifest.equals( manifest ) )
{
log( "Updating jar since jar manifest has changed", Project.MSG_VERBOSE );
return false;
}
}
catch( Exception e )
{
// any problems and we will rebuild
log( "Updating jar since cannot read current jar manifest: " + e.getClass().getName() + e.getMessage(),
Project.MSG_VERBOSE );
return false;
}
finally
{
if( theZipFile != null )
{
try
{
theZipFile.close();
}
catch( IOException e )
{
//ignore
}
}
}
}
else if( manifestFile.lastModified() > zipFile.lastModified() )
{
return false;
}
return super.isUpToDate( scanners, zipFile );
}

/**
* Make sure we don't think we already have a MANIFEST next time this task
* gets executed.
*/
protected void cleanUp()
{
super.cleanUp();
}

protected boolean createEmptyZip( File zipFile )
{
// Jar files always contain a manifest and can never be empty
return false;
}

protected void finalizeZipOutputStream( ZipOutputStream zOut )
throws IOException, BuildException
{
if( index )
{
createIndexList( zOut );
}
}

protected void initZipOutputStream( ZipOutputStream zOut )
throws IOException, BuildException
{
try
{
execManifest = Manifest.getDefaultManifest();

if( manifest != null )
{
execManifest.merge( manifest );
}
for( Enumeration e = execManifest.getWarnings(); e.hasMoreElements(); )
{
log( "Manifest warning: " + ( String )e.nextElement(), Project.MSG_WARN );
}

zipDir( null, zOut, "META-INF/" );
// time to write the manifest
ByteArrayOutputStream baos = new ByteArrayOutputStream();
PrintWriter writer = new PrintWriter( baos );
execManifest.write( writer );
writer.flush();

ByteArrayInputStream bais = new ByteArrayInputStream( baos.toByteArray() );
super.zipFile( bais, zOut, "META-INF/MANIFEST.MF", System.currentTimeMillis() );
super.initZipOutputStream( zOut );
}
catch( ManifestException e )
{
log( "Manifest is invalid: " + e.getMessage(), Project.MSG_ERR );
throw new BuildException( "Invalid Manifest", e, getLocation() );
}
}

protected void zipFile( File file, ZipOutputStream zOut, String vPath )
throws IOException
{
// If the file being added is META-INF/MANIFEST.MF, we warn if it's not the
// one specified in the "manifest" attribute - or if it's being added twice,
// meaning the same file is specified by the "manifeset" attribute and in
// a <fileset> element.
if( vPath.equalsIgnoreCase( "META-INF/MANIFEST.MF" ) )
{
log( "Warning: selected " + archiveType + " files include a META-INF/MANIFEST.MF which will be ignored " +
"(please use manifest attribute to " + archiveType + " task)", Project.MSG_WARN );
}
else
{
super.zipFile( file, zOut, vPath );
}

}

protected void zipFile( InputStream is, ZipOutputStream zOut, String vPath, long lastModified )
throws IOException
{
// If the file being added is META-INF/MANIFEST.MF, we merge it with the
// current manifest
if( vPath.equalsIgnoreCase( "META-INF/MANIFEST.MF" ) )
{
try
{
zipManifestEntry( is );
}
catch( IOException e )
{
throw new BuildException( "Unable to read manifest file: ", e );
}
}
else
{
super.zipFile( is, zOut, vPath, lastModified );
}
}

/**
* Create the index list to speed up classloading. This is a JDK 1.3+
* specific feature and is enabled by default. {@link
* http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html#JAR%20Index}
*
* @param zOut the zip stream representing the jar being built.
* @throws IOException thrown if there is an error while creating the index
* and adding it to the zip stream.
*/
private void createIndexList( ZipOutputStream zOut )
throws IOException
{
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// encoding must be UTF8 as specified in the specs.
PrintWriter writer = new PrintWriter( new OutputStreamWriter( baos, "UTF8" ) );

// version-info blankline
writer.println( "JarIndex-Version: 1.0" );
writer.println();

// header newline
writer.println( zipFile.getName() );

// JarIndex is sorting the directories by ascending order.
// it's painful to do in JDK 1.1 and it has no value but cosmetic
// since it will be read into a hashtable by the classloader.
Enumeration enum = addedDirs.keys();
while( enum.hasMoreElements() )
{
String dir = ( String )enum.nextElement();

// try to be smart, not to be fooled by a weird directory name
// @fixme do we need to check for directories starting by ./ ?
dir = dir.replace( '\\', '/' );
int pos = dir.lastIndexOf( '/' );
if( pos != -1 )
{
dir = dir.substring( 0, pos );
}

// looks like nothing from META-INF should be added
// and the check is not case insensitive.
// see sun.misc.JarIndex
if( dir.startsWith( "META-INF" ) )
{
continue;
}
// name newline
writer.println( dir );
}

writer.flush();
ByteArrayInputStream bais = new ByteArrayInputStream( baos.toByteArray() );
super.zipFile( bais, zOut, INDEX_NAME, System.currentTimeMillis() );
}



/**
* Handle situation when we encounter a manifest file If we haven't been
* given one, we use this one. If we have, we merge the manifest in,
* provided it is a new file and not the old one from the JAR we are
* updating
*
* @param is Description of Parameter
* @exception IOException Description of Exception
*/
private void zipManifestEntry( InputStream is )
throws IOException
{
try
{
if( execManifest == null )
{
execManifest = new Manifest( new InputStreamReader( is ) );
}
else if( isAddingNewFiles() )
{
execManifest.merge( new Manifest( new InputStreamReader( is ) ) );
}
}
catch( ManifestException e )
{
log( "Manifest is invalid: " + e.getMessage(), Project.MSG_ERR );
throw new BuildException( "Invalid Manifest", e, getLocation() );
}
}
}

+ 456
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Java.java View File

@@ -0,0 +1,456 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.ExitException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.CommandlineJava;
import org.apache.tools.ant.types.Environment;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;

/**
* This task acts as a loader for java applications but allows to use the same
* JVM for the called application thus resulting in much faster operation.
*
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">
* stefano@apache.org</a>
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/
public class Java extends Task
{

private CommandlineJava cmdl = new CommandlineJava();
private boolean fork = false;
private File dir = null;
private PrintStream outStream = null;
private boolean failOnError = false;
private File out;

/**
* Set the command line arguments for the class.
*
* @param s The new Args value
*/
public void setArgs( String s )
{
log( "The args attribute is deprecated. " +
"Please use nested arg elements.",
Project.MSG_WARN );
cmdl.createArgument().setLine( s );
}

/**
* Set the class name.
*
* @param s The new Classname value
* @exception BuildException Description of Exception
*/
public void setClassname( String s )
throws BuildException
{
if( cmdl.getJar() != null )
{
throw new BuildException( "Cannot use 'jar' and 'classname' attributes in same command" );
}
cmdl.setClassname( s );
}

/**
* Set the classpath to be used for this compilation.
*
* @param s The new Classpath value
*/
public void setClasspath( Path s )
{
createClasspath().append( s );
}

/**
* Adds a reference to a CLASSPATH defined elsewhere.
*
* @param r The new ClasspathRef value
*/
public void setClasspathRef( Reference r )
{
createClasspath().setRefid( r );
}

/**
* The working directory of the process
*
* @param d The new Dir value
*/
public void setDir( File d )
{
this.dir = d;
}

/**
* Throw a BuildException if process returns non 0.
*
* @param fail The new Failonerror value
*/
public void setFailonerror( boolean fail )
{
failOnError = fail;
}

/**
* Set the forking flag.
*
* @param s The new Fork value
*/
public void setFork( boolean s )
{
this.fork = s;
}

public void setJVMVersion( String value )
{
cmdl.setVmversion( value );
}

/**
* set the jar name...
*
* @param jarfile The new Jar value
* @exception BuildException Description of Exception
*/
public void setJar( File jarfile )
throws BuildException
{
if( cmdl.getClassname() != null )
{
throw new BuildException( "Cannot use 'jar' and 'classname' attributes in same command." );
}
cmdl.setJar( jarfile.getAbsolutePath() );
}

/**
* Set the command used to start the VM (only if fork==false).
*
* @param s The new Jvm value
*/
public void setJvm( String s )
{
cmdl.setVm( s );
}

/**
* Set the command line arguments for the JVM.
*
* @param s The new Jvmargs value
*/
public void setJvmargs( String s )
{
log( "The jvmargs attribute is deprecated. " +
"Please use nested jvmarg elements.",
Project.MSG_WARN );
cmdl.createVmArgument().setLine( s );
}

/**
* -mx or -Xmx depending on VM version
*
* @param max The new Maxmemory value
*/
public void setMaxmemory( String max )
{
cmdl.setMaxmemory( max );
}

/**
* File the output of the process is redirected to.
*
* @param out The new Output value
*/
public void setOutput( File out )
{
this.out = out;
}

/**
* Add a nested sysproperty element.
*
* @param sysp The feature to be added to the Sysproperty attribute
*/
public void addSysproperty( Environment.Variable sysp )
{
cmdl.addSysproperty( sysp );
}

/**
* Clear out the arguments to this java task.
*/
public void clearArgs()
{
cmdl.clearJavaArgs();
}

/**
* Creates a nested arg element.
*
* @return Description of the Returned Value
*/
public Commandline.Argument createArg()
{
return cmdl.createArgument();
}

/**
* Creates a nested classpath element
*
* @return Description of the Returned Value
*/
public Path createClasspath()
{
return cmdl.createClasspath( project ).createPath();
}

/**
* Creates a nested jvmarg element.
*
* @return Description of the Returned Value
*/
public Commandline.Argument createJvmarg()
{
return cmdl.createVmArgument();
}

/**
* Do the execution.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
int err = -1;
if( ( err = executeJava() ) != 0 )
{
if( failOnError )
{
throw new BuildException( "Java returned: " + err, location );
}
else
{
log( "Java Result: " + err, Project.MSG_ERR );
}
}
}

/**
* Do the execution and return a return code.
*
* @return the return code from the execute java class if it was executed in
* a separate VM (fork = "yes").
* @exception BuildException Description of Exception
*/
public int executeJava()
throws BuildException
{
String classname = cmdl.getClassname();
if( classname == null && cmdl.getJar() == null )
{
throw new BuildException( "Classname must not be null." );
}
if( !fork && cmdl.getJar() != null )
{
throw new BuildException( "Cannot execute a jar in non-forked mode. Please set fork='true'. " );
}

if( fork )
{
log( "Forking " + cmdl.toString(), Project.MSG_VERBOSE );

return run( cmdl.getCommandline() );
}
else
{
if( cmdl.getVmCommand().size() > 1 )
{
log( "JVM args ignored when same JVM is used.", Project.MSG_WARN );
}
if( dir != null )
{
log( "Working directory ignored when same JVM is used.", Project.MSG_WARN );
}

log( "Running in same VM " + cmdl.getJavaCommand().toString(),
Project.MSG_VERBOSE );
try
{
run( cmdl );
return 0;
}
catch( ExitException ex )
{
return ex.getStatus();
}
}
}

protected void handleErrorOutput( String line )
{
if( outStream != null )
{
outStream.println( line );
}
else
{
super.handleErrorOutput( line );
}
}

protected void handleOutput( String line )
{
if( outStream != null )
{
outStream.println( line );
}
else
{
super.handleOutput( line );
}
}

/**
* Executes the given classname with the given arguments as it was a command
* line application.
*
* @param classname Description of Parameter
* @param args Description of Parameter
* @exception BuildException Description of Exception
*/
protected void run( String classname, Vector args )
throws BuildException
{
CommandlineJava cmdj = new CommandlineJava();
cmdj.setClassname( classname );
for( int i = 0; i < args.size(); i++ )
{
cmdj.createArgument().setValue( ( String )args.elementAt( i ) );
}
run( cmdj );
}

/**
* Executes the given classname with the given arguments as it was a command
* line application.
*
* @param command Description of Parameter
* @exception BuildException Description of Exception
*/
private void run( CommandlineJava command )
throws BuildException
{
ExecuteJava exe = new ExecuteJava();
exe.setJavaCommand( command.getJavaCommand() );
exe.setClasspath( command.getClasspath() );
exe.setSystemProperties( command.getSystemProperties() );
if( out != null )
{
try
{
outStream = new PrintStream( new FileOutputStream( out ) );
exe.execute( project );
}
catch( IOException io )
{
throw new BuildException( io );
}
finally
{
if( outStream != null )
{
outStream.close();
}
}
}
else
{
exe.execute( project );
}
}

/**
* Executes the given classname with the given arguments in a separate VM.
*
* @param command Description of Parameter
* @return Description of the Returned Value
* @exception BuildException Description of Exception
*/
private int run( String[] command )
throws BuildException
{
FileOutputStream fos = null;
try
{
Execute exe = null;
if( out == null )
{
exe = new Execute( new LogStreamHandler( this, Project.MSG_INFO,
Project.MSG_WARN ),
null );
}
else
{
fos = new FileOutputStream( out );
exe = new Execute( new PumpStreamHandler( fos ), null );
}

exe.setAntRun( project );

if( dir == null )
{
dir = project.getBaseDir();
}
else if( !dir.exists() || !dir.isDirectory() )
{
throw new BuildException( dir.getAbsolutePath() + " is not a valid directory",
location );
}

exe.setWorkingDirectory( dir );

exe.setCommandline( command );
try
{
return exe.execute();
}
catch( IOException e )
{
throw new BuildException( e );
}
}
catch( IOException io )
{
throw new BuildException( io );
}
finally
{
if( fos != null )
{
try
{
fos.close();
}
catch( IOException io )
{}
}
}
}
}

+ 941
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Javac.java View File

@@ -0,0 +1,941 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.compilers.CompilerAdapter;
import org.apache.tools.ant.taskdefs.compilers.CompilerAdapterFactory;
import org.apache.myrmidon.framework.Os;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.GlobPatternMapper;
import org.apache.tools.ant.util.SourceFileScanner;

/**
* Task to compile Java source files. This task can take the following
* arguments:
* <ul>
* <li> sourcedir
* <li> destdir
* <li> deprecation
* <li> classpath
* <li> bootclasspath
* <li> extdirs
* <li> optimize
* <li> debug
* <li> encoding
* <li> target
* <li> depend
* <li> vebose
* <li> failonerror
* <li> includeantruntime
* <li> includejavaruntime
* <li> source
* </ul>
* Of these arguments, the <b>sourcedir</b> and <b>destdir</b> are required. <p>
*
* When this task executes, it will recursively scan the sourcedir and destdir
* looking for Java source files to compile. This task makes its compile
* decision based on timestamp.
*
* @author James Davidson <a href="mailto:duncan@x180.com">duncan@x180.com</a>
* @author Robin Green <a href="mailto:greenrd@hotmail.com">greenrd@hotmail.com
* </a>
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @author <a href="mailto:jayglanville@home.com">J D Glanville</a>
*/

public class Javac extends MatchingTask
{

private final static String FAIL_MSG
= "Compile failed, messages should have been provided.";
private boolean debug = false;
private boolean optimize = false;
private boolean deprecation = false;
private boolean depend = false;
private boolean verbose = false;
private boolean includeAntRuntime = true;
private boolean includeJavaRuntime = false;
private String fork = "false";
private String forkedExecutable = null;
private boolean nowarn = false;
private Vector implementationSpecificArgs = new Vector();

protected boolean failOnError = true;
protected File[] compileList = new File[0];
private Path bootclasspath;
private Path compileClasspath;
private String debugLevel;
private File destDir;
private String encoding;
private Path extdirs;
private String memoryInitialSize;
private String memoryMaximumSize;

private String source;

private Path src;
private String target;

/**
* Adds a reference to a CLASSPATH defined elsewhere.
*
* @param r The new BootClasspathRef value
*/
public void setBootClasspathRef( Reference r )
{
createBootclasspath().setRefid( r );
}

/**
* Sets the bootclasspath that will be used to compile the classes against.
*
* @param bootclasspath The new Bootclasspath value
*/
public void setBootclasspath( Path bootclasspath )
{
if( this.bootclasspath == null )
{
this.bootclasspath = bootclasspath;
}
else
{
this.bootclasspath.append( bootclasspath );
}
}

/**
* Set the classpath to be used for this compilation.
*
* @param classpath The new Classpath value
*/
public void setClasspath( Path classpath )
{
if( compileClasspath == null )
{
compileClasspath = classpath;
}
else
{
compileClasspath.append( classpath );
}
}

/**
* Adds a reference to a CLASSPATH defined elsewhere.
*
* @param r The new ClasspathRef value
*/
public void setClasspathRef( Reference r )
{
createClasspath().setRefid( r );
}

/**
* Set the debug flag.
*
* @param debug The new Debug value
*/
public void setDebug( boolean debug )
{
this.debug = debug;
}

/**
* Set the value of debugLevel.
*
* @param v Value to assign to debugLevel.
*/
public void setDebugLevel( String v )
{
this.debugLevel = v;
}

/**
* Set the depend flag.
*
* @param depend The new Depend value
*/
public void setDepend( boolean depend )
{
this.depend = depend;
}

/**
* Set the deprecation flag.
*
* @param deprecation The new Deprecation value
*/
public void setDeprecation( boolean deprecation )
{
this.deprecation = deprecation;
}

/**
* Set the destination directory into which the Java source files should be
* compiled.
*
* @param destDir The new Destdir value
*/
public void setDestdir( File destDir )
{
this.destDir = destDir;
}

/**
* Set the Java source file encoding name.
*
* @param encoding The new Encoding value
*/
public void setEncoding( String encoding )
{
this.encoding = encoding;
}

/**
* Sets the extension directories that will be used during the compilation.
*
* @param extdirs The new Extdirs value
*/
public void setExtdirs( Path extdirs )
{
if( this.extdirs == null )
{
this.extdirs = extdirs;
}
else
{
this.extdirs.append( extdirs );
}
}

/**
* Throw a BuildException if compilation fails
*
* @param fail The new Failonerror value
*/
public void setFailonerror( boolean fail )
{
failOnError = fail;
}

/**
* Sets whether to fork the javac compiler.
*
* @param f "true|false|on|off|yes|no" or the name of the javac executable.
*/
public void setFork( String f )
{
if( f.equalsIgnoreCase( "on" )
|| f.equalsIgnoreCase( "true" )
|| f.equalsIgnoreCase( "yes" ) )
{
fork = "true";
forkedExecutable = getSystemJavac();
}
else if( f.equalsIgnoreCase( "off" )
|| f.equalsIgnoreCase( "false" )
|| f.equalsIgnoreCase( "no" ) )
{
fork = "false";
forkedExecutable = null;
}
else
{
fork = "true";
forkedExecutable = f;
}
}

/**
* Include ant's own classpath in this task's classpath?
*
* @param include The new Includeantruntime value
*/
public void setIncludeantruntime( boolean include )
{
includeAntRuntime = include;
}

/**
* Sets whether or not to include the java runtime libraries to this task's
* classpath.
*
* @param include The new Includejavaruntime value
*/
public void setIncludejavaruntime( boolean include )
{
includeJavaRuntime = include;
}

/**
* Set the memoryInitialSize flag.
*
* @param memoryInitialSize The new MemoryInitialSize value
*/
public void setMemoryInitialSize( String memoryInitialSize )
{
this.memoryInitialSize = memoryInitialSize;
}

/**
* Set the memoryMaximumSize flag.
*
* @param memoryMaximumSize The new MemoryMaximumSize value
*/
public void setMemoryMaximumSize( String memoryMaximumSize )
{
this.memoryMaximumSize = memoryMaximumSize;
}

/**
* Sets whether the -nowarn option should be used.
*
* @param flag The new Nowarn value
*/
public void setNowarn( boolean flag )
{
this.nowarn = flag;
}

/**
* Set the optimize flag.
*
* @param optimize The new Optimize value
*/
public void setOptimize( boolean optimize )
{
this.optimize = optimize;
}

/**
* Proceed if compilation fails
*
* @param proceed The new Proceed value
*/
public void setProceed( boolean proceed )
{
failOnError = !proceed;
}

/**
* Set the value of source.
*
* @param v Value to assign to source.
*/
public void setSource( String v )
{
this.source = v;
}

/**
* Set the source dirs to find the source Java files.
*
* @param srcDir The new Srcdir value
*/
public void setSrcdir( Path srcDir )
{
if( src == null )
{
src = srcDir;
}
else
{
src.append( srcDir );
}
}

/**
* Sets the target VM that the classes will be compiled for. Valid strings
* are "1.1", "1.2", and "1.3".
*
* @param target The new Target value
*/
public void setTarget( String target )
{
this.target = target;
}

/**
* Set the verbose flag.
*
* @param verbose The new Verbose value
*/
public void setVerbose( boolean verbose )
{
this.verbose = verbose;
}

/**
* Gets the bootclasspath that will be used to compile the classes against.
*
* @return The Bootclasspath value
*/
public Path getBootclasspath()
{
return bootclasspath;
}

/**
* Gets the classpath to be used for this compilation.
*
* @return The Classpath value
*/
public Path getClasspath()
{
return compileClasspath;
}

/**
* Get the additional implementation specific command line arguments.
*
* @return array of command line arguments, guaranteed to be non-null.
*/
public String[] getCurrentCompilerArgs()
{
Vector args = new Vector();
for( Enumeration enum = implementationSpecificArgs.elements();
enum.hasMoreElements();
)
{
String[] curr =
( ( ImplementationSpecificArgument )enum.nextElement() ).getParts();
for( int i = 0; i < curr.length; i++ )
{
args.addElement( curr[i] );
}
}
String[] res = new String[args.size()];
args.copyInto( res );
return res;
}

/**
* Gets the debug flag.
*
* @return The Debug value
*/
public boolean getDebug()
{
return debug;
}

/**
* Get the value of debugLevel.
*
* @return value of debugLevel.
*/
public String getDebugLevel()
{
return debugLevel;
}

/**
* Gets the depend flag.
*
* @return The Depend value
*/
public boolean getDepend()
{
return depend;
}

/**
* Gets the deprecation flag.
*
* @return The Deprecation value
*/
public boolean getDeprecation()
{
return deprecation;
}

/**
* Gets the destination directory into which the java source files should be
* compiled.
*
* @return The Destdir value
*/
public File getDestdir()
{
return destDir;
}

/**
* Gets the java source file encoding name.
*
* @return The Encoding value
*/
public String getEncoding()
{
return encoding;
}

/**
* Gets the extension directories that will be used during the compilation.
*
* @return The Extdirs value
*/
public Path getExtdirs()
{
return extdirs;
}

/**
* Gets the failonerror flag.
*
* @return The Failonerror value
*/
public boolean getFailonerror()
{
return failOnError;
}

/**
* Gets the list of files to be compiled.
*
* @return The FileList value
*/
public File[] getFileList()
{
return compileList;
}

/**
* Gets whether or not the ant classpath is to be included in the task's
* classpath.
*
* @return The Includeantruntime value
*/
public boolean getIncludeantruntime()
{
return includeAntRuntime;
}

/**
* Gets whether or not the java runtime should be included in this task's
* classpath.
*
* @return The Includejavaruntime value
*/
public boolean getIncludejavaruntime()
{
return includeJavaRuntime;
}

/**
* The name of the javac executable to use in fork-mode.
*
* @return The JavacExecutable value
*/
public String getJavacExecutable()
{
if( forkedExecutable == null && isForkedJavac() )
{
forkedExecutable = getSystemJavac();
}
else if( forkedExecutable != null && !isForkedJavac() )
{
forkedExecutable = null;
}
return forkedExecutable;
}

/**
* Gets the memoryInitialSize flag.
*
* @return The MemoryInitialSize value
*/
public String getMemoryInitialSize()
{
return memoryInitialSize;
}

/**
* Gets the memoryMaximumSize flag.
*
* @return The MemoryMaximumSize value
*/
public String getMemoryMaximumSize()
{
return memoryMaximumSize;
}

/**
* Should the -nowarn option be used.
*
* @return The Nowarn value
*/
public boolean getNowarn()
{
return nowarn;
}

/**
* Gets the optimize flag.
*
* @return The Optimize value
*/
public boolean getOptimize()
{
return optimize;
}

/**
* Get the value of source.
*
* @return value of source.
*/
public String getSource()
{
return source;
}

/**
* Gets the source dirs to find the source java files.
*
* @return The Srcdir value
*/
public Path getSrcdir()
{
return src;
}

/**
* Gets the target VM that the classes will be compiled for.
*
* @return The Target value
*/
public String getTarget()
{
return target;
}

/**
* Gets the verbose flag.
*
* @return The Verbose value
*/
public boolean getVerbose()
{
return verbose;
}

/**
* Is this a forked invocation of JDK's javac?
*
* @return The ForkedJavac value
*/
public boolean isForkedJavac()
{
return !"false".equals( fork ) ||
"extJavac".equals( project.getProperty( "build.compiler" ) );
}

/**
* Maybe creates a nested classpath element.
*
* @return Description of the Returned Value
*/
public Path createBootclasspath()
{
if( bootclasspath == null )
{
bootclasspath = new Path( project );
}
return bootclasspath.createPath();
}

/**
* Maybe creates a nested classpath element.
*
* @return Description of the Returned Value
*/
public Path createClasspath()
{
if( compileClasspath == null )
{
compileClasspath = new Path( project );
}
return compileClasspath.createPath();
}

/**
* Adds an implementation specific command line argument.
*
* @return Description of the Returned Value
*/
public ImplementationSpecificArgument createCompilerArg()
{
ImplementationSpecificArgument arg =
new ImplementationSpecificArgument();
implementationSpecificArgs.addElement( arg );
return arg;
}

/**
* Maybe creates a nested classpath element.
*
* @return Description of the Returned Value
*/
public Path createExtdirs()
{
if( extdirs == null )
{
extdirs = new Path( project );
}
return extdirs.createPath();
}

/**
* Create a nested src element for multiple source path support.
*
* @return a nested src element.
*/
public Path createSrc()
{
if( src == null )
{
src = new Path( project );
}
return src.createPath();
}

/**
* Executes the task.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
// first off, make sure that we've got a srcdir

if( src == null )
{
throw new BuildException( "srcdir attribute must be set!", location );
}
String[] list = src.list();
if( list.length == 0 )
{
throw new BuildException( "srcdir attribute must be set!", location );
}

if( destDir != null && !destDir.isDirectory() )
{
throw new BuildException( "destination directory \"" + destDir + "\" does not exist or is not a directory", location );
}

// scan source directories and dest directory to build up
// compile lists
resetFileLists();
for( int i = 0; i < list.length; i++ )
{
File srcDir = ( File )project.resolveFile( list[i] );
if( !srcDir.exists() )
{
throw new BuildException( "srcdir \"" + srcDir.getPath() + "\" does not exist!", location );
}

DirectoryScanner ds = this.getDirectoryScanner( srcDir );

String[] files = ds.getIncludedFiles();

scanDir( srcDir, destDir != null ? destDir : srcDir, files );
}

// compile the source files

String compiler = determineCompiler();

if( compileList.length > 0 )
{

CompilerAdapter adapter = CompilerAdapterFactory.getCompiler(
compiler, this );
log( "Compiling " + compileList.length +
" source file"
+ ( compileList.length == 1 ? "" : "s" )
+ ( destDir != null ? " to " + destDir : "" ) );

// now we need to populate the compiler adapter
adapter.setJavac( this );

// finally, lets execute the compiler!!
if( !adapter.execute() )
{
if( failOnError )
{
throw new BuildException( FAIL_MSG, location );
}
else
{
log( FAIL_MSG, Project.MSG_ERR );
}
}
}
}

protected String getSystemJavac()
{
// This is the most common extension case - exe for windows and OS/2,
// nothing for *nix.
String extension = Os.isFamily( "dos" ) ? ".exe" : "";

// Look for java in the java.home/../bin directory. Unfortunately
// on Windows java.home doesn't always refer to the correct location,
// so we need to fall back to assuming java is somewhere on the
// PATH.
java.io.File jExecutable =
new java.io.File( System.getProperty( "java.home" ) +
"/../bin/javac" + extension );

if( jExecutable.exists() && !Os.isFamily( "netware" ) )
{
return jExecutable.getAbsolutePath();
}
else
{
return "javac";
}
}

protected boolean isJdkCompiler( String compiler )
{
return "modern".equals( compiler ) ||
"classic".equals( compiler ) ||
"javac1.1".equals( compiler ) ||
"javac1.2".equals( compiler ) ||
"javac1.3".equals( compiler ) ||
"javac1.4".equals( compiler );
}

/**
* Recreate src
*
* @return a nested src element.
*/
protected Path recreateSrc()
{
src = null;
return createSrc();
}

/**
* Clear the list of files to be compiled and copied..
*/
protected void resetFileLists()
{
compileList = new File[0];
}

/**
* Scans the directory looking for source files to be compiled. The results
* are returned in the class variable compileList
*
* @param srcDir Description of Parameter
* @param destDir Description of Parameter
* @param files Description of Parameter
*/
protected void scanDir( File srcDir, File destDir, String files[] )
{
GlobPatternMapper m = new GlobPatternMapper();
m.setFrom( "*.java" );
m.setTo( "*.class" );
SourceFileScanner sfs = new SourceFileScanner( this );
File[] newFiles = sfs.restrictAsFiles( files, srcDir, destDir, m );

if( newFiles.length > 0 )
{
File[] newCompileList = new File[compileList.length +
newFiles.length];
System.arraycopy( compileList, 0, newCompileList, 0,
compileList.length );
System.arraycopy( newFiles, 0, newCompileList,
compileList.length, newFiles.length );
compileList = newCompileList;
}
}

private String determineCompiler()
{
String compiler = project.getProperty( "build.compiler" );

if( !"false".equals( fork ) )
{
if( compiler != null )
{
if( isJdkCompiler( compiler ) )
{
log( "Since fork is true, ignoring build.compiler setting.",
Project.MSG_WARN );
compiler = "extJavac";
}
else
{
log( "Since build.compiler setting isn't classic or modern, ignoring fork setting.", Project.MSG_WARN );
}
}
else
{
compiler = "extJavac";
}
}

if( compiler == null )
{
if( Project.getJavaVersion() != Project.JAVA_1_1 &&
Project.getJavaVersion() != Project.JAVA_1_2 )
{
compiler = "modern";
}
else
{
compiler = "classic";
}
}
return compiler;
}

/**
* Adds an "implementation" attribute to Commandline$Attribute used to
* filter command line attributes based on the current implementation.
*
* @author RT
*/
public class ImplementationSpecificArgument
extends Commandline.Argument
{

private String impl;

public void setImplementation( String impl )
{
this.impl = impl;
}

public String[] getParts()
{
if( impl == null || impl.equals( determineCompiler() ) )
{
return super.getParts();
}
else
{
return new String[0];
}
}
}

}

+ 95
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/JavacOutputStream.java View File

@@ -0,0 +1,95 @@
/*
* 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.tools.ant.taskdefs;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.tools.ant.Task;

/**
* Serves as an output stream to Javac. This let's us print messages out to the
* log and detect whether or not Javac had an error while compiling.
*
* @author James Duncan Davidson (duncan@x180.com)
* @deprecated use returnvalue of compile to detect compilation failure.
*/

class JavacOutputStream extends OutputStream
{
private boolean errorFlag = false;
private StringBuffer line;

private Task task;

/**
* Constructs a new JavacOutputStream with the given task as the output
* source for messages.
*
* @param task Description of Parameter
*/

JavacOutputStream( Task task )
{
this.task = task;
line = new StringBuffer();
}

/**
* Write a character to the output stream. This method looks to make sure
* that there isn't an error being reported and will flush each line of
* input out to the project's log stream.
*
* @param c Description of Parameter
* @exception IOException Description of Exception
*/

public void write( int c )
throws IOException
{
char cc = ( char )c;
if( cc == '\r' || cc == '\n' )
{
// line feed
if( line.length() > 0 )
{
processLine();
}
}
else
{
line.append( cc );
}
}

/**
* Returns the error status of the compile. If no errors occured, this
* method will return false, else this method will return true.
*
* @return The ErrorFlag value
*/

boolean getErrorFlag()
{
return errorFlag;
}

/**
* Processes a line of input and determines if an error occured.
*/

private void processLine()
{
String s = line.toString();
if( s.indexOf( "error" ) > -1 )
{
errorFlag = true;
}
task.log( s );
line = new StringBuffer();
}
}


+ 1473
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Javadoc.java
File diff suppressed because it is too large
View File


+ 128
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Jikes.java View File

@@ -0,0 +1,128 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;

/**
* Encapsulates a Jikes compiler, by directly executing an external process.
*
* @author skanthak@muehlheim.de
* @deprecated merged into the class Javac.
*/
public class Jikes
{
protected String command;
protected JikesOutputParser jop;
protected Project project;

/**
* Constructs a new Jikes obect.
*
* @param jop - Parser to send jike's output to
* @param command - name of jikes executeable
* @param project Description of Parameter
*/
protected Jikes( JikesOutputParser jop, String command, Project project )
{
super();
this.jop = jop;
this.command = command;
this.project = project;
}

/**
* Do the compile with the specified arguments.
*
* @param args - arguments to pass to process on command line
*/
protected void compile( String[] args )
{
String[] commandArray = null;
File tmpFile = null;

try
{
String myos = System.getProperty( "os.name" );

// Windows has a 32k limit on total arg size, so
// create a temporary file to store all the arguments

// There have been reports that 300 files could be compiled
// so 250 is a conservative approach
if( myos.toLowerCase().indexOf( "windows" ) >= 0
&& args.length > 250 )
{
PrintWriter out = null;
try
{
tmpFile = new File( "jikes" + ( new Random( System.currentTimeMillis() ) ).nextLong() );
out = new PrintWriter( new FileWriter( tmpFile ) );
for( int i = 0; i < args.length; i++ )
{
out.println( args[i] );
}
out.flush();
commandArray = new String[]{command,
"@" + tmpFile.getAbsolutePath()};
}
catch( IOException e )
{
throw new BuildException( "Error creating temporary file", e );
}
finally
{
if( out != null )
{
try
{
out.close();
}
catch( Throwable t )
{}
}
}
}
else
{
commandArray = new String[args.length + 1];
commandArray[0] = command;
System.arraycopy( args, 0, commandArray, 1, args.length );
}

// We assume, that everything jikes writes goes to
// standard output, not to standard error. The option
// -Xstdout that is given to Jikes in Javac.doJikesCompile()
// should guarantee this. At least I hope so. :)
try
{
Execute exe = new Execute( jop );
exe.setAntRun( project );
exe.setWorkingDirectory( project.getBaseDir() );
exe.setCommandline( commandArray );
exe.execute();
}
catch( IOException e )
{
throw new BuildException( "Error running Jikes compiler", e );
}
}
finally
{
if( tmpFile != null )
{
tmpFile.delete();
}
}
}
}

+ 174
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/JikesOutputParser.java View File

@@ -0,0 +1,174 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;

/**
* Parses output from jikes and passes errors and warnings into the right
* logging channels of Project. TODO: Parsing could be much better
*
* @author skanthak@muehlheim.de
* @deprecated use Jikes' exit value to detect compilation failure.
*/
public class JikesOutputParser implements ExecuteStreamHandler
{
protected boolean errorFlag = false;
protected boolean error = false;

protected BufferedReader br;
protected boolean emacsMode;// no errors so far
protected int errors, warnings;
protected Task task;

/**
* Construct a new Parser object
*
* @param task - task in whichs context we are called
* @param emacsMode Description of Parameter
*/
protected JikesOutputParser( Task task, boolean emacsMode )
{
super();
this.task = task;
this.emacsMode = emacsMode;
}

/**
* Ignore.
*
* @param is The new ProcessErrorStream value
*/
public void setProcessErrorStream( InputStream is ) { }

/**
* Ignore.
*
* @param os The new ProcessInputStream value
*/
public void setProcessInputStream( OutputStream os ) { }

/**
* Set the inputstream
*
* @param is The new ProcessOutputStream value
* @exception IOException Description of Exception
*/
public void setProcessOutputStream( InputStream is )
throws IOException
{
br = new BufferedReader( new InputStreamReader( is ) );
}

/**
* Invokes parseOutput.
*
* @exception IOException Description of Exception
*/
public void start()
throws IOException
{
parseOutput( br );
}

/**
* Ignore.
*/
public void stop() { }

/**
* Indicate if there were errors during the compile
*
* @return if errors ocured
*/
protected boolean getErrorFlag()
{
return errorFlag;
}

/**
* Parse the output of a jikes compiler
*
* @param reader - Reader used to read jikes's output
* @exception IOException Description of Exception
*/
protected void parseOutput( BufferedReader reader )
throws IOException
{
if( emacsMode )
parseEmacsOutput( reader );
else
parseStandardOutput( reader );
}

private void setError( boolean err )
{
error = err;
if( error )
errorFlag = true;
}

private void log( String line )
{
if( !emacsMode )
{
task.log( "", ( error ? Project.MSG_ERR : Project.MSG_WARN ) );
}
task.log( line, ( error ? Project.MSG_ERR : Project.MSG_WARN ) );
}

private void parseEmacsOutput( BufferedReader reader )
throws IOException
{
// This may change, if we add advanced parsing capabilities.
parseStandardOutput( reader );
}

private void parseStandardOutput( BufferedReader reader )
throws IOException
{
String line;
String lower;
// We assume, that every output, jike does, stands for an error/warning
// XXX
// Is this correct?

// TODO:
// A warning line, that shows code, which contains a variable
// error will cause some trouble. The parser should definitely
// be much better.

while( ( line = reader.readLine() ) != null )
{
lower = line.toLowerCase();
if( line.trim().equals( "" ) )
continue;
if( lower.indexOf( "error" ) != -1 )
setError( true );
else if( lower.indexOf( "warning" ) != -1 )
setError( false );
else
{
// If we don't know the type of the line
// and we are in emacs mode, it will be
// an error, because in this mode, jikes won't
// always print "error", but sometimes other
// keywords like "Syntax". We should look for
// all those keywords.
if( emacsMode )
setError( true );
}
log( line );
}
}
}

+ 202
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/KeySubst.java View File

@@ -0,0 +1,202 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Hashtable;
import java.util.StringTokenizer;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

/**
* Keyword substitution. Input file is written to output file. Do not make input
* file same as output file. Keywords in input files look like this:
*
* @author Jon S. Stevens <a href="mailto:jon@clearink.com">jon@clearink.com</a>
* @foo@. See the docs for the setKeys method to understand how to do the
* substitutions.
* @deprecated KeySubst is deprecated. Use Filter + CopyDir instead.
*/
public class KeySubst extends Task
{
private File source = null;
private File dest = null;
private String sep = "*";
private Hashtable replacements = new Hashtable();


public static void main( String[] args )
{
try
{
Hashtable hash = new Hashtable();
hash.put( "VERSION", "1.0.3" );
hash.put( "b", "ffff" );
System.out.println( KeySubst.replace( "$f ${VERSION} f ${b} jj $", hash ) );
}
catch( Exception e )
{
e.printStackTrace();
}
}

/**
* Does replacement on text using the hashtable of keys.
*
* @param origString Description of Parameter
* @param keys Description of Parameter
* @return Description of the Returned Value
* @exception BuildException Description of Exception
* @returns the string with the replacements in it.
*/
public static String replace( String origString, Hashtable keys )
throws BuildException
{
StringBuffer finalString = new StringBuffer();
int index = 0;
int i = 0;
String key = null;
while( ( index = origString.indexOf( "${", i ) ) > -1 )
{
key = origString.substring( index + 2, origString.indexOf( "}", index + 3 ) );
finalString.append( origString.substring( i, index ) );
if( keys.containsKey( key ) )
{
finalString.append( keys.get( key ) );
}
else
{
finalString.append( "${" );
finalString.append( key );
finalString.append( "}" );
}
i = index + 3 + key.length();
}
finalString.append( origString.substring( i ) );
return finalString.toString();
}

/**
* Set the destination file.
*
* @param dest The new Dest value
*/
public void setDest( File dest )
{
this.dest = dest;
}

/**
* Format string is like this: <p>
*
* name=value*name2=value <p>
*
* Names are case sensitive. <p>
*
* Use the setSep() method to change the * to something else if you need to
* use * as a name or value.
*
* @param keys The new Keys value
*/
public void setKeys( String keys )
{
if( keys != null && keys.length() > 0 )
{
StringTokenizer tok =
new StringTokenizer( keys, this.sep, false );
while( tok.hasMoreTokens() )
{
String token = tok.nextToken().trim();
StringTokenizer itok =
new StringTokenizer( token, "=", false );

String name = itok.nextToken();
String value = itok.nextToken();
// log ( "Name: " + name );
// log ( "Value: " + value );
replacements.put( name, value );
}
}
}

/**
* Sets the seperator between name=value arguments in setKeys(). By default
* it is "*".
*
* @param sep The new Sep value
*/
public void setSep( String sep )
{
this.sep = sep;
}

/**
* Set the source file.
*
* @param s The new Src value
*/
public void setSrc( File s )
{
this.source = s;
}

/**
* Do the execution.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
log( "!! KeySubst is deprecated. Use Filter + CopyDir instead. !!" );
log( "Performing Substitions" );
if( source == null || dest == null )
{
log( "Source and destinations must not be null" );
return;
}
BufferedReader br = null;
BufferedWriter bw = null;
try
{
br = new BufferedReader( new FileReader( source ) );
dest.delete();
bw = new BufferedWriter( new FileWriter( dest ) );

String line = null;
String newline = null;
int length;
line = br.readLine();
while( line != null )
{
if( line.length() == 0 )
{
bw.newLine();
}
else
{
newline = KeySubst.replace( line, replacements );
bw.write( newline );
bw.newLine();
}
line = br.readLine();
}
bw.flush();
bw.close();
br.close();
}
catch( IOException ioe )
{
ioe.printStackTrace();
}
}
}

+ 116
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/LogOutputStream.java View File

@@ -0,0 +1,116 @@
/*
* 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.tools.ant.taskdefs;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;


/**
* Logs each line written to this stream to the log system of ant. Tries to be
* smart about line separators.<br>
* TODO: This class can be split to implement other line based processing of
* data written to the stream.
*
* @author thomas.haas@softwired-inc.com
*/
public class LogOutputStream extends OutputStream
{

private ByteArrayOutputStream buffer = new ByteArrayOutputStream();
private boolean skip = false;
private int level = Project.MSG_INFO;

private Task task;

/**
* Creates a new instance of this class.
*
* @param task the task for whom to log
* @param level loglevel used to log data written to this stream.
*/
public LogOutputStream( Task task, int level )
{
this.task = task;
this.level = level;
}

public int getMessageLevel()
{
return level;
}


/**
* Writes all remaining
*
* @exception IOException Description of Exception
*/
public void close()
throws IOException
{
if( buffer.size() > 0 )
processBuffer();
super.close();
}


/**
* Write the data to the buffer and flush the buffer, if a line separator is
* detected.
*
* @param cc data to log (byte).
* @exception IOException Description of Exception
*/
public void write( int cc )
throws IOException
{
final byte c = ( byte )cc;
if( ( c == '\n' ) || ( c == '\r' ) )
{
if( !skip )
processBuffer();
}
else
buffer.write( cc );
skip = ( c == '\r' );
}


/**
* Converts the buffer to a string and sends it to <code>processLine</code>
*/
protected void processBuffer()
{
processLine( buffer.toString() );
buffer.reset();
}

/**
* Logs a line to the log system of ant.
*
* @param line the line to log.
*/
protected void processLine( String line )
{
processLine( line, level );
}

/**
* Logs a line to the log system of ant.
*
* @param line the line to log.
* @param level Description of Parameter
*/
protected void processLine( String line, int level )
{
task.log( line, level );
}
}

+ 48
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/LogStreamHandler.java View File

@@ -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.tools.ant.taskdefs;
import java.io.IOException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

/**
* Logs standard output and error of a subprocess to the log system of ant.
*
* @author thomas.haas@softwired-inc.com
*/
public class LogStreamHandler extends PumpStreamHandler
{

/**
* Creates a new instance of this class.
*
* @param task the task for whom to log
* @param outlevel the loglevel used to log standard output
* @param errlevel the loglevel used to log standard error
*/
public LogStreamHandler( Task task, int outlevel, int errlevel )
{
super( new LogOutputStream( task, outlevel ),
new LogOutputStream( task, errlevel ) );
}

public void stop()
{
super.stop();
try
{
getErr().close();
getOut().close();
}
catch( IOException e )
{
// plain impossible
throw new BuildException( e );
}
}
}

+ 950
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Manifest.java View File

@@ -0,0 +1,950 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.EnumeratedAttribute;

/**
* Class to manage Manifest information
*
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/
public class Manifest extends Task
{
/**
* The standard manifest version header
*/
public final static String ATTRIBUTE_MANIFEST_VERSION = "Manifest-Version";

/**
* The standard Signature Version header
*/
public final static String ATTRIBUTE_SIGNATURE_VERSION = "Signature-Version";

/**
* The Name Attribute is the first in a named section
*/
public final static String ATTRIBUTE_NAME = "Name";

/**
* The From Header is disallowed in a Manifest
*/
public final static String ATTRIBUTE_FROM = "From";

/**
* The Class-Path Header is special - it can be duplicated
*/
public final static String ATTRIBUTE_CLASSPATH = "class-path";

/**
* Default Manifest version if one is not specified
*/
public final static String DEFAULT_MANIFEST_VERSION = "1.0";

/**
* The max length of a line in a Manifest
*/
public final static int MAX_LINE_LENGTH = 70;

/**
* The version of this manifest
*/
private String manifestVersion = DEFAULT_MANIFEST_VERSION;

/**
* The main section of this manifest
*/
private Section mainSection = new Section();

/**
* The named sections of this manifest
*/
private Hashtable sections = new Hashtable();

private File manifestFile;

private Mode mode;

/**
* Construct an empty manifest
*/
public Manifest()
{
mode = new Mode();
mode.setValue( "replace" );
manifestVersion = null;
}

/**
* Read a manifest file from the given reader
*
* @param r Description of Parameter
* @exception ManifestException Description of Exception
* @exception IOException Description of Exception
* @throws ManifestException if the manifest is not valid according to the
* JAR spec
* @throws IOException if the manifest cannot be read from the reader.
*/
public Manifest( Reader r )
throws ManifestException, IOException
{
BufferedReader reader = new BufferedReader( r );
// This should be the manifest version
String nextSectionName = mainSection.read( reader );
String readManifestVersion = mainSection.getAttributeValue( ATTRIBUTE_MANIFEST_VERSION );
if( readManifestVersion != null )
{
manifestVersion = readManifestVersion;
mainSection.removeAttribute( ATTRIBUTE_MANIFEST_VERSION );
}

String line = null;
while( ( line = reader.readLine() ) != null )
{
if( line.length() == 0 )
{
continue;
}

Section section = new Section();
if( nextSectionName == null )
{
Attribute sectionName = new Attribute( line );
if( !sectionName.getName().equalsIgnoreCase( ATTRIBUTE_NAME ) )
{
throw new ManifestException( "Manifest sections should start with a \"" + ATTRIBUTE_NAME +
"\" attribute and not \"" + sectionName.getName() + "\"" );
}
nextSectionName = sectionName.getValue();
}
else
{
// we have already started reading this section
// this line is the first attribute. set it and then let the normal
// read handle the rest
Attribute firstAttribute = new Attribute( line );
section.addAttributeAndCheck( firstAttribute );
}

section.setName( nextSectionName );
nextSectionName = section.read( reader );
addConfiguredSection( section );
}
}

/**
* Construct a manifest from Ant's default manifest file.
*
* @return The DefaultManifest value
* @exception BuildException Description of Exception
*/
public static Manifest getDefaultManifest()
throws BuildException
{
try
{
String s = "/org/apache/tools/ant/defaultManifest.mf";
InputStream in = Manifest.class.getResourceAsStream( s );
if( in == null )
{
throw new BuildException( "Could not find default manifest: " + s );
}
try
{
return new Manifest( new InputStreamReader( in, "ASCII" ) );
}
catch( UnsupportedEncodingException e )
{
return new Manifest( new InputStreamReader( in ) );
}
}
catch( ManifestException e )
{
throw new BuildException( "Default manifest is invalid !!" );
}
catch( IOException e )
{
throw new BuildException( "Unable to read default manifest", e );
}
}

/**
* The name of the manifest file to write (if used as a task).
*
* @param f The new File value
*/
public void setFile( File f )
{
manifestFile = f;
}

/**
* Shall we update or replace an existing manifest?
*
* @param m The new Mode value
*/
public void setMode( Mode m )
{
mode = m;
}

/**
* Get the warnings for this manifest.
*
* @return an enumeration of warning strings
*/
public Enumeration getWarnings()
{
Vector warnings = new Vector();

for( Enumeration e2 = mainSection.getWarnings(); e2.hasMoreElements(); )
{
warnings.addElement( e2.nextElement() );
}

// create a vector and add in the warnings for all the sections
for( Enumeration e = sections.elements(); e.hasMoreElements(); )
{
Section section = ( Section )e.nextElement();
for( Enumeration e2 = section.getWarnings(); e2.hasMoreElements(); )
{
warnings.addElement( e2.nextElement() );
}
}

return warnings.elements();
}

public void addConfiguredAttribute( Attribute attribute )
throws ManifestException
{
mainSection.addConfiguredAttribute( attribute );
}

public void addConfiguredSection( Section section )
throws ManifestException
{
if( section.getName() == null )
{
throw new BuildException( "Sections must have a name" );
}
sections.put( section.getName().toLowerCase(), section );
}

public boolean equals( Object rhs )
{
if( !( rhs instanceof Manifest ) )
{
return false;
}

Manifest rhsManifest = ( Manifest )rhs;
if( manifestVersion == null )
{
if( rhsManifest.manifestVersion != null )
{
return false;
}
}
else if( !manifestVersion.equals( rhsManifest.manifestVersion ) )
{
return false;
}
if( sections.size() != rhsManifest.sections.size() )
{
return false;
}

if( !mainSection.equals( rhsManifest.mainSection ) )
{
return false;
}

for( Enumeration e = sections.elements(); e.hasMoreElements(); )
{
Section section = ( Section )e.nextElement();
Section rhsSection = ( Section )rhsManifest.sections.get( section.getName().toLowerCase() );
if( !section.equals( rhsSection ) )
{
return false;
}
}

return true;
}

/**
* Create or update the Manifest when used as a task.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
if( manifestFile == null )
{
throw new BuildException( "the file attribute is required" );
}

Manifest toWrite = getDefaultManifest();

if( mode.getValue().equals( "update" ) && manifestFile.exists() )
{
FileReader f = null;
try
{
f = new FileReader( manifestFile );
toWrite.merge( new Manifest( f ) );
}
catch( ManifestException m )
{
throw new BuildException( "Existing manifest " + manifestFile
+ " is invalid", m, location );
}
catch( IOException e )
{
throw new BuildException( "Failed to read " + manifestFile,
e, location );
}
finally
{
if( f != null )
{
try
{
f.close();
}
catch( IOException e )
{}
}
}
}

try
{
toWrite.merge( this );
}
catch( ManifestException m )
{
throw new BuildException( "Manifest is invalid", m, location );
}

PrintWriter w = null;
try
{
w = new PrintWriter( new FileWriter( manifestFile ) );
toWrite.write( w );
}
catch( IOException e )
{
throw new BuildException( "Failed to write " + manifestFile,
e, location );
}
finally
{
if( w != null )
{
w.close();
}
}
}

/**
* Merge the contents of the given manifest into this manifest
*
* @param other the Manifest to be merged with this one.
* @throws ManifestException if there is a problem merging the manfest
* according to the Manifest spec.
*/
public void merge( Manifest other )
throws ManifestException
{
if( other.manifestVersion != null )
{
manifestVersion = other.manifestVersion;
}
mainSection.merge( other.mainSection );
for( Enumeration e = other.sections.keys(); e.hasMoreElements(); )
{
String sectionName = ( String )e.nextElement();
Section ourSection = ( Section )sections.get( sectionName );
Section otherSection = ( Section )other.sections.get( sectionName );
if( ourSection == null )
{
sections.put( sectionName.toLowerCase(), otherSection );
}
else
{
ourSection.merge( otherSection );
}
}

}

/**
* Convert the manifest to its string representation
*
* @return a multiline string with the Manifest as it appears in a Manifest
* file.
*/
public String toString()
{
StringWriter sw = new StringWriter();
try
{
write( new PrintWriter( sw ) );
}
catch( IOException e )
{
return null;
}
return sw.toString();
}

/**
* Write the manifest out to a print writer.
*
* @param writer the Writer to which the manifest is written
* @throws IOException if the manifest cannot be written
*/
public void write( PrintWriter writer )
throws IOException
{
writer.println( ATTRIBUTE_MANIFEST_VERSION + ": " + manifestVersion );
String signatureVersion = mainSection.getAttributeValue( ATTRIBUTE_SIGNATURE_VERSION );
if( signatureVersion != null )
{
writer.println( ATTRIBUTE_SIGNATURE_VERSION + ": " + signatureVersion );
mainSection.removeAttribute( ATTRIBUTE_SIGNATURE_VERSION );
}
mainSection.write( writer );
if( signatureVersion != null )
{
try
{
mainSection.addConfiguredAttribute( new Attribute( ATTRIBUTE_SIGNATURE_VERSION, signatureVersion ) );
}
catch( ManifestException e )
{
// shouldn't happen - ignore
}
}

for( Enumeration e = sections.elements(); e.hasMoreElements(); )
{
Section section = ( Section )e.nextElement();
section.write( writer );
}
}

/**
* Class to hold manifest attributes
*
* @author RT
*/
public static class Attribute
{
/**
* The attribute's name
*/
private String name = null;

/**
* The attribute's value
*/
private String value = null;

/**
* Construct an empty attribute
*/
public Attribute() { }

/**
* Construct an attribute by parsing a line from the Manifest
*
* @param line the line containing the attribute name and value
* @exception ManifestException Description of Exception
* @throws ManifestException if the line is not valid
*/
public Attribute( String line )
throws ManifestException
{
parse( line );
}

/**
* Construct a manifest by specifying its name and value
*
* @param name the attribute's name
* @param value the Attribute's value
*/
public Attribute( String name, String value )
{
this.name = name;
this.value = value;
}

/**
* Set the Attribute's name
*
* @param name the attribute's name
*/
public void setName( String name )
{
this.name = name;
}

/**
* Set the Attribute's value
*
* @param value the attribute's value
*/
public void setValue( String value )
{
this.value = value;
}

/**
* Get the Attribute's name
*
* @return the attribute's name.
*/
public String getName()
{
return name;
}

/**
* Get the Attribute's value
*
* @return the attribute's value.
*/
public String getValue()
{
return value;
}

/**
* Add a continuation line from the Manifest file When lines are too
* long in a manifest, they are continued on the next line by starting
* with a space. This method adds the continuation data to the attribute
* value by skipping the first character.
*
* @param line The feature to be added to the Continuation attribute
*/
public void addContinuation( String line )
{
value += line.substring( 1 );
}

public boolean equals( Object rhs )
{
if( !( rhs instanceof Attribute ) )
{
return false;
}

Attribute rhsAttribute = ( Attribute )rhs;
return ( name != null && rhsAttribute.name != null &&
name.toLowerCase().equals( rhsAttribute.name.toLowerCase() ) &&
value != null && value.equals( rhsAttribute.value ) );
}

/**
* Parse a line into name and value pairs
*
* @param line the line to be parsed
* @throws ManifestException if the line does not contain a colon
* separating the name and value
*/
public void parse( String line )
throws ManifestException
{
int index = line.indexOf( ": " );
if( index == -1 )
{
throw new ManifestException( "Manifest line \"" + line + "\" is not valid as it does not " +
"contain a name and a value separated by ': ' " );
}
name = line.substring( 0, index );
value = line.substring( index + 2 );
}

/**
* Write the attribute out to a print writer.
*
* @param writer the Writer to which the attribute is written
* @throws IOException if the attribte value cannot be written
*/
public void write( PrintWriter writer )
throws IOException
{
String line = name + ": " + value;
while( line.getBytes().length > MAX_LINE_LENGTH )
{
// try to find a MAX_LINE_LENGTH byte section
int breakIndex = MAX_LINE_LENGTH;
String section = line.substring( 0, breakIndex );
while( section.getBytes().length > MAX_LINE_LENGTH && breakIndex > 0 )
{
breakIndex--;
section = line.substring( 0, breakIndex );
}
if( breakIndex == 0 )
{
throw new IOException( "Unable to write manifest line " + name + ": " + value );
}
writer.println( section );
line = " " + line.substring( breakIndex );
}
writer.println( line );
}
}

/**
* Helper class for Manifest's mode attribute.
*
* @author RT
*/
public static class Mode extends EnumeratedAttribute
{
public String[] getValues()
{
return new String[]{"update", "replace"};
}
}

/**
* Class to represent an individual section in the Manifest. A section
* consists of a set of attribute values, separated from other sections by a
* blank line.
*
* @author RT
*/
public static class Section
{
private Vector warnings = new Vector();

/**
* The section's name if any. The main section in a manifest is unnamed.
*/
private String name = null;

/**
* The section's attributes.
*/
private Hashtable attributes = new Hashtable();

/**
* Set the Section's name
*
* @param name the section's name
*/
public void setName( String name )
{
this.name = name;
}

/**
* Get the value of the attribute with the name given.
*
* @param attributeName the name of the attribute to be returned.
* @return the attribute's value or null if the attribute does not exist
* in the section
*/
public String getAttributeValue( String attributeName )
{
Object attribute = attributes.get( attributeName.toLowerCase() );
if( attribute == null )
{
return null;
}
if( attribute instanceof Attribute )
{
return ( ( Attribute )attribute ).getValue();
}
else
{
String value = "";
for( Enumeration e = ( ( Vector )attribute ).elements(); e.hasMoreElements(); )
{
Attribute classpathAttribute = ( Attribute )e.nextElement();
value += classpathAttribute.getValue() + " ";
}
return value.trim();
}
}

/**
* Get the Section's name
*
* @return the section's name.
*/
public String getName()
{
return name;
}

public Enumeration getWarnings()
{
return warnings.elements();
}

/**
* Add an attribute to the section
*
* @param attribute the attribute to be added.
* @return the value of the attribute if it is a name attribute - null
* other wise
* @throws ManifestException if the attribute already exists in this
* section.
*/
public String addAttributeAndCheck( Attribute attribute )
throws ManifestException
{
if( attribute.getName() == null || attribute.getValue() == null )
{
throw new BuildException( "Attributes must have name and value" );
}
if( attribute.getName().equalsIgnoreCase( ATTRIBUTE_NAME ) )
{
warnings.addElement( "\"" + ATTRIBUTE_NAME + "\" attributes should not occur in the " +
"main section and must be the first element in all " +
"other sections: \"" + attribute.getName() + ": " + attribute.getValue() + "\"" );
return attribute.getValue();
}

if( attribute.getName().toLowerCase().startsWith( ATTRIBUTE_FROM.toLowerCase() ) )
{
warnings.addElement( "Manifest attributes should not start with \"" +
ATTRIBUTE_FROM + "\" in \"" + attribute.getName() + ": " + attribute.getValue() + "\"" );
}
else
{
// classpath attributes go into a vector
String attributeName = attribute.getName().toLowerCase();
if( attributeName.equals( ATTRIBUTE_CLASSPATH ) )
{
Vector classpathAttrs = ( Vector )attributes.get( attributeName );
if( classpathAttrs == null )
{
classpathAttrs = new Vector();
attributes.put( attributeName, classpathAttrs );
}
classpathAttrs.addElement( attribute );
}
else if( attributes.containsKey( attributeName ) )
{
throw new ManifestException( "The attribute \"" + attribute.getName() + "\" may not " +
"occur more than once in the same section" );
}
else
{
attributes.put( attributeName, attribute );
}
}
return null;
}

public void addConfiguredAttribute( Attribute attribute )
throws ManifestException
{
String check = addAttributeAndCheck( attribute );
if( check != null )
{
throw new BuildException( "Specify the section name using the \"name\" attribute of the <section> element rather " +
"than using a \"Name\" manifest attribute" );
}
}

public boolean equals( Object rhs )
{
if( !( rhs instanceof Section ) )
{
return false;
}

Section rhsSection = ( Section )rhs;
if( attributes.size() != rhsSection.attributes.size() )
{
return false;
}

for( Enumeration e = attributes.elements(); e.hasMoreElements(); )
{
Attribute attribute = ( Attribute )e.nextElement();
Attribute rshAttribute = ( Attribute )rhsSection.attributes.get( attribute.getName().toLowerCase() );
if( !attribute.equals( rshAttribute ) )
{
return false;
}
}

return true;
}

/**
* Merge in another section
*
* @param section the section to be merged with this one.
* @throws ManifestException if the sections cannot be merged.
*/
public void merge( Section section )
throws ManifestException
{
if( name == null && section.getName() != null ||
name != null && !( name.equalsIgnoreCase( section.getName() ) ) )
{
throw new ManifestException( "Unable to merge sections with different names" );
}

for( Enumeration e = section.attributes.keys(); e.hasMoreElements(); )
{
String attributeName = ( String )e.nextElement();
if( attributeName.equals( ATTRIBUTE_CLASSPATH ) &&
attributes.containsKey( attributeName ) )
{
// classpath entries are vetors which are merged
Vector classpathAttrs = ( Vector )section.attributes.get( attributeName );
Vector ourClasspathAttrs = ( Vector )attributes.get( attributeName );
for( Enumeration e2 = classpathAttrs.elements(); e2.hasMoreElements(); )
{
ourClasspathAttrs.addElement( e2.nextElement() );
}
}
else
{
// the merge file always wins
attributes.put( attributeName, section.attributes.get( attributeName ) );
}
}

// add in the warnings
for( Enumeration e = section.warnings.elements(); e.hasMoreElements(); )
{
warnings.addElement( e.nextElement() );
}
}

/**
* Read a section through a reader
*
* @param reader the reader from which the section is read
* @return the name of the next section if it has been read as part of
* this section - This only happens if the Manifest is malformed.
* @throws ManifestException if the section is not valid according to
* the JAR spec
* @throws IOException if the section cannot be read from the reader.
*/
public String read( BufferedReader reader )
throws ManifestException, IOException
{
Attribute attribute = null;
while( true )
{
String line = reader.readLine();
if( line == null || line.length() == 0 )
{
return null;
}
if( line.charAt( 0 ) == ' ' )
{
// continuation line
if( attribute == null )
{
if( name != null )
{
// a continuation on the first line is a continuation of the name - concatenate
// this line and the name
name += line.substring( 1 );
}
else
{
throw new ManifestException( "Can't start an attribute with a continuation line " + line );
}
}
else
{
attribute.addContinuation( line );
}
}
else
{
attribute = new Attribute( line );
String nameReadAhead = addAttributeAndCheck( attribute );
if( nameReadAhead != null )
{
return nameReadAhead;
}
}
}
}

/**
* Remove tge given attribute from the section
*
* @param attributeName the name of the attribute to be removed.
*/
public void removeAttribute( String attributeName )
{
attributes.remove( attributeName.toLowerCase() );
}

/**
* Write the section out to a print writer.
*
* @param writer the Writer to which the section is written
* @throws IOException if the section cannot be written
*/
public void write( PrintWriter writer )
throws IOException
{
if( name != null )
{
Attribute nameAttr = new Attribute( ATTRIBUTE_NAME, name );
nameAttr.write( writer );
}
for( Enumeration e = attributes.elements(); e.hasMoreElements(); )
{
Object object = e.nextElement();
if( object instanceof Attribute )
{
Attribute attribute = ( Attribute )object;
attribute.write( writer );
}
else
{
Vector attrList = ( Vector )object;
for( Enumeration e2 = attrList.elements(); e2.hasMoreElements(); )
{
Attribute attribute = ( Attribute )e2.nextElement();
attribute.write( writer );
}
}
}
writer.println();
}
}

}

+ 29
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ManifestException.java View File

@@ -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.tools.ant.taskdefs;



/**
* Exception thrown indicating problems in a JAR Manifest
*
* @author <a href="mailto:conor@apache.org">Conor MacNeill</a>
*/
public class ManifestException extends Exception
{

/**
* Constructs an exception with the given descriptive message.
*
* @param msg Description of or information about the exception.
*/
public ManifestException( String msg )
{
super( msg );
}
}

+ 207
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/MatchingTask.java View File

@@ -0,0 +1,207 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.util.StringTokenizer;
import java.util.Vector;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.PatternSet;

/**
* This is an abstract task that should be used by all those tasks that require
* to include or exclude files based on pattern matching.
*
* @author Arnout J. Kuiper <a href="mailto:ajkuiper@wxs.nl">ajkuiper@wxs.nl</a>
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">
* stefano@apache.org</a>
* @author Sam Ruby <a href="mailto:rubys@us.ibm.com">rubys@us.ibm.com</a>
* @author Jon S. Stevens <a href="mailto:jon@clearink.com">jon@clearink.com</a>
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/

public abstract class MatchingTask extends Task
{

protected boolean useDefaultExcludes = true;
protected FileSet fileset = new FileSet();

/**
* Sets whether default exclusions should be used or not.
*
* @param useDefaultExcludes "true"|"on"|"yes" when default exclusions
* should be used, "false"|"off"|"no" when they shouldn't be used.
*/
public void setDefaultexcludes( boolean useDefaultExcludes )
{
this.useDefaultExcludes = useDefaultExcludes;
}

/**
* Sets the set of exclude patterns. Patterns may be separated by a comma or
* a space.
*
* @param excludes the string containing the exclude patterns
*/
public void setExcludes( String excludes )
{
fileset.setExcludes( excludes );
}

/**
* Sets the name of the file containing the includes patterns.
*
* @param excludesfile A string containing the filename to fetch the include
* patterns from.
*/
public void setExcludesfile( File excludesfile )
{
fileset.setExcludesfile( excludesfile );
}

/**
* Sets the set of include patterns. Patterns may be separated by a comma or
* a space.
*
* @param includes the string containing the include patterns
*/
public void setIncludes( String includes )
{
fileset.setIncludes( includes );
}

/**
* Sets the name of the file containing the includes patterns.
*
* @param includesfile A string containing the filename to fetch the include
* patterns from.
*/
public void setIncludesfile( File includesfile )
{
fileset.setIncludesfile( includesfile );
}

/**
* List of filenames and directory names to not include. They should be
* either , or " " (space) separated. The ignored files will be logged.
*
* @param ignoreString the string containing the files to ignore.
*/
public void XsetIgnore( String ignoreString )
{
log( "The ignore attribute is deprecated." +
"Please use the excludes attribute.",
Project.MSG_WARN );
if( ignoreString != null && ignoreString.length() > 0 )
{
Vector tmpExcludes = new Vector();
StringTokenizer tok = new StringTokenizer( ignoreString, ", ", false );
while( tok.hasMoreTokens() )
{
createExclude().setName( "**/" + tok.nextToken().trim() + "/**" );
}
}
}

/**
* Set this to be the items in the base directory that you want to be
* included. You can also specify "*" for the items (ie: items="*") and it
* will include all the items in the base directory.
*
* @param itemString the string containing the files to include.
*/
public void XsetItems( String itemString )
{
log( "The items attribute is deprecated. " +
"Please use the includes attribute.",
Project.MSG_WARN );
if( itemString == null || itemString.equals( "*" )
|| itemString.equals( "." ) )
{
createInclude().setName( "**" );
}
else
{
StringTokenizer tok = new StringTokenizer( itemString, ", " );
while( tok.hasMoreTokens() )
{
String pattern = tok.nextToken().trim();
if( pattern.length() > 0 )
{
createInclude().setName( pattern + "/**" );
}
}
}
}

/**
* add a name entry on the exclude list
*
* @return Description of the Returned Value
*/
public PatternSet.NameEntry createExclude()
{
return fileset.createExclude();
}

/**
* add a name entry on the include files list
*
* @return Description of the Returned Value
*/
public PatternSet.NameEntry createExcludesFile()
{
return fileset.createExcludesFile();
}

/**
* add a name entry on the include list
*
* @return Description of the Returned Value
*/
public PatternSet.NameEntry createInclude()
{
return fileset.createInclude();
}

/**
* add a name entry on the include files list
*
* @return Description of the Returned Value
*/
public PatternSet.NameEntry createIncludesFile()
{
return fileset.createIncludesFile();
}

/**
* add a set of patterns
*
* @return Description of the Returned Value
*/
public PatternSet createPatternSet()
{
return fileset.createPatternSet();
}

/**
* Returns the directory scanner needed to access the files to process.
*
* @param baseDir Description of Parameter
* @return The DirectoryScanner value
*/
protected DirectoryScanner getDirectoryScanner( File baseDir )
{
fileset.setDir( baseDir );
fileset.setDefaultexcludes( useDefaultExcludes );
return fileset.getDirectoryScanner( project );
}

}

+ 55
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Mkdir.java View File

@@ -0,0 +1,55 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;


/**
* Creates a given directory.
*
* @author duncan@x180.com
*/

public class Mkdir extends Task
{

private File dir;

public void setDir( File dir )
{
this.dir = dir;
}

public void execute()
throws BuildException
{
if( dir == null )
{
throw new BuildException( "dir attribute is required", location );
}

if( dir.isFile() )
{
throw new BuildException( "Unable to create directory as a file already exists with that name: " + dir.getAbsolutePath() );
}

if( !dir.exists() )
{
boolean result = dir.mkdirs();
if( result == false )
{
String msg = "Directory " + dir.getAbsolutePath() + " creation was not " +
"successful for an unknown reason";
throw new BuildException( msg, location );
}
log( "Created dir: " + dir.getAbsolutePath() );
}
}
}

+ 302
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Move.java View File

@@ -0,0 +1,302 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.FilterSet;
import org.apache.tools.ant.types.FilterSetCollection;

/**
* Moves a file or directory to a new file or directory. By default, the
* destination is overwriten when existing. When overwrite is turned off, then
* files are only moved if the source file is newer than the destination file,
* or when the destination file does not exist.</p> <p>
*
* Source files and directories are only deleted when the file or directory has
* been copied to the destination successfully. Filtering also works.</p> <p>
*
* This implementation is based on Arnout Kuiper's initial design document, the
* following mailing list discussions, and the copyfile/copydir tasks.</p>
*
* @author Glenn McAllister <a href="mailto:glennm@ca.ibm.com">glennm@ca.ibm.com
* </a>
* @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
*/
public class Move extends Copy
{

public Move()
{
super();
forceOverwrite = true;
}

/**
* Go and delete the directory tree.
*
* @param d Description of Parameter
*/
protected void deleteDir( File d )
{
String[] list = d.list();
if( list == null )
return;// on an io error list() can return null

for( int i = 0; i < list.length; i++ )
{
String s = list[i];
File f = new File( d, s );
if( f.isDirectory() )
{
deleteDir( f );
}
else
{
throw new BuildException( "UNEXPECTED ERROR - The file " + f.getAbsolutePath() + " should not exist!" );
}
}
log( "Deleting directory " + d.getAbsolutePath(), verbosity );
if( !d.delete() )
{
throw new BuildException( "Unable to delete directory " + d.getAbsolutePath() );
}
}

//************************************************************************
// protected and private methods
//************************************************************************

protected void doFileOperations()
{
//Attempt complete directory renames, if any, first.
if( completeDirMap.size() > 0 )
{
Enumeration e = completeDirMap.keys();
while( e.hasMoreElements() )
{
File fromDir = ( File )e.nextElement();
File toDir = ( File )completeDirMap.get( fromDir );
try
{
log( "Attempting to rename dir: " + fromDir +
" to " + toDir, verbosity );
renameFile( fromDir, toDir, filtering, forceOverwrite );
}
catch( IOException ioe )
{
String msg = "Failed to rename dir " + fromDir
+ " to " + toDir
+ " due to " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}
}
}
if( fileCopyMap.size() > 0 )
{// files to move
log( "Moving " + fileCopyMap.size() + " files to " +
destDir.getAbsolutePath() );

Enumeration e = fileCopyMap.keys();
while( e.hasMoreElements() )
{
String fromFile = ( String )e.nextElement();
String toFile = ( String )fileCopyMap.get( fromFile );

if( fromFile.equals( toFile ) )
{
log( "Skipping self-move of " + fromFile, verbosity );
continue;
}

boolean moved = false;
File f = new File( fromFile );

if( f.exists() )
{//Is this file still available to be moved?
File d = new File( toFile );

try
{
log( "Attempting to rename: " + fromFile +
" to " + toFile, verbosity );
moved = renameFile( f, d, filtering, forceOverwrite );
}
catch( IOException ioe )
{
String msg = "Failed to rename " + fromFile
+ " to " + toFile
+ " due to " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}

if( !moved )
{
try
{
log( "Moving " + fromFile + " to " + toFile, verbosity );

FilterSetCollection executionFilters = new FilterSetCollection();
if( filtering )
{
executionFilters.addFilterSet( project.getGlobalFilterSet() );
}
for( Enumeration filterEnum = getFilterSets().elements(); filterEnum.hasMoreElements(); )
{
executionFilters.addFilterSet( ( FilterSet )filterEnum.nextElement() );
}
getFileUtils().copyFile( f, d, executionFilters,
forceOverwrite );

f = new File( fromFile );
if( !f.delete() )
{
throw new BuildException( "Unable to delete file "
+ f.getAbsolutePath() );
}
}
catch( IOException ioe )
{
String msg = "Failed to copy " + fromFile + " to "
+ toFile
+ " due to " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}
}
}
}
}

if( includeEmpty )
{
Enumeration e = dirCopyMap.elements();
int count = 0;
while( e.hasMoreElements() )
{
File d = new File( ( String )e.nextElement() );
if( !d.exists() )
{
if( !d.mkdirs() )
{
log( "Unable to create directory " + d.getAbsolutePath(), Project.MSG_ERR );
}
else
{
count++;
}
}
}

if( count > 0 )
{
log( "Moved " + count + " empty directories to " + destDir.getAbsolutePath() );
}
}

if( filesets.size() > 0 )
{
Enumeration e = filesets.elements();
while( e.hasMoreElements() )
{
FileSet fs = ( FileSet )e.nextElement();
File dir = fs.getDir( project );

if( okToDelete( dir ) )
{
deleteDir( dir );
}
}
}
}

/**
* Its only ok to delete a directory tree if there are no files in it.
*
* @param d Description of Parameter
* @return Description of the Returned Value
*/
protected boolean okToDelete( File d )
{
String[] list = d.list();
if( list == null )
return false;// maybe io error?

for( int i = 0; i < list.length; i++ )
{
String s = list[i];
File f = new File( d, s );
if( f.isDirectory() )
{
if( !okToDelete( f ) )
return false;
}
else
{
return false;// found a file
}
}

return true;
}

/**
* Attempts to rename a file from a source to a destination. If overwrite is
* set to true, this method overwrites existing file even if the destination
* file is newer. Otherwise, the source file is renamed only if the
* destination file is older than it. Method then checks if token filtering
* is used. If it is, this method returns false assuming it is the
* responsibility to the copyFile method.
*
* @param sourceFile Description of Parameter
* @param destFile Description of Parameter
* @param filtering Description of Parameter
* @param overwrite Description of Parameter
* @return Description of the Returned Value
* @exception BuildException Description of Exception
* @throws IOException
*/
protected boolean renameFile( File sourceFile, File destFile,
boolean filtering, boolean overwrite )
throws IOException, BuildException
{

boolean renamed = true;
if( !filtering )
{
// ensure that parent dir of dest file exists!
// not using getParentFile method to stay 1.1 compat
String parentPath = destFile.getParent();
if( parentPath != null )
{
File parent = new File( parentPath );
if( !parent.exists() )
{
parent.mkdirs();
}
}

if( destFile.exists() )
{
if( !destFile.delete() )
{
throw new BuildException( "Unable to remove existing file "
+ destFile );
}
}
renamed = sourceFile.renameTo( destFile );
}
else
{
renamed = false;
}
return renamed;
}
}

+ 93
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Pack.java View File

@@ -0,0 +1,93 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

/**
* Abstract Base class for pack tasks.
*
* @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
*/

public abstract class Pack extends Task
{
protected File source;

protected File zipFile;

public void setSrc( File src )
{
source = src;
}

public void setZipfile( File zipFile )
{
this.zipFile = zipFile;
}

public void execute()
throws BuildException
{
validate();
log( "Building: " + zipFile.getAbsolutePath() );
pack();
}

protected abstract void pack();

protected void zipFile( File file, OutputStream zOut )
throws IOException
{
FileInputStream fIn = new FileInputStream( file );
try
{
zipFile( fIn, zOut );
}
finally
{
fIn.close();
}
}

private void validate()
{
if( zipFile == null )
{
throw new BuildException( "zipfile attribute is required", location );
}

if( source == null )
{
throw new BuildException( "src attribute is required", location );
}

if( source.isDirectory() )
{
throw new BuildException( "Src attribute must not " +
"represent a directory!", location );
}
}

private void zipFile( InputStream in, OutputStream zOut )
throws IOException
{
byte[] buffer = new byte[8 * 1024];
int count = 0;
do
{
zOut.write( buffer, 0, count );
count = in.read( buffer, 0, buffer.length );
}while ( count != -1 );
}
}

+ 170
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Parallel.java View File

@@ -0,0 +1,170 @@
/*
* 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.tools.ant.taskdefs;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Location;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskContainer;


/**
* Implements a multi threaded task execution. <p>
*
*
*
* @author Thomas Christen <a href="mailto:chr@active.ch">chr@active.ch</a>
* @author <a href="mailto:conor@apache.org">Conor MacNeill </a>
*/
public class Parallel extends Task
implements TaskContainer
{

/**
* Collection holding the nested tasks
*/
private Vector nestedTasks = new Vector();


/**
* Add a nested task to execute parallel (asynchron). <p>
*
*
*
* @param nestedTask Nested task to be executed in parallel
* @exception BuildException Description of Exception
*/
public void addTask( Task nestedTask )
throws BuildException
{
nestedTasks.addElement( nestedTask );
}

/**
* Block execution until the specified time or for a specified amount of
* milliseconds and if defined, execute the wait status.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
TaskThread[] threads = new TaskThread[nestedTasks.size()];
int threadNumber = 0;
for( Enumeration e = nestedTasks.elements(); e.hasMoreElements(); threadNumber++ )
{
Task nestedTask = ( Task )e.nextElement();
threads[threadNumber] = new TaskThread( threadNumber, nestedTask );
}

// now start all threads
for( int i = 0; i < threads.length; ++i )
{
threads[i].start();
}

// now join to all the threads
for( int i = 0; i < threads.length; ++i )
{
try
{
threads[i].join();
}
catch( InterruptedException ie )
{
// who would interrupt me at a time like this?
}
}

// now did any of the threads throw an exception
StringBuffer exceptionMessage = new StringBuffer();
String lSep = System.getProperty( "line.separator" );
int numExceptions = 0;
Throwable firstException = null;
Location firstLocation = Location.UNKNOWN_LOCATION;
;
for( int i = 0; i < threads.length; ++i )
{
Throwable t = threads[i].getException();
if( t != null )
{
numExceptions++;
if( firstException == null )
{
firstException = t;
}
if( t instanceof BuildException &&
firstLocation == Location.UNKNOWN_LOCATION )
{
firstLocation = ( ( BuildException )t ).getLocation();
}
exceptionMessage.append( lSep );
exceptionMessage.append( t.getMessage() );
}
}

if( numExceptions == 1 )
{
if( firstException instanceof BuildException )
{
throw ( BuildException )firstException;
}
else
{
throw new BuildException( firstException );
}
}
else if( numExceptions > 1 )
{
throw new BuildException( exceptionMessage.toString(), firstLocation );
}
}

class TaskThread extends Thread
{
private Throwable exception;
private Task task;
private int taskNumber;

/**
* Construct a new TaskThread<p>
*
*
*
* @param task the Task to be executed in a seperate thread
* @param taskNumber Description of Parameter
*/
TaskThread( int taskNumber, Task task )
{
this.task = task;
this.taskNumber = taskNumber;
}

public Throwable getException()
{
return exception;
}

/**
* Executes the task within a thread and takes care about Exceptions
* raised within the task.
*/
public void run()
{
try
{
task.perform();
}
catch( Throwable t )
{
exception = t;
}
}
}
}

+ 157
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Patch.java View File

@@ -0,0 +1,157 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Commandline;

/**
* Task as a layer on top of patch. Patch applies a diff file to an original.
*
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
*/
public class Patch extends Task
{
private boolean havePatchfile = false;
private Commandline cmd = new Commandline();

private File originalFile;

/**
* Shall patch write backups.
*
* @param backups The new Backups value
*/
public void setBackups( boolean backups )
{
if( backups )
{
cmd.createArgument().setValue( "-b" );
}
}

/**
* Ignore whitespace differences.
*
* @param ignore The new Ignorewhitespace value
*/
public void setIgnorewhitespace( boolean ignore )
{
if( ignore )
{
cmd.createArgument().setValue( "-l" );
}
}

/**
* The file to patch.
*
* @param file The new Originalfile value
*/
public void setOriginalfile( File file )
{
originalFile = file;
}

/**
* The file containing the diff output.
*
* @param file The new Patchfile value
*/
public void setPatchfile( File file )
{
if( !file.exists() )
{
throw new BuildException( "patchfile " + file + " doesn\'t exist",
location );
}
cmd.createArgument().setValue( "-i" );
cmd.createArgument().setFile( file );
havePatchfile = true;
}

/**
* Work silently unless an error occurs.
*
* @param q The new Quiet value
*/
public void setQuiet( boolean q )
{
if( q )
{
cmd.createArgument().setValue( "-s" );
}
}

/**
* Assume patch was created with old and new files swapped.
*
* @param r The new Reverse value
*/
public void setReverse( boolean r )
{
if( r )
{
cmd.createArgument().setValue( "-R" );
}
}

/**
* Strip the smallest prefix containing <i>num</i> leading slashes from
* filenames. <p>
*
* patch's <i>-p</i> option.
*
* @param num The new Strip value
* @exception BuildException Description of Exception
*/
public void setStrip( int num )
throws BuildException
{
if( num < 0 )
{
throw new BuildException( "strip has to be >= 0", location );
}
cmd.createArgument().setValue( "-p" + num );
}

public void execute()
throws BuildException
{
if( !havePatchfile )
{
throw new BuildException( "patchfile argument is required",
location );
}

Commandline toExecute = ( Commandline )cmd.clone();
toExecute.setExecutable( "patch" );

if( originalFile != null )
{
toExecute.createArgument().setFile( originalFile );
}

Execute exe = new Execute( new LogStreamHandler( this, Project.MSG_INFO,
Project.MSG_WARN ),
null );
exe.setCommandline( toExecute.getCommandline() );
try
{
exe.execute();
}
catch( IOException e )
{
throw new BuildException( e );
}
}

}// Patch

+ 397
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/PathConvert.java View File

@@ -0,0 +1,397 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;

/**
* This task converts path and classpath information to a specific target OS
* format. The resulting formatted path is placed into a specified property. <p>
*
* LIMITATION: Currently this implementation groups all machines into one of two
* types: Unix or Windows. Unix is defined as NOT windows.
*
* @author Larry Streepy <a href="mailto:streepy@healthlanguage.com">
* streepy@healthlanguage.com</a>
*/
public class PathConvert extends Task
{

// Members
private Path path = null;// Path to be converted
private Reference refid = null;// Reference to path/fileset to convert
private String targetOS = null;// The target OS type
private boolean targetWindows = false;// Set when targetOS is set
private boolean onWindows = false;// Set if we're running on windows
private String property = null;// The property to receive the results
private Vector prefixMap = new Vector();// Path prefix map
private String pathSep = null;// User override on path sep char
private String dirSep = null;

/**
* Override the default directory separator string for the target os
*
* @param sep The new DirSep value
*/
public void setDirSep( String sep )
{
dirSep = sep;
}

/**
* Override the default path separator string for the target os
*
* @param sep The new PathSep value
*/
public void setPathSep( String sep )
{
pathSep = sep;
}

/**
* Set the value of the proprty attribute - this is the property into which
* our converted path will be placed.
*
* @param p The new Property value
*/
public void setProperty( String p )
{
property = p;
}

/**
* Adds a reference to a PATH or FILESET defined elsewhere.
*
* @param r The new Refid value
*/
public void setRefid( Reference r )
{
if( path != null )
throw noChildrenAllowed();

refid = r;
}

/**
* Set the value of the targetos attribute
*
* @param target The new Targetos value
*/
public void setTargetos( String target )
{

targetOS = target.toLowerCase();

if( !targetOS.equals( "windows" ) && !target.equals( "unix" ) &&
!targetOS.equals( "netware" ) )
{
throw new BuildException( "targetos must be one of 'unix', 'netware', or 'windows'" );
}

// Currently, we deal with only two path formats: Unix and Windows
// And Unix is everything that is not Windows

// for NetWare, piggy-back on Windows, since in the validateSetup code,
// the same assumptions can be made as with windows -
// that ; is the path separator

targetWindows = ( targetOS.equals( "windows" ) || targetOS.equals( "netware" ) );
}

/**
* Has the refid attribute of this element been set?
*
* @return The Reference value
*/
public boolean isReference()
{
return refid != null;
}

/**
* Create a nested MAP element
*
* @return Description of the Returned Value
*/
public MapEntry createMap()
{

MapEntry entry = new MapEntry();
prefixMap.addElement( entry );
return entry;
}

/**
* Create a nested PATH element
*
* @return Description of the Returned Value
*/
public Path createPath()
{

if( isReference() )
throw noChildrenAllowed();

if( path == null )
{
path = new Path( getProject() );
}
return path.createPath();
}

/**
* Do the execution.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{

// If we are a reference, the create a Path from the reference
if( isReference() )
{
path = new Path( getProject() ).createPath();

Object obj = refid.getReferencedObject( getProject() );

if( obj instanceof Path )
{
path.setRefid( refid );
}
else if( obj instanceof FileSet )
{
FileSet fs = ( FileSet )obj;
path.addFileset( fs );
}
else
{
throw new BuildException( "'refid' does not refer to a path or fileset" );
}
}

validateSetup();// validate our setup

// Currently, we deal with only two path formats: Unix and Windows
// And Unix is everything that is not Windows
// (with the exception for NetWare below)

String osname = System.getProperty( "os.name" ).toLowerCase();

// for NetWare, piggy-back on Windows, since here and in the
// apply code, the same assumptions can be made as with windows -
// that \\ is an OK separator, and do comparisons case-insensitive.
onWindows = ( ( osname.indexOf( "windows" ) >= 0 ) ||
( osname.indexOf( "netware" ) >= 0 ) );

// Determine the from/to char mappings for dir sep
char fromDirSep = onWindows ? '\\' : '/';
char toDirSep = dirSep.charAt( 0 );

StringBuffer rslt = new StringBuffer( 100 );

// Get the list of path components in canonical form
String[] elems = path.list();

for( int i = 0; i < elems.length; i++ )
{
String elem = elems[i];

elem = mapElement( elem );// Apply the path prefix map

// Now convert the path and file separator characters from the
// current os to the target os.

elem = elem.replace( fromDirSep, toDirSep );

if( i != 0 )
rslt.append( pathSep );
rslt.append( elem );
}

// Place the result into the specified property
String value = rslt.toString();

log( "Set property " + property + " = " + value, Project.MSG_VERBOSE );

getProject().setNewProperty( property, value );
}

/**
* Apply the configured map to a path element. The map is used to convert
* between Windows drive letters and Unix paths. If no map is configured,
* then the input string is returned unchanged.
*
* @param elem The path element to apply the map to
* @return String Updated element
*/
private String mapElement( String elem )
{

int size = prefixMap.size();

if( size != 0 )
{

// Iterate over the map entries and apply each one. Stop when one of the
// entries actually changes the element

for( int i = 0; i < size; i++ )
{
MapEntry entry = ( MapEntry )prefixMap.elementAt( i );
String newElem = entry.apply( elem );

// Note I'm using "!=" to see if we got a new object back from
// the apply method.

if( newElem != elem )
{
elem = newElem;
break;// We applied one, so we're done
}
}
}

return elem;
}

/**
* Creates an exception that indicates that this XML element must not have
* child elements if the refid attribute is set.
*
* @return Description of the Returned Value
*/
private BuildException noChildrenAllowed()
{
return new BuildException( "You must not specify nested PATH elements when using refid" );
}

/**
* Validate that all our parameters have been properly initialized.
*
* @throws BuildException if something is not setup properly
*/
private void validateSetup()
throws BuildException
{

if( path == null )
throw new BuildException( "You must specify a path to convert" );

if( property == null )
throw new BuildException( "You must specify a property" );

// Must either have a target OS or both a dirSep and pathSep

if( targetOS == null && pathSep == null && dirSep == null )
throw new BuildException( "You must specify at least one of targetOS, dirSep, or pathSep" );

// Determine the separator strings. The dirsep and pathsep attributes
// override the targetOS settings.
String dsep = File.separator;
String psep = File.pathSeparator;

if( targetOS != null )
{
psep = targetWindows ? ";" : ":";
dsep = targetWindows ? "\\" : "/";
}

if( pathSep != null )
{// override with pathsep=
psep = pathSep;
}

if( dirSep != null )
{// override with dirsep=
dsep = dirSep;
}

pathSep = psep;
dirSep = dsep;
}

/**
* Helper class, holds the nested <map> values. Elements will look like
* this: &lt;map from="d:" to="/foo"/> <p>
*
* When running on windows, the prefix comparison will be case insensitive.
*
* @author RT
*/
public class MapEntry
{

// Members
private String from = null;
private String to = null;

/**
* Set the "from" attribute of the map entry
*
* @param from The new From value
*/
public void setFrom( String from )
{
this.from = from;
}

/**
* Set the "to" attribute of the map entry
*
* @param to The new To value
*/
public void setTo( String to )
{
this.to = to;
}

/**
* Apply this map entry to a given path element
*
* @param elem Path element to process
* @return String Updated path element after mapping
*/
public String apply( String elem )
{
if( from == null || to == null )
{
throw new BuildException( "Both 'from' and 'to' must be set in a map entry" );
}

// If we're on windows, then do the comparison ignoring case
String cmpElem = onWindows ? elem.toLowerCase() : elem;
String cmpFrom = onWindows ? from.toLowerCase() : from;

// If the element starts with the configured prefix, then convert the prefix
// to the configured 'to' value.

if( cmpElem.startsWith( cmpFrom ) )
{
int len = from.length();

if( len >= elem.length() )
{
elem = to;
}
else
{
elem = to + elem.substring( len );
}
}

return elem;
}
}// User override on directory sep char
}

+ 90
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/ProcessDestroyer.java View File

@@ -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.tools.ant.taskdefs;
import java.lang.reflect.Method;
import java.util.Enumeration;
import java.util.Vector;

/**
* Destroys all registered <code>Process</code>es when the VM exits.
*
* @author <a href="mailto:mnewcomb@tacintel.com">Michael Newcomb</a>
*/
class ProcessDestroyer
extends Thread
{

private Vector processes = new Vector();

/**
* Constructs a <code>ProcessDestroyer</code> and registers it as a shutdown
* hook.
*/
public ProcessDestroyer()
{
try
{
// check to see if the method exists (support pre-JDK 1.3 VMs)
//
Class[] paramTypes = {Thread.class};
Method addShutdownHook =
Runtime.class.getMethod( "addShutdownHook", paramTypes );

// add the hook
//
Object[] args = {this};
addShutdownHook.invoke( Runtime.getRuntime(), args );
}
catch( Exception e )
{
// it just won't be added as a shutdown hook... :(
}
}

/**
* Returns <code>true</code> if the specified <code>Process</code> was
* successfully added to the list of processes to destroy upon VM exit.
*
* @param process the process to add
* @return <code>true</code> if the specified <code>Process</code> was
* successfully added
*/
public boolean add( Process process )
{
processes.addElement( process );
return processes.contains( process );
}

/**
* Returns <code>true</code> if the specified <code>Process</code> was
* successfully removed from the list of processes to destroy upon VM exit.
*
* @param process the process to remove
* @return <code>true</code> if the specified <code>Process</code> was
* successfully removed
*/
public boolean remove( Process process )
{
return processes.removeElement( process );
}

/**
* Invoked by the VM when it is exiting.
*/
public void run()
{
synchronized( processes )
{
Enumeration e = processes.elements();
while( e.hasMoreElements() )
{
( ( Process )e.nextElement() ).destroy();
}
}
}
}

+ 394
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Property.java View File

@@ -0,0 +1,394 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Properties;
import java.util.Vector;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;

/**
* Will set a Project property. Used to be a hack in ProjectHelper Will not
* override values set by the command line or parent projects.
*
* @author costin@dnt.ro
* @author <a href="mailto:rubys@us.ibm.com">Sam Ruby</a>
* @author <a href="mailto:glennm@ca.ibm.com">Glenn McAllister</a>
*/
public class Property extends Task
{
protected Path classpath;
protected String env;
protected File file;

protected String name;
protected Reference ref;
protected String resource;

protected boolean userProperty;
protected String value;// set read-only properties

public Property()
{
super();
}

protected Property( boolean userProperty )
{
this.userProperty = userProperty;
}

public void setClasspath( Path classpath )
{
if( this.classpath == null )
{
this.classpath = classpath;
}
else
{
this.classpath.append( classpath );
}
}

public void setClasspathRef( Reference r )
{
createClasspath().setRefid( r );
}

public void setEnvironment( String env )
{
this.env = env;
}

public void setFile( File file )
{
this.file = file;
}

public void setLocation( File location )
{
setValue( location.getAbsolutePath() );
}

public void setName( String name )
{
this.name = name;
}

public void setRefid( Reference ref )
{
this.ref = ref;
}

public void setResource( String resource )
{
this.resource = resource;
}

/**
* @param userProperty The new UserProperty value
* @deprecated This was never a supported feature and has been deprecated
* without replacement
*/
public void setUserProperty( boolean userProperty )
{
log( "DEPRECATED: Ignoring request to set user property in Property task.",
Project.MSG_WARN );
}

public void setValue( String value )
{
this.value = value;
}

public String getEnvironment()
{
return env;
}

public File getFile()
{
return file;
}

public String getName()
{
return name;
}

public Reference getRefid()
{
return ref;
}

public String getResource()
{
return resource;
}

public String getValue()
{
return value;
}

public Path createClasspath()
{
if( this.classpath == null )
{
this.classpath = new Path( project );
}
return this.classpath.createPath();
}

public void execute()
throws BuildException
{
if( name != null )
{
if( value == null && ref == null )
{
throw new BuildException( "You must specify value, location or refid with the name attribute",
location );
}
}
else
{
if( file == null && resource == null && env == null )
{
throw new BuildException( "You must specify file, resource or environment when not using the name attribute",
location );
}
}

if( ( name != null ) && ( value != null ) )
{
addProperty( name, value );
}

if( file != null )
loadFile( file );

if( resource != null )
loadResource( resource );

if( env != null )
loadEnvironment( env );

if( ( name != null ) && ( ref != null ) )
{
Object obj = ref.getReferencedObject( getProject() );
if( obj != null )
{
addProperty( name, obj.toString() );
}
}
}

public String toString()
{
return value == null ? "" : value;
}

protected void addProperties( Properties props )
{
resolveAllProperties( props );
Enumeration e = props.keys();
while( e.hasMoreElements() )
{
String name = ( String )e.nextElement();
String value = ( String )props.getProperty( name );

String v = project.replaceProperties( value );
addProperty( name, v );
}
}

protected void addProperty( String n, String v )
{
if( userProperty )
{
if( project.getUserProperty( n ) == null )
{
project.setUserProperty( n, v );
}
else
{
log( "Override ignored for " + n, Project.MSG_VERBOSE );
}
}
else
{
project.setNewProperty( n, v );
}
}

protected void loadEnvironment( String prefix )
{
Properties props = new Properties();
if( !prefix.endsWith( "." ) )
prefix += ".";
log( "Loading Environment " + prefix, Project.MSG_VERBOSE );
Vector osEnv = Execute.getProcEnvironment();
for( Enumeration e = osEnv.elements(); e.hasMoreElements(); )
{
String entry = ( String )e.nextElement();
int pos = entry.indexOf( '=' );
if( pos == -1 )
{
log( "Ignoring: " + entry, Project.MSG_WARN );
}
else
{
props.put( prefix + entry.substring( 0, pos ),
entry.substring( pos + 1 ) );
}
}
addProperties( props );
}

protected void loadFile( File file )
throws BuildException
{
Properties props = new Properties();
log( "Loading " + file.getAbsolutePath(), Project.MSG_VERBOSE );
try
{
if( file.exists() )
{
FileInputStream fis = new FileInputStream( file );
try
{
props.load( fis );
}
finally
{
if( fis != null )
{
fis.close();
}
}
addProperties( props );
}
else
{
log( "Unable to find property file: " + file.getAbsolutePath(),
Project.MSG_VERBOSE );
}
}
catch( IOException ex )
{
throw new BuildException( ex );
}
}

protected void loadResource( String name )
{
Properties props = new Properties();
log( "Resource Loading " + name, Project.MSG_VERBOSE );
try
{
ClassLoader cL = null;
InputStream is = null;

if( classpath != null )
{
cL = new AntClassLoader( project, classpath );
}
else
{
cL = this.getClass().getClassLoader();
}

if( cL == null )
{
is = ClassLoader.getSystemResourceAsStream( name );
}
else
{
is = cL.getResourceAsStream( name );
}

if( is != null )
{
props.load( is );
addProperties( props );
}
else
{
log( "Unable to find resource " + name, Project.MSG_WARN );
}
}
catch( IOException ex )
{
throw new BuildException( ex );
}
}

private void resolveAllProperties( Properties props )
throws BuildException
{
for( Enumeration e = props.keys(); e.hasMoreElements(); )
{
String name = ( String )e.nextElement();
String value = props.getProperty( name );

boolean resolved = false;
while( !resolved )
{
Vector fragments = new Vector();
Vector propertyRefs = new Vector();
ProjectHelper.parsePropertyString( value, fragments, propertyRefs );

resolved = true;
if( propertyRefs.size() != 0 )
{
StringBuffer sb = new StringBuffer();
Enumeration i = fragments.elements();
Enumeration j = propertyRefs.elements();
while( i.hasMoreElements() )
{
String fragment = ( String )i.nextElement();
if( fragment == null )
{
String propertyName = ( String )j.nextElement();
if( propertyName.equals( name ) )
{
throw new BuildException( "Property " + name + " was circularly defined." );
}
fragment = getProject().getProperty( propertyName );
if( fragment == null )
{
if( props.containsKey( propertyName ) )
{
fragment = props.getProperty( propertyName );
resolved = false;
}
else
{
fragment = "${" + propertyName + "}";
}
}
}
sb.append( fragment );
}
value = sb.toString();
props.put( name, value );
}
}
}
}
}

+ 130
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/PumpStreamHandler.java View File

@@ -0,0 +1,130 @@
/*
* 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.tools.ant.taskdefs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* Copies standard output and error of subprocesses to standard output and error
* of the parent process. TODO: standard input of the subprocess is not
* implemented.
*
* @author thomas.haas@softwired-inc.com
*/
public class PumpStreamHandler implements ExecuteStreamHandler
{
private Thread errorThread;

private Thread inputThread;

private OutputStream out, err;

public PumpStreamHandler( OutputStream out, OutputStream err )
{
this.out = out;
this.err = err;
}

public PumpStreamHandler( OutputStream outAndErr )
{
this( outAndErr, outAndErr );
}

public PumpStreamHandler()
{
this( System.out, System.err );
}


public void setProcessErrorStream( InputStream is )
{
createProcessErrorPump( is, err );
}


public void setProcessInputStream( OutputStream os ) { }

public void setProcessOutputStream( InputStream is )
{
createProcessOutputPump( is, out );
}


public void start()
{
inputThread.start();
errorThread.start();
}


public void stop()
{
try
{
inputThread.join();
}
catch( InterruptedException e )
{}
try
{
errorThread.join();
}
catch( InterruptedException e )
{}
try
{
err.flush();
}
catch( IOException e )
{}
try
{
out.flush();
}
catch( IOException e )
{}
}

protected OutputStream getErr()
{
return err;
}

protected OutputStream getOut()
{
return out;
}

protected void createProcessErrorPump( InputStream is, OutputStream os )
{
errorThread = createPump( is, os );
}

protected void createProcessOutputPump( InputStream is, OutputStream os )
{
inputThread = createPump( is, os );
}


/**
* Creates a stream pumper to copy the given input stream to the given
* output stream.
*
* @param is Description of Parameter
* @param os Description of Parameter
* @return Description of the Returned Value
*/
protected Thread createPump( InputStream is, OutputStream os )
{
final Thread result = new Thread( new StreamPumper( is, os ) );
result.setDaemon( true );
return result;
}

}

+ 239
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Recorder.java View File

@@ -0,0 +1,239 @@
/*
* 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.tools.ant.taskdefs;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Hashtable;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.EnumeratedAttribute;

/**
* This task is the manager for RecorderEntry's. It is this class that holds all
* entries, modifies them every time the &lt;recorder&gt; task is called, and
* addes them to the build listener process.
*
* @author <a href="mailto:jayglanville@home.com">J D Glanville</a>
* @version 0.5
* @see RecorderEntry
*/
public class Recorder extends Task
{
/**
* The list of recorder entries.
*/
private static Hashtable recorderEntries = new Hashtable();

//////////////////////////////////////////////////////////////////////
// ATTRIBUTES

/**
* The name of the file to record to.
*/
private String filename = null;
/**
* Whether or not to append. Need Boolean to record an unset state (null).
*/
private Boolean append = null;
/**
* Whether to start or stop recording. Need Boolean to record an unset state
* (null).
*/
private Boolean start = null;
/**
* What level to log? -1 means not initialized yet.
*/
private int loglevel = -1;

/**
* Sets the action for the associated recorder entry.
*
* @param action The action for the entry to take: start or stop.
*/
public void setAction( ActionChoices action )
{
if( action.getValue().equalsIgnoreCase( "start" ) )
{
start = Boolean.TRUE;
}
else
{
start = Boolean.FALSE;
}
}

/**
* Whether or not the logger should append to a previous file.
*
* @param append The new Append value
*/
public void setAppend( boolean append )
{
this.append = new Boolean( append );
}

/**
* Sets the level to which this recorder entry should log to.
*
* @param level The new Loglevel value
* @see VerbosityLevelChoices
*/
public void setLoglevel( VerbosityLevelChoices level )
{
//I hate cascading if/elseif clauses !!!
String lev = level.getValue();
if( lev.equalsIgnoreCase( "error" ) )
{
loglevel = Project.MSG_ERR;
}
else if( lev.equalsIgnoreCase( "warn" ) )
{
loglevel = Project.MSG_WARN;
}
else if( lev.equalsIgnoreCase( "info" ) )
{
loglevel = Project.MSG_INFO;
}
else if( lev.equalsIgnoreCase( "verbose" ) )
{
loglevel = Project.MSG_VERBOSE;
}
else if( lev.equalsIgnoreCase( "debug" ) )
{
loglevel = Project.MSG_DEBUG;
}
}

//////////////////////////////////////////////////////////////////////
// CONSTRUCTORS / INITIALIZERS

//////////////////////////////////////////////////////////////////////
// ACCESSOR METHODS

/**
* Sets the name of the file to log to, and the name of the recorder entry.
*
* @param fname File name of logfile.
*/
public void setName( String fname )
{
filename = fname;
}

//////////////////////////////////////////////////////////////////////
// CORE / MAIN BODY

/**
* The main execution.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
if( filename == null )
throw new BuildException( "No filename specified" );

getProject().log( "setting a recorder for name " + filename,
Project.MSG_DEBUG );

// get the recorder entry
RecorderEntry recorder = getRecorder( filename, getProject() );
// set the values on the recorder
recorder.setMessageOutputLevel( loglevel );
recorder.setRecordState( start );
}

/**
* Gets the recorder that's associated with the passed in name. If the
* recorder doesn't exist, then a new one is created.
*
* @param name Description of Parameter
* @param proj Description of Parameter
* @return The Recorder value
* @exception BuildException Description of Exception
*/
protected RecorderEntry getRecorder( String name, Project proj )
throws BuildException
{
Object o = recorderEntries.get( name );
RecorderEntry entry;
if( o == null )
{
// create a recorder entry
try
{
entry = new RecorderEntry( name );
PrintStream out = null;
if( append == null )
{
out = new PrintStream(
new FileOutputStream( name ) );
}
else
{
out = new PrintStream(
new FileOutputStream( name, append.booleanValue() ) );
}
entry.setErrorPrintStream( out );
entry.setOutputPrintStream( out );
}
catch( IOException ioe )
{
throw new BuildException( "Problems creating a recorder entry",
ioe );
}
proj.addBuildListener( entry );
recorderEntries.put( name, entry );
}
else
{
entry = ( RecorderEntry )o;
}
return entry;
}

//////////////////////////////////////////////////////////////////////
// INNER CLASSES

/**
* A list of possible values for the <code>setAction()</code> method.
* Possible values include: start and stop.
*
* @author RT
*/
public static class ActionChoices extends EnumeratedAttribute
{
private final static String[] values = {"start", "stop"};

public String[] getValues()
{
return values;
}
}

/**
* A list of possible values for the <code>setLoglevel()</code> method.
* Possible values include: error, warn, info, verbose, debug.
*
* @author RT
*/
public static class VerbosityLevelChoices extends EnumeratedAttribute
{
private final static String[] values = {"error", "warn", "info",
"verbose", "debug"};

public String[] getValues()
{
return values;
}
}

}

+ 208
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/RecorderEntry.java View File

@@ -0,0 +1,208 @@
/*
* 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.tools.ant.taskdefs;
import java.io.PrintStream;
import org.apache.tools.ant.BuildEvent;
import org.apache.tools.ant.BuildLogger;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.util.StringUtils;


/**
* This is a class that represents a recorder. This is the listener to the build
* process.
*
* @author <a href="mailto:jayglanville@home.com">J D Glanville</a>
* @version 0.5
*/
public class RecorderEntry implements BuildLogger
{

//////////////////////////////////////////////////////////////////////
// ATTRIBUTES

/**
* The name of the file associated with this recorder entry.
*/
private String filename = null;
/**
* The state of the recorder (recorder on or off).
*/
private boolean record = true;
/**
* The current verbosity level to record at.
*/
private int loglevel = Project.MSG_INFO;
/**
* The output PrintStream to record to.
*/
private PrintStream out = null;
/**
* The start time of the last know target.
*/
private long targetStartTime = 0l;

//////////////////////////////////////////////////////////////////////
// CONSTRUCTORS / INITIALIZERS

/**
* @param name The name of this recorder (used as the filename).
*/
protected RecorderEntry( String name )
{
filename = name;
}

private static String formatTime( long millis )
{
long seconds = millis / 1000;
long minutes = seconds / 60;

if( minutes > 0 )
{
return Long.toString( minutes ) + " minute"
+ ( minutes == 1 ? " " : "s " )
+ Long.toString( seconds % 60 ) + " second"
+ ( seconds % 60 == 1 ? "" : "s" );
}
else
{
return Long.toString( seconds ) + " second"
+ ( seconds % 60 == 1 ? "" : "s" );
}

}

public void setEmacsMode( boolean emacsMode )
{
throw new java.lang.RuntimeException( "Method setEmacsMode() not yet implemented." );
}

public void setErrorPrintStream( PrintStream err )
{
out = err;
}

public void setMessageOutputLevel( int level )
{
if( level >= Project.MSG_ERR && level <= Project.MSG_DEBUG )
loglevel = level;
}

public void setOutputPrintStream( PrintStream output )
{
out = output;
}

/**
* Turns off or on this recorder.
*
* @param state true for on, false for off, null for no change.
*/
public void setRecordState( Boolean state )
{
if( state != null )
record = state.booleanValue();
}

//////////////////////////////////////////////////////////////////////
// ACCESSOR METHODS

/**
* @return the name of the file the output is sent to.
*/
public String getFilename()
{
return filename;
}

public void buildFinished( BuildEvent event )
{
log( "< BUILD FINISHED", Project.MSG_DEBUG );

Throwable error = event.getException();
if( error == null )
{
out.println( StringUtils.LINE_SEP + "BUILD SUCCESSFUL" );
}
else
{
out.println( StringUtils.LINE_SEP + "BUILD FAILED" + StringUtils.LINE_SEP );
error.printStackTrace( out );
}
out.flush();
out.close();
}

public void buildStarted( BuildEvent event )
{
log( "> BUILD STARTED", Project.MSG_DEBUG );
}

public void messageLogged( BuildEvent event )
{
log( "--- MESSAGE LOGGED", Project.MSG_DEBUG );

StringBuffer buf = new StringBuffer();
if( event.getTask() != null )
{
String name = "[" + event.getTask().getTaskName() + "]";
/**
* @todo replace 12 with DefaultLogger.LEFT_COLUMN_SIZE
*/
for( int i = 0; i < ( 12 - name.length() ); i++ )
{
buf.append( " " );
}// for
buf.append( name );
}// if
buf.append( event.getMessage() );

log( buf.toString(), event.getPriority() );
}

public void targetFinished( BuildEvent event )
{
log( "<< TARGET FINISHED -- " + event.getTarget(), Project.MSG_DEBUG );
String time = formatTime( System.currentTimeMillis() - targetStartTime );
log( event.getTarget() + ": duration " + time, Project.MSG_VERBOSE );
out.flush();
}

public void targetStarted( BuildEvent event )
{
log( ">> TARGET STARTED -- " + event.getTarget(), Project.MSG_DEBUG );
log( StringUtils.LINE_SEP + event.getTarget().getName() + ":", Project.MSG_INFO );
targetStartTime = System.currentTimeMillis();
}

public void taskFinished( BuildEvent event )
{
log( "<<< TASK FINISHED -- " + event.getTask(), Project.MSG_DEBUG );
out.flush();
}

public void taskStarted( BuildEvent event )
{
log( ">>> TASK STARTED -- " + event.getTask(), Project.MSG_DEBUG );
}

/**
* The thing that actually sends the information to the output.
*
* @param mesg The message to log.
* @param level The verbosity level of the message.
*/
private void log( String mesg, int level )
{
if( record && ( level <= loglevel ) )
{
out.println( mesg );
}
}
}

+ 92
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Rename.java View File

@@ -0,0 +1,92 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;

/**
* Renames a file.
*
* @author haas@softwired.ch
* @deprecated The rename task is deprecated. Use move instead.
*/
public class Rename extends Task
{
private boolean replace = true;
private File dest;

private File src;

/**
* Sets the new name of the file.
*
* @param dest the new name of the file.
*/
public void setDest( File dest )
{
this.dest = dest;
}

/**
* Sets wheter an existing file should be replaced.
*
* @param replace <code>on</code>, if an existing file should be replaced.
*/
public void setReplace( String replace )
{
this.replace = project.toBoolean( replace );
}


/**
* Sets the file to be renamed.
*
* @param src the file to rename
*/
public void setSrc( File src )
{
this.src = src;
}


/**
* Renames the file <code>src</code> to <code>dest</code>
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
log( "DEPRECATED - The rename task is deprecated. Use move instead." );

if( dest == null )
{
throw new BuildException( "dest attribute is required", location );
}

if( src == null )
{
throw new BuildException( "src attribute is required", location );
}

if( replace && dest.exists() )
{
if( !dest.delete() )
{
throw new BuildException( "Unable to remove existing file " +
dest );
}
}
if( !src.renameTo( dest ) )
{
throw new BuildException( "Unable to rename " + src + " to " +
dest );
}
}
}

+ 591
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Replace.java View File

@@ -0,0 +1,591 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.Reader;
import java.io.Writer;
import java.util.Properties;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.util.FileUtils;

/**
* Replaces all occurrences of one or more string tokens with given values in
* the indicated files. Each value can be either a string or the value of a
* property available in a designated property file.
*
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">
* stefano@apache.org</a>
* @author <a href="mailto:erik@desknetinc.com">Erik Langenbach</a>
*/
public class Replace extends MatchingTask
{

private File src = null;
private NestedString token = null;
private NestedString value = new NestedString();

private File propertyFile = null;
private Properties properties = null;
private Vector replacefilters = new Vector();

private File dir = null;
private boolean summary = false;

/**
* The encoding used to read and write files - if null, uses default
*/
private String encoding = null;

private FileUtils fileUtils = FileUtils.newFileUtils();

private int fileCount;
private int replaceCount;


/**
* Set the source files path when using matching tasks.
*
* @param dir The new Dir value
*/
public void setDir( File dir )
{
this.dir = dir;
}

/**
* Set the file encoding to use on the files read and written by replace
*
* @param encoding the encoding to use on the files
*/
public void setEncoding( String encoding )
{
this.encoding = encoding;
}


/**
* Set the source file.
*
* @param file The new File value
*/
public void setFile( File file )
{
this.src = file;
}

/**
* Sets a file to be searched for property values.
*
* @param filename The new PropertyFile value
*/
public void setPropertyFile( File filename )
{
propertyFile = filename;
}

/**
* Request a summary
*
* @param summary true if you would like a summary logged of the replace
* operation
*/
public void setSummary( boolean summary )
{
this.summary = summary;
}

/**
* Set the string token to replace.
*
* @param token The new Token value
*/
public void setToken( String token )
{
createReplaceToken().addText( token );
}

/**
* Set the string value to use as token replacement.
*
* @param value The new Value value
*/
public void setValue( String value )
{
createReplaceValue().addText( value );
}

public Properties getProperties( File propertyFile )
throws BuildException
{
Properties properties = new Properties();

try
{
properties.load( new FileInputStream( propertyFile ) );
}
catch( FileNotFoundException e )
{
String message = "Property file (" + propertyFile.getPath() + ") not found.";
throw new BuildException( message );
}
catch( IOException e )
{
String message = "Property file (" + propertyFile.getPath() + ") cannot be loaded.";
throw new BuildException( message );
}

return properties;
}

/**
* Nested &lt;replacetoken&gt; element.
*
* @return Description of the Returned Value
*/
public NestedString createReplaceToken()
{
if( token == null )
{
token = new NestedString();
}
return token;
}

/**
* Nested &lt;replacevalue&gt; element.
*
* @return Description of the Returned Value
*/
public NestedString createReplaceValue()
{
return value;
}

/**
* Add nested &lt;replacefilter&gt; element.
*
* @return Description of the Returned Value
*/
public Replacefilter createReplacefilter()
{
Replacefilter filter = new Replacefilter();
replacefilters.addElement( filter );
return filter;
}

/**
* Do the execution.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
validateAttributes();

if( propertyFile != null )
{
properties = getProperties( propertyFile );
}

validateReplacefilters();
fileCount = 0;
replaceCount = 0;

if( src != null )
{
processFile( src );
}

if( dir != null )
{
DirectoryScanner ds = super.getDirectoryScanner( dir );
String[] srcs = ds.getIncludedFiles();

for( int i = 0; i < srcs.length; i++ )
{
File file = new File( dir, srcs[i] );
processFile( file );
}
}

if( summary )
{
log( "Replaced " + replaceCount + " occurrences in " + fileCount + " files.", Project.MSG_INFO );
}
}

/**
* Validate attributes provided for this task in .xml build file.
*
* @exception BuildException if any supplied attribute is invalid or any
* mandatory attribute is missing
*/
public void validateAttributes()
throws BuildException
{
if( src == null && dir == null )
{
String message = "Either the file or the dir attribute " + "must be specified";
throw new BuildException( message, location );
}
if( propertyFile != null && !propertyFile.exists() )
{
String message = "Property file " + propertyFile.getPath() + " does not exist.";
throw new BuildException( message, location );
}
if( token == null && replacefilters.size() == 0 )
{
String message = "Either token or a nested replacefilter "
+ "must be specified";
throw new BuildException( message, location );
}
if( token != null && "".equals( token.getText() ) )
{
String message = "The token attribute must not be an empty string.";
throw new BuildException( message, location );
}
}

/**
* Validate nested elements.
*
* @exception BuildException if any supplied attribute is invalid or any
* mandatory attribute is missing
*/
public void validateReplacefilters()
throws BuildException
{
for( int i = 0; i < replacefilters.size(); i++ )
{
Replacefilter element = ( Replacefilter )replacefilters.elementAt( i );
element.validate();
}
}

/**
* Perform the replacement on the given file. The replacement is performed
* on a temporary file which then replaces the original file.
*
* @param src the source file
* @exception BuildException Description of Exception
*/
private void processFile( File src )
throws BuildException
{
if( !src.exists() )
{
throw new BuildException( "Replace: source file " + src.getPath() + " doesn't exist", location );
}

File temp = fileUtils.createTempFile( "rep", ".tmp",
fileUtils.getParentFile( src ) );

Reader reader = null;
Writer writer = null;
try
{
reader = encoding == null ? new FileReader( src )
: new InputStreamReader( new FileInputStream( src ), encoding );
writer = encoding == null ? new FileWriter( temp )
: new OutputStreamWriter( new FileOutputStream( temp ), encoding );

BufferedReader br = new BufferedReader( reader );
BufferedWriter bw = new BufferedWriter( writer );

// read the entire file into a StringBuffer
// size of work buffer may be bigger than needed
// when multibyte characters exist in the source file
// but then again, it might be smaller than needed on
// platforms like Windows where length can't be trusted
int fileLengthInBytes = ( int )( src.length() );
StringBuffer tmpBuf = new StringBuffer( fileLengthInBytes );
int readChar = 0;
int totread = 0;
while( true )
{
readChar = br.read();
if( readChar < 0 )
{
break;
}
tmpBuf.append( ( char )readChar );
totread++;
}

// create a String so we can use indexOf
String buf = tmpBuf.toString();

//Preserve original string (buf) so we can compare the result
String newString = new String( buf );

if( token != null )
{
// line separators in values and tokens are "\n"
// in order to compare with the file contents, replace them
// as needed
String linesep = System.getProperty( "line.separator" );
String val = stringReplace( value.getText(), "\n", linesep );
String tok = stringReplace( token.getText(), "\n", linesep );

// for each found token, replace with value
log( "Replacing in " + src.getPath() + ": " + token.getText() + " --> " + value.getText(), Project.MSG_VERBOSE );
newString = stringReplace( newString, tok, val );
}

if( replacefilters.size() > 0 )
{
newString = processReplacefilters( newString, src.getPath() );
}

boolean changes = !newString.equals( buf );
if( changes )
{
bw.write( newString, 0, newString.length() );
bw.flush();
}

// cleanup
bw.close();
writer = null;
br.close();
reader = null;

// If there were changes, move the new one to the old one;
// otherwise, delete the new one
if( changes )
{
++fileCount;
src.delete();
temp.renameTo( src );
temp = null;
}
}
catch( IOException ioe )
{
throw new BuildException( "IOException in " + src + " - " +
ioe.getClass().getName() + ":" + ioe.getMessage(), ioe, location );
}
finally
{
if( reader != null )
{
try
{
reader.close();
}
catch( IOException e )
{}
}
if( writer != null )
{
try
{
writer.close();
}
catch( IOException e )
{}
}
if( temp != null )
{
temp.delete();
}
}

}

private String processReplacefilters( String buffer, String filename )
{
String newString = new String( buffer );

for( int i = 0; i < replacefilters.size(); i++ )
{
Replacefilter filter = ( Replacefilter )replacefilters.elementAt( i );

//for each found token, replace with value
log( "Replacing in " + filename + ": " + filter.getToken() + " --> " + filter.getReplaceValue(), Project.MSG_VERBOSE );
newString = stringReplace( newString, filter.getToken(), filter.getReplaceValue() );
}

return newString;
}

/**
* Replace occurrences of str1 in string str with str2
*
* @param str Description of Parameter
* @param str1 Description of Parameter
* @param str2 Description of Parameter
* @return Description of the Returned Value
*/
private String stringReplace( String str, String str1, String str2 )
{
StringBuffer ret = new StringBuffer();
int start = 0;
int found = str.indexOf( str1 );
while( found >= 0 )
{
// write everything up to the found str1
if( found > start )
{
ret.append( str.substring( start, found ) );
}

// write the replacement str2
if( str2 != null )
{
ret.append( str2 );
}

// search again
start = found + str1.length();
found = str.indexOf( str1, start );
++replaceCount;
}

// write the remaining characters
if( str.length() > start )
{
ret.append( str.substring( start, str.length() ) );
}

return ret.toString();
}

//Inner class
public class NestedString
{

private StringBuffer buf = new StringBuffer();

public String getText()
{
return buf.toString();
}

public void addText( String val )
{
buf.append( val );
}
}

//Inner class
public class Replacefilter
{
private String property;
private String token;
private String value;

public void setProperty( String property )
{
this.property = property;
}

public void setToken( String token )
{
this.token = token;
}

public void setValue( String value )
{
this.value = value;
}

public String getProperty()
{
return property;
}

public String getReplaceValue()
{
if( property != null )
{
return ( String )properties.getProperty( property );
}
else if( value != null )
{
return value;
}
else if( Replace.this.value != null )
{
return Replace.this.value.getText();
}
else
{
//Default is empty string
return new String( "" );
}
}

public String getToken()
{
return token;
}

public String getValue()
{
return value;
}

public void validate()
throws BuildException
{
//Validate mandatory attributes
if( token == null )
{
String message = "token is a mandatory attribute " + "of replacefilter.";
throw new BuildException( message );
}

if( "".equals( token ) )
{
String message = "The token attribute must not be an empty string.";
throw new BuildException( message );
}

//value and property are mutually exclusive attributes
if( ( value != null ) && ( property != null ) )
{
String message = "Either value or property " + "can be specified, but a replacefilter " + "element cannot have both.";
throw new BuildException( message );
}

if( ( property != null ) )
{
//the property attribute must have access to a property file
if( propertyFile == null )
{
String message = "The replacefilter's property attribute " + "can only be used with the replacetask's " + "propertyFile attribute.";
throw new BuildException( message );
}

//Make sure property exists in property file
if( properties == null ||
properties.getProperty( property ) == null )
{
String message = "property \"" + property + "\" was not found in " + propertyFile.getPath();
throw new BuildException( message );
}
}
}
}

}

+ 688
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Rmic.java View File

@@ -0,0 +1,688 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import java.rmi.Remote;
import java.util.Vector;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.rmic.RmicAdapter;
import org.apache.tools.ant.taskdefs.rmic.RmicAdapterFactory;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.FileNameMapper;
import org.apache.tools.ant.util.SourceFileScanner;

/**
* Task to compile RMI stubs and skeletons. This task can take the following
* arguments:
* <ul>
* <li> base: The base directory for the compiled stubs and skeletons
* <li> class: The name of the class to generate the stubs from
* <li> stubVersion: The version of the stub prototol to use (1.1, 1.2,
* compat)
* <li> sourceBase: The base directory for the generated stubs and skeletons
*
* <li> classpath: Additional classpath, appended before the system classpath
*
* <li> iiop: Generate IIOP compatable output
* <li> iiopopts: Include IIOP options
* <li> idl: Generate IDL output
* <li> idlopts: Include IDL options
* <li> includeantruntime
* <li> includejavaruntime
* <li> extdirs
* </ul>
* Of these arguments, <b>base</b> is required. <p>
*
* If classname is specified then only that classname will be compiled. If it is
* absent, then <b>base</b> is traversed for classes according to patterns. <p>
*
*
*
* @author duncan@x180.com
* @author ludovic.claude@websitewatchers.co.uk
* @author David Maclean <a href="mailto:david@cm.co.za">david@cm.co.za</a>
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @author Takashi Okamoto tokamoto@rd.nttdata.co.jp
*/

public class Rmic extends MatchingTask
{

private final static String FAIL_MSG
= "Rmic failed, messages should have been provided.";
private boolean verify = false;
private boolean filtering = false;

private boolean iiop = false;
private boolean idl = false;
private boolean debug = false;
private boolean includeAntRuntime = true;
private boolean includeJavaRuntime = false;

private Vector compileList = new Vector();

private ClassLoader loader = null;

private File baseDir;
private String classname;
private Path compileClasspath;
private Path extdirs;
private String idlopts;
private String iiopopts;
private File sourceBase;
private String stubVersion;

/**
* Sets the base directory to output generated class.
*
* @param base The new Base value
*/
public void setBase( File base )
{
this.baseDir = base;
}

/**
* Sets the class name to compile.
*
* @param classname The new Classname value
*/
public void setClassname( String classname )
{
this.classname = classname;
}

/**
* Set the classpath to be used for this compilation.
*
* @param classpath The new Classpath value
*/
public void setClasspath( Path classpath )
{
if( compileClasspath == null )
{
compileClasspath = classpath;
}
else
{
compileClasspath.append( classpath );
}
}

/**
* Adds a reference to a CLASSPATH defined elsewhere.
*
* @param r The new ClasspathRef value
*/
public void setClasspathRef( Reference r )
{
createClasspath().setRefid( r );
}

/**
* Sets the debug flag.
*
* @param debug The new Debug value
*/
public void setDebug( boolean debug )
{
this.debug = debug;
}

/**
* Sets the extension directories that will be used during the compilation.
*
* @param extdirs The new Extdirs value
*/
public void setExtdirs( Path extdirs )
{
if( this.extdirs == null )
{
this.extdirs = extdirs;
}
else
{
this.extdirs.append( extdirs );
}
}

public void setFiltering( boolean filter )
{
filtering = filter;
}

/**
* Indicates that IDL output should be generated. This defaults to false if
* not set.
*
* @param idl The new Idl value
*/
public void setIdl( boolean idl )
{
this.idl = idl;
}

/**
* pass additional arguments for idl compile
*
* @param idlopts The new Idlopts value
*/
public void setIdlopts( String idlopts )
{
this.idlopts = idlopts;
}

/**
* Indicates that IIOP compatible stubs should be generated. This defaults
* to false if not set.
*
* @param iiop The new Iiop value
*/
public void setIiop( boolean iiop )
{
this.iiop = iiop;
}

/**
* pass additional arguments for iiop
*
* @param iiopopts The new Iiopopts value
*/
public void setIiopopts( String iiopopts )
{
this.iiopopts = iiopopts;
}

/**
* Include ant's own classpath in this task's classpath?
*
* @param include The new Includeantruntime value
*/
public void setIncludeantruntime( boolean include )
{
includeAntRuntime = include;
}

/**
* Sets whether or not to include the java runtime libraries to this task's
* classpath.
*
* @param include The new Includejavaruntime value
*/
public void setIncludejavaruntime( boolean include )
{
includeJavaRuntime = include;
}

/**
* Sets the source dirs to find the source java files.
*
* @param sourceBase The new SourceBase value
*/
public void setSourceBase( File sourceBase )
{
this.sourceBase = sourceBase;
}

/**
* Sets the stub version.
*
* @param stubVersion The new StubVersion value
*/
public void setStubVersion( String stubVersion )
{
this.stubVersion = stubVersion;
}

/**
* Indicates that the classes found by the directory match should be checked
* to see if they implement java.rmi.Remote. This defaults to false if not
* set.
*
* @param verify The new Verify value
*/
public void setVerify( boolean verify )
{
this.verify = verify;
}

/**
* Gets the base directory to output generated class.
*
* @return The Base value
*/
public File getBase()
{
return this.baseDir;
}

/**
* Gets the class name to compile.
*
* @return The Classname value
*/
public String getClassname()
{
return classname;
}

/**
* Gets the classpath.
*
* @return The Classpath value
*/
public Path getClasspath()
{
return compileClasspath;
}

public Vector getCompileList()
{
return compileList;
}

/**
* Gets the debug flag.
*
* @return The Debug value
*/
public boolean getDebug()
{
return debug;
}

/**
* Gets the extension directories that will be used during the compilation.
*
* @return The Extdirs value
*/
public Path getExtdirs()
{
return extdirs;
}

/**
* Gets file list to compile.
*
* @return The FileList value
*/
public Vector getFileList()
{
return compileList;
}

public boolean getFiltering()
{
return filtering;
}

/*
* Gets IDL flags.
*/
public boolean getIdl()
{
return idl;
}

/**
* Gets additional arguments for idl compile.
*
* @return The Idlopts value
*/
public String getIdlopts()
{
return idlopts;
}

/**
* Gets iiop flags.
*
* @return The Iiop value
*/
public boolean getIiop()
{
return iiop;
}

/**
* Gets additional arguments for iiop.
*
* @return The Iiopopts value
*/
public String getIiopopts()
{
return iiopopts;
}

/**
* Gets whether or not the ant classpath is to be included in the task's
* classpath.
*
* @return The Includeantruntime value
*/
public boolean getIncludeantruntime()
{
return includeAntRuntime;
}

/**
* Gets whether or not the java runtime should be included in this task's
* classpath.
*
* @return The Includejavaruntime value
*/
public boolean getIncludejavaruntime()
{
return includeJavaRuntime;
}

/**
* Classloader for the user-specified classpath.
*
* @return The Loader value
*/
public ClassLoader getLoader()
{
return loader;
}

/**
* Returns the topmost interface that extends Remote for a given class - if
* one exists.
*
* @param testClass Description of Parameter
* @return The RemoteInterface value
*/
public Class getRemoteInterface( Class testClass )
{
if( Remote.class.isAssignableFrom( testClass ) )
{
Class[] interfaces = testClass.getInterfaces();
if( interfaces != null )
{
for( int i = 0; i < interfaces.length; i++ )
{
if( Remote.class.isAssignableFrom( interfaces[i] ) )
{
return interfaces[i];
}
}
}
}
return null;
}

/**
* Gets the source dirs to find the source java files.
*
* @return The SourceBase value
*/
public File getSourceBase()
{
return sourceBase;
}

public String getStubVersion()
{
return stubVersion;
}

/**
* Get verify flag.
*
* @return The Verify value
*/
public boolean getVerify()
{
return verify;
}

/**
* Load named class and test whether it can be rmic'ed
*
* @param classname Description of Parameter
* @return The ValidRmiRemote value
*/
public boolean isValidRmiRemote( String classname )
{
try
{
Class testClass = loader.loadClass( classname );
// One cannot RMIC an interface for "classic" RMI (JRMP)
if( testClass.isInterface() && !iiop && !idl )
{
return false;
}
return isValidRmiRemote( testClass );
}
catch( ClassNotFoundException e )
{
log( "Unable to verify class " + classname +
". It could not be found.", Project.MSG_WARN );
}
catch( NoClassDefFoundError e )
{
log( "Unable to verify class " + classname +
". It is not defined.", Project.MSG_WARN );
}
catch( Throwable t )
{
log( "Unable to verify class " + classname +
". Loading caused Exception: " +
t.getMessage(), Project.MSG_WARN );
}
// we only get here if an exception has been thrown
return false;
}

/**
* Creates a nested classpath element.
*
* @return Description of the Returned Value
*/
public Path createClasspath()
{
if( compileClasspath == null )
{
compileClasspath = new Path( project );
}
return compileClasspath.createPath();
}

/**
* Maybe creates a nested extdirs element.
*
* @return Description of the Returned Value
*/
public Path createExtdirs()
{
if( extdirs == null )
{
extdirs = new Path( project );
}
return extdirs.createPath();
}

public void execute()
throws BuildException
{
if( baseDir == null )
{
throw new BuildException( "base attribute must be set!", location );
}
if( !baseDir.exists() )
{
throw new BuildException( "base does not exist!", location );
}

if( verify )
{
log( "Verify has been turned on.", Project.MSG_INFO );
}

String compiler = project.getProperty( "build.rmic" );
RmicAdapter adapter = RmicAdapterFactory.getRmic( compiler, this );

// now we need to populate the compiler adapter
adapter.setRmic( this );

Path classpath = adapter.getClasspath();
loader = new AntClassLoader( project, classpath );

// scan base dirs to build up compile lists only if a
// specific classname is not given
if( classname == null )
{
DirectoryScanner ds = this.getDirectoryScanner( baseDir );
String[] files = ds.getIncludedFiles();
scanDir( baseDir, files, adapter.getMapper() );
}
else
{
// otherwise perform a timestamp comparison - at least
scanDir( baseDir,
new String[]{classname.replace( '.', File.separatorChar ) + ".class"},
adapter.getMapper() );
}

int fileCount = compileList.size();
if( fileCount > 0 )
{
log( "RMI Compiling " + fileCount +
" class" + ( fileCount > 1 ? "es" : "" ) + " to " + baseDir,
Project.MSG_INFO );

// finally, lets execute the compiler!!
if( !adapter.execute() )
{
throw new BuildException( FAIL_MSG, location );
}
}

/*
* Move the generated source file to the base directory. If
* base directory and sourcebase are the same, the generated
* sources are already in place.
*/
if( null != sourceBase && !baseDir.equals( sourceBase ) )
{
if( idl )
{
log( "Cannot determine sourcefiles in idl mode, ",
Project.MSG_WARN );
log( "sourcebase attribute will be ignored.", Project.MSG_WARN );
}
else
{
for( int j = 0; j < fileCount; j++ )
{
moveGeneratedFile( baseDir, sourceBase,
( String )compileList.elementAt( j ),
adapter );
}
}
}
compileList.removeAllElements();
}

/**
* Scans the directory looking for class files to be compiled. The result is
* returned in the class variable compileList.
*
* @param baseDir Description of Parameter
* @param files Description of Parameter
* @param mapper Description of Parameter
*/
protected void scanDir( File baseDir, String files[],
FileNameMapper mapper )
{

String[] newFiles = files;
if( idl )
{
log( "will leave uptodate test to rmic implementation in idl mode.",
Project.MSG_VERBOSE );
}
else if( iiop
&& iiopopts != null && iiopopts.indexOf( "-always" ) > -1 )
{
log( "no uptodate test as -always option has been specified",
Project.MSG_VERBOSE );
}
else
{
SourceFileScanner sfs = new SourceFileScanner( this );
newFiles = sfs.restrict( files, baseDir, baseDir, mapper );
}

for( int i = 0; i < newFiles.length; i++ )
{
String classname = newFiles[i].replace( File.separatorChar, '.' );
classname = classname.substring( 0, classname.lastIndexOf( ".class" ) );
compileList.addElement( classname );
}
}

/**
* Check to see if the class or (super)interfaces implement java.rmi.Remote.
*
* @param testClass Description of Parameter
* @return The ValidRmiRemote value
*/
private boolean isValidRmiRemote( Class testClass )
{
return getRemoteInterface( testClass ) != null;
}

/**
* Move the generated source file(s) to the base directory
*
* @param baseDir Description of Parameter
* @param sourceBaseFile Description of Parameter
* @param classname Description of Parameter
* @param adapter Description of Parameter
* @exception BuildException Description of Exception
*/
private void moveGeneratedFile( File baseDir, File sourceBaseFile,
String classname,
RmicAdapter adapter )
throws BuildException
{

String classFileName =
classname.replace( '.', File.separatorChar ) + ".class";
String[] generatedFiles =
adapter.getMapper().mapFileName( classFileName );

for( int i = 0; i < generatedFiles.length; i++ )
{
String sourceFileName =
classFileName.substring( 0, classFileName.length() - 6 ) + ".java";
File oldFile = new File( baseDir, sourceFileName );
File newFile = new File( sourceBaseFile, sourceFileName );
try
{
project.copyFile( oldFile, newFile, filtering );
oldFile.delete();
}
catch( IOException ioe )
{
String msg = "Failed to copy " + oldFile + " to " +
newFile + " due to " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}
}
}

}


+ 867
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/SQLExec.java View File

@@ -0,0 +1,867 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.Reader;
import java.io.StringReader;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.SQLWarning;
import java.sql.Statement;
import java.util.Enumeration;
import java.util.Properties;
import java.util.StringTokenizer;
import java.util.Vector;
import org.apache.tools.ant.AntClassLoader;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.ProjectHelper;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;

/**
* Reads in a text file containing SQL statements seperated with semicolons and
* executes it in a given db. Comments may be created with REM -- or //.
*
* @author <a href="mailto:jeff@custommonkey.org">Jeff Martin</a>
* @author <A href="mailto:gholam@xtra.co.nz">Michael McCallum</A>
* @author <A href="mailto:tim.stephenson@sybase.com">Tim Stephenson</A>
*/
public class SQLExec extends Task
{

private int goodSql = 0, totalSql = 0;

private Vector filesets = new Vector();

/**
* Database connection
*/
private Connection conn = null;

/**
* Autocommit flag. Default value is false
*/
private boolean autocommit = false;

/**
* SQL statement
*/
private Statement statement = null;

/**
* DB driver.
*/
private String driver = null;

/**
* DB url.
*/
private String url = null;

/**
* User name.
*/
private String userId = null;

/**
* Password
*/
private String password = null;

/**
* SQL input file
*/
private File srcFile = null;

/**
* SQL input command
*/
private String sqlCommand = "";

/**
* SQL transactions to perform
*/
private Vector transactions = new Vector();

/**
* SQL Statement delimiter
*/
private String delimiter = ";";

/**
* The delimiter type indicating whether the delimiter will only be
* recognized on a line by itself
*/
private String delimiterType = DelimiterType.NORMAL;

/**
* Print SQL results.
*/
private boolean print = false;

/**
* Print header columns.
*/
private boolean showheaders = true;

/**
* Results Output file.
*/
private File output = null;

/**
* RDBMS Product needed for this SQL.
*/
private String rdbms = null;

/**
* RDBMS Version needed for this SQL.
*/
private String version = null;

/**
* Action to perform if an error is found
*/
private String onError = "abort";

/**
* Encoding to use when reading SQL statements from a file
*/
private String encoding = null;

private Path classpath;

private AntClassLoader loader;

/**
* Set the autocommit flag for the DB connection.
*
* @param autocommit The new Autocommit value
*/
public void setAutocommit( boolean autocommit )
{
this.autocommit = autocommit;
}

/**
* Set the classpath for loading the driver.
*
* @param classpath The new Classpath value
*/
public void setClasspath( Path classpath )
{
if( this.classpath == null )
{
this.classpath = classpath;
}
else
{
this.classpath.append( classpath );
}
}

/**
* Set the classpath for loading the driver using the classpath reference.
*
* @param r The new ClasspathRef value
*/
public void setClasspathRef( Reference r )
{
createClasspath().setRefid( r );
}

/**
* Set the statement delimiter. <p>
*
* For example, set this to "go" and delimitertype to "ROW" for Sybase ASE
* or MS SQL Server.</p>
*
* @param delimiter The new Delimiter value
*/
public void setDelimiter( String delimiter )
{
this.delimiter = delimiter;
}

/**
* Set the Delimiter type for this sql task. The delimiter type takes two
* values - normal and row. Normal means that any occurence of the delimiter
* terminate the SQL command whereas with row, only a line containing just
* the delimiter is recognized as the end of the command.
*
* @param delimiterType The new DelimiterType value
*/
public void setDelimiterType( DelimiterType delimiterType )
{
this.delimiterType = delimiterType.getValue();
}

/**
* Set the JDBC driver to be used.
*
* @param driver The new Driver value
*/
public void setDriver( String driver )
{
this.driver = driver;
}

/**
* Set the file encoding to use on the sql files read in
*
* @param encoding the encoding to use on the files
*/
public void setEncoding( String encoding )
{
this.encoding = encoding;
}

/**
* Set the action to perform onerror
*
* @param action The new Onerror value
*/
public void setOnerror( OnError action )
{
this.onError = action.getValue();
}

/**
* Set the output file.
*
* @param output The new Output value
*/
public void setOutput( File output )
{
this.output = output;
}


/**
* Set the password for the DB connection.
*
* @param password The new Password value
*/
public void setPassword( String password )
{
this.password = password;
}

/**
* Set the print flag.
*
* @param print The new Print value
*/
public void setPrint( boolean print )
{
this.print = print;
}

/**
* Set the rdbms required
*
* @param vendor The new Rdbms value
*/
public void setRdbms( String vendor )
{
this.rdbms = vendor.toLowerCase();
}

/**
* Set the showheaders flag.
*
* @param showheaders The new Showheaders value
*/
public void setShowheaders( boolean showheaders )
{
this.showheaders = showheaders;
}

/**
* Set the name of the sql file to be run.
*
* @param srcFile The new Src value
*/
public void setSrc( File srcFile )
{
this.srcFile = srcFile;
}

/**
* Set the DB connection url.
*
* @param url The new Url value
*/
public void setUrl( String url )
{
this.url = url;
}

/**
* Set the user name for the DB connection.
*
* @param userId The new Userid value
*/
public void setUserid( String userId )
{
this.userId = userId;
}

/**
* Set the version required
*
* @param version The new Version value
*/
public void setVersion( String version )
{
this.version = version.toLowerCase();
}

/**
* Adds a set of files (nested fileset attribute).
*
* @param set The feature to be added to the Fileset attribute
*/
public void addFileset( FileSet set )
{
filesets.addElement( set );
}

/**
* Set the sql command to execute
*
* @param sql The feature to be added to the Text attribute
*/
public void addText( String sql )
{
this.sqlCommand += sql;
}

/**
* Create the classpath for loading the driver.
*
* @return Description of the Returned Value
*/
public Path createClasspath()
{
if( this.classpath == null )
{
this.classpath = new Path( project );
}
return this.classpath.createPath();
}


/**
* Set the sql command to execute
*
* @return Description of the Returned Value
*/
public Transaction createTransaction()
{
Transaction t = new Transaction();
transactions.addElement( t );
return t;
}

/**
* Load the sql file and then execute it
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
sqlCommand = sqlCommand.trim();

if( srcFile == null && sqlCommand.length() == 0 && filesets.isEmpty() )
{
if( transactions.size() == 0 )
{
throw new BuildException( "Source file or fileset, transactions or sql statement must be set!", location );
}
}
else
{
// deal with the filesets
for( int i = 0; i < filesets.size(); i++ )
{
FileSet fs = ( FileSet )filesets.elementAt( i );
DirectoryScanner ds = fs.getDirectoryScanner( project );
File srcDir = fs.getDir( project );

String[] srcFiles = ds.getIncludedFiles();

// Make a transaction for each file
for( int j = 0; j < srcFiles.length; j++ )
{
Transaction t = createTransaction();
t.setSrc( new File( srcDir, srcFiles[j] ) );
}
}

// Make a transaction group for the outer command
Transaction t = createTransaction();
t.setSrc( srcFile );
t.addText( sqlCommand );
}

if( driver == null )
{
throw new BuildException( "Driver attribute must be set!", location );
}
if( userId == null )
{
throw new BuildException( "User Id attribute must be set!", location );
}
if( password == null )
{
throw new BuildException( "Password attribute must be set!", location );
}
if( url == null )
{
throw new BuildException( "Url attribute must be set!", location );
}
if( srcFile != null && !srcFile.exists() )
{
throw new BuildException( "Source file does not exist!", location );
}
Driver driverInstance = null;
// Load the driver using the
try
{
Class dc;
if( classpath != null )
{
log( "Loading " + driver + " using AntClassLoader with classpath " + classpath,
Project.MSG_VERBOSE );

loader = new AntClassLoader( project, classpath );
dc = loader.loadClass( driver );
}
else
{
log( "Loading " + driver + " using system loader.", Project.MSG_VERBOSE );
dc = Class.forName( driver );
}
driverInstance = ( Driver )dc.newInstance();
}
catch( ClassNotFoundException e )
{
throw new BuildException( "Class Not Found: JDBC driver " + driver + " could not be loaded", location );
}
catch( IllegalAccessException e )
{
throw new BuildException( "Illegal Access: JDBC driver " + driver + " could not be loaded", location );
}
catch( InstantiationException e )
{
throw new BuildException( "Instantiation Exception: JDBC driver " + driver + " could not be loaded", location );
}

try
{
log( "connecting to " + url, Project.MSG_VERBOSE );
Properties info = new Properties();
info.put( "user", userId );
info.put( "password", password );
conn = driverInstance.connect( url, info );

if( conn == null )
{
// Driver doesn't understand the URL
throw new SQLException( "No suitable Driver for " + url );
}

if( !isValidRdbms( conn ) )
return;

conn.setAutoCommit( autocommit );

statement = conn.createStatement();

PrintStream out = System.out;
try
{
if( output != null )
{
log( "Opening PrintStream to output file " + output, Project.MSG_VERBOSE );
out = new PrintStream( new BufferedOutputStream( new FileOutputStream( output ) ) );
}

// Process all transactions
for( Enumeration e = transactions.elements();
e.hasMoreElements(); )
{

( ( Transaction )e.nextElement() ).runTransaction( out );
if( !autocommit )
{
log( "Commiting transaction", Project.MSG_VERBOSE );
conn.commit();
}
}
}
finally
{
if( out != null && out != System.out )
{
out.close();
}
}
}
catch( IOException e )
{
if( !autocommit && conn != null && onError.equals( "abort" ) )
{
try
{
conn.rollback();
}
catch( SQLException ex )
{}
}
throw new BuildException( e );
}
catch( SQLException e )
{
if( !autocommit && conn != null && onError.equals( "abort" ) )
{
try
{
conn.rollback();
}
catch( SQLException ex )
{}
}
throw new BuildException( e );
}
finally
{
try
{
if( statement != null )
{
statement.close();
}
if( conn != null )
{
conn.close();
}
}
catch( SQLException e )
{}
}

log( goodSql + " of " + totalSql +
" SQL statements executed successfully" );
}

/**
* Verify if connected to the correct RDBMS
*
* @param conn Description of Parameter
* @return The ValidRdbms value
*/
protected boolean isValidRdbms( Connection conn )
{
if( rdbms == null && version == null )
return true;

try
{
DatabaseMetaData dmd = conn.getMetaData();

if( rdbms != null )
{
String theVendor = dmd.getDatabaseProductName().toLowerCase();

log( "RDBMS = " + theVendor, Project.MSG_VERBOSE );
if( theVendor == null || theVendor.indexOf( rdbms ) < 0 )
{
log( "Not the required RDBMS: " + rdbms, Project.MSG_VERBOSE );
return false;
}
}

if( version != null )
{
String theVersion = dmd.getDatabaseProductVersion().toLowerCase();

log( "Version = " + theVersion, Project.MSG_VERBOSE );
if( theVersion == null ||
!( theVersion.startsWith( version ) ||
theVersion.indexOf( " " + version ) >= 0 ) )
{
log( "Not the required version: \"" + version + "\"", Project.MSG_VERBOSE );
return false;
}
}
}
catch( SQLException e )
{
// Could not get the required information
log( "Failed to obtain required RDBMS information", Project.MSG_ERR );
return false;
}

return true;
}

/**
* Exec the sql statement.
*
* @param sql Description of Parameter
* @param out Description of Parameter
* @exception SQLException Description of Exception
*/
protected void execSQL( String sql, PrintStream out )
throws SQLException
{
// Check and ignore empty statements
if( "".equals( sql.trim() ) )
return;

try
{
totalSql++;
if( !statement.execute( sql ) )
{
log( statement.getUpdateCount() + " rows affected",
Project.MSG_VERBOSE );
}
else
{
if( print )
{
printResults( out );
}
}

SQLWarning warning = conn.getWarnings();
while( warning != null )
{
log( warning + " sql warning", Project.MSG_VERBOSE );
warning = warning.getNextWarning();
}
conn.clearWarnings();
goodSql++;
}
catch( SQLException e )
{
log( "Failed to execute: " + sql, Project.MSG_ERR );
if( !onError.equals( "continue" ) )
throw e;
log( e.toString(), Project.MSG_ERR );
}
}

/**
* print any results in the statement.
*
* @param out Description of Parameter
* @exception java.sql.SQLException Description of Exception
*/
protected void printResults( PrintStream out )
throws java.sql.SQLException
{
ResultSet rs = null;
do
{
rs = statement.getResultSet();
if( rs != null )
{
log( "Processing new result set.", Project.MSG_VERBOSE );
ResultSetMetaData md = rs.getMetaData();
int columnCount = md.getColumnCount();
StringBuffer line = new StringBuffer();
if( showheaders )
{
for( int col = 1; col < columnCount; col++ )
{
line.append( md.getColumnName( col ) );
line.append( "," );
}
line.append( md.getColumnName( columnCount ) );
out.println( line );
line.setLength( 0 );
}
while( rs.next() )
{
boolean first = true;
for( int col = 1; col <= columnCount; col++ )
{
String columnValue = rs.getString( col );
if( columnValue != null )
{
columnValue = columnValue.trim();
}

if( first )
{
first = false;
}
else
{
line.append( "," );
}
line.append( columnValue );
}
out.println( line );
line.setLength( 0 );
}
}
}while ( statement.getMoreResults() );
out.println();
}

protected void runStatements( Reader reader, PrintStream out )
throws SQLException, IOException
{
String sql = "";
String line = "";

BufferedReader in = new BufferedReader( reader );

try
{
while( ( line = in.readLine() ) != null )
{
line = line.trim();
line = project.replaceProperties( line );
if( line.startsWith( "//" ) )
continue;
if( line.startsWith( "--" ) )
continue;
StringTokenizer st = new StringTokenizer( line );
if( st.hasMoreTokens() )
{
String token = st.nextToken();
if( "REM".equalsIgnoreCase( token ) )
{
continue;
}
}

sql += " " + line;
sql = sql.trim();

// SQL defines "--" as a comment to EOL
// and in Oracle it may contain a hint
// so we cannot just remove it, instead we must end it
if( line.indexOf( "--" ) >= 0 )
sql += "\n";

if( delimiterType.equals( DelimiterType.NORMAL ) && sql.endsWith( delimiter ) ||
delimiterType.equals( DelimiterType.ROW ) && line.equals( delimiter ) )
{
log( "SQL: " + sql, Project.MSG_VERBOSE );
execSQL( sql.substring( 0, sql.length() - delimiter.length() ), out );
sql = "";
}
}

// Catch any statements not followed by ;
if( !sql.equals( "" ) )
{
execSQL( sql, out );
}
}
catch( SQLException e )
{
throw e;
}

}

public static class DelimiterType extends EnumeratedAttribute
{
public final static String NORMAL = "normal";
public final static String ROW = "row";

public String[] getValues()
{
return new String[]{NORMAL, ROW};
}
}

/**
* Enumerated attribute with the values "continue", "stop" and "abort" for
* the onerror attribute.
*
* @author RT
*/
public static class OnError extends EnumeratedAttribute
{
public String[] getValues()
{
return new String[]{"continue", "stop", "abort"};
}
}

/**
* Contains the definition of a new transaction element. Transactions allow
* several files or blocks of statements to be executed using the same JDBC
* connection and commit operation in between.
*
* @author RT
*/
public class Transaction
{
private File tSrcFile = null;
private String tSqlCommand = "";

public void setSrc( File src )
{
this.tSrcFile = src;
}

public void addText( String sql )
{
this.tSqlCommand += sql;
}

private void runTransaction( PrintStream out )
throws IOException, SQLException
{
if( tSqlCommand.length() != 0 )
{
log( "Executing commands", Project.MSG_INFO );
runStatements( new StringReader( tSqlCommand ), out );
}

if( tSrcFile != null )
{
log( "Executing file: " + tSrcFile.getAbsolutePath(),
Project.MSG_INFO );
Reader reader = ( encoding == null ) ? new FileReader( tSrcFile )
: new InputStreamReader( new FileInputStream( tSrcFile ), encoding );
runStatements( reader, out );
reader.close();
}
}
}

}

+ 406
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/SendEmail.java View File

@@ -0,0 +1,406 @@
/*
* 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.tools.ant.taskdefs;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Enumeration;
import java.util.StringTokenizer;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.mail.MailMessage;

/**
* A task to send SMTP email. <p>
*
*
* <tableborder="1" cellpadding="3" cellspacing="0">
*
* <trbgcolor="#CCCCFF">
*
* <th>
* Attribute
* </th>
*
* <th>
* Description
* </th>
*
* <th>
* Required
* </th>
*
* </tr>
*
* <tr>
*
* <td>
* from
* </td>
*
* <td>
* Email address of sender.
* </td>
*
* <td>
* Yes
* </td>
*
* </tr>
*
* <tr>
*
* <td>
* mailhost
* </td>
*
* <td>
* Host name of the mail server.
* </td>
*
* <td>
* No, default to &quot;localhost&quot;
* </td>
*
* </tr>
*
* <tr>
*
* <td>
* toList
* </td>
*
* <td>
* Comma-separated list of recipients.
* </td>
*
* <td>
* Yes
* </td>
*
* </tr>
*
* <tr>
*
* <td>
* subject
* </td>
*
* <td>
* Email subject line.
* </td>
*
* <td>
* No
* </td>
*
* </tr>
*
* <tr>
*
* <td>
* files
* </td>
*
* <td>
* Filename(s) of text to send in the body of the email. Multiple files
* are comma-separated.
* </td>
*
* <tdrowspan="2">
* One of these two attributes
* </td>
*
* </tr>
*
* <tr>
*
* <td>
* message
* </td>
*
* <td>
* Message to send inthe body of the email.
* </td>
*
* </tr>
*
* </table>
*
* <tr>
*
* <td>
* includefilenames
* </td>
*
* <td>
* Includes filenames before file contents when set to true.
* </td>
*
* <td>
* No, default is <I>false</I>
* </td>
*
* </tr>
* <p>
*
*
*
* @author glenn_twiggs@bmc.com
* @author <a href="mailto:umagesh@rediffmail.com">Magesh Umasankar</a>
*/
public class SendEmail extends Task
{
private String mailhost = "localhost";
private int mailport = MailMessage.DEFAULT_PORT;
private Vector files = new Vector();
/**
* failure flag
*/
private boolean failOnError = true;
private String from;
private boolean includefilenames;
private String message;
private String subject;
private String toList;


/**
* Creates new SendEmail
*/
public SendEmail() { }

/**
* Sets the FailOnError attribute of the MimeMail object
*
* @param failOnError The new FailOnError value
* @since 1.5
*/
public void setFailOnError( boolean failOnError )
{
this.failOnError = failOnError;
}

/**
* Sets the file parameter of this build task.
*
* @param filenames Filenames to include as the message body of this email.
*/
public void setFiles( String filenames )
{
StringTokenizer t = new StringTokenizer( filenames, ", " );

while( t.hasMoreTokens() )
{
files.addElement( project.resolveFile( t.nextToken() ) );
}
}

/**
* Sets the from parameter of this build task.
*
* @param from Email address of sender.
*/
public void setFrom( String from )
{
this.from = from;
}

/**
* Sets Includefilenames attribute
*
* @param includefilenames Set to true if file names are to be included.
* @since 1.5
*/
public void setIncludefilenames( boolean includefilenames )
{
this.includefilenames = includefilenames;
}

/**
* Sets the mailhost parameter of this build task.
*
* @param mailhost Mail host name.
*/
public void setMailhost( String mailhost )
{
this.mailhost = mailhost;
}

/**
* Sets the mailport parameter of this build task.
*
* @param value mail port name.
*/
public void setMailport( Integer value )
{
this.mailport = value.intValue();
}

/**
* Sets the message parameter of this build task.
*
* @param message Message body of this email.
*/
public void setMessage( String message )
{
this.message = message;
}

/**
* Sets the subject parameter of this build task.
*
* @param subject Subject of this email.
*/
public void setSubject( String subject )
{
this.subject = subject;
}

/**
* Sets the toList parameter of this build task.
*
* @param toList Comma-separated list of email recipient addreses.
*/
public void setToList( String toList )
{
this.toList = toList;
}

/**
* Executes this build task.
*
* @throws BuildException if there is an error during task execution.
*/
public void execute()
throws BuildException
{
try
{
MailMessage mailMessage = new MailMessage( mailhost );
mailMessage.setPort( mailport );

if( from != null )
{
mailMessage.from( from );
}
else
{
throw new BuildException( "Attribute \"from\" is required." );
}

if( toList != null )
{
StringTokenizer t = new StringTokenizer( toList, ", ", false );

while( t.hasMoreTokens() )
{
mailMessage.to( t.nextToken() );
}
}
else
{
throw new BuildException( "Attribute \"toList\" is required." );
}

if( subject != null )
{
mailMessage.setSubject( subject );
}

if( !files.isEmpty() )
{
PrintStream out = mailMessage.getPrintStream();

for( Enumeration e = files.elements(); e.hasMoreElements(); )
{
File file = ( File )e.nextElement();

if( file.exists() && file.canRead() )
{
int bufsize = 1024;
int length;
byte[] buf = new byte[bufsize];
if( includefilenames )
{
String filename = file.getName();
int filenamelength = filename.length();
out.println( filename );
for( int star = 0; star < filenamelength; star++ )
{
out.print( '=' );
}
out.println();
}
BufferedInputStream in = null;
try
{
in = new BufferedInputStream(
new FileInputStream( file ), bufsize );
while( ( length = in.read( buf, 0, bufsize ) ) != -1 )
{
out.write( buf, 0, length );
}
if( includefilenames )
{
out.println();
}
}
finally
{
if( in != null )
{
try
{
in.close();
}
catch( IOException ioe )
{}
}
}

}
else
{
throw new BuildException( "File \"" + file.getName()
+ "\" does not exist or is not readable." );
}
}
}
else if( message != null )
{
PrintStream out = mailMessage.getPrintStream();
out.print( message );
}
else
{
throw new BuildException( "Attribute \"file\" or \"message\" is required." );
}

log( "Sending email" );
mailMessage.sendAndClose();
}
catch( IOException ioe )
{
String err = "IO error sending mail " + ioe.toString();
if( failOnError )
{
throw new BuildException( err, ioe, location );
}
else
{
log( err, Project.MSG_ERR );
}
}
}

}

+ 60
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Sequential.java View File

@@ -0,0 +1,60 @@
/*
* 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.tools.ant.taskdefs;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.TaskContainer;


/**
* Implements a single threaded task execution. <p>
*
*
*
* @author Thomas Christen <a href="mailto:chr@active.ch">chr@active.ch</a>
*/
public class Sequential extends Task
implements TaskContainer
{

/**
* Optional Vector holding the nested tasks
*/
private Vector nestedTasks = new Vector();

/**
* Add a nested task to Sequential. <p>
*
*
*
* @param nestedTask Nested task to execute Sequential <p>
*
*
*/
public void addTask( Task nestedTask )
{
nestedTasks.addElement( nestedTask );
}

/**
* Execute all nestedTasks.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
for( Enumeration e = nestedTasks.elements(); e.hasMoreElements(); )
{
Task nestedTask = ( Task )e.nextElement();
nestedTask.perform();
}
}
}

+ 335
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/SignJar.java View File

@@ -0,0 +1,335 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.FileSet;

/**
* Sign a archive.
*
* @author Peter Donald <a href="mailto:donaldp@apache.org">donaldp@apache.org
* </a>
* @author Nick Fortescue <a href="mailto:nick@ox.compsoc.net">
* nick@ox.compsoc.net</a>
*/
public class SignJar extends Task
{

/**
* the filesets of the jars to sign
*/
protected Vector filesets = new Vector();

/**
* The alias of signer.
*/
protected String alias;
protected boolean internalsf;

/**
* The name of the jar file.
*/
protected File jar;
protected String keypass;

/**
* The name of keystore file.
*/
protected File keystore;
/**
* Whether to assume a jar which has an appropriate .SF file in is already
* signed.
*/
protected boolean lazy;
protected boolean sectionsonly;
protected File sigfile;
protected File signedjar;

protected String storepass;
protected String storetype;
protected boolean verbose;

public void setAlias( final String alias )
{
this.alias = alias;
}

public void setInternalsf( final boolean internalsf )
{
this.internalsf = internalsf;
}

public void setJar( final File jar )
{
this.jar = jar;
}

public void setKeypass( final String keypass )
{
this.keypass = keypass;
}

public void setKeystore( final File keystore )
{
this.keystore = keystore;
}

public void setLazy( final boolean lazy )
{
this.lazy = lazy;
}

public void setSectionsonly( final boolean sectionsonly )
{
this.sectionsonly = sectionsonly;
}

public void setSigfile( final File sigfile )
{
this.sigfile = sigfile;
}

public void setSignedjar( final File signedjar )
{
this.signedjar = signedjar;
}

public void setStorepass( final String storepass )
{
this.storepass = storepass;
}

public void setStoretype( final String storetype )
{
this.storetype = storetype;
}

public void setVerbose( final boolean verbose )
{
this.verbose = verbose;
}

/**
* Adds a set of files (nested fileset attribute).
*
* @param set The feature to be added to the Fileset attribute
*/
public void addFileset( final FileSet set )
{
filesets.addElement( set );
}


public void execute()
throws BuildException
{
if( null == jar && null == filesets )
{
throw new BuildException( "jar must be set through jar attribute or nested filesets" );
}
if( null != jar )
{
doOneJar( jar, signedjar );
return;
}
else
{
//Assume null != filesets

// deal with the filesets
for( int i = 0; i < filesets.size(); i++ )
{
FileSet fs = ( FileSet )filesets.elementAt( i );
DirectoryScanner ds = fs.getDirectoryScanner( project );
String[] jarFiles = ds.getIncludedFiles();
for( int j = 0; j < jarFiles.length; j++ )
{
doOneJar( new File( fs.getDir( project ), jarFiles[j] ), null );
}
}
}
}

protected boolean isSigned( File file )
{
final String SIG_START = "META-INF/";
final String SIG_END = ".SF";

if( !file.exists() )
{
return false;
}
ZipFile jarFile = null;
try
{
jarFile = new ZipFile( file );
if( null == alias )
{
Enumeration entries = jarFile.entries();
while( entries.hasMoreElements() )
{
String name = ( ( ZipEntry )entries.nextElement() ).getName();
if( name.startsWith( SIG_START ) && name.endsWith( SIG_END ) )
{
return true;
}
}
return false;
}
else
{
return jarFile.getEntry( SIG_START + alias.toUpperCase() +
SIG_END ) != null;
}
}
catch( IOException e )
{
return false;
}
finally
{
if( jarFile != null )
{
try
{
jarFile.close();
}
catch( IOException e )
{}
}
}
}

protected boolean isUpToDate( File jarFile, File signedjarFile )
{
if( null == jarFile )
{
return false;
}

if( null != signedjarFile )
{

if( !jarFile.exists() )
return false;
if( !signedjarFile.exists() )
return false;
if( jarFile.equals( signedjarFile ) )
return false;
if( signedjarFile.lastModified() > jarFile.lastModified() )
return true;
}
else
{
if( lazy )
{
return isSigned( jarFile );
}
}

return false;
}

private void doOneJar( File jarSource, File jarTarget )
throws BuildException
{
if( project.getJavaVersion().equals( Project.JAVA_1_1 ) )
{
throw new BuildException( "The signjar task is only available on JDK versions 1.2 or greater" );
}

if( null == alias )
{
throw new BuildException( "alias attribute must be set" );
}

if( null == storepass )
{
throw new BuildException( "storepass attribute must be set" );
}

if( isUpToDate( jarSource, jarTarget ) )
return;

final StringBuffer sb = new StringBuffer();

final ExecTask cmd = ( ExecTask )project.createTask( "exec" );
cmd.setExecutable( "jarsigner" );

if( null != keystore )
{
cmd.createArg().setValue( "-keystore" );
cmd.createArg().setValue( keystore.toString() );
}

if( null != storepass )
{
cmd.createArg().setValue( "-storepass" );
cmd.createArg().setValue( storepass );
}

if( null != storetype )
{
cmd.createArg().setValue( "-storetype" );
cmd.createArg().setValue( storetype );
}

if( null != keypass )
{
cmd.createArg().setValue( "-keypass" );
cmd.createArg().setValue( keypass );
}

if( null != sigfile )
{
cmd.createArg().setValue( "-sigfile" );
cmd.createArg().setValue( sigfile.toString() );
}

if( null != jarTarget )
{
cmd.createArg().setValue( "-signedjar" );
cmd.createArg().setValue( jarTarget.toString() );
}

if( verbose )
{
cmd.createArg().setValue( "-verbose" );
}

if( internalsf )
{
cmd.createArg().setValue( "-internalsf" );
}

if( sectionsonly )
{
cmd.createArg().setValue( "-sectionsonly" );
}

cmd.createArg().setValue( jarSource.toString() );

cmd.createArg().setValue( alias );

log( "Signing Jar : " + jarSource.getAbsolutePath() );
cmd.setFailonerror( true );
cmd.setTaskName( getTaskName() );
cmd.execute();
}

}


+ 183
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Sleep.java View File

@@ -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.tools.ant.taskdefs;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;

/**
* A task to sleep for a period of time
*
* @author steve_l@iseran.com steve loughran
* @created 01 May 2001
*/

public class Sleep extends Task
{
/**
* failure flag
*/
private boolean failOnError = true;

/**
* Description of the Field
*/
private int seconds = 0;
/**
* Description of the Field
*/
private int hours = 0;
/**
* Description of the Field
*/
private int minutes = 0;
/**
* Description of the Field
*/
private int milliseconds = 0;


/**
* Creates new instance
*/
public Sleep() { }


/**
* Sets the FailOnError attribute of the MimeMail object
*
* @param failOnError The new FailOnError value
*/
public void setFailOnError( boolean failOnError )
{
this.failOnError = failOnError;
}


/**
* Sets the Hours attribute of the Sleep object
*
* @param hours The new Hours value
*/
public void setHours( int hours )
{
this.hours = hours;
}


/**
* Sets the Milliseconds attribute of the Sleep object
*
* @param milliseconds The new Milliseconds value
*/
public void setMilliseconds( int milliseconds )
{
this.milliseconds = milliseconds;
}


/**
* Sets the Minutes attribute of the Sleep object
*
* @param minutes The new Minutes value
*/
public void setMinutes( int minutes )
{
this.minutes = minutes;
}


/**
* Sets the Seconds attribute of the Sleep object
*
* @param seconds The new Seconds value
*/
public void setSeconds( int seconds )
{
this.seconds = seconds;
}


/**
* sleep for a period of time
*
* @param millis time to sleep
*/
public void doSleep( long millis )
{
try
{
Thread.currentThread().sleep( millis );
}
catch( InterruptedException ie )
{
}
}


/**
* Executes this build task. throws org.apache.tools.ant.BuildException if
* there is an error during task execution.
*
* @exception BuildException Description of Exception
*/
public void execute()
throws BuildException
{
try
{
validate();
long sleepTime = getSleepTime();
log( "sleeping for " + sleepTime + " milliseconds",
Project.MSG_VERBOSE );
doSleep( sleepTime );
}
catch( Exception e )
{
if( failOnError )
{
throw new BuildException( e );
}
else
{
String text = e.toString();
log( text, Project.MSG_ERR );
}
}
}


/**
* verify parameters
*
* @throws BuildException if something is invalid
*/
public void validate()
throws BuildException
{
long sleepTime = getSleepTime();
if( getSleepTime() < 0 )
{
throw new BuildException( "Negative sleep periods are not supported" );
}
}


/**
* return time to sleep
*
* @return sleep time. if below 0 then there is an error
*/

private long getSleepTime()
{
return ( ( ( ( long )hours * 60 ) + minutes ) * 60 + seconds ) * 1000 + milliseconds;
}

}


+ 68
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/StreamPumper.java View File

@@ -0,0 +1,68 @@
/*
* 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.tools.ant.taskdefs;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

/**
* Copies all data from an input stream to an output stream.
*
* @author thomas.haas@softwired-inc.com
*/
public class StreamPumper implements Runnable
{

// TODO: make SIZE and SLEEP instance variables.
// TODO: add a status flag to note if an error occured in run.

private final static int SLEEP = 5;
private final static int SIZE = 128;
private InputStream is;
private OutputStream os;


/**
* Create a new stream pumper.
*
* @param is input stream to read data from
* @param os output stream to write data to.
*/
public StreamPumper( InputStream is, OutputStream os )
{
this.is = is;
this.os = os;
}


/**
* Copies data from the input stream to the output stream. Terminates as
* soon as the input stream is closed or an error occurs.
*/
public void run()
{
final byte[] buf = new byte[SIZE];

int length;
try
{
while( ( length = is.read( buf ) ) > 0 )
{
os.write( buf, 0, length );
try
{
Thread.sleep( SLEEP );
}
catch( InterruptedException e )
{}
}
}
catch( IOException e )
{}
}
}

+ 481
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/Tar.java View File

@@ -0,0 +1,481 @@
/*
* 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.tools.ant.taskdefs;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Enumeration;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.EnumeratedAttribute;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.util.MergingMapper;
import org.apache.tools.ant.util.SourceFileScanner;
import org.apache.tools.tar.TarConstants;
import org.apache.tools.tar.TarEntry;
import org.apache.tools.tar.TarOutputStream;

/**
* Creates a TAR archive.
*
* @author Stefano Mazzocchi <a href="mailto:stefano@apache.org">
* stefano@apache.org</a>
* @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
* @author <a href="mailto:umagesh@apache.org">Magesh Umasankar</a>
*/

public class Tar extends MatchingTask
{

/**
* @deprecated Tar.WARN is deprecated and is replaced with
* Tar.TarLongFileMode.WARN
*/
public final static String WARN = "warn";
/**
* @deprecated Tar.FAIL is deprecated and is replaced with
* Tar.TarLongFileMode.FAIL
*/
public final static String FAIL = "fail";
/**
* @deprecated Tar.TRUNCATE is deprecated and is replaced with
* Tar.TarLongFileMode.TRUNCATE
*/
public final static String TRUNCATE = "truncate";
/**
* @deprecated Tar.GNU is deprecated and is replaced with
* Tar.TarLongFileMode.GNU
*/
public final static String GNU = "gnu";
/**
* @deprecated Tar.OMIT is deprecated and is replaced with
* Tar.TarLongFileMode.OMIT
*/
public final static String OMIT = "omit";

private TarLongFileMode longFileMode = new TarLongFileMode();

Vector filesets = new Vector();
Vector fileSetFiles = new Vector();

/**
* Indicates whether the user has been warned about long files already.
*/
private boolean longWarningGiven = false;
File baseDir;

File tarFile;

/**
* This is the base directory to look in for things to tar.
*
* @param baseDir The new Basedir value
*/
public void setBasedir( File baseDir )
{
this.baseDir = baseDir;
}

/**
* Set how to handle long files. Allowable values are truncate - paths are
* truncated to the maximum length fail - paths greater than the maximim
* cause a build exception warn - paths greater than the maximum cause a
* warning and GNU is used gnu - GNU extensions are used for any paths
* greater than the maximum. omit - paths greater than the maximum are
* omitted from the archive
*
* @param mode The new Longfile value
* @deprecated setLongFile(String) is deprecated and is replaced with
* setLongFile(Tar.TarLongFileMode) to make Ant's Introspection
* mechanism do the work and also to encapsulate operations on the mode
* in its own class.
*/
public void setLongfile( String mode )
{
log( "DEPRECATED - The setLongfile(String) method has been deprecated."
+ " Use setLongfile(Tar.TarLongFileMode) instead." );
this.longFileMode = new TarLongFileMode();
longFileMode.setValue( mode );
}

/**
* Set how to handle long files. Allowable values are truncate - paths are
* truncated to the maximum length fail - paths greater than the maximim
* cause a build exception warn - paths greater than the maximum cause a
* warning and GNU is used gnu - GNU extensions are used for any paths
* greater than the maximum. omit - paths greater than the maximum are
* omitted from the archive
*
* @param mode The new Longfile value
*/
public void setLongfile( TarLongFileMode mode )
{
this.longFileMode = mode;
}


/**
* This is the name/location of where to create the tar file.
*
* @param tarFile The new Tarfile value
*/
public void setTarfile( File tarFile )
{
this.tarFile = tarFile;
}

public TarFileSet createTarFileSet()
{
TarFileSet fileset = new TarFileSet();
filesets.addElement( fileset );
return fileset;
}

public void execute()
throws BuildException
{
if( tarFile == null )
{
throw new BuildException( "tarfile attribute must be set!",
location );
}

if( tarFile.exists() && tarFile.isDirectory() )
{
throw new BuildException( "tarfile is a directory!",
location );
}

if( tarFile.exists() && !tarFile.canWrite() )
{
throw new BuildException( "Can not write to the specified tarfile!",
location );
}

if( baseDir != null )
{
if( !baseDir.exists() )
{
throw new BuildException( "basedir does not exist!", location );
}

// add the main fileset to the list of filesets to process.
TarFileSet mainFileSet = new TarFileSet( fileset );
mainFileSet.setDir( baseDir );
filesets.addElement( mainFileSet );
}

if( filesets.size() == 0 )
{
throw new BuildException( "You must supply either a basdir attribute or some nested filesets.",
location );
}

// check if tr is out of date with respect to each
// fileset
boolean upToDate = true;
for( Enumeration e = filesets.elements(); e.hasMoreElements(); )
{
TarFileSet fs = ( TarFileSet )e.nextElement();
String[] files = fs.getFiles( project );

if( !archiveIsUpToDate( files ) )
{
upToDate = false;
}

for( int i = 0; i < files.length; ++i )
{
if( tarFile.equals( new File( fs.getDir( project ), files[i] ) ) )
{
throw new BuildException( "A tar file cannot include itself", location );
}
}
}

if( upToDate )
{
log( "Nothing to do: " + tarFile.getAbsolutePath() + " is up to date.",
Project.MSG_INFO );
return;
}

log( "Building tar: " + tarFile.getAbsolutePath(), Project.MSG_INFO );

TarOutputStream tOut = null;
try
{
tOut = new TarOutputStream( new FileOutputStream( tarFile ) );
tOut.setDebug( true );
if( longFileMode.isTruncateMode() )
{
tOut.setLongFileMode( TarOutputStream.LONGFILE_TRUNCATE );
}
else if( longFileMode.isFailMode() ||
longFileMode.isOmitMode() )
{
tOut.setLongFileMode( TarOutputStream.LONGFILE_ERROR );
}
else
{
// warn or GNU
tOut.setLongFileMode( TarOutputStream.LONGFILE_GNU );
}

longWarningGiven = false;
for( Enumeration e = filesets.elements(); e.hasMoreElements(); )
{
TarFileSet fs = ( TarFileSet )e.nextElement();
String[] files = fs.getFiles( project );
for( int i = 0; i < files.length; i++ )
{
File f = new File( fs.getDir( project ), files[i] );
String name = files[i].replace( File.separatorChar, '/' );
tarFile( f, tOut, name, fs );
}
}
}
catch( IOException ioe )
{
String msg = "Problem creating TAR: " + ioe.getMessage();
throw new BuildException( msg, ioe, location );
}
finally
{
if( tOut != null )
{
try
{
// close up
tOut.close();
}
catch( IOException e )
{}
}
}
}

protected boolean archiveIsUpToDate( String[] files )
{
SourceFileScanner sfs = new SourceFileScanner( this );
MergingMapper mm = new MergingMapper();
mm.setTo( tarFile.getAbsolutePath() );
return sfs.restrict( files, baseDir, null, mm ).length == 0;
}

protected void tarFile( File file, TarOutputStream tOut, String vPath,
TarFileSet tarFileSet )
throws IOException
{
FileInputStream fIn = null;

// don't add "" to the archive
if( vPath.length() <= 0 )
{
return;
}

if( file.isDirectory() && !vPath.endsWith( "/" ) )
{
vPath += "/";
}

try
{
if( vPath.length() >= TarConstants.NAMELEN )
{
if( longFileMode.isOmitMode() )
{
log( "Omitting: " + vPath, Project.MSG_INFO );
return;
}
else if( longFileMode.isWarnMode() )
{
log( "Entry: " + vPath + " longer than " +
TarConstants.NAMELEN + " characters.", Project.MSG_WARN );
if( !longWarningGiven )
{
log( "Resulting tar file can only be processed successfully"
+ " by GNU compatible tar commands", Project.MSG_WARN );
longWarningGiven = true;
}
}
else if( longFileMode.isFailMode() )
{
throw new BuildException(
"Entry: " + vPath + " longer than " +
TarConstants.NAMELEN + "characters.", location );
}
}

TarEntry te = new TarEntry( vPath );
te.setModTime( file.lastModified() );
if( !file.isDirectory() )
{
te.setSize( file.length() );
te.setMode( tarFileSet.getMode() );
}
te.setUserName( tarFileSet.getUserName() );
te.setGroupName( tarFileSet.getGroup() );

tOut.putNextEntry( te );

if( !file.isDirectory() )
{
fIn = new FileInputStream( file );

byte[] buffer = new byte[8 * 1024];
int count = 0;
do
{
tOut.write( buffer, 0, count );
count = fIn.read( buffer, 0, buffer.length );
}while ( count != -1 );
}

tOut.closeEntry();
}
finally
{
if( fIn != null )
fIn.close();
}
}

public static class TarFileSet extends FileSet
{
private String[] files = null;

private int mode = 0100644;

private String userName = "";
private String groupName = "";


public TarFileSet( FileSet fileset )
{
super( fileset );
}

public TarFileSet()
{
super();
}

public void setGroup( String groupName )
{
this.groupName = groupName;
}

public void setMode( String octalString )
{
this.mode = 0100000 | Integer.parseInt( octalString, 8 );
}

public void setUserName( String userName )
{
this.userName = userName;
}

/**
* Get a list of files and directories specified in the fileset.
*
* @param p Description of Parameter
* @return a list of file and directory names, relative to the baseDir
* for the project.
*/
public String[] getFiles( Project p )
{
if( files == null )
{
DirectoryScanner ds = getDirectoryScanner( p );
String[] directories = ds.getIncludedDirectories();
String[] filesPerSe = ds.getIncludedFiles();
files = new String[directories.length + filesPerSe.length];
System.arraycopy( directories, 0, files, 0, directories.length );
System.arraycopy( filesPerSe, 0, files, directories.length,
filesPerSe.length );
}

return files;
}

public String getGroup()
{
return groupName;
}

public int getMode()
{
return mode;
}

public String getUserName()
{
return userName;
}

}

/**
* Valid Modes for LongFile attribute to Tar Task
*
* @author <a href="mailto:umagesh@apache.org">Magesh Umasankar</a>
*/
public static class TarLongFileMode extends EnumeratedAttribute
{

// permissable values for longfile attribute
public final static String WARN = "warn";
public final static String FAIL = "fail";
public final static String TRUNCATE = "truncate";
public final static String GNU = "gnu";
public final static String OMIT = "omit";

private final String[] validModes = {WARN, FAIL, TRUNCATE, GNU, OMIT};

public TarLongFileMode()
{
super();
setValue( WARN );
}

public String[] getValues()
{
return validModes;
}

public boolean isFailMode()
{
return FAIL.equalsIgnoreCase( getValue() );
}

public boolean isGnuMode()
{
return GNU.equalsIgnoreCase( getValue() );
}

public boolean isOmitMode()
{
return OMIT.equalsIgnoreCase( getValue() );
}

public boolean isTruncateMode()
{
return TRUNCATE.equalsIgnoreCase( getValue() );
}

public boolean isWarnMode()
{
return WARN.equalsIgnoreCase( getValue() );
}
}
}

+ 84
- 0
proposal/myrmidon/src/main/org/apache/tools/ant/taskdefs/TaskOutputStream.java View File

@@ -0,0 +1,84 @@
/*
* 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.tools.ant.taskdefs;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.tools.ant.Task;

/**
* Redirects text written to a stream thru the standard ant logging mechanism.
* This class is useful for integrating with tools that write to System.out and
* System.err. For example, the following will cause all text written to
* System.out to be logged with "info" priority: <pre>System.setOut(new PrintStream(new TaskOutputStream(project, Project.MSG_INFO)));</pre>
*
* @author James Duncan Davidson (duncan@x180.com)
* @deprecated use LogOutputStream instead.
*/

public class TaskOutputStream extends OutputStream
{
private StringBuffer line;
private int msgOutputLevel;

private Task task;

/**
* Constructs a new JavacOutputStream with the given project as the output
* source for messages.
*
* @param task Description of Parameter
* @param msgOutputLevel Description of Parameter
*/

TaskOutputStream( Task task, int msgOutputLevel )
{
this.task = task;
this.msgOutputLevel = msgOutputLevel;

line = new StringBuffer();
}

/**
* Write a character to the output stream. This method looks to make sure
* that there isn't an error being reported and will flush each line of
* input out to the project's log stream.
*
* @param c Description of Parameter
* @exception IOException Description of Exception
*/

public void write( int c )
throws IOException
{
char cc = ( char )c;
if( cc == '\r' || cc == '\n' )
{
// line feed
if( line.length() > 0 )
{
processLine();
}
}
else
{
line.append( cc );
}
}

/**
* Processes a line of input and determines if an error occured.
*/

private void processLine()
{
String s = line.toString();
task.log( s, msgOutputLevel );
line = new StringBuffer();
}
}


Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save