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