Browse Source

Antlib proposal Second installment

Submitted by:	Jose Alberto Fernandez <j_a_fernandez@yahoo.com>


git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271407 13f79535-47bb-0310-9956-ffa450edef68
master
Stefan Bodewig 23 years ago
parent
commit
4ad59f0fc5
15 changed files with 2172 additions and 304 deletions
  1. +2
    -0
      proposal/sandbox/antlib/.cvsignore
  2. +9
    -0
      proposal/sandbox/antlib/build.xml
  3. +706
    -0
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/IntrospectionHelper.java
  4. +169
    -105
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java
  5. +797
    -0
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/ProjectHelper.java
  6. +5
    -0
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java
  7. +211
    -143
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java
  8. +1
    -0
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java
  9. +1
    -23
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java
  10. +7
    -30
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java
  11. +21
    -3
      proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java
  12. +30
    -0
      proposal/sandbox/antlib/src/testcases/build.xml
  13. +5
    -0
      proposal/sandbox/antlib/src/testcases/case-antlib.xml
  14. +39
    -0
      proposal/sandbox/antlib/src/testcases/case.xml
  15. +169
    -0
      proposal/sandbox/antlib/src/testcases/org/apache/ant/contrib/Case.java

+ 2
- 0
proposal/sandbox/antlib/.cvsignore View File

@@ -0,0 +1,2 @@
build
dist

+ 9
- 0
proposal/sandbox/antlib/build.xml View File

@@ -6,6 +6,7 @@
<property name='build' location='build' />
<property name='dist' location='dist' />
<property name='classes' location='${build}/classes' />
<property name='testcases' location='src/testcases' />

<property name="debug" value="true" />
<property name="deprecation" value="false" />
@@ -19,6 +20,8 @@
<fileset dir='${orig-classes}'>
<include name='**' />
<exclude name='org/apache/tools/ant/Project.class' />
<exclude name='org/apache/tools/ant/ProjectHelper.class' />
<exclude name='org/apache/tools/ant/IntrospectionHelper.class' />
<exclude name='org/apache/tools/ant/TaskAdapter.class' />
<exclude name='org/apache/tools/ant/taskdefs/Ant.class' />
</fileset>
@@ -54,6 +57,12 @@
<delete dir='${build}' />
</target>

<target name='test'>
<ant dir='${testcases}' inheritAll='false'/>
<ant dir='${testcases}'
antfile='${testcases}/case.xml' inheritAll='false'/>
</target>

<target name='cleanall' depends='clean'>
<delete dir='${dist}' />
</target>

+ 706
- 0
proposal/sandbox/antlib/src/main/org/apache/tools/ant/IntrospectionHelper.java View File

@@ -0,0 +1,706 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Ant", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.tools.ant;

import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.EnumeratedAttribute;

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Constructor;
import java.io.File;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;

/**
* 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 {

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

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

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

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

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

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

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

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

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[] {});
}

});
nestedStorers.remove(propName);

} 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;
}

});
nestedStorers.remove(name);
} catch (NoSuchMethodException nse) {
}
}
}
}

/**
* Factory method for helper objects.
*/
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.
*/
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);
}
}

/**
* Adds PCDATA areas.
*/
public void addText(Project project, Object element, String text) {
if (addText == null) {
// Element doesn't handle text content
if ( text.trim().length() == 0 ) {
// Only whitespace - ignore
return;
}
else {
// Not whitespace - fail
String msg = getElementName(project, element) +
" 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);
}
}

/**
* Creates a named nested element.
*/
public Object createElement(Project project, Object element, String elementName)
throws BuildException {
try {
// First check if there are any roles supported by this class
Object nestedElement = project.createInRole(element, elementName);
if (nestedElement == null) {
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);
}
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);
}
}

/**
* Creates a named nested element.
*/
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);
}
}

/**
* returns the type of a named nested element.
*/
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;
}

/**
* returns the type of a named attribute.
*/
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;
}

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

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

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

/**
* Create a proper implementation of AttributeSetter for the given
* attribute type.
*/
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;
}

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.
*/
private String getPropertyName(String methodName, String prefix) {
int start = prefix.length();
return methodName.substring(start).toLowerCase(Locale.US);
}

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

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

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

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

public void targetStarted(BuildEvent event) {}
public void targetFinished(BuildEvent event) {}
public void taskStarted(BuildEvent event) {}
public void taskFinished(BuildEvent event) {}
public void messageLogged(BuildEvent event) {}
}

+ 169
- 105
proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java View File

@@ -63,12 +63,15 @@ import java.util.Properties;
import java.util.Enumeration;
import java.util.Stack;
import java.lang.reflect.Modifier;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;


import org.apache.tools.ant.types.DataTypeAdapterTask;
import org.apache.tools.ant.types.FilterSet;
import org.apache.tools.ant.types.FilterSetCollection;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.types.Path;

