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();
 

Reply via email to