diff --git a/src/main/org/apache/tools/ant/IntrospectionHelper.java b/src/main/org/apache/tools/ant/IntrospectionHelper.java
index 549adbca0..2b28d8b4f 100644
--- a/src/main/org/apache/tools/ant/IntrospectionHelper.java
+++ b/src/main/org/apache/tools/ant/IntrospectionHelper.java
@@ -352,32 +352,32 @@ public final class IntrospectionHelper {
* null
.
* @param value The value to set the attribute to. This may be interpreted
* or converted to the necessary type if the setter method
- * doesn't just take a string. Must not be null
.
+ * doesn't accept an object of the supplied type.
*
* @exception BuildException if the introspected class doesn't support
* the given attribute, or if the setting
* method fails.
*/
public void setAttribute(Project p, Object element, String attributeName,
- String value) throws BuildException {
+ Object value) throws BuildException {
AttributeSetter as = (AttributeSetter) attributeSetters.get(
attributeName.toLowerCase(Locale.US));
- if (as == null) {
+ if (as == null && value != null) {
if (element instanceof DynamicAttributeNS) {
DynamicAttributeNS dc = (DynamicAttributeNS) element;
String uriPlusPrefix = ProjectHelper.extractUriFromComponentName(attributeName);
String uri = ProjectHelper.extractUriFromComponentName(uriPlusPrefix);
String localName = ProjectHelper.extractNameFromComponentName(attributeName);
String qName = "".equals(uri) ? localName : uri + ":" + localName;
- dc.setDynamicAttribute(uri, localName, qName, value);
+ dc.setDynamicAttribute(uri, localName, qName, value.toString());
return;
}
if (element instanceof DynamicAttribute) {
DynamicAttribute dc = (DynamicAttribute) element;
- dc.setDynamicAttribute(attributeName.toLowerCase(Locale.US), value);
+ dc.setDynamicAttribute(attributeName.toLowerCase(Locale.US), value.toString());
return;
}
- if (attributeName.indexOf(':') != -1) {
+ if (attributeName.indexOf(':') >= 0) {
return; // Ignore attribute from unknown uri's
}
String msg = getElementName(p, element)
@@ -385,7 +385,7 @@ public final class IntrospectionHelper {
throw new UnsupportedAttributeException(msg, attributeName);
}
try {
- as.set(p, element, value);
+ as.setObject(p, element, value);
} catch (IllegalAccessException ie) {
// impossible as getMethods should only return public methods
throw new BuildException(ie);
@@ -394,6 +394,29 @@ public final class IntrospectionHelper {
}
}
+ /**
+ * Sets the named attribute in the given element, which is part of the
+ * given project.
+ *
+ * @param p The project containing the element. This is used when files
+ * need to be resolved. Must not be null
.
+ * @param element The element to set the attribute in. Must not be
+ * null
.
+ * @param attributeName The name of the attribute to set. Must not be
+ * null
.
+ * @param value The value to set the attribute to. This may be interpreted
+ * or converted to the necessary type if the setter method
+ * doesn't just take a string. Must not be null
.
+ *
+ * @exception BuildException if the introspected class doesn't support
+ * the given attribute, or if the setting
+ * method fails.
+ */
+ public void setAttribute(Project p, Object element, String attributeName,
+ String value) throws BuildException {
+ setAttribute(p, element, attributeName, (Object) value);
+ }
+
/**
* Adds PCDATA to an element, using the element's
* void addText(String)
method, if it has one. If no
@@ -921,7 +944,7 @@ public final class IntrospectionHelper {
// simplest case - setAttribute expects String
if (java.lang.String.class.equals(reflectedArg)) {
- return new AttributeSetter(m) {
+ return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException {
m.invoke(parent, (Object[]) new String[] { value });
@@ -930,7 +953,7 @@ public final class IntrospectionHelper {
}
// char and Character get special treatment - take the first character
if (java.lang.Character.class.equals(reflectedArg)) {
- return new AttributeSetter(m) {
+ return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException {
if (value.length() == 0) {
@@ -943,7 +966,7 @@ public final class IntrospectionHelper {
}
// boolean and Boolean get special treatment because we have a nice method in Project
if (java.lang.Boolean.class.equals(reflectedArg)) {
- return new AttributeSetter(m) {
+ return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException {
m.invoke(parent, (Object[]) new Boolean[] {
@@ -953,7 +976,7 @@ public final class IntrospectionHelper {
}
// Class doesn't have a String constructor but a decent factory method
if (java.lang.Class.class.equals(reflectedArg)) {
- return new AttributeSetter(m) {
+ return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException {
try {
@@ -966,7 +989,7 @@ public final class IntrospectionHelper {
}
// resolve relative paths through Project
if (java.io.File.class.equals(reflectedArg)) {
- return new AttributeSetter(m) {
+ return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException {
m.invoke(parent, new Object[] { p.resolveFile(value) });
@@ -975,7 +998,7 @@ public final class IntrospectionHelper {
}
// EnumeratedAttributes have their own helper class
if (EnumeratedAttribute.class.isAssignableFrom(reflectedArg)) {
- return new AttributeSetter(m) {
+ return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException {
try {
@@ -995,7 +1018,7 @@ public final class IntrospectionHelper {
//ignore
}
if (enumClass != null && enumClass.isAssignableFrom(reflectedArg)) {
- return new AttributeSetter(m) {
+ return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException {
try {
@@ -1020,7 +1043,7 @@ public final class IntrospectionHelper {
};
}
if (java.lang.Long.class.equals(reflectedArg)) {
- return new AttributeSetter(m) {
+ return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException {
try {
@@ -1059,7 +1082,7 @@ public final class IntrospectionHelper {
final boolean finalIncludeProject = includeProject;
final Constructor finalConstructor = c;
- return new AttributeSetter(m) {
+ return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException {
try {
@@ -1307,9 +1330,28 @@ public final class IntrospectionHelper {
*/
private abstract static class AttributeSetter {
private Method method; // the method called to set the attribute
-
- protected AttributeSetter(Method m) {
+ private Class type;
+ protected AttributeSetter(Method m, Class type) {
method = m;
+ this.type = type;
+ }
+ void setObject(Project p, Object parent, Object value)
+ throws InvocationTargetException, IllegalAccessException, BuildException {
+ if (type != null) {
+ Class useType = type;
+ if (type.isPrimitive()) {
+ if (value == null) {
+ throw new BuildException("Attempt to set primitive "
+ + method.getName().substring(4) + " to null on " + parent);
+ }
+ useType = (Class) PRIMITIVE_TYPE_MAP.get(type);
+ }
+ if (value == null || useType.isInstance(value)) {
+ method.invoke(parent, new Object[] { value });
+ return;
+ }
+ }
+ set(p, parent, value.toString());
}
abstract void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException;
diff --git a/src/main/org/apache/tools/ant/Project.java b/src/main/org/apache/tools/ant/Project.java
index ae5bc76c6..e02af0837 100644
--- a/src/main/org/apache/tools/ant/Project.java
+++ b/src/main/org/apache/tools/ant/Project.java
@@ -526,8 +526,7 @@ public class Project implements ResourceFactory {
* @since 1.5
*/
public void setNewProperty(String name, String value) {
- PropertyHelper.getPropertyHelper(this).setNewProperty(null, name,
- value);
+ PropertyHelper.getPropertyHelper(this).setNewProperty(name, value);
}
/**
@@ -540,8 +539,7 @@ public class Project implements ResourceFactory {
* @see #setProperty(String,String)
*/
public void setUserProperty(String name, String value) {
- PropertyHelper.getPropertyHelper(this).setUserProperty(null, name,
- value);
+ PropertyHelper.getPropertyHelper(this).setUserProperty(name, value);
}
/**
@@ -557,8 +555,7 @@ public class Project implements ResourceFactory {
* @see #setProperty(String,String)
*/
public void setInheritedProperty(String name, String value) {
- PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
- ph.setInheritedProperty(null, name, value);
+ PropertyHelper.getPropertyHelper(this).setInheritedProperty(name, value);
}
/**
@@ -570,8 +567,7 @@ public class Project implements ResourceFactory {
* @param value The property value. Must not be null
.
*/
private void setPropertyInternal(String name, String value) {
- PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
- ph.setProperty(null, name, value, false);
+ PropertyHelper.getPropertyHelper(this).setProperty(name, value, false);
}
/**
@@ -584,8 +580,7 @@ public class Project implements ResourceFactory {
* or if a null
name is provided.
*/
public String getProperty(String propertyName) {
- PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
- return (String) ph.getProperty(null, propertyName);
+ return (String) PropertyHelper.getPropertyHelper(this).getProperty(propertyName);
}
/**
@@ -602,10 +597,8 @@ public class Project implements ResourceFactory {
* @exception BuildException if the given value has an unclosed
* property name, e.g. ${xxx
.
*/
- public String replaceProperties(String value)
- throws BuildException {
- PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
- return ph.replaceProperties(null, value, null);
+ public String replaceProperties(String value) throws BuildException {
+ return PropertyHelper.getPropertyHelper(this).replaceProperties(null, value, null);
}
/**
@@ -618,8 +611,7 @@ public class Project implements ResourceFactory {
* or if a null
name is provided.
*/
public String getUserProperty(String propertyName) {
- PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
- return (String) ph.getUserProperty(null, propertyName);
+ return (String) PropertyHelper.getPropertyHelper(this).getUserProperty(propertyName);
}
/**
@@ -628,8 +620,7 @@ public class Project implements ResourceFactory {
* (including user properties).
*/
public Hashtable getProperties() {
- PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
- return ph.getProperties();
+ return PropertyHelper.getPropertyHelper(this).getProperties();
}
/**
@@ -637,8 +628,7 @@ public class Project implements ResourceFactory {
* @return a hashtable containing just the user properties.
*/
public Hashtable getUserProperties() {
- PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
- return ph.getUserProperties();
+ return PropertyHelper.getPropertyHelper(this).getUserProperties();
}
/**
@@ -654,8 +644,7 @@ public class Project implements ResourceFactory {
* @since Ant 1.5
*/
public void copyUserProperties(Project other) {
- PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
- ph.copyUserProperties(other);
+ PropertyHelper.getPropertyHelper(this).copyUserProperties(other);
}
/**
@@ -671,8 +660,7 @@ public class Project implements ResourceFactory {
* @since Ant 1.5
*/
public void copyInheritedProperties(Project other) {
- PropertyHelper ph = PropertyHelper.getPropertyHelper(this);
- ph.copyInheritedProperties(other);
+ PropertyHelper.getPropertyHelper(this).copyInheritedProperties(other);
}
/**
@@ -1978,13 +1966,13 @@ public class Project implements ResourceFactory {
// Check for old id behaviour
ret = resolveIdReference(key, this);
if (ret == null && !key.equals(MagicNames.REFID_PROPERTY_HELPER)) {
- Vector p = new Vector();
- PropertyHelper.getPropertyHelper(this).parsePropertyString(
- key, new Vector(), p);
- if (p.size() == 1) {
- log("Unresolvable reference " + key
- + " might be a misuse of property expansion syntax.",
- MSG_WARN);
+ try {
+ if (PropertyHelper.getPropertyHelper(this).containsProperties(key)) {
+ log("Unresolvable reference " + key
+ + " might be a misuse of property expansion syntax.", MSG_WARN);
+ }
+ } catch (Exception e) {
+ //ignore
}
}
return ret;
diff --git a/src/main/org/apache/tools/ant/PropertyHelper.java b/src/main/org/apache/tools/ant/PropertyHelper.java
index 48d5e6ead..4ab1951f8 100644
--- a/src/main/org/apache/tools/ant/PropertyHelper.java
+++ b/src/main/org/apache/tools/ant/PropertyHelper.java
@@ -17,7 +17,13 @@
*/
package org.apache.tools.ant;
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
import java.util.Vector;
import java.util.Enumeration;
@@ -34,6 +40,14 @@ import java.util.Enumeration;
Need to discuss this and find if we need more.
*/
+/* update for impending Ant 1.8:
+
+ - I can't see any reason for ns and would like to deprecate it.
+ - Replacing chaining with delegates for certain behavioral aspects.
+ - Object value seems valuable as outlined.
+
+ */
+
/** NOT FINAL. API MAY CHANGE
*
* Deals with properties - substitution, dynamic properties, etc.
@@ -45,8 +59,87 @@ import java.util.Enumeration;
*/
public class PropertyHelper {
+ /**
+ * Marker interface for a PropertyHelper delegate.
+ * @since Ant 1.8
+ */
+ public interface Delegate {
+ }
+
+ /**
+ * Describes an entity capable of evaluating a property name for value.
+ * @since Ant 1.8
+ */
+ public interface PropertyEvaluator extends Delegate {
+ /**
+ * Evaluate a property.
+ * @param property the property's String "identifier".
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return Object value.
+ */
+ Object evaluate(String property, PropertyHelper propertyHelper);
+ }
+
+ /**
+ * Describes an entity capable of expanding properties embedded in a string.
+ * @since Ant 1.8
+ */
+ public interface PropertyExpander extends Delegate {
+ /**
+ * Parse the next property name.
+ * @param s the String to parse.
+ * @param pos the ParsePosition in use.
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return parsed String if any, else null
.
+ */
+ String parsePropertyName(String s, ParsePosition pos, PropertyHelper propertyHelper);
+ }
+
+ private static final PropertyEvaluator TO_STRING = new PropertyEvaluator() {
+ private String prefix = "toString:";
+ public Object evaluate(String property, PropertyHelper propertyHelper) {
+ Object o = null;
+ if (property.startsWith(prefix) && propertyHelper.getProject() != null) {
+ o = propertyHelper.getProject().getReference(property.substring(prefix.length()));
+ }
+ return o == null ? null : o.toString();
+ }
+ };
+
+ private static final PropertyExpander DEFAULT_EXPANDER = new PropertyExpander() {
+ public String parsePropertyName(String s, ParsePosition pos, PropertyHelper propertyHelper) {
+ int index = pos.getIndex();
+ if (s.indexOf("${", index) == index) {
+ int end = s.indexOf('}', index);
+ if (end < 0) {
+ throw new BuildException("Syntax error in property: " + s);
+ }
+ int start = index + 2;
+ pos.setIndex(end + 1);
+ return s.substring(start, end);
+ }
+ return null;
+ }
+ };
+
+ /** dummy */
+ private static final PropertyExpander SKIP_$$ = new PropertyExpander() {
+ /**
+ * {@inheritDoc}
+ * @see org.apache.tools.ant.PropertyHelper.PropertyExpander#parsePropertyName(java.lang.String, java.text.ParsePosition, org.apache.tools.ant.PropertyHelper)
+ */
+ public String parsePropertyName(String s, ParsePosition pos, PropertyHelper propertyHelper) {
+ int index = pos.getIndex();
+ if (s.indexOf("$$", index) == index) {
+ pos.setIndex(++index);
+ }
+ return null;
+ }
+ };
+
private Project project;
private PropertyHelper next;
+ private Hashtable delegates = new Hashtable();
/** Project properties map (usually String to String). */
private Hashtable properties = new Hashtable();
@@ -55,7 +148,6 @@ public class PropertyHelper {
* Map of "user" properties (as created in the Ant task, for example).
* Note that these key/value pairs are also always put into the
* project properties, so only the project properties need to be queried.
- * Mapping is String to String.
*/
private Hashtable userProperties = new Hashtable();
@@ -63,7 +155,6 @@ public class PropertyHelper {
* Map of inherited "user" properties - that are those "user"
* properties that have been created by tasks and not been set
* from the command line or a GUI tool.
- * Mapping is String to String.
*/
private Hashtable inheritedProperties = new Hashtable();
@@ -71,6 +162,9 @@ public class PropertyHelper {
* Default constructor.
*/
protected PropertyHelper() {
+ add(TO_STRING);
+ add(SKIP_$$);
+ add(DEFAULT_EXPANDER);
}
//override facility for subclasses to put custom hashtables in
@@ -94,7 +188,8 @@ public class PropertyHelper {
return project;
}
- /** There are 2 ways to hook into property handling:
+ /**
+ * There are 2 ways to hook into property handling:
* - you can replace the main PropertyHelper. The replacement is required
* to support the same semantics (of course :-)
*
@@ -162,6 +257,7 @@ public class PropertyHelper {
* @return true if this helper has stored the property, false if it
* couldn't. Each helper should delegate to the next one (unless it
* has a good reason not to).
+ * @deprecated PropertyHelper chaining is deprecated.
*/
public boolean setPropertyHook(String ns, String name,
Object value,
@@ -177,13 +273,15 @@ public class PropertyHelper {
return false;
}
- /** Get a property. If all hooks return null, the default
+ /**
+ * Get a property. If all hooks return null, the default
* tables will be used.
*
* @param ns namespace of the sought property.
* @param name name of the sought property.
* @param user True if this is a user property.
* @return The property, if returned by a hook, or null if none.
+ * @deprecated PropertyHelper chaining is deprecated.
*/
public Object getPropertyHook(String ns, String name, boolean user) {
if (getNext() != null) {
@@ -225,6 +323,7 @@ public class PropertyHelper {
* @exception BuildException if the string contains an opening
* ${
without a closing
* }
+ * @deprecated We can do better than this.
*/
public void parsePropertyString(String value, Vector fragments,
Vector propertyRefs) throws BuildException {
@@ -250,58 +349,150 @@ public class PropertyHelper {
* null
if the original string is null
.
*/
public String replaceProperties(String ns, String value, Hashtable keys) throws BuildException {
- if (value == null || value.indexOf('$') == -1) {
+ return replaceProperties(value);
+ }
+
+ /**
+ * Replaces ${xxx}
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.
+ * May be null
, in which case this
+ * method returns immediately with no effect.
+ *
+ * @exception BuildException if the string contains an opening
+ * ${
without a closing
+ * }
+ * @return the original string with the properties replaced, or
+ * null
if the original string is null
.
+ */
+ public String replaceProperties(String value) throws BuildException {
+ Object o = parseProperties(value);
+ return o == null || o instanceof String ? (String) o : o.toString();
+ }
+
+ /**
+ * Decode properties from a String representation. If the entire
+ * contents of the String resolve to a single property, that value
+ * is returned. Otherwise a String is returned.
+ *
+ * @param value The string to be scanned for property references.
+ * May be null
, in which case this
+ * method returns immediately with no effect.
+ *
+ * @exception BuildException if the string contains an opening
+ * ${
without a closing
+ * }
+ * @return the original string with the properties replaced, or
+ * null
if the original string is null
.
+ */
+ public Object parseProperties(String value) throws BuildException {
+ if (value == null || "".equals(value)) {
return value;
}
- Vector fragments = new Vector();
- Vector propertyRefs = new Vector();
- parsePropertyString(value, fragments, propertyRefs);
+ ParsePosition pos = new ParsePosition(0);
+ Object o = parseNextProperty(value, pos);
+ if (o != null && pos.getIndex() == value.length()) {
+ return o;
+ }
+ StringBuffer sb = new StringBuffer(value.length() * 2);
+ if (o == null) {
+ sb.append(value.charAt(pos.getIndex()));
+ pos.setIndex(pos.getIndex() + 1);
+ } else {
+ sb.append(o);
+ }
+ while (pos.getIndex() < value.length()) {
+ o = parseNextProperty(value, pos);
+ if (o == null) {
+ sb.append(value.charAt(pos.getIndex()));
+ pos.setIndex(pos.getIndex() + 1);
+ } else {
+ sb.append(o);
+ }
+ }
+ return sb.toString();
+ }
- StringBuffer sb = new StringBuffer();
- Enumeration i = fragments.elements();
- Enumeration j = propertyRefs.elements();
+ /**
+ * Learn whether a String contains replaceable properties.
+ * @param value the String to check.
+ * @return true
if value
contains property notation.
+ */
+ public boolean containsProperties(String value) {
+ if (value == null) {
+ return false;
+ }
+ for (ParsePosition pos = new ParsePosition(0); pos.getIndex() < value.length();) {
+ if (parsePropertyName(value, pos) != null) {
+ return true;
+ }
+ pos.setIndex(pos.getIndex() + 1);
+ }
+ return false;
+ }
- while (i.hasMoreElements()) {
- String fragment = (String) i.nextElement();
- if (fragment == null) {
- String propertyName = (String) j.nextElement();
- Object replacement = null;
+ /**
+ * Return any property that can be parsed from the specified position in the specified String.
+ * @param value String to parse
+ * @param pos ParsePosition
+ * @return Object or null if no property is at the current location.
+ */
+ public Object parseNextProperty(String value, ParsePosition pos) {
+ int start = pos.getIndex();
+ String propertyName = parsePropertyName(value, pos);
+ if (propertyName != null) {
+ Object result = getProperty(propertyName);
+ if (result != null) {
+ return result;
+ }
+ getProject().log("Property \"" + propertyName
+ + "\" has not been set", Project.MSG_VERBOSE);
+ return value.substring(start, pos.getIndex());
+ }
+ return null;
+ }
- // try to get it from the project or keys
- // Backward compatibility
- if (keys != null) {
- replacement = keys.get(propertyName);
- }
- if (replacement == null) {
- replacement = getProperty(ns, propertyName);
- }
- if (replacement == null) {
- project.log("Property \"" + propertyName
- + "\" has not been set", Project.MSG_VERBOSE);
- }
- fragment = (replacement != null)
- ? replacement.toString() : "${" + propertyName + "}";
+ private String parsePropertyName(String value, ParsePosition pos) {
+ for (Iterator iter = getDelegates(PropertyExpander.class).iterator(); iter.hasNext();) {
+ String propertyName = ((PropertyExpander) iter.next()).parsePropertyName(value, pos, this);
+ if (propertyName == null) {
+ continue;
}
- sb.append(fragment);
+ return propertyName;
}
- return sb.toString();
+ return null;
}
// -------------------- Default implementation --------------------
// Methods used to support the default behavior and provide backward
// compatibility. Some will be deprecated, you should avoid calling them.
- /** Default implementation of setProperty. Will be called from Project.
+ /**
+ * Default implementation of setProperty. Will be called from Project.
+ * This is the original 1.5 implementation, with calls to the hook
+ * added.
+ * @param ns The namespace for the property (currently not used).
+ * @param name The name of the property.
+ * @param value The value to set the property to.
+ * @param verbose If this is true output extra log messages.
+ * @return true if the property is set.
+ * @deprecated namespaces are unnecessary.
+ */
+ public boolean setProperty(String ns, String name, Object value, boolean verbose) {
+ return setProperty(name, value, verbose);
+ }
+
+ /**
+ * Default implementation of setProperty. Will be called from Project.
* This is the original 1.5 implementation, with calls to the hook
* added.
- * @param ns The namespace for the property (currently not used).
* @param name The name of the property.
* @param value The value to set the property to.
* @param verbose If this is true output extra log messages.
* @return true if the property is set.
*/
- public synchronized boolean setProperty(String ns, String name,
- Object value, boolean verbose) {
+ public synchronized boolean setProperty(String name, Object value, boolean verbose) {
// user (CLI) properties take precedence
if (null != userProperties.get(name)) {
if (verbose) {
@@ -311,10 +502,10 @@ public class PropertyHelper {
return false;
}
- boolean done = setPropertyHook(ns, name, value, false, false, false);
- if (done) {
- return true;
- }
+// boolean done = setPropertyHook(ns, name, value, false, false, false);
+// if (done) {
+// return true;
+// }
if (null != properties.get(name) && verbose) {
project.log("Overriding previous definition of property \"" + name
@@ -342,20 +533,33 @@ public class PropertyHelper {
* @param value The new value of the property.
* Must not be null
.
* @since Ant 1.6
+ * @deprecated namespaces are unnecessary.
*/
- public synchronized void setNewProperty(String ns, String name,
- Object value) {
+ public void setNewProperty(String ns, String name, Object value) {
+ setNewProperty(name, value);
+ }
+
+ /**
+ * Sets a property if no value currently exists. If the property
+ * exists already, a message is logged and the method returns with
+ * no other effect.
+ *
+ * @param name The name of property to set.
+ * Must not be null
.
+ * @param value The new value of the property.
+ * Must not be null
.
+ * @since Ant 1.8
+ */
+ public synchronized void setNewProperty(String name, Object value) {
if (null != properties.get(name)) {
- project.log("Override ignored for property \"" + name
- + "\"", Project.MSG_VERBOSE);
- return;
- }
- boolean done = setPropertyHook(ns, name, value, false, false, true);
- if (done) {
+ project.log("Override ignored for property \"" + name + "\"", Project.MSG_VERBOSE);
return;
}
- project.log("Setting project property: " + name + " -> "
- + value, Project.MSG_DEBUG);
+// boolean done = setPropertyHook(ns, name, value, false, false, true);
+// if (done) {
+// return;
+// }
+ project.log("Setting project property: " + name + " -> " + value, Project.MSG_DEBUG);
if (name != null && value != null) {
properties.put(name, value);
}
@@ -369,17 +573,28 @@ public class PropertyHelper {
* Must not be null
.
* @param value The new value of the property.
* Must not be null
.
+ * @deprecated namespaces are unnecessary.
+ */
+ public void setUserProperty(String ns, String name, Object value) {
+ setUserProperty(name, value);
+ }
+
+ /**
+ * Sets a user property, which cannot be overwritten by
+ * set/unset property calls. Any previous value is overwritten.
+ * @param name The name of property to set.
+ * Must not be null
.
+ * @param value The new value of the property.
+ * Must not be null
.
*/
- public synchronized void setUserProperty(String ns, String name,
- Object value) {
- project.log("Setting ro project property: " + name + " -> "
- + value, Project.MSG_DEBUG);
+ public synchronized void setUserProperty(String name, Object value) {
+ project.log("Setting ro project property: " + name + " -> " + value, Project.MSG_DEBUG);
userProperties.put(name, value);
- boolean done = setPropertyHook(ns, name, value, false, true, false);
- if (done) {
- return;
- }
+// boolean done = setPropertyHook(ns, name, value, false, true, false);
+// if (done) {
+// return;
+// }
properties.put(name, value);
}
@@ -394,19 +609,33 @@ public class PropertyHelper {
* Must not be null
.
* @param value The new value of the property.
* Must not be null
.
+ * @deprecated namespaces are unnecessary.
*/
- public synchronized void setInheritedProperty(String ns, String name,
- Object value) {
+ public void setInheritedProperty(String ns, String name, Object value) {
+ setInheritedProperty(name, value);
+ }
+
+ /**
+ * Sets an inherited user property, which cannot be overwritten by set/unset
+ * property calls. Any previous value is overwritten. Also marks
+ * these properties as properties that have not come from the
+ * command line.
+ *
+ * @param name The name of property to set.
+ * Must not be null
.
+ * @param value The new value of the property.
+ * Must not be null
.
+ */
+ public synchronized void setInheritedProperty(String name, Object value) {
inheritedProperties.put(name, value);
- project.log("Setting ro project property: " + name + " -> "
- + value, Project.MSG_DEBUG);
+ project.log("Setting ro project property: " + name + " -> " + value, Project.MSG_DEBUG);
userProperties.put(name, value);
- boolean done = setPropertyHook(ns, name, value, true, false, false);
- if (done) {
- return;
- }
+// boolean done = setPropertyHook(ns, name, value, true, false, false);
+// if (done) {
+// return;
+// }
properties.put(name, value);
}
@@ -422,17 +651,39 @@ public class PropertyHelper {
* the return value is also null
.
* @return the property value, or null
for no match
* or if a null
name is provided.
+ * @deprecated namespaces are unnecessary.
*/
public synchronized Object getProperty(String ns, String name) {
+ return getProperty(name);
+ }
+
+ /**
+ * Returns the value of a property, if it is set. You can override
+ * this method in order to plug your own storage.
+ *
+ * @param name The name of the property.
+ * May be null
, in which case
+ * the return value is also null
.
+ * @return the property value, or null
for no match
+ * or if a null
name is provided.
+ */
+ public synchronized Object getProperty(String name) {
if (name == null) {
return null;
}
- Object o = getPropertyHook(ns, name, false);
- if (o != null) {
- return o;
+ for (Iterator iter = getDelegates(PropertyEvaluator.class).iterator(); iter.hasNext();) {
+ Object o = ((PropertyEvaluator) iter.next()).evaluate(name, this);
+ if (o != null) {
+ return o;
+ }
}
+// Object o = getPropertyHook(ns, name, false);
+// if (o != null) {
+// return o;
+// }
return properties.get(name);
}
+
/**
* Returns the value of a user property, if it is set.
*
@@ -442,15 +693,32 @@ public class PropertyHelper {
* the return value is also null
.
* @return the property value, or null
for no match
* or if a null
name is provided.
+ * @deprecated namespaces are unnecessary.
*/
- public synchronized Object getUserProperty(String ns, String name) {
+ public Object getUserProperty(String ns, String name) {
+ return getUserProperty(name);
+ }
+
+ /**
+ * Returns the value of a user property, if it is set.
+ *
+ * @param name The name of the property.
+ * May be null
, in which case
+ * the return value is also null
.
+ * @return the property value, or null
for no match
+ * or if a null
name is provided.
+ * @deprecated namespaces are unnecessary.
+ */
+ public synchronized Object getUserProperty(String name) {
if (name == null) {
return null;
}
+/*
Object o = getPropertyHook(ns, name, true);
if (o != null) {
return o;
}
+*/
return userProperties.get(name);
}
@@ -568,7 +836,8 @@ public class PropertyHelper {
// this is used for backward compatibility (for code that calls
// the parse method in ProjectHelper).
- /** Default parsing method. It is here only to support backward compatibility
+ /**
+ * Default parsing method. It is here only to support backward compatibility
* for the static ProjectHelper.parsePropertyString().
*/
static void parsePropertyStringDefault(String value, Vector fragments, Vector propertyRefs)
@@ -624,4 +893,53 @@ public class PropertyHelper {
fragments.addElement(value.substring(prev));
}
}
+
+ /**
+ * Add the specified delegate object to this PropertyHelper.
+ * Delegates are processed in LIFO order.
+ * @param delegate the delegate to add.
+ * @since Ant 1.8
+ */
+ public synchronized void add(Delegate delegate) {
+ List d = getDelegateInterfaces(delegate);
+ for (Iterator iter = d.iterator(); iter.hasNext();) {
+ Object key = iter.next();
+ List list = (List) delegates.get(key);
+ if (list == null) {
+ list = new ArrayList();
+ delegates.put(key, list);
+ }
+ if (!list.contains(delegate)) {
+ list.add(0, delegate);
+ }
+ }
+ }
+
+ /**
+ * Get the Collection of delegates of the specified type.
+ * @param type delegate type.
+ * @return Collection.
+ * @since Ant 1.8
+ */
+ protected synchronized List getDelegates(Class type) {
+ return delegates.containsKey(type)
+ ? (List) new ArrayList((Collection) delegates.get(type)) : Collections.EMPTY_LIST;
+ }
+
+ /**
+ * Get all Delegate interfaces (excluding Delegate itself) from the specified Delegate.
+ * @param d the Delegate to inspect.
+ * @return List
+ * @since Ant 1.8
+ */
+ protected List getDelegateInterfaces(Delegate d) {
+ Class[] c = d.getClass().getInterfaces();
+ ArrayList result = new ArrayList();
+ for (int i = 0; i < c.length; i++) {
+ if (Delegate.class.isAssignableFrom(c[i]) && !Delegate.class.equals(c[i])) {
+ result.add(c[i]);
+ }
+ }
+ return result;
+ }
}
diff --git a/src/main/org/apache/tools/ant/RuntimeConfigurable.java b/src/main/org/apache/tools/ant/RuntimeConfigurable.java
index 91de4cf5d..be1de7e60 100644
--- a/src/main/org/apache/tools/ant/RuntimeConfigurable.java
+++ b/src/main/org/apache/tools/ant/RuntimeConfigurable.java
@@ -387,9 +387,9 @@ public class RuntimeConfigurable implements Serializable {
String value = (String) attributeMap.get(name);
// reflect these into the target
- value = p.replaceProperties(value);
+ Object attrValue = PropertyHelper.getPropertyHelper(p).parseProperties(value);
try {
- ih.setAttribute(p, target, name, value);
+ ih.setAttribute(p, target, name, attrValue);
} catch (UnsupportedAttributeException be) {
// id attribute must be set externally
if (name.equals("id")) {
diff --git a/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java b/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java
new file mode 100644
index 000000000..adfe7bb02
--- /dev/null
+++ b/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+package org.apache.tools.ant.taskdefs;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.MagicNames;
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.PropertyHelper;
+import org.apache.tools.ant.Task;
+
+/**
+ * This task is designed to allow the user to install a different
+ * PropertyHelper on the current Project. This task also allows the
+ * installation of PropertyHelper delegates on either the newly installed
+ * or existing PropertyHelper.
+ * @since Ant 1.8
+ */
+public class PropertyHelperTask extends Task {
+ private PropertyHelper propertyHelper;
+ private List delegates;
+
+ /**
+ * Construct the PropertyHelperTask, which is useless without a Project instance.
+ * @param Project the associated Ant project instance.
+ */
+ public PropertyHelperTask(Project project) {
+ if (project == null) {
+ throw new IllegalArgumentException();
+ }
+ setProject(project);
+ }
+
+ /**
+ * Add a new PropertyHelper to be set on the Project.
+ * @param propertyHelper the PropertyHelper to set.
+ */
+ public synchronized void addConfigured(PropertyHelper propertyHelper) {
+ if (this.propertyHelper != null) {
+ throw new BuildException("Only one PropertyHelper can be installed");
+ }
+ this.propertyHelper = propertyHelper;
+ }
+
+ /**
+ * Add a PropertyHelper delegate to the existing or new PropertyHelper.
+ * @param delegate the delegate to add.
+ */
+ public synchronized void addConfigured(PropertyHelper.Delegate delegate) {
+ if (delegates == null) {
+ delegates = new ArrayList();
+ }
+ delegates.add(delegate);
+ }
+
+ /**
+ * Execute the task.
+ * @throws BuildException on error.
+ */
+ public void execute() throws BuildException {
+ if (propertyHelper == null && delegates == null) {
+ throw new BuildException("Either a new PropertyHelper"
+ + " or one or more PropertyHelper delegates are required");
+ }
+ PropertyHelper ph = propertyHelper;
+ if (ph == null) {
+ ph = (PropertyHelper) getProject().getReference(MagicNames.REFID_PROPERTY_HELPER);
+ } else {
+ ph = propertyHelper;
+ }
+ synchronized (ph) {
+ if (delegates != null) {
+ for (Iterator iter = delegates.iterator(); iter.hasNext();) {
+ PropertyHelper.Delegate delegate = (PropertyHelper.Delegate) iter.next();
+ log("Adding PropertyHelper delegate " + delegate, Project.MSG_DEBUG);
+ ph.add(delegate);
+ }
+ }
+ }
+ if (propertyHelper != null) {
+ log("Installing PropertyHelper " + propertyHelper, Project.MSG_DEBUG);
+ //TODO copy existing properties to new PH?
+ getProject().addReference(MagicNames.REFID_PROPERTY_HELPER, propertyHelper);
+ }
+ }
+
+}
diff --git a/src/main/org/apache/tools/ant/taskdefs/defaults.properties b/src/main/org/apache/tools/ant/taskdefs/defaults.properties
index 49e48b914..6e18840df 100644
--- a/src/main/org/apache/tools/ant/taskdefs/defaults.properties
+++ b/src/main/org/apache/tools/ant/taskdefs/defaults.properties
@@ -57,6 +57,7 @@ patch=org.apache.tools.ant.taskdefs.Patch
pathconvert=org.apache.tools.ant.taskdefs.PathConvert
presetdef=org.apache.tools.ant.taskdefs.PreSetDef
property=org.apache.tools.ant.taskdefs.Property
+propertyhelper=org.apache.tools.ant.taskdefs.PropertyHelperTask
record=org.apache.tools.ant.taskdefs.Recorder
replace=org.apache.tools.ant.taskdefs.Replace
retry=org.apache.tools.ant.taskdefs.Retry