/**
* Central representation of an Ant project. This class defines a
@@ -90,6 +93,9 @@ public class Project {
public final static int MSG_VERBOSE = 3;
public final static int MSG_DEBUG = 4;

public final static String TASK_ROLE = "task";
public final static String DATATYPE_ROLE = "datatype";

// private set of constants to represent the state
// of a DFS of the Target dependencies
private final static String VISITING = "VISITING";
@@ -165,30 +171,26 @@ public class Project {
fileUtils = FileUtils.newFileUtils();
symbols = new SymbolTable();
symbols.setProject(this);
loadDefinitions();
}
/**
* create a new ant project that inherits from caler project
* @param p the calling project
*/
public Project(Project p) {
private Project(Project p) {
fileUtils = FileUtils.newFileUtils();
symbols = new SymbolTable(p);
symbols = new SymbolTable(p.getSymbols());
symbols.setProject(this);
}
/**
* Initialise the project.
*
* This involves setting the default task definitions and loading the
* system properties.
* Loads the core definitions into the Root project.
*/
public void init() throws BuildException {
setJavaVersionProperty();
// Initialize simbol table just in case
symbols.addRole("task", TaskContainer.class, TaskAdapter.class);
symbols.addRole("datatype", TaskContainer.class,
private void loadDefinitions() {
// Initialize symbol table just in case
symbols.addRole(TASK_ROLE, TaskContainer.class, TaskAdapter.class);
symbols.addRole(DATATYPE_ROLE, TaskContainer.class,
DataTypeAdapterTask.class);

String defs = "/org/apache/tools/ant/taskdefs/defaults.properties";
@@ -248,7 +250,23 @@ public class Project {
} catch (IOException ioe) {
throw new BuildException("Can't load default datatype list");
}
}

/**
* Creates a subproject of the current project.
*/
public Project createSubProject() {
return new Project(this);
}

/**
* Initialise the project.
*
* This involves setting the default task definitions and loading the
* system properties.
*/
public void init() throws BuildException {
setJavaVersionProperty();
setSystemProperties();
}

@@ -275,7 +293,7 @@ public class Project {
/**
* Get the symbols associated with this project.
*/
public SymbolTable getSymbols() {
private SymbolTable getSymbols() { // Package protected on purpose
return symbols;
}

@@ -618,6 +636,46 @@ public class Project {
}
}

public ClassLoader addToLoader(String loader, Path path) {
return symbols.addToLoader(loader, path);
}

public boolean addRoleDefinition(String role,
Class roleClass, Class adapter)
{
return symbols.addRole(role, roleClass, adapter);
}

/**
* test for a role name being in use already
*
* @param name the name to test
* @return true if it is a task or a datatype
*/
public boolean isRoleDefined(String name) {
return (symbols.getRole(name) != null);
}

public void addDefinitionOnRole(String role,
String type, Class clz)
{
Class old = symbols.add(role, type, clz);
// Special management for Tasks
if (TASK_ROLE.equals(role) && null != old && !old.equals(clz)) {
invalidateCreatedTasks(type);
}
}

/**
* test for a name being in use already on this role
*
* @param name the name to test
* @return true if it is a task or a datatype
*/
public boolean isDefinedOnRole(String role, String name) {
return (symbols.get(role, name) != null);
}
/**
* add a new task definition, complain if there is an overwrite attempt
* @param taskName name of the task
@@ -627,21 +685,14 @@ public class Project {
*/
public void addTaskDefinition(String taskName, Class taskClass)
throws BuildException {
Class old = symbols.add("task", taskName, taskClass);
if (null != old && !old.equals(taskClass)) {
invalidateCreatedTasks(taskName);
}

String msg =
" +User task: " + taskName + " " + taskClass.getName();
log(msg, MSG_DEBUG);
checkTaskClass(taskClass);
addDefinitionOnRole(TASK_ROLE, taskName, taskClass);
}

/**
* Checks a class, whether it is suitable for serving as ant task.
* @throws BuildException and logs as Project.MSG_ERR for
* conditions, that will cause the task execution to fail.
* @deprecated this is done now when added to SymbolTable
*/
public void checkTaskClass(final Class taskClass) throws BuildException {
if( !Task.class.isAssignableFrom(taskClass) ) {
@@ -653,7 +704,7 @@ public class Project {
* get the current task definition hashtable
*/
public Hashtable getTaskDefinitions() {
return symbols.getTaskDefinitions();
return symbols.getDefinitions(TASK_ROLE);
}

/**
@@ -662,18 +713,14 @@ public class Project {
* @param typeClass full datatype classname
*/
public void addDataTypeDefinition(String typeName, Class typeClass) {
symbols.add("datatype", typeName, typeClass);

String msg =
" +User datatype: " + typeName + " " + typeClass.getName();
log(msg, MSG_DEBUG);
addDefinitionOnRole(DATATYPE_ROLE, typeName, typeClass);
}

/**
* get the current task definition hashtable
*/
public Hashtable getDataTypeDefinitions() {
return symbols.getDataTypeDefinitions();
return symbols.getDefinitions(DATATYPE_ROLE);
}

/**
@@ -701,7 +748,7 @@ public class Project {
* in the project.
* @see Project#addOrReplaceTarget to replace existing Targets.
*/
public void addTarget(String targetName, Target target)
public void addTarget(String targetName, Target target)
throws BuildException {
if (targets.get(targetName) != null) {
throw new BuildException("Duplicate target: `"+targetName+"'");
@@ -737,6 +784,88 @@ public class Project {
return targets;
}

/**
* Create a new element instance on a Role
* @param role name of the role to use
* @param type name of the element to create
* @return null if element unknown on this role
*/
public Object createForRole(String role, String type) {
SymbolTable.Factory f = symbols.get(role, type);
if (f == null) return null;

try {
Object o = f.create(this);
// Do special book keeping for ProjectComponents
if ( o instanceof ProjectComponent ) {
((ProjectComponent)o).setProject(this);
if (o instanceof Task) {
Task task = (Task) o;
task.setTaskType(type);
// set default value, can be changed by the user
task.setTaskName(type);
addCreatedTask(type, task);
}
}
String msg = " +" + role + ": " + type;
log (msg, MSG_DEBUG);
return o;
}
catch (Throwable t) {
String msg = "Could not create " + role + " of type: "
+ type + " due to " + t;
throw new BuildException(msg, t);
}
}

/**
*
*/
public Object createInRole(Object container, String type) {
Class clz = container.getClass();
String roles[] = symbols.findRoles(clz);
Object theOne = null;
Method add = null;

for(int i = 0; i < roles.length; i++) {
Object o = createForRole(roles[i], type);
if (o != null) {
if (theOne != null) {
String msg = "Element " + type +
" is ambiguous for container " + clz.getName();
if (theOne instanceof RoleAdapter)
theOne = ((RoleAdapter)theOne).getProxy();
if (o instanceof RoleAdapter)
o = ((RoleAdapter)o).getProxy();
log(msg, MSG_ERR);
log("cannot distinguish between " +
theOne.getClass().getName() +
" and " + o.getClass().getName(), MSG_ERR);
throw new BuildException(msg);
}
theOne = o;
add = symbols.getRole(roles[i]).getInterfaceMethod();
}
}
if (theOne != null) {
try {
add.invoke(container, new Object[]{theOne});
}
catch(InvocationTargetException ite) {
if (ite.getTargetException() instanceof BuildException) {
throw (BuildException)ite.getTargetException();
}
throw new BuildException(ite.getTargetException());
}
catch(Exception e) {
throw new BuildException(e);
}
}
return theOne;
}

/**
* create a new task instance
* @param taskType name of the task
@@ -744,39 +873,7 @@ public class Project {
* @return null if the task name is unknown
*/
public Task createTask(String taskType) throws BuildException {
Class c = symbols.get("task", taskType);

if (c == null) {
return null;
}
try {
Object o = c.newInstance();
Task task = null;
if( o instanceof Task ) {
task=(Task)o;
} else {
// "Generic" Bean - use the setter pattern
// and an Adapter
TaskAdapter taskA=new TaskAdapter();
taskA.setProxy( o );
task=taskA;
}
task.setProject(this);
task.setTaskType(taskType);

// set default value, can be changed by the user
task.setTaskName(taskType);

String msg = " +Task: " + taskType;
log (msg, MSG_DEBUG);
addCreatedTask(taskType, task);
return task;
} catch (Throwable t) {
String msg = "Could not create task of type: "
+ taskType + " due to " + t;
throw new BuildException(msg, t);
}
return (Task) createForRole(TASK_ROLE, taskType);
}

/**
@@ -820,47 +917,11 @@ public class Project {
* @return null if the datatype name is unknown
*/
public Object createDataType(String typeName) throws BuildException {
Class c = symbols.get("datatype", typeName);

if (c == null) {
return null;
}

try {
java.lang.reflect.Constructor ctor = null;
boolean noArg = false;
// DataType can have a "no arg" constructor or take a single
// Project argument.
try {
ctor = c.getConstructor(new Class[0]);
noArg = true;
} catch (NoSuchMethodException nse) {
ctor = c.getConstructor(new Class[] {Project.class});
noArg = false;
}

Object o = null;
if (noArg) {
o = ctor.newInstance(new Object[0]);
} else {
o = ctor.newInstance(new Object[] {this});
}
if (o instanceof ProjectComponent) {
((ProjectComponent)o).setProject(this);
}
String msg = " +DataType: " + typeName;
log (msg, MSG_DEBUG);
return o;
} catch (java.lang.reflect.InvocationTargetException ite) {
Throwable t = ite.getTargetException();
String msg = "Could not create datatype of type: "
+ typeName + " due to " + t;
throw new BuildException(msg, t);
} catch (Throwable t) {
String msg = "Could not create datatype of type: "
+ typeName + " due to " + t;
throw new BuildException(msg, t);
}
// This is to make the function backward compatible
// Since we know if it returning an adapter for it
DataTypeAdapterTask dt =
(DataTypeAdapterTask) createForRole(DATATYPE_ROLE, typeName);
return (dt != null? dt.getProxy() : null);
}

/**
@@ -1227,7 +1288,10 @@ public class Project {
}

public void addReference(String name, Object value) {
if (null != references.get(name)) {
Object o = references.get(name);
if (null != o && o != value
&& (!(o instanceof RoleAdapter)
|| ((RoleAdapter)o).getProxy() != value)) {
log("Overriding previous definition of reference to " + name,
MSG_WARN);
}


+ 797
- 0
proposal/sandbox/antlib/src/main/org/apache/tools/ant/ProjectHelper.java View File

@@ -0,0 +1,797 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Ant", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.tools.ant;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Hashtable;
import java.util.Vector;
import java.util.Enumeration;
import java.util.Locale;
import org.xml.sax.Locator;
import org.xml.sax.InputSource;
import org.xml.sax.HandlerBase;
import org.xml.sax.SAXParseException;
import org.xml.sax.SAXException;
import org.xml.sax.DocumentHandler;
import org.xml.sax.AttributeList;


import javax.xml.parsers.SAXParserFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.ParserConfigurationException;

/**
* Configures a Project (complete with Targets and Tasks) based on
* a XML build file.
*
* @author duncan@x180.com
*/

public class ProjectHelper {

private static SAXParserFactory parserFactory = null;

private org.xml.sax.Parser parser;
private Project project;
private File buildFile;
private File buildFileParent;
private Locator locator;

/**
* Configures the Project with the contents of the specified XML file.
*/
public static void configureProject(Project project, File buildFile) throws BuildException {
new ProjectHelper(project, buildFile).parse();
}

/**
* Constructs a new Ant parser for the specified XML file.
*/
private ProjectHelper(Project project, File buildFile) {
this.project = project;
this.buildFile = new File(buildFile.getAbsolutePath());
buildFileParent = new File(this.buildFile.getParent());
}

/**
* Parses the project file.
*/
private void parse() throws BuildException {
FileInputStream inputStream = null;
InputSource inputSource = null;
try {
SAXParser saxParser = getParserFactory().newSAXParser();
parser = saxParser.getParser();

String uri = "file:" + buildFile.getAbsolutePath().replace('\\', '/');
for (int index = uri.indexOf('#'); index != -1; index = uri.indexOf('#')) {
uri = uri.substring(0, index) + "%23" + uri.substring(index+1);
}
inputStream = new FileInputStream(buildFile);
inputSource = new InputSource(inputStream);
inputSource.setSystemId(uri);
project.log("parsing buildfile " + buildFile + " with URI = " + uri, Project.MSG_VERBOSE);
saxParser.parse(inputSource, new RootHandler());
}
catch(ParserConfigurationException exc) {
throw new BuildException("Parser has not been configured correctly", exc);
}
catch(SAXParseException exc) {
Location location =
new Location(buildFile.toString(), exc.getLineNumber(), exc.getColumnNumber());

Throwable t = exc.getException();
if (t instanceof BuildException) {
BuildException be = (BuildException) t;
if (be.getLocation() == Location.UNKNOWN_LOCATION) {
be.setLocation(location);
}
throw be;
}
throw new BuildException(exc.getMessage(), t, location);
}
catch(SAXException exc) {
Throwable t = exc.getException();
if (t instanceof BuildException) {
throw (BuildException) t;
}
throw new BuildException(exc.getMessage(), t);
}
catch(FileNotFoundException exc) {
throw new BuildException(exc);
}
catch(IOException exc) {
throw new BuildException("Error reading project file", exc);
}
finally {
if (inputStream != null) {
try {
inputStream.close();
}
catch (IOException ioe) {
// ignore this
}
}
}
}

/**
* The common superclass for all sax event handlers in Ant. Basically
* throws an exception in each method, so subclasses should override
* what they can handle.
*
* Each type of xml element (task, target, etc) in ant will
* have its own subclass of AbstractHandler.
*
* In the constructor, this class takes over the handling of sax
* events from the parent handler, and returns
* control back to the parent in the endElement method.
*/
private class AbstractHandler extends HandlerBase {
protected DocumentHandler parentHandler;

public AbstractHandler(DocumentHandler parentHandler) {
this.parentHandler = parentHandler;

// Start handling SAX events
parser.setDocumentHandler(this);
}

public void startElement(String tag, AttributeList attrs) throws SAXParseException {
throw new SAXParseException("Unexpected element \"" + tag + "\"", locator);
}

public void characters(char[] buf, int start, int end) throws SAXParseException {
String s = new String(buf, start, end).trim();

if (s.length() > 0) {
throw new SAXParseException("Unexpected text \"" + s + "\"", locator);
}
}

/**
* Called when this element and all elements nested into it have been
* handled.
*/
protected void finished() {}

public void endElement(String name) throws SAXException {

finished();
// Let parent resume handling SAX events
parser.setDocumentHandler(parentHandler);
}
}

/**
* Handler for the root element. It's only child must be the "project" element.
*/
private class RootHandler extends HandlerBase {

/**
* resolve file: URIs as relative to the build file.
*/
public InputSource resolveEntity(String publicId,
String systemId) {
project.log("resolving systemId: " + systemId, Project.MSG_VERBOSE);
if (systemId.startsWith("file:")) {
String path = systemId.substring(5);
int index = path.indexOf("file:");
// we only have to handle these for backward compatibility
// since they are in the FAQ.
while (index != -1) {
path = path.substring(0, index) + path.substring(index + 5);
index = path.indexOf("file:");
}
String entitySystemId = path;
index = path.indexOf("%23");
// convert these to #
while (index != -1) {
path = path.substring(0, index) + "#" + path.substring(index + 3);
index = path.indexOf("%23");
}

File file = new File(path);
if (!file.isAbsolute()) {
file = new File(buildFileParent, path);
}
try {
InputSource inputSource = new InputSource(new FileInputStream(file));
inputSource.setSystemId("file:" + entitySystemId);
return inputSource;
} catch (FileNotFoundException fne) {
project.log(file.getAbsolutePath()+" could not be found",
Project.MSG_WARN);
}
}
// use default if not file or file not found
return null;
}

public void startElement(String tag, AttributeList attrs) throws SAXParseException {
if (tag.equals("project")) {
new ProjectHandler(this).init(tag, attrs);
} else {
throw new SAXParseException("Config file is not of expected XML type", locator);
}
}

public void setDocumentLocator(Locator locator) {
ProjectHelper.this.locator = locator;
}
}

/**
* Handler for the top level "project" element.
*/
private class ProjectHandler extends AbstractHandler {
public ProjectHandler(DocumentHandler parentHandler) {
super(parentHandler);
}

public void init(String tag, AttributeList attrs) throws SAXParseException {
String def = null;
String name = null;
String id = null;
String baseDir = null;

for (int i = 0; i < attrs.getLength(); i++) {
String key = attrs.getName(i);
String value = attrs.getValue(i);

if (key.equals("default")) {
def = value;
} else if (key.equals("name")) {
name = value;
} else if (key.equals("id")) {
id = value;
} else if (key.equals("basedir")) {
baseDir = value;
} else {
throw new SAXParseException("Unexpected attribute \"" + attrs.getName(i) + "\"", locator);
}
}

if (def == null) {
throw new SAXParseException("The default attribute of project is required",
locator);
}

project.setDefaultTarget(def);

if (name != null) {
project.setName(name);
project.addReference(name, project);
}

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

if (project.getProperty("basedir") != null) {
project.setBasedir(project.getProperty("basedir"));
} else {
if (baseDir == null) {
project.setBasedir(buildFileParent.getAbsolutePath());
} else {
// check whether the user has specified an absolute path
if ((new File(baseDir)).isAbsolute()) {
project.setBasedir(baseDir);
} else {
project.setBaseDir(project.resolveFile(baseDir, buildFileParent));
}
}
}

}

public void startElement(String name, AttributeList attrs) throws SAXParseException {
if (name.equals("taskdef")) {
handleTopTask(name, attrs);
} else if (name.equals("typedef")) {
handleTopTask(name, attrs);
} else if (name.equals("antlib")) {
handleTopTask(name, attrs);
} else if (name.equals("property")) {
handleTopTask(name, attrs);
} else if (name.equals("target")) {
handleTarget(name, attrs);
} else if (project.isDefinedOnRole(Project.DATATYPE_ROLE, name)) {
handleTopTask(name, attrs);
} else {
throw new SAXParseException("Unexpected element \"" + name + "\"", locator);
}
}

private void handleTopTask(String name, AttributeList attrs)
throws SAXParseException {
InmediateTarget target = new InmediateTarget(name);
(new TaskHandler(this, target, null, target)).init(name, attrs);
}

private void handleTarget(String tag, AttributeList attrs) throws SAXParseException {
new TargetHandler(this).init(tag, attrs);
}

}

/**
* Handler for "target" elements.
*/
private class TargetHandler extends AbstractHandler {
private Target target;

public TargetHandler(DocumentHandler parentHandler) {
super(parentHandler);
}

public void init(String tag, AttributeList attrs) throws SAXParseException {
String name = null;
String depends = "";
String ifCond = null;
String unlessCond = null;
String id = null;
String description = null;

for (int i = 0; i < attrs.getLength(); i++) {
String key = attrs.getName(i);
String value = attrs.getValue(i);

if (key.equals("name")) {
name = value;
} else if (key.equals("depends")) {
depends = value;
} else if (key.equals("if")) {
ifCond = value;
} else if (key.equals("unless")) {
unlessCond = value;
} else if (key.equals("id")) {
id = value;
} else if (key.equals("description")) {
description = value;
} else {
throw new SAXParseException("Unexpected attribute \"" + key + "\"", locator);
}
}

if (name == null) {
throw new SAXParseException("target element appears without a name attribute", locator);
}

target = new Target();
target.setName(name);
target.setIf(ifCond);
target.setUnless(unlessCond);
target.setDescription(description);
project.addTarget(name, target);

if (id != null && !id.equals("")) {
project.addReference(id, target);
}

// take care of dependencies

if (depends.length() > 0) {
target.setDepends(depends);
}
}

public void startElement(String name, AttributeList attrs) throws SAXParseException {
new TaskHandler(this, target, null, target).init(name, attrs);
}
}

/**
* Handler for all task elements.
*/
private class TaskHandler extends AbstractHandler {
private Target target;
private TaskContainer container;
private Task task;
private RuntimeConfigurable parentWrapper;
private RuntimeConfigurable wrapper = null;

public TaskHandler(DocumentHandler parentHandler, TaskContainer container, RuntimeConfigurable parentWrapper, Target target) {
super(parentHandler);
this.container = container;
this.parentWrapper = parentWrapper;
this.target = target;
}

public void init(String tag, AttributeList attrs) throws SAXParseException {
try {
task = (Task)project.createInRole(container, tag);
} catch (BuildException e) {
// swallow here, will be thrown again in
// UnknownElement.maybeConfigure if the problem persists.
}

if (task == null) {
task = new UnknownElement(tag);
task.setProject(project);
task.setTaskType(tag);
task.setTaskName(tag);
container.addTask(task);
}

task.setLocation(new Location(buildFile.toString(),
locator.getLineNumber(),
locator.getColumnNumber()));
configureId(task, attrs);

task.setOwningTarget(target);
task.init();
wrapper = task.getRuntimeConfigurableWrapper();
wrapper.setAttributes(attrs);
if (parentWrapper != null) {
parentWrapper.addChild(wrapper);
}
}

protected void finished() {
if (container instanceof InmediateTarget) {
((InmediateTarget)container).execute();
}
}

public void characters(char[] buf, int start, int end) throws SAXParseException {
if (wrapper == null) {
try {
addText(project, task, buf, start, end);
} catch (BuildException exc) {
throw new SAXParseException(exc.getMessage(), locator, exc);
}
} else {
wrapper.addText(buf, start, end);
}
}

public void startElement(String name, AttributeList attrs) throws SAXParseException {
if (task instanceof TaskContainer) {
// task can contain other tasks - no other nested elements possible
new TaskHandler(this, (TaskContainer)task, wrapper, target).init(name, attrs);
}
else {
new NestedElementHandler(this, task, wrapper, target).init(name, attrs);
}
}
}

/**
* Handler for all nested properties.
*/
private class NestedElementHandler extends AbstractHandler {
private Object parent;
private Object child;
private RuntimeConfigurable parentWrapper;
private RuntimeConfigurable childWrapper = null;
private Target target;

public NestedElementHandler(DocumentHandler parentHandler,
Object parent,
RuntimeConfigurable parentWrapper,
Target target) {
super(parentHandler);

if (parent instanceof RoleAdapter) {
this.parent = ((RoleAdapter) parent).getProxy();
} else {
this.parent = parent;
}
this.parentWrapper = parentWrapper;
this.target = target;
}

public void init(String propType, AttributeList attrs) throws SAXParseException {
Class parentClass = parent.getClass();
IntrospectionHelper ih =
IntrospectionHelper.getHelper(parentClass);

try {
String elementName = propType.toLowerCase(Locale.US);
if (parent instanceof UnknownElement) {
UnknownElement uc = new UnknownElement(elementName);
uc.setProject(project);
((UnknownElement) parent).addChild(uc);
// Set this parameters just in case is a Task
uc.setTaskType(elementName);
uc.setTaskName(elementName);
child = uc;
} else {
child = ih.createElement(project, parent, elementName);
}

configureId(child, attrs);

if (parentWrapper != null) {
childWrapper = new RuntimeConfigurable(child, propType);
childWrapper.setAttributes(attrs);
parentWrapper.addChild(childWrapper);
} else {
configure(child, attrs, project);
ih.storeElement(project, parent, child, elementName);
}
} catch (BuildException exc) {
throw new SAXParseException(exc.getMessage(), locator, exc);
}
}

public void characters(char[] buf, int start, int end) throws SAXParseException {
if (parentWrapper == null) {
try {
addText(project, child, buf, start, end);
} catch (BuildException exc) {
throw new SAXParseException(exc.getMessage(), locator, exc);
}
} else {
childWrapper.addText(buf, start, end);
}
}

public void startElement(String name, AttributeList attrs) throws SAXParseException {
if (child instanceof TaskContainer) {
// taskcontainer nested element can contain other tasks - no other
// nested elements possible
new TaskHandler(this, (TaskContainer)child, childWrapper, target).init(name, attrs);
}
else {
new NestedElementHandler(this, child, childWrapper, target).init(name, attrs);
}
}
}

/**
* Special target type for top level Tasks and Datatypes.
* This will allow eliminating special cases.
*/
private class InmediateTarget extends Target {
/**
* Create a target for a top level task or datatype.
* @param name the name of the task to be run on this target.
*/
InmediateTarget(String name) {
super();
setProject(project);
setName("Top level " + name);
}
}

public static void configure(Object target, AttributeList attrs,
Project project) throws BuildException {
if( target instanceof RoleAdapter ) {
target=((RoleAdapter)target).getProxy();
}

IntrospectionHelper ih =
IntrospectionHelper.getHelper(target.getClass());

project.addBuildListener(ih);

for (int i = 0; i < attrs.getLength(); i++) {
// reflect these into the target
String value=replaceProperties(project, attrs.getValue(i),
project.getProperties() );
try {
ih.setAttribute(project, target,
attrs.getName(i).toLowerCase(Locale.US), value);

} catch (BuildException be) {
// id attribute must be set externally
if (!attrs.getName(i).equals("id")) {
throw be;
}
}
}
}

/**
* Adds the content of #PCDATA sections to an element.
*/
public static void addText(Project project, Object target, char[] buf, int start, int end)
throws BuildException {
addText(project, target, new String(buf, start, end));
}

/**
* Adds the content of #PCDATA sections to an element.
*/
public static void addText(Project project, Object target, String text)
throws BuildException {

if (text == null ) {
return;
}

if(target instanceof RoleAdapter) {
target = ((RoleAdapter) target).getProxy();
}

IntrospectionHelper.getHelper(target.getClass()).addText(project, target, text);
}

/**
* Stores a configured child element into its parent object
*/
public static void storeChild(Project project, Object parent, Object child, String tag) {
IntrospectionHelper ih = IntrospectionHelper.getHelper(parent.getClass());
ih.storeElement(project, parent, child, tag);
}

/**
* Replace ${} style constructions in the given value with the string value of
* the corresponding data types.
*
* @param value the string to be scanned for property references.
* @since 1.5
*/
public static String replaceProperties(Project project, String value)
throws BuildException {
return project.replaceProperties(value);
}

/**
* Replace ${} style constructions in the given value with the string value of
* the corresponding data types.
*
* @param value the string to be scanned for property references.
*/
public static String replaceProperties(Project project, String value, Hashtable keys)
throws BuildException {
if (value == null) {
return null;
}

Vector fragments = new Vector();
Vector propertyRefs = new Vector();
parsePropertyString(value, fragments, propertyRefs);

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 (!keys.containsKey(propertyName)) {
project.log("Property ${" + propertyName + "} has not been set", Project.MSG_VERBOSE);
}
fragment = (keys.containsKey(propertyName)) ? (String) keys.get(propertyName)
: "${" + propertyName + "}";
}
sb.append(fragment);
}
return sb.toString();
}

/**
* This method will parse a string containing ${value} style
* property values into two lists. The first list is a collection
* of text fragments, while the other is a set of string property names
* null entries in the first list indicate a property reference from the
* second list.
*/
public static void parsePropertyString(String value, Vector fragments, Vector propertyRefs)
throws BuildException {
int prev = 0;
int pos;
while ((pos = value.indexOf("$", prev)) >= 0) {
if (pos > 0) {
fragments.addElement(value.substring(prev, pos));
}

if( pos == (value.length() - 1)) {
fragments.addElement("$");
prev = pos + 1;
}
else if (value.charAt(pos + 1) != '{' ) {
fragments.addElement(value.substring(pos + 1, pos + 2));
prev = pos + 2;
} else {
int endName = value.indexOf('}', pos);
if (endName < 0) {
throw new BuildException("Syntax error in property: "
+ value );
}
String propertyName = value.substring(pos + 2, endName);
fragments.addElement(null);
propertyRefs.addElement(propertyName);
prev = endName + 1;
}
}

if (prev < value.length()) {
fragments.addElement(value.substring(prev));
}
}

private static SAXParserFactory getParserFactory() {
if (parserFactory == null) {
parserFactory = SAXParserFactory.newInstance();
}

return parserFactory;
}

/**
* Scan AttributeList for the id attribute and maybe add a
* reference to project.
*
* <p>Moved out of {@link #configure configure} to make it happen
* at parser time.</p>
*/
private void configureId(Object target, AttributeList attr) {
String id = attr.getValue("id");
if (id != null) {
if( target instanceof RoleAdapter ) {
((RoleAdapter)target).setId(id);
}
project.addReference(id, target);
}
}

}

+ 5
- 0
proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java View File

@@ -55,6 +55,11 @@ package org.apache.tools.ant;

public interface RoleAdapter {

/**
* Obtain the id in case it is needed.
*/
public void setId(String id);

/**
* Set the object being adapted.
* @param o the object being adapted


+ 211
- 143
proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java View File

@@ -54,6 +54,7 @@
package org.apache.tools.ant;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
@@ -96,8 +97,8 @@ public class SymbolTable {
* from that defined in the calling Project.
* @param p the calling project
*/
public SymbolTable(Project p) {
parentTable = p.getSymbols();
public SymbolTable(SymbolTable st) {
parentTable = st;
}

/**
@@ -108,6 +109,54 @@ public class SymbolTable {
this.project = p;
}

/**
* Get the specified loader for the project.
* @param name the name of the loader
* @return the corresponding ANT classloader
*/
private AntClassLoader getLoader(String name) {
AntClassLoader cl = (AntClassLoader) loaders.get(name);
if (cl == null && parentTable != null) {
return parentTable.getLoader(name);
}
return cl;
}

/**
* Add the specified class-path to a loader.
* If the loader is defined in an ancestor project then a new
* classloader inheritin from the one already existing
* will be created, otherwise the path willbe added to the existing
* ClassLoader.
* @param name the name of the loader to use.
* @param clspath the path to be added to the classloader
*/
public ClassLoader addToLoader(String name, Path clspath) {
// Find if the loader is already defined in the current project
AntClassLoader cl = (AntClassLoader) loaders.get(name);
if (cl == null) {
// Is it inherited from the calling project
if (parentTable != null) {
cl = parentTable.getLoader(name);
}
cl = new AntClassLoader(cl, project, clspath, true);
loaders.put(name, cl);
}
else {
// Add additional path to the existing definition
String[] pathElements = clspath.list();
for (int i = 0; i < pathElements.length; ++i) {
try {
cl.addPathElement(pathElements[i]);
}
catch (BuildException e) {
// ignore path elements invalid relative to the project
}
}
}
return cl;
}
/**
* Find all the roles supported by a Class
* on this symbol table.
@@ -133,13 +182,13 @@ public class SymbolTable {
list.addElement(role);
}
}
if (parentTable != null) findRoles(clz, list);
if (parentTable != null) parentTable.findRoles(clz, list);
}
/**
* Get the Role definition
* @param role the name of the role
* @return the method used to support objects on this role
* @return the Role description
*/
public Role getRole(String role) {
Role r = (Role) roles.get(role);
@@ -171,112 +220,6 @@ public class SymbolTable {
return (old != null);
}

/**
* Verify if the interface is valid.
* @param clz the interface to validate
* @return the method defined by the interface
*/
private Method validInterface(Class clz) {
Method m[] = clz.getDeclaredMethods();
if (m.length == 1
&& java.lang.Void.TYPE.equals(m[0].getReturnType())) {
Class args[] = m[0].getParameterTypes();
if (args.length == 1
&& !java.lang.String.class.equals(args[0])
&& !args[0].isArray()
&& !args[0].isPrimitive()) {
return m[0];
}
else {
throw new BuildException("Invalid role interface method in: "
+ clz.getName());
}
}
else {
throw new BuildException("More than one method on role interface");
}
}

/**
* Verify if the adapter is valid with respect to the interface.
* @param clz the class adapter to validate
* @param mtd the method whose only argument must match
* @return the static method to use for validating adaptees
*/
private Method validAdapter(Class clz, Method mtd) {
if (clz == null) return null;
checkClass(clz);
if (!mtd.getParameterTypes()[0].isAssignableFrom(clz)) {
String msg = "Adapter " + clz.getName() +
" is incompatible with role interface " +
mtd.getDeclaringClass().getName();
throw new BuildException(msg);
}
String msg = "Class " + clz.getName() + " is not an adapter: ";
if (!RoleAdapter.class.isAssignableFrom(clz)) {
throw new BuildException(msg + "does not implement RoleAdapter");
}
try {
Method chk = clz.getMethod("checkClass", CHECK_ADAPTER_PARAMS);
if (!Modifier.isStatic(chk.getModifiers())) {
throw new BuildException(msg + "checkClass() is not static");
}
return chk;
}
catch(NoSuchMethodException nme){
throw new BuildException(msg + "checkClass() not found", nme);
}
}

/**
* Get the specified loader for the project.
* @param name the name of the loader
* @return the corresponding ANT classloader
*/
private AntClassLoader getLoader(String name) {
AntClassLoader cl = (AntClassLoader) loaders.get(name);
if (cl == null && parentTable != null) {
return parentTable.getLoader(name);
}
return cl;
}

/**
* Add the specified class-path to a loader.
* If the loader is defined in an ancestor project then a new
* classloader inheritin from the one already existing
* will be created, otherwise the path willbe added to the existing
* ClassLoader.
* @param name the name of the loader to use.
* @param clspath the path to be added to the classloader
*/
public ClassLoader addToLoader(String name, Path clspath) {
// Find if the loader is already defined in the current project
AntClassLoader cl = (AntClassLoader) loaders.get(name);
if (cl == null) {
// Is it inherited from the calling project
if (parentTable != null) {
cl = parentTable.getLoader(name);
}
cl = new AntClassLoader(cl, project, clspath, true);
loaders.put(name, cl);
}
else {
// Add additional path to the existing definition
String[] pathElements = clspath.list();
for (int i = 0; i < pathElements.length; ++i) {
try {
cl.addPathElement(pathElements[i]);
}
catch (BuildException e) {
// ignore path elements invalid relative to the project
}
}
}
return cl;
}
/**
* Add a new type of element to a role.
* @param role the role for this Class.
@@ -291,13 +234,13 @@ public class SymbolTable {
throw new BuildException("Unknown role: " + role);
}
// Check if it is already defined
Class old = get(role, name);
Factory old = get(role, name);
if (old != null) {
if (old.equals(clz)) {
if (old.getOriginalClass().equals(clz)) {
project.log("Ignoring override for "+ role + " " + name
+ ", it is already defined by the same class.",
project.MSG_VERBOSE);
return old;
return old.getOriginalClass();
}
else {
project.log("Trying to override old definition of " +
@@ -305,26 +248,33 @@ public class SymbolTable {
project.MSG_WARN);
}
}
checkClass(clz);
Factory f = checkClass(clz);
// Check that the Class is compatible with the role definition
r.verifyAdaptability(role, clz);
f = r.verifyAdaptability(role, f);
// Record the new type
Hashtable defTable = (Hashtable)defs.get(role);
if (defTable == null) {
defTable = new Hashtable();
defs.put(role, defTable);
}
defTable.put(name, clz);
return old;
defTable.put(name, f);

String msg =
" +User " + role + ": " + name + " " + clz.getName();
project.log(msg, project.MSG_DEBUG);
return (old != null ? old.getOriginalClass() : null);
}

/**
* Checks a class, whether it is suitable for serving in ANT.
* @return the factory to use when instantiating the class
* @throws BuildException and logs as Project.MSG_ERR for
* conditions, that will cause execution to fail.
*/
void checkClass(final Class clz)
Factory checkClass(final Class clz) // Package on purpose
throws BuildException {
if (clz == null) return null;

if(!Modifier.isPublic(clz.getModifiers())) {
final String message = clz + " is not public";
project.log(message, Project.MSG_ERR);
@@ -342,8 +292,37 @@ public class SymbolTable {
// getConstructor finds public constructors only.
try {
clz.getConstructor(new Class[0]);
return new Factory(){
public Object create(Project p) {
try {
return clz.newInstance();
}
catch(Exception e) {
throw new BuildException(e);
}
}

public Class getOriginalClass() {
return clz;
}
};
} catch (NoSuchMethodException nse) {
clz.getConstructor(new Class[] {Project.class});
final Constructor c =
clz.getConstructor(new Class[] {Project.class});
return new Factory(){
public Object create(Project p) {
try {
return c.newInstance(new Object[]{p});
}
catch(Exception e) {
throw new BuildException(e);
}
}

public Class getOriginalClass() {
return clz;
}
};
}
} catch(NoSuchMethodException e) {
final String message =
@@ -359,11 +338,11 @@ public class SymbolTable {
* @param name the name of the element to sea
* @return the Class implementation
*/
public Class get(String role, String name) {
public Factory get(String role, String name) {
Hashtable defTable = (Hashtable)defs.get(role);
if (defTable != null) {
Class clz = (Class)defTable.get(name);
if (clz != null) return clz;
Factory f = (Factory)defTable.get(name);
if (f != null) return f;
}
if (parentTable != null) {
return parentTable.get(role, name);
@@ -372,19 +351,12 @@ public class SymbolTable {
}

/**
* Get a Hashtable that is usable for manipulating Tasks,
* Get a Hashtable that is usable for manipulating elements on Role.
* @param role the role of the elements in the table
* @return a Hashtable that delegates to the Symbol table.
*/
public Hashtable getTaskDefinitions() {
return new SymbolHashtable("task");
}

/**
* Get a Hashtable that is usable for manipulating Datatypes,
* @return a Hashtable that delegates to the Symbol table.
*/
public Hashtable getDataTypeDefinitions() {
return new SymbolHashtable("datatype");
Hashtable getDefinitions(String role) { // package scope on purpose
return new SymbolHashtable(role);
}

/**
@@ -402,16 +374,43 @@ public class SymbolTable {
}

public synchronized Object get(Object key) {
return SymbolTable.this.get(role, (String)key);
Factory f = SymbolTable.this.get(role, (String)key);
return (f == null? null : f.getOriginalClass());
}
}

/**
* Factory for creating ANT objects.
* Class objects are not instanciated directly but through a Factory
* which is able to resolve issues such as proxys and such.
*/
public static interface Factory {
/**
* Creates an object for the Role
* @param the project in which it is created
* @return the instantiated object with a proxy if necessary
*/
public Object create(Project p);

/**
* Creates an object for the Role, adapted if necessary
* for a particular interface.
*/
// public Object adaptFor(Class clz, Project p, Object o);

/**
* The original class of the object without proxy.
*/
public Class getOriginalClass();
}

/**
* The definition of a role
*/
public class Role {
private Method interfaceMethod;
private Method adapterVerifier;
private Factory adapterFactory;
/**
* Creates a new Role object
@@ -420,6 +419,7 @@ public class SymbolTable {
*/
Role(Class roleClz, Class adapterClz) {
interfaceMethod = validInterface(roleClz);
adapterFactory = checkClass(adapterClz);
adapterVerifier = validAdapter(adapterClz, interfaceMethod);
}

@@ -433,12 +433,11 @@ public class SymbolTable {
/**
* Instantiate a new adapter for this role.
*/
public RoleAdapter createAdapter() {
if (adapterVerifier == null) return null;
public RoleAdapter createAdapter(Project p) {
if (adapterFactory == null) return null;
try {
return (RoleAdapter)
adapterVerifier.getDeclaringClass().newInstance();
return (RoleAdapter) adapterFactory.create(p);
}
catch(BuildException be) {
throw be;
@@ -451,11 +450,12 @@ public class SymbolTable {
/**
* Verify if the class can be adapted to use by the role
* @param role the name of the role to verify
* @param clz the class to verify
* @param f the factory for the class to verify
*/
public void verifyAdaptability(String role, Class clz) {
public Factory verifyAdaptability(String role, final Factory f) {
final Class clz = f.getOriginalClass();
if (interfaceMethod.getParameterTypes()[0].isAssignableFrom(clz)) {
return;
return f;
}
if (adapterVerifier == null) {
String msg = "Class " + clz.getName() +
@@ -464,8 +464,18 @@ public class SymbolTable {
}
try {
try {
adapterVerifier.invoke(null,
new Object[]{clz, project});
adapterVerifier.invoke(null, new Object[]{clz, project});
return new Factory(){
public Object create(Project p) {
RoleAdapter ra = createAdapter(p);
ra.setProxy(f.create(p));
return ra;
}

public Class getOriginalClass() {
return clz;
}
};
}
catch (InvocationTargetException ite) {
throw ite.getTargetException();
@@ -487,5 +497,63 @@ public class SymbolTable {
public boolean isImplementedBy(Class clz) {
return interfaceMethod.getDeclaringClass().isAssignableFrom(clz);
}

/**
* Verify if the interface is valid.
* @param clz the interface to validate
* @return the method defined by the interface
*/
private Method validInterface(Class clz) {
Method m[] = clz.getDeclaredMethods();
if (m.length == 1
&& java.lang.Void.TYPE.equals(m[0].getReturnType())) {
Class args[] = m[0].getParameterTypes();
if (args.length == 1
&& !java.lang.String.class.equals(args[0])
&& !args[0].isArray()
&& !args[0].isPrimitive()) {
return m[0];
}
else {
throw new BuildException("Invalid role interface method in: "
+ clz.getName());
}
}
else {
throw new BuildException("More than one method on role interface");
}
}
/**
* Verify if the adapter is valid with respect to the interface.
* @param clz the class adapter to validate
* @param mtd the method whose only argument must match
* @return the static method to use for validating adaptees
*/
private Method validAdapter(Class clz, Method mtd) {
if (clz == null) return null;
if (!mtd.getParameterTypes()[0].isAssignableFrom(clz)) {
String msg = "Adapter " + clz.getName() +
" is incompatible with role interface " +
mtd.getDeclaringClass().getName();
throw new BuildException(msg);
}
String msg = "Class " + clz.getName() + " is not an adapter: ";
if (!RoleAdapter.class.isAssignableFrom(clz)) {
throw new BuildException(msg + "does not implement RoleAdapter");
}
try {
Method chk = clz.getMethod("checkClass", CHECK_ADAPTER_PARAMS);
if (!Modifier.isStatic(chk.getModifiers())) {
throw new BuildException(msg + "checkClass() is not static");
}
return chk;
}
catch(NoSuchMethodException nme){
throw new BuildException(msg + "checkClass() not found", nme);
}
}
}
}

+ 1
- 0
proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java View File

@@ -165,4 +165,5 @@ public class TaskAdapter extends Task implements RoleAdapter {
return this.proxy ;
}

public void setId(String id) {}
}

+ 1
- 23
proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java View File

@@ -138,10 +138,8 @@ public class Ant extends Task {
}

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

private void reinit() {
@@ -185,26 +183,6 @@ public class Ant extends Task {
}
}

// 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) {


+ 7
- 30
proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java View File

@@ -418,7 +418,7 @@ public class Antlib extends Task {
if (classpath != null) {
clspath.append(classpath);
}
return project.getSymbols().addToLoader(loaderId, clspath);
return project.addToLoader(loaderId, clspath);
}


@@ -505,8 +505,6 @@ public class Antlib extends Task {

private int level = 0;

private SymbolTable symbols = null;

private String name = null;
private String className = null;
private String adapter = null;
@@ -520,7 +518,6 @@ public class Antlib extends Task {
AntLibraryHandler(ClassLoader classloader, Properties als) {
this.classloader = classloader;
this.aliasMap = als;
this.symbols = project.getSymbols();
}

/**
@@ -591,15 +588,15 @@ public class Antlib extends Task {

try {
if ("role".equals(tag)) {
if (isRoleInUse(name)) {
if (project.isRoleDefined(name)) {
String msg = "Cannot override role: " + name;
log(msg, Project.MSG_WARN);
return;
}
// Defining a new role
symbols.addRole(name, loadClass(className),
(adapter == null?
null : loadClass(adapter)));
project.addRoleDefinition(name, loadClass(className),
(adapter == null?
null : loadClass(adapter)));
return;
}

@@ -610,12 +607,12 @@ public class Antlib extends Task {
name = alias;
}
//catch an attempted override of an existing name
if (!override && isInUse(tag, name)) {
if (!override && project.isDefinedOnRole(tag, name)) {
String msg = "Cannot override " + tag + ": " + name;
log(msg, Project.MSG_WARN);
return;
}
symbols.add(tag, name, loadClass(className));
project.addDefinitionOnRole(tag, name, loadClass(className));
}
catch(BuildException be) {
throw new SAXParseException(be.getMessage(), locator, be);
@@ -651,26 +648,6 @@ public class Antlib extends Task {
}
}

/**
* test for a name being in use already on this role
*
* @param name the name to test
* @return true if it is a task or a datatype
*/
private boolean isInUse(String role, String name) {
return (symbols.get(role, name) != null);
}

/**
* test for a role name being in use already
*
* @param name the name to test
* @return true if it is a task or a datatype
*/
private boolean isRoleInUse(String name) {
return (symbols.getRole(name) != null);
}

//end inner class AntLibraryHandler
}



+ 21
- 3
proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java View File

@@ -66,6 +66,7 @@ import org.apache.tools.ant.*;
public class DataTypeAdapterTask extends Task implements RoleAdapter {

Object proxy;
String id = null;
/**
* Checks a class, whether it is suitable to be adapted.
@@ -83,14 +84,27 @@ public class DataTypeAdapterTask extends Task implements RoleAdapter {
* Do the execution.
*/
public void execute() throws BuildException {
if (id != null) {
// Need to re-register this reference
// The container has register the Adapter instead
project.addReference(id, proxy);
}
}

/**
* Propagate configuration of Project
*/
public void setProject(Project p) {
super.setProject(p);

// Check to see if the DataType has a setProject method to set
if (proxy instanceof ProjectComponent) {
((ProjectComponent)proxy).setProject(project);
((ProjectComponent)proxy).setProject(p);
return;
}

// This may not be needed
// We are trying to set project even it is was not declared
// We are trying to set project even if is was not declared
// just like TaskAdapter does for beans, this is not done
// by the original code
Method setProjectM = null;
@@ -99,7 +113,7 @@ public class DataTypeAdapterTask extends Task implements RoleAdapter {
setProjectM =
c.getMethod( "setProject", new Class[] {Project.class});
if(setProjectM != null) {
setProjectM.invoke(proxy, new Object[] {project});
setProjectM.invoke(proxy, new Object[] {p});
}
} catch (NoSuchMethodException e) {
// ignore this if the class being used as a task does not have
@@ -122,4 +136,8 @@ public class DataTypeAdapterTask extends Task implements RoleAdapter {
return this.proxy ;
}

public void setId(String id) {
log("Setting adapter id to: " + id, Project.MSG_DEBUG);
this.id = id;
}
}

+ 30
- 0
proposal/sandbox/antlib/src/testcases/build.xml View File

@@ -0,0 +1,30 @@
<?xml version="1.0"?>

<project name="local" default="libs" >

<taskdef name="antjar" classname="org.apache.tools.ant.taskdefs.Antjar" />
<property name="src" location="." />
<property name="classes" location="../../build/testcases" />
<property name="contrib" location="../../build/case_contrib.jar" />

<target name="libs" depends="compile" >
<antjar destfile="${contrib}" antxml="${src}/case-antlib.xml" >
<fileset dir="${classes}" >
<include name="org/**" />
</fileset>
</antjar>
</target>

<target name="compile" >
<mkdir dir="${classes}" />
<javac srcdir="${src}" destdir="${classes}" >
<include name="org/**/*.java" />
</javac>
</target>

<target name="clean" >
<delete dir="${classes}" />
<delete file='${contrib}'/>
</target>

</project>

+ 5
- 0
proposal/sandbox/antlib/src/testcases/case-antlib.xml View File

@@ -0,0 +1,5 @@
<?xml version="1.0" ?>

<antlib version="1.0" >
<task name="case" class="org.apache.ant.contrib.Case" />
</antlib>

+ 39
- 0
proposal/sandbox/antlib/src/testcases/case.xml View File

@@ -0,0 +1,39 @@
<?xml version="1.0"?>

<project name="case-test" default="test" basedir=".">

<property name="value" value="task.xml" />

<target name="init">
<taskdef name="antlib" classname="org.apache.tools.ant.taskdefs.Antlib" />
<antlib file="../../build/case_contrib.jar" />
</target>

<target name="test" depends="init,case,test1,test2,test3">
<echo message="Value=${value}" />
</target>

<target name="case" >
<case property="value" >
<when value="task.xml" property="value.xml" />
</case>
<case property="location" >
<when value="loc" property="location.fail" />
<when value="" property="location.fail" />
<else property="location.unset" />
</case>
</target>

<target name="test1" if="value.xml">
<echo message="Value equals to itself" />
</target>

<target name="test2" if="location.fail">
<fail message="Location passed" />
</target>

<target name="test3" if="location.unset">
<echo message="Location does not exists" />
</target>

</project>

+ 169
- 0
proposal/sandbox/antlib/src/testcases/org/apache/ant/contrib/Case.java View File

@@ -0,0 +1,169 @@
/*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*/

package org.apache.ant.contrib;

import java.io.*;
import java.util.*;
import org.apache.tools.ant.*;
import org.apache.tools.ant.types.*;

/**
* Will set one of the given properties depending on the result of testing
* the value of another property.
*
* <!ELEMENT case (when*, else) >
* <!ATTLIST case property CDATA #REQUIRED > The name of the property to test
* <!ELEMENT when EMPTY >
* <!ATTLIST when value CDATA #REQUIRED > The value to compare and set prop.
* <!ATTLIST when property CDATA #REQUIRED > The name of the property to set
* <!ELEMENT else EMPTY >
* <!ATTLIST else property CDATA #REQUIRED > The name of the property to set otherwise
* <!ATTLIST else value CDATA #IMPLIED > The value to set; default "true".
*
* @author Jose Alberto Fernandez <a href="mailto:jfernandez@viquity.com">jfernandez@viquity.com</a>
*/

public class Case extends Task {

public class When {
private String property;
private String value;

public void setProperty(String name) {
property = name;
}

public String getProperty() {
return property;
}

public void setValue(String val) {
value = val;
}

public String getValue() {
return value;
}

public boolean tryCase(String caseValue) throws BuildException {
if (property == null)
throw new BuildException("Property attribute is mandatory");

if (value == null)
throw new BuildException("Value attribute is mandatory");

if (!value.equals(caseValue)) return false;

if (getProject().getProperty(property) == null) {
getProject().setProperty(property, value);
} else {
log("Override ignored for " + property, Project.MSG_VERBOSE);
}
return true;
}

public void doElse() throws BuildException {
if (property == null)
throw new BuildException("Property attribute is mandatory");

String elseValue = (value == null) ? "true" : value;

if (getProject().getProperty(property) == null) {
getProject().setProperty(property, elseValue);
} else {
log("Override ignored for " + property, Project.MSG_VERBOSE);
}
}
}
private String caseProperty;
private Vector whenList = new Vector();
private When elseCase = null;

public When createWhen() throws BuildException {
When w = new When();
whenList.addElement(w);
return w;
}

public When createElse() throws BuildException {
if (elseCase != null)
throw new BuildException("Only one else element allowed per case");

return (elseCase = new When());
}

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

public void execute() throws BuildException {
if (caseProperty == null) {
throw new BuildException("property attribute is required",
location);
}
String caseValue = getProject().getProperty(caseProperty);

for (Enumeration e = whenList.elements(); e.hasMoreElements(); ) {
When w = (When)e.nextElement();
if (w.tryCase(caseValue)) return;
}
if (elseCase != null)
elseCase.doElse();
}

}

Loading…
Cancel
Save