Hi,
the appended patch will move ${} expansion (and the execution of all
tasks, including <property>) to runtime instead of parser time.
As there are so many ways this could be implemented I wanted to make
sure I'm not totally of the hook and have introduced silly design
flaws. Therefore I wanted to get you comments before I commit
anything.
The patched version has been working for me (bootstrapped Ant and
built Tomcat as well as my private projects successfully).
<project name="test" default="echo">
<target name="echo1">
<echo message="${echo1}" />
</target>
<property name="echo1" value="set globally" />
<target name="echo2" depends="setup2">
<echo message="${echo2}" />
</target>
<target name="setup2">
<property name="echo2" value="set after first use" />
</target>
<target name="setup3a">
<property name="echo3" value="set in unused target" />
</target>
<target name="setup3">
<property name="echo3" value="set before first use" />
</target>
<target name="echo3" depends="setup3">
<echo message="${echo3}" />
</target>
<target name="echo" depends="echo1,echo2,echo3" />
</project>
will now generate
echo1:
set globally
setup2:
echo2:
set after first use
setup3:
echo3:
set before first use
echo:
instead of
echo1:
${echo1}
setup2:
echo2:
${echo2}
setup3:
echo3:
set in unused target
echo:
Comments please (including how stupid I am if this should be true).
Stefan
diff -uNr jakarta-ant/src/main/org/apache/tools/ant/ProjectHelper.java ant.runtime/src/main/org/apache/tools/ant/ProjectHelper.java
--- jakarta-ant/src/main/org/apache/tools/ant/ProjectHelper.java Fri Sep 8 11:04:38 2000
+++ ant.runtime/src/main/org/apache/tools/ant/ProjectHelper.java Fri Sep 8 12:31:59 2000
@@ -354,6 +354,7 @@
private class TaskHandler extends AbstractHandler {
private Target target;
private Task task;
+ private RuntimeConfigurable wrapper = null;
public TaskHandler(DocumentHandler parentHandler, Target target) {
super(parentHandler);
@@ -363,35 +364,37 @@
public void init(String tag, AttributeList attrs) throws SAXParseException {
task = project.createTask(tag);
- configure(task, attrs);
+
task.setLocation(new Location(buildFile.toString(), locator.getLineNumber(), locator.getColumnNumber()));
+ configureId(task, attrs);
task.init();
// Top level tasks don't have associated targets
if (target != null) {
task.setOwningTarget(target);
target.addTask(task);
+ wrapper = task.getRuntimeConfigurableWrapper();
+ wrapper.setAttributes(attrs);
} else {
+ configure(task, attrs, project);
task.execute();
}
}
public void characters(char[] buf, int start, int end) throws SAXParseException {
- String text = new String(buf, start, end).trim();
- if (text.length() == 0) return;
-
- IntrospectionHelper ih =
- IntrospectionHelper.getHelper(task.getClass());
-
- try {
- ih.addText(task, text);
- } catch (BuildException exc) {
- throw new SAXParseException(exc.getMessage(), locator, exc);
+ if (wrapper == null) {
+ try {
+ addText(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 {
- new NestedElementHandler(this, task).init(name, attrs);
+ new NestedElementHandler(this, task, wrapper).init(name, attrs);
}
}
@@ -401,11 +404,16 @@
private class NestedElementHandler extends AbstractHandler {
private Object target;
private Object child;
+ private RuntimeConfigurable parentWrapper;
+ private RuntimeConfigurable childWrapper = null;
- public NestedElementHandler(DocumentHandler parentHandler, Object target) {
+ public NestedElementHandler(DocumentHandler parentHandler,
+ Object target,
+ RuntimeConfigurable parentWrapper) {
super(parentHandler);
this.target = target;
+ this.parentWrapper = parentWrapper;
}
public void init(String propType, AttributeList attrs) throws SAXParseException {
@@ -415,28 +423,34 @@
try {
child = ih.createElement(target, propType.toLowerCase());
- configure(child, attrs);
+ configureId(child, attrs);
+
+ if (parentWrapper != null) {
+ childWrapper = new RuntimeConfigurable(child);
+ childWrapper.setAttributes(attrs);
+ parentWrapper.addChild(childWrapper);
+ } else {
+ configure(child, attrs, project);
+ }
} catch (BuildException exc) {
throw new SAXParseException(exc.getMessage(), locator, exc);
}
}
public void characters(char[] buf, int start, int end) throws SAXParseException {
- String text = new String(buf, start, end).trim();
- if (text.length() == 0) return;
-
- IntrospectionHelper ih =
- IntrospectionHelper.getHelper(child.getClass());
-
- try {
- ih.addText(child, text);
- } catch (BuildException exc) {
- throw new SAXParseException(exc.getMessage(), locator, exc);
+ if (parentWrapper == null) {
+ try {
+ addText(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 {
- new NestedElementHandler(this, child).init(name, attrs);
+ new NestedElementHandler(this, child, childWrapper).init(name, attrs);
}
}
@@ -457,32 +471,28 @@
throw new BuildException("Unknown data type "+propType);
}
- configure(element, attrs);
+ configureId(element, attrs);
+ configure(element, attrs, project);
} catch (BuildException exc) {
throw new SAXParseException(exc.getMessage(), locator, exc);
}
}
public void characters(char[] buf, int start, int end) throws SAXParseException {
- String text = new String(buf, start, end).trim();
- if (text.length() == 0) return;
-
- IntrospectionHelper ih =
- IntrospectionHelper.getHelper(element.getClass());
-
try {
- ih.addText(element, text);
+ addText(element, buf, start, end);
} catch (BuildException exc) {
throw new SAXParseException(exc.getMessage(), locator, exc);
}
}
public void startElement(String name, AttributeList attrs) throws SAXParseException {
- new NestedElementHandler(this, element).init(name, attrs);
+ new NestedElementHandler(this, element, null).init(name, attrs);
}
}
- private void configure(Object target, AttributeList attrs) throws BuildException {
+ public static void configure(Object target, AttributeList attrs,
+ Project project) throws BuildException {
if( target instanceof TaskAdapter )
target=((TaskAdapter)target).getProxy();
@@ -498,18 +508,38 @@
attrs.getName(i).toLowerCase(), value);
} catch (BuildException be) {
- if (attrs.getName(i).equals("id")) {
- project.addReference(attrs.getValue(i), target);
- } else {
- be.setLocation(new Location(buildFile.toString(),
- locator.getLineNumber(),
- locator.getColumnNumber()));
+ // 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(Object target, char[] buf, int start, int end)
+ throws BuildException {
+ addText(target, new String(buf, start, end).trim());
+ }
+
+ /**
+ * Adds the content of #PCDATA sections to an element.
+ */
+ public static void addText(Object target, String text)
+ throws BuildException {
+
+ if (text == null || text.length() == 0) {
+ return;
+ }
+
+ if(target instanceof TaskAdapter)
+ target = ((TaskAdapter) target).getProxy();
+
+ IntrospectionHelper.getHelper(target.getClass()).addText(target, text);
+ }
+
/** Replace ${NAME} with the property value
*/
@@ -562,4 +592,19 @@
return parserFactory;
}
+
+ /**
+ * Scan AttributeList for the id attribute and maybe add a
+ * reference to project.
+ *
+ * <p>Moved out of [EMAIL PROTECTED] #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) {
+ project.addReference(id, target);
+ }
+ }
+
}
diff -uNr jakarta-ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java ant.runtime/src/main/org/apache/tools/ant/RuntimeConfigurable.java
--- jakarta-ant/src/main/org/apache/tools/ant/RuntimeConfigurable.java Thu Jan 1 01:00:00 1970
+++ ant.runtime/src/main/org/apache/tools/ant/RuntimeConfigurable.java Fri Sep 8 12:03:31 2000
@@ -0,0 +1,130 @@
+/*
+ * The Apache Software License, Version 1.1
+ *
+ * Copyright (c) 2000 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 [EMAIL PROTECTED]
+ *
+ * 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.util.Enumeration;
+import java.util.Vector;
+import org.xml.sax.AttributeList;
+import org.xml.sax.helpers.AttributeListImpl;
+
+/**
+ * Wrapper class that holds the attributes of a Task (or elements
+ * nested below that level) and takes care of configuring that element
+ * at runtime.
+ *
+ * @author <a href="[EMAIL PROTECTED]">Stefan Bodewig</a>
+ */
+public class RuntimeConfigurable {
+
+ private Vector children = new Vector();
+ private Object wrappedObject = null;
+ private AttributeList attributes;
+ private StringBuffer characters = new StringBuffer();
+
+ /**
+ * @param proxy The element to wrap.
+ */
+ public RuntimeConfigurable(Object proxy) {
+ wrappedObject = proxy;
+ }
+
+ /**
+ * Set's the attributes for the wrapped element.
+ */
+ public void setAttributes(AttributeList attributes) {
+ this.attributes = new AttributeListImpl(attributes);
+ }
+
+ /**
+ * Adds child elements to the wrapped element.
+ */
+ public void addChild(RuntimeConfigurable child) {
+ children.addElement(child);
+ }
+
+ /**
+ * Add characters from #PCDATA areas to the wrapped element.
+ */
+ public void addText(String data) {
+ characters.append(data);
+ }
+
+ /**
+ * Add characters from #PCDATA areas to the wrapped element.
+ */
+ public void addText(char[] buf, int start, int end) {
+ addText(new String(buf, start, end).trim());
+ }
+
+ /**
+ * Configure the wrapped element and all children.
+ */
+ public void maybeConfigure(Project p) throws BuildException {
+ if (attributes != null) {
+ ProjectHelper.configure(wrappedObject, attributes, p);
+ attributes = null;
+ }
+ if (characters.length() != 0) {
+ ProjectHelper.addText(wrappedObject, characters.toString());
+ characters.setLength(0);
+ }
+ Enumeration enum = children.elements();
+ while (enum.hasMoreElements()) {
+ RuntimeConfigurable child = (RuntimeConfigurable) enum.nextElement();
+ child.maybeConfigure(p);
+ }
+ }
+
+}
diff -uNr jakarta-ant/src/main/org/apache/tools/ant/Target.java ant.runtime/src/main/org/apache/tools/ant/Target.java
--- jakarta-ant/src/main/org/apache/tools/ant/Target.java Tue Aug 22 17:38:33 2000
+++ ant.runtime/src/main/org/apache/tools/ant/Target.java Fri Sep 8 11:49:07 2000
@@ -138,6 +138,7 @@
try {
project.fireTaskStarted(task);
+ task.maybeConfigure();
task.execute();
project.fireTaskFinished(task, null);
}
diff -uNr jakarta-ant/src/main/org/apache/tools/ant/Task.java ant.runtime/src/main/org/apache/tools/ant/Task.java
--- jakarta-ant/src/main/org/apache/tools/ant/Task.java Wed Jul 12 13:51:30 2000
+++ ant.runtime/src/main/org/apache/tools/ant/Task.java Fri Sep 8 12:05:05 2000
@@ -68,6 +68,7 @@
protected Location location = Location.UNKNOWN_LOCATION;
protected String taskName = null;
protected String taskType = null;
+ protected RuntimeConfigurable wrapper;
/**
* Sets the project object of this task. This method is used by
@@ -192,6 +193,25 @@
*/
public void setLocation(Location location) {
this.location = location;
+ }
+
+ /**
+ * Returns the wrapper class for runtime configuration.
+ */
+ public RuntimeConfigurable getRuntimeConfigurableWrapper() {
+ if (wrapper == null) {
+ wrapper = new RuntimeConfigurable(this);
+ }
+ return wrapper;
+ }
+
+ /**
+ * Configure this task - if it hasn't been done already.
+ */
+ public void maybeConfigure() throws BuildException {
+ if (wrapper != null) {
+ wrapper.maybeConfigure(project);
+ }
}
}
diff -uNr jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Available.java ant.runtime/src/main/org/apache/tools/ant/taskdefs/Available.java
--- jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Available.java Thu Aug 10 14:58:54 2000
+++ ant.runtime/src/main/org/apache/tools/ant/taskdefs/Available.java Fri Sep 8 11:39:49 2000
@@ -94,7 +94,7 @@
this.resource = resource;
}
- public void init() throws BuildException {
+ public void execute() throws BuildException {
if ((classname != null) && !checkClass(classname)) return;
if ((file != null) && !checkFile(file)) return;
if ((resource != null) && !checkResource(resource)) return;
diff -uNr jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Filter.java ant.runtime/src/main/org/apache/tools/ant/taskdefs/Filter.java
--- jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Filter.java Sun Feb 13 19:24:18 2000
+++ ant.runtime/src/main/org/apache/tools/ant/taskdefs/Filter.java Fri Sep 8 11:40:17 2000
@@ -75,7 +75,7 @@
this.value = value;
}
- public void init() throws BuildException {
+ public void execute() throws BuildException {
project.addFilter(token, value);
}
}
diff -uNr jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Property.java ant.runtime/src/main/org/apache/tools/ant/taskdefs/Property.java
--- jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Property.java Mon Sep 4 15:28:32 2000
+++ ant.runtime/src/main/org/apache/tools/ant/taskdefs/Property.java Fri Sep 8 11:40:03 2000
@@ -107,7 +107,7 @@
return resource;
}
- public void init() throws BuildException {
+ public void execute() throws BuildException {
try {
if ((name != null) && (value != null)) {
addProperty(name, value);
diff -uNr jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Tstamp.java ant.runtime/src/main/org/apache/tools/ant/taskdefs/Tstamp.java
--- jakarta-ant/src/main/org/apache/tools/ant/taskdefs/Tstamp.java Fri Feb 11 17:17:03 2000
+++ ant.runtime/src/main/org/apache/tools/ant/taskdefs/Tstamp.java Fri Sep 8 11:40:30 2000
@@ -67,7 +67,7 @@
*/
public class Tstamp extends Task {
- public void init() throws BuildException {
+ public void execute() throws BuildException {
try {
Date d = new Date();