" + 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) {}
+}
diff --git a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java
index 7ffdd1849..ab3a23b84 100644
--- a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java
+++ b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/Project.java
@@ -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);
}
diff --git a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/ProjectHelper.java b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/ProjectHelper.java
new file mode 100644
index 000000000..f832bce3e
--- /dev/null
+++ b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/ProjectHelper.java
@@ -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
+ * .
+ */
+
+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.
+ *
+ * Moved out of {@link #configure configure} to make it happen
+ * at parser time.
+ */
+ 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);
+ }
+ }
+
+}
diff --git a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java
index e028ebe92..57c1f62d9 100644
--- a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java
+++ b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/RoleAdapter.java
@@ -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
diff --git a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java
index e6b8c843b..e009e1438 100644
--- a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java
+++ b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/SymbolTable.java
@@ -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);
+ }
+ }
+
}
}
diff --git a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java
index 213f4f014..d083a92bd 100644
--- a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java
+++ b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/TaskAdapter.java
@@ -165,4 +165,5 @@ public class TaskAdapter extends Task implements RoleAdapter {
return this.proxy ;
}
+ public void setId(String id) {}
}
diff --git a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java
index 13d42380d..5d6d8d699 100644
--- a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java
+++ b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Ant.java
@@ -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) {
diff --git a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java
index c084791c4..924b81315 100644
--- a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java
+++ b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/taskdefs/Antlib.java
@@ -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
}
diff --git a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java
index 18dc7c542..4669a0789 100644
--- a/proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java
+++ b/proposal/sandbox/antlib/src/main/org/apache/tools/ant/types/DataTypeAdapterTask.java
@@ -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;
+ }
}
diff --git a/proposal/sandbox/antlib/src/testcases/build.xml b/proposal/sandbox/antlib/src/testcases/build.xml
new file mode 100644
index 000000000..e0f34f241
--- /dev/null
+++ b/proposal/sandbox/antlib/src/testcases/build.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/proposal/sandbox/antlib/src/testcases/case-antlib.xml b/proposal/sandbox/antlib/src/testcases/case-antlib.xml
new file mode 100644
index 000000000..e5ae1e847
--- /dev/null
+++ b/proposal/sandbox/antlib/src/testcases/case-antlib.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/proposal/sandbox/antlib/src/testcases/case.xml b/proposal/sandbox/antlib/src/testcases/case.xml
new file mode 100644
index 000000000..b289106d5
--- /dev/null
+++ b/proposal/sandbox/antlib/src/testcases/case.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/proposal/sandbox/antlib/src/testcases/org/apache/ant/contrib/Case.java b/proposal/sandbox/antlib/src/testcases/org/apache/ant/contrib/Case.java
new file mode 100644
index 000000000..e2363eb71
--- /dev/null
+++ b/proposal/sandbox/antlib/src/testcases/org/apache/ant/contrib/Case.java
@@ -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
+ * .
+ */
+
+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.
+ *
+ *
+ * The name of the property to test
+ *
+ * The value to compare and set prop.
+ * The name of the property to set
+ *
+ * The name of the property to set otherwise
+ * The value to set; default "true".
+ *
+ * @author Jose Alberto Fernandez jfernandez@viquity.com
+ */
+
+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();
+ }
+
+}