This has a lot of consequences, most notably those listed in WHATSNEW. This also affects the behavior of several other tasks like <available> who will now do their job at runtime instead of parser time as well. I've changed the version number in build.xml to reflect this bigger change. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@267988 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -3,6 +3,16 @@ Changes from Ant 1.1 to the current sources | |||
| Changes that could break older environments: | |||
| -------------------------------------------- | |||
| * Semantics of <property> has changed again in the hope to be more | |||
| intuitive. ${} expansion now happens at runtime and <property> tags | |||
| living inside of targets only take effect if they are visited at | |||
| runtime. | |||
| As a side effect of this change, task's attributes get set at runtime | |||
| not at parser time as well, which might change the results of | |||
| <script>s or other custom tasks that reference other tasks by their id | |||
| attribute. | |||
| * copying of support files in <javac> has been removed - as well as | |||
| the filtering attribute. | |||
| @@ -12,7 +12,7 @@ | |||
| <property name="Name" value="Ant"/> | |||
| <property name="name" value="ant"/> | |||
| <property name="version" value="1.2alpha"/> | |||
| <property name="version" value="1.2alpha2"/> | |||
| <property name="ant.home" value="."/> | |||
| <property name="src.bin.dir" value="src/bin"/> | |||
| @@ -25,7 +25,7 @@ | |||
| <li>Dave Walend (<a href="mailto:dwalend@cs.tufts.edu">dwalend@cs.tufts.edu</a>)</li> | |||
| </ul> | |||
| <p>Version 1.2 - 2000/09/07</p> | |||
| <p>Version 1.2 - 2000/09/14</p> | |||
| <hr> | |||
| <h2>Table of Contents</h2> | |||
| @@ -4778,8 +4778,44 @@ output. | |||
| throws a <code>BuildException</code>. This method implements the task | |||
| itself.</li> | |||
| </ol> | |||
| <p>It is important to know that Ant first calls the setters for the attributes | |||
| it encounters for a specific task in the buildfile, before it executes is.</p> | |||
| <h3>The life cycle of a task</h3> | |||
| <ol> | |||
| <li>The task gets instantiated using a no-arg constructor at parser | |||
| time. This means even tasks that are never executed get | |||
| instantiated.</li> | |||
| <li>The tasks gets references to its project and location inside the | |||
| build file via their inherited <code>project</code> and | |||
| <code>location</code> variables.</li> | |||
| <li>If the user specified an id attribute to this task, the project | |||
| registers a reference to this newly created task - at parser | |||
| time.</li> | |||
| <li><code>init()</code> is called at parser time.</li> | |||
| <li>The task gets a reference to the target it belongs to via its | |||
| inherited <code>target</code> variable.</li> | |||
| <li>All child elements of the XML element corresponding to this task | |||
| are created via this task's <code>createXXX()</code> methods or | |||
| instantiated and added to this task via its <code>addXXX()</code> | |||
| methods - at parser time.</li> | |||
| <li>All attributes of this task get set via their corresponding | |||
| <code>setXXX</code> methods - at runtime.</li> | |||
| <li>The content character data sections inside the XML element | |||
| corresponding to this task is added to the task via its | |||
| <code>addText</code> method - at runtime.</li> | |||
| <li>All attributes of all child elements get set via their corresponding | |||
| <code>setXXX</code> methods - at runtime.</li> | |||
| <li><code>execute()</code> is called at runtime.</li> | |||
| </ol> | |||
| <h3>Example</h3> | |||
| <p>Let's write our own task, that prints a message on the System.out stream. The | |||
| task has one attribute called "message".</p> | |||
| <blockquote> | |||
| @@ -354,6 +354,7 @@ public class ProjectHelper { | |||
| 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 class ProjectHelper { | |||
| 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 @@ public class ProjectHelper { | |||
| 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 @@ public class ProjectHelper { | |||
| 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 @@ public class ProjectHelper { | |||
| 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 @@ public class ProjectHelper { | |||
| 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 @@ public class ProjectHelper { | |||
| return parserFactory; | |||
| } | |||
| /** | |||
| * Scan AttributeList for the id attribute and maybe add a | |||
| * reference to project. | |||
| * | |||
| * <p>Moved out of {@link #configure configure} to make it happen | |||
| * at parser time.</p> | |||
| */ | |||
| private void configureId(Object target, AttributeList attr) { | |||
| String id = attr.getValue("id"); | |||
| if (id != null) { | |||
| project.addReference(id, target); | |||
| } | |||
| } | |||
| } | |||
| @@ -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 apache@apache.org. | |||
| * | |||
| * 5. Products derived from this software may not be called "Apache" | |||
| * nor may "Apache" appear in their names without prior written | |||
| * permission of the Apache Group. | |||
| * | |||
| * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED | |||
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | |||
| * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR | |||
| * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||
| * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||
| * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF | |||
| * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | |||
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |||
| * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |||
| * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |||
| * SUCH DAMAGE. | |||
| * ==================================================================== | |||
| * | |||
| * This software consists of voluntary contributions made by many | |||
| * individuals on behalf of the Apache Software Foundation. For more | |||
| * information on the Apache Software Foundation, please see | |||
| * <http://www.apache.org/>. | |||
| */ | |||
| package org.apache.tools.ant; | |||
| import java.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="stefan.bodewig@megabit.net">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); | |||
| } | |||
| } | |||
| } | |||
| @@ -138,6 +138,7 @@ public class Target { | |||
| try { | |||
| project.fireTaskStarted(task); | |||
| task.maybeConfigure(); | |||
| task.execute(); | |||
| project.fireTaskFinished(task, null); | |||
| } | |||
| @@ -68,6 +68,7 @@ public abstract class Task { | |||
| 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 | |||
| @@ -193,5 +194,24 @@ public abstract class Task { | |||
| 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); | |||
| } | |||
| } | |||
| } | |||
| @@ -94,7 +94,7 @@ public class Available extends Task { | |||
| 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; | |||
| @@ -75,7 +75,7 @@ public class Filter extends Task { | |||
| this.value = value; | |||
| } | |||
| public void init() throws BuildException { | |||
| public void execute() throws BuildException { | |||
| project.addFilter(token, value); | |||
| } | |||
| } | |||
| @@ -107,7 +107,7 @@ public class Property extends Task { | |||
| return resource; | |||
| } | |||
| public void init() throws BuildException { | |||
| public void execute() throws BuildException { | |||
| try { | |||
| if ((name != null) && (value != null)) { | |||
| addProperty(name, value); | |||
| @@ -67,7 +67,7 @@ import java.text.*; | |||
| */ | |||
| public class Tstamp extends Task { | |||
| public void init() throws BuildException { | |||
| public void execute() throws BuildException { | |||
| try { | |||
| Date d = new Date(); | |||