Browse Source

BZ 42736: PropertyHelper API changes

git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@554394 13f79535-47bb-0310-9956-ffa450edef68
master
Matthew Jason Benson 18 years ago
parent
commit
1e599c6651
6 changed files with 576 additions and 122 deletions
  1. +60
    -18
      src/main/org/apache/tools/ant/IntrospectionHelper.java
  2. +19
    -31
      src/main/org/apache/tools/ant/Project.java
  3. +389
    -71
      src/main/org/apache/tools/ant/PropertyHelper.java
  4. +2
    -2
      src/main/org/apache/tools/ant/RuntimeConfigurable.java
  5. +105
    -0
      src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java
  6. +1
    -0
      src/main/org/apache/tools/ant/taskdefs/defaults.properties

+ 60
- 18
src/main/org/apache/tools/ant/IntrospectionHelper.java View File

@@ -352,32 +352,32 @@ public final class IntrospectionHelper {
* <code>null</code>. * <code>null</code>.
* @param value The value to set the attribute to. This may be interpreted * @param value The value to set the attribute to. This may be interpreted
* or converted to the necessary type if the setter method * or converted to the necessary type if the setter method
* doesn't just take a string. Must not be <code>null</code>.
* doesn't accept an object of the supplied type.
* *
* @exception BuildException if the introspected class doesn't support * @exception BuildException if the introspected class doesn't support
* the given attribute, or if the setting * the given attribute, or if the setting
* method fails. * method fails.
*/ */
public void setAttribute(Project p, Object element, String attributeName, public void setAttribute(Project p, Object element, String attributeName,
String value) throws BuildException {
Object value) throws BuildException {
AttributeSetter as = (AttributeSetter) attributeSetters.get( AttributeSetter as = (AttributeSetter) attributeSetters.get(
attributeName.toLowerCase(Locale.US)); attributeName.toLowerCase(Locale.US));
if (as == null) {
if (as == null && value != null) {
if (element instanceof DynamicAttributeNS) { if (element instanceof DynamicAttributeNS) {
DynamicAttributeNS dc = (DynamicAttributeNS) element; DynamicAttributeNS dc = (DynamicAttributeNS) element;
String uriPlusPrefix = ProjectHelper.extractUriFromComponentName(attributeName); String uriPlusPrefix = ProjectHelper.extractUriFromComponentName(attributeName);
String uri = ProjectHelper.extractUriFromComponentName(uriPlusPrefix); String uri = ProjectHelper.extractUriFromComponentName(uriPlusPrefix);
String localName = ProjectHelper.extractNameFromComponentName(attributeName); String localName = ProjectHelper.extractNameFromComponentName(attributeName);
String qName = "".equals(uri) ? localName : uri + ":" + localName; String qName = "".equals(uri) ? localName : uri + ":" + localName;
dc.setDynamicAttribute(uri, localName, qName, value);
dc.setDynamicAttribute(uri, localName, qName, value.toString());
return; return;
} }
if (element instanceof DynamicAttribute) { if (element instanceof DynamicAttribute) {
DynamicAttribute dc = (DynamicAttribute) element; DynamicAttribute dc = (DynamicAttribute) element;
dc.setDynamicAttribute(attributeName.toLowerCase(Locale.US), value);
dc.setDynamicAttribute(attributeName.toLowerCase(Locale.US), value.toString());
return; return;
} }
if (attributeName.indexOf(':') != -1) {
if (attributeName.indexOf(':') >= 0) {
return; // Ignore attribute from unknown uri's return; // Ignore attribute from unknown uri's
} }
String msg = getElementName(p, element) String msg = getElementName(p, element)
@@ -385,7 +385,7 @@ public final class IntrospectionHelper {
throw new UnsupportedAttributeException(msg, attributeName); throw new UnsupportedAttributeException(msg, attributeName);
} }
try { try {
as.set(p, element, value);
as.setObject(p, element, value);
} catch (IllegalAccessException ie) { } catch (IllegalAccessException ie) {
// impossible as getMethods should only return public methods // impossible as getMethods should only return public methods
throw new BuildException(ie); 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 <code>null</code>.
* @param element The element to set the attribute in. Must not be
* <code>null</code>.
* @param attributeName The name of the attribute to set. Must not be
* <code>null</code>.
* @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 <code>null</code>.
*
* @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 * Adds PCDATA to an element, using the element's
* <code>void addText(String)</code> method, if it has one. If no * <code>void addText(String)</code> method, if it has one. If no
@@ -921,7 +944,7 @@ public final class IntrospectionHelper {


// simplest case - setAttribute expects String // simplest case - setAttribute expects String
if (java.lang.String.class.equals(reflectedArg)) { 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) public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException { throws InvocationTargetException, IllegalAccessException {
m.invoke(parent, (Object[]) new String[] { value }); 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 // char and Character get special treatment - take the first character
if (java.lang.Character.class.equals(reflectedArg)) { 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) public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException { throws InvocationTargetException, IllegalAccessException {
if (value.length() == 0) { 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 // boolean and Boolean get special treatment because we have a nice method in Project
if (java.lang.Boolean.class.equals(reflectedArg)) { 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) public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException { throws InvocationTargetException, IllegalAccessException {
m.invoke(parent, (Object[]) new Boolean[] { 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 // Class doesn't have a String constructor but a decent factory method
if (java.lang.Class.class.equals(reflectedArg)) { 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) public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException { throws InvocationTargetException, IllegalAccessException, BuildException {
try { try {
@@ -966,7 +989,7 @@ public final class IntrospectionHelper {
} }
// resolve relative paths through Project // resolve relative paths through Project
if (java.io.File.class.equals(reflectedArg)) { 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) public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException { throws InvocationTargetException, IllegalAccessException {
m.invoke(parent, new Object[] { p.resolveFile(value) }); m.invoke(parent, new Object[] { p.resolveFile(value) });
@@ -975,7 +998,7 @@ public final class IntrospectionHelper {
} }
// EnumeratedAttributes have their own helper class // EnumeratedAttributes have their own helper class
if (EnumeratedAttribute.class.isAssignableFrom(reflectedArg)) { if (EnumeratedAttribute.class.isAssignableFrom(reflectedArg)) {
return new AttributeSetter(m) {
return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value) public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException { throws InvocationTargetException, IllegalAccessException, BuildException {
try { try {
@@ -995,7 +1018,7 @@ public final class IntrospectionHelper {
//ignore //ignore
} }
if (enumClass != null && enumClass.isAssignableFrom(reflectedArg)) { if (enumClass != null && enumClass.isAssignableFrom(reflectedArg)) {
return new AttributeSetter(m) {
return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value) public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException { throws InvocationTargetException, IllegalAccessException, BuildException {
try { try {
@@ -1020,7 +1043,7 @@ public final class IntrospectionHelper {
}; };
} }
if (java.lang.Long.class.equals(reflectedArg)) { 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) public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException { throws InvocationTargetException, IllegalAccessException, BuildException {
try { try {
@@ -1059,7 +1082,7 @@ public final class IntrospectionHelper {
final boolean finalIncludeProject = includeProject; final boolean finalIncludeProject = includeProject;
final Constructor finalConstructor = c; final Constructor finalConstructor = c;


return new AttributeSetter(m) {
return new AttributeSetter(m, arg) {
public void set(Project p, Object parent, String value) public void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException { throws InvocationTargetException, IllegalAccessException, BuildException {
try { try {
@@ -1307,9 +1330,28 @@ public final class IntrospectionHelper {
*/ */
private abstract static class AttributeSetter { private abstract static class AttributeSetter {
private Method method; // the method called to set the attribute 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; 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) abstract void set(Project p, Object parent, String value)
throws InvocationTargetException, IllegalAccessException, BuildException; throws InvocationTargetException, IllegalAccessException, BuildException;


+ 19
- 31
src/main/org/apache/tools/ant/Project.java View File

@@ -526,8 +526,7 @@ public class Project implements ResourceFactory {
* @since 1.5 * @since 1.5
*/ */
public void setNewProperty(String name, String value) { 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) * @see #setProperty(String,String)
*/ */
public void setUserProperty(String name, String value) { 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) * @see #setProperty(String,String)
*/ */
public void setInheritedProperty(String name, String value) { 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 <code>null</code>. * @param value The property value. Must not be <code>null</code>.
*/ */
private void setPropertyInternal(String name, String value) { 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 <code>null</code> name is provided. * or if a <code>null</code> name is provided.
*/ */
public String getProperty(String propertyName) { 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 * @exception BuildException if the given value has an unclosed
* property name, e.g. <code>${xxx</code>. * property name, e.g. <code>${xxx</code>.
*/ */
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 <code>null</code> name is provided. * or if a <code>null</code> name is provided.
*/ */
public String getUserProperty(String propertyName) { 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). * (including user properties).
*/ */
public Hashtable getProperties() { 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. * @return a hashtable containing just the user properties.
*/ */
public Hashtable getUserProperties() { 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 * @since Ant 1.5
*/ */
public void copyUserProperties(Project other) { 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 * @since Ant 1.5
*/ */
public void copyInheritedProperties(Project other) { 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 // Check for old id behaviour
ret = resolveIdReference(key, this); ret = resolveIdReference(key, this);
if (ret == null && !key.equals(MagicNames.REFID_PROPERTY_HELPER)) { 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; return ret;


+ 389
- 71
src/main/org/apache/tools/ant/PropertyHelper.java View File

@@ -17,7 +17,13 @@
*/ */
package org.apache.tools.ant; 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.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector; import java.util.Vector;
import java.util.Enumeration; import java.util.Enumeration;


@@ -34,6 +40,14 @@ import java.util.Enumeration;
Need to discuss this and find if we need more. 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 /** NOT FINAL. API MAY CHANGE
* *
* Deals with properties - substitution, dynamic properties, etc. * Deals with properties - substitution, dynamic properties, etc.
@@ -45,8 +59,87 @@ import java.util.Enumeration;
*/ */
public class PropertyHelper { 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 <code>null</code>.
*/
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 Project project;
private PropertyHelper next; private PropertyHelper next;
private Hashtable delegates = new Hashtable();


/** Project properties map (usually String to String). */ /** Project properties map (usually String to String). */
private Hashtable properties = new Hashtable(); private Hashtable properties = new Hashtable();
@@ -55,7 +148,6 @@ public class PropertyHelper {
* Map of "user" properties (as created in the Ant task, for example). * Map of "user" properties (as created in the Ant task, for example).
* Note that these key/value pairs are also always put into the * Note that these key/value pairs are also always put into the
* project properties, so only the project properties need to be queried. * project properties, so only the project properties need to be queried.
* Mapping is String to String.
*/ */
private Hashtable userProperties = new Hashtable(); private Hashtable userProperties = new Hashtable();


@@ -63,7 +155,6 @@ public class PropertyHelper {
* Map of inherited "user" properties - that are those "user" * Map of inherited "user" properties - that are those "user"
* properties that have been created by tasks and not been set * properties that have been created by tasks and not been set
* from the command line or a GUI tool. * from the command line or a GUI tool.
* Mapping is String to String.
*/ */
private Hashtable inheritedProperties = new Hashtable(); private Hashtable inheritedProperties = new Hashtable();


@@ -71,6 +162,9 @@ public class PropertyHelper {
* Default constructor. * Default constructor.
*/ */
protected PropertyHelper() { protected PropertyHelper() {
add(TO_STRING);
add(SKIP_$$);
add(DEFAULT_EXPANDER);
} }


//override facility for subclasses to put custom hashtables in //override facility for subclasses to put custom hashtables in
@@ -94,7 +188,8 @@ public class PropertyHelper {
return project; 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 * - you can replace the main PropertyHelper. The replacement is required
* to support the same semantics (of course :-) * 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 * @return true if this helper has stored the property, false if it
* couldn't. Each helper should delegate to the next one (unless it * couldn't. Each helper should delegate to the next one (unless it
* has a good reason not to). * has a good reason not to).
* @deprecated PropertyHelper chaining is deprecated.
*/ */
public boolean setPropertyHook(String ns, String name, public boolean setPropertyHook(String ns, String name,
Object value, Object value,
@@ -177,13 +273,15 @@ public class PropertyHelper {
return false; 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. * tables will be used.
* *
* @param ns namespace of the sought property. * @param ns namespace of the sought property.
* @param name name of the sought property. * @param name name of the sought property.
* @param user True if this is a user property. * @param user True if this is a user property.
* @return The property, if returned by a hook, or null if none. * @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) { public Object getPropertyHook(String ns, String name, boolean user) {
if (getNext() != null) { if (getNext() != null) {
@@ -225,6 +323,7 @@ public class PropertyHelper {
* @exception BuildException if the string contains an opening * @exception BuildException if the string contains an opening
* <code>${</code> without a closing * <code>${</code> without a closing
* <code>}</code> * <code>}</code>
* @deprecated We can do better than this.
*/ */
public void parsePropertyString(String value, Vector fragments, public void parsePropertyString(String value, Vector fragments,
Vector propertyRefs) throws BuildException { Vector propertyRefs) throws BuildException {
@@ -250,58 +349,150 @@ public class PropertyHelper {
* <code>null</code> if the original string is <code>null</code>. * <code>null</code> if the original string is <code>null</code>.
*/ */
public String replaceProperties(String ns, String value, Hashtable keys) throws BuildException { public String replaceProperties(String ns, String value, Hashtable keys) throws BuildException {
if (value == null || value.indexOf('$') == -1) {
return replaceProperties(value);
}

/**
* Replaces <code>${xxx}</code> 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 <code>null</code>, in which case this
* method returns immediately with no effect.
*
* @exception BuildException if the string contains an opening
* <code>${</code> without a closing
* <code>}</code>
* @return the original string with the properties replaced, or
* <code>null</code> if the original string is <code>null</code>.
*/
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 <code>null</code>, in which case this
* method returns immediately with no effect.
*
* @exception BuildException if the string contains an opening
* <code>${</code> without a closing
* <code>}</code>
* @return the original string with the properties replaced, or
* <code>null</code> if the original string is <code>null</code>.
*/
public Object parseProperties(String value) throws BuildException {
if (value == null || "".equals(value)) {
return 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 <code>true</code> if <code>value</code> 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 -------------------- // -------------------- Default implementation --------------------
// Methods used to support the default behavior and provide backward // Methods used to support the default behavior and provide backward
// compatibility. Some will be deprecated, you should avoid calling them. // 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 * This is the original 1.5 implementation, with calls to the hook
* added. * added.
* @param ns The namespace for the property (currently not used).
* @param name The name of the property. * @param name The name of the property.
* @param value The value to set the property to. * @param value The value to set the property to.
* @param verbose If this is true output extra log messages. * @param verbose If this is true output extra log messages.
* @return true if the property is set. * @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 // user (CLI) properties take precedence
if (null != userProperties.get(name)) { if (null != userProperties.get(name)) {
if (verbose) { if (verbose) {
@@ -311,10 +502,10 @@ public class PropertyHelper {
return false; 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) { if (null != properties.get(name) && verbose) {
project.log("Overriding previous definition of property \"" + name project.log("Overriding previous definition of property \"" + name
@@ -342,20 +533,33 @@ public class PropertyHelper {
* @param value The new value of the property. * @param value The new value of the property.
* Must not be <code>null</code>. * Must not be <code>null</code>.
* @since Ant 1.6 * @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 <code>null</code>.
* @param value The new value of the property.
* Must not be <code>null</code>.
* @since Ant 1.8
*/
public synchronized void setNewProperty(String name, Object value) {
if (null != properties.get(name)) { 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; 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) { if (name != null && value != null) {
properties.put(name, value); properties.put(name, value);
} }
@@ -369,17 +573,28 @@ public class PropertyHelper {
* Must not be <code>null</code>. * Must not be <code>null</code>.
* @param value The new value of the property. * @param value The new value of the property.
* Must not be <code>null</code>. * Must not be <code>null</code>.
* @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 <code>null</code>.
* @param value The new value of the property.
* Must not be <code>null</code>.
*/ */
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); 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); properties.put(name, value);
} }


@@ -394,19 +609,33 @@ public class PropertyHelper {
* Must not be <code>null</code>. * Must not be <code>null</code>.
* @param value The new value of the property. * @param value The new value of the property.
* Must not be <code>null</code>. * Must not be <code>null</code>.
* @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 <code>null</code>.
* @param value The new value of the property.
* Must not be <code>null</code>.
*/
public synchronized void setInheritedProperty(String name, Object value) {
inheritedProperties.put(name, 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); 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); properties.put(name, value);
} }


@@ -422,17 +651,39 @@ public class PropertyHelper {
* the return value is also <code>null</code>. * the return value is also <code>null</code>.
* @return the property value, or <code>null</code> for no match * @return the property value, or <code>null</code> for no match
* or if a <code>null</code> name is provided. * or if a <code>null</code> name is provided.
* @deprecated namespaces are unnecessary.
*/ */
public synchronized Object getProperty(String ns, String name) { 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 <code>null</code>, in which case
* the return value is also <code>null</code>.
* @return the property value, or <code>null</code> for no match
* or if a <code>null</code> name is provided.
*/
public synchronized Object getProperty(String name) {
if (name == null) { if (name == null) {
return 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); return properties.get(name);
} }

/** /**
* Returns the value of a user property, if it is set. * Returns the value of a user property, if it is set.
* *
@@ -442,15 +693,32 @@ public class PropertyHelper {
* the return value is also <code>null</code>. * the return value is also <code>null</code>.
* @return the property value, or <code>null</code> for no match * @return the property value, or <code>null</code> for no match
* or if a <code>null</code> name is provided. * or if a <code>null</code> 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 <code>null</code>, in which case
* the return value is also <code>null</code>.
* @return the property value, or <code>null</code> for no match
* or if a <code>null</code> name is provided.
* @deprecated namespaces are unnecessary.
*/
public synchronized Object getUserProperty(String name) {
if (name == null) { if (name == null) {
return null; return null;
} }
/*
Object o = getPropertyHook(ns, name, true); Object o = getPropertyHook(ns, name, true);
if (o != null) { if (o != null) {
return o; return o;
} }
*/
return userProperties.get(name); return userProperties.get(name);
} }


@@ -568,7 +836,8 @@ public class PropertyHelper {
// this is used for backward compatibility (for code that calls // this is used for backward compatibility (for code that calls
// the parse method in ProjectHelper). // 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(). * for the static ProjectHelper.parsePropertyString().
*/ */
static void parsePropertyStringDefault(String value, Vector fragments, Vector propertyRefs) static void parsePropertyStringDefault(String value, Vector fragments, Vector propertyRefs)
@@ -624,4 +893,53 @@ public class PropertyHelper {
fragments.addElement(value.substring(prev)); 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<Class>
* @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;
}
} }

+ 2
- 2
src/main/org/apache/tools/ant/RuntimeConfigurable.java View File

@@ -387,9 +387,9 @@ public class RuntimeConfigurable implements Serializable {
String value = (String) attributeMap.get(name); String value = (String) attributeMap.get(name);


// reflect these into the target // reflect these into the target
value = p.replaceProperties(value);
Object attrValue = PropertyHelper.getPropertyHelper(p).parseProperties(value);
try { try {
ih.setAttribute(p, target, name, value);
ih.setAttribute(p, target, name, attrValue);
} catch (UnsupportedAttributeException be) { } catch (UnsupportedAttributeException be) {
// id attribute must be set externally // id attribute must be set externally
if (name.equals("id")) { if (name.equals("id")) {


+ 105
- 0
src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java View File

@@ -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);
}
}

}

+ 1
- 0
src/main/org/apache/tools/ant/taskdefs/defaults.properties View File

@@ -57,6 +57,7 @@ patch=org.apache.tools.ant.taskdefs.Patch
pathconvert=org.apache.tools.ant.taskdefs.PathConvert pathconvert=org.apache.tools.ant.taskdefs.PathConvert
presetdef=org.apache.tools.ant.taskdefs.PreSetDef presetdef=org.apache.tools.ant.taskdefs.PreSetDef
property=org.apache.tools.ant.taskdefs.Property property=org.apache.tools.ant.taskdefs.Property
propertyhelper=org.apache.tools.ant.taskdefs.PropertyHelperTask
record=org.apache.tools.ant.taskdefs.Recorder record=org.apache.tools.ant.taskdefs.Recorder
replace=org.apache.tools.ant.taskdefs.Replace replace=org.apache.tools.ant.taskdefs.Replace
retry=org.apache.tools.ant.taskdefs.Retry retry=org.apache.tools.ant.taskdefs.Retry


Loading…
Cancel
Save