Author: mbenson Date: Sun Jul 8 10:23:25 2007 New Revision: 554394 URL: http://svn.apache.org/viewvc?view=rev&rev=554394 Log: BZ 42736: PropertyHelper API changes
Added: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java (with props) Modified: ant/core/trunk/src/main/org/apache/tools/ant/IntrospectionHelper.java ant/core/trunk/src/main/org/apache/tools/ant/Project.java ant/core/trunk/src/main/org/apache/tools/ant/PropertyHelper.java ant/core/trunk/src/main/org/apache/tools/ant/RuntimeConfigurable.java ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/defaults.properties Modified: ant/core/trunk/src/main/org/apache/tools/ant/IntrospectionHelper.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/IntrospectionHelper.java?view=diff&rev=554394&r1=554393&r2=554394 ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/IntrospectionHelper.java (original) +++ ant/core/trunk/src/main/org/apache/tools/ant/IntrospectionHelper.java Sun Jul 8 10:23:25 2007 @@ -352,32 +352,32 @@ * <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>. + * 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 @@ 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); @@ -395,6 +395,29 @@ } /** + * 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 * <code>void addText(String)</code> method, if it has one. If no * such method is present, a BuildException is thrown if the @@ -921,7 +944,7 @@ // 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 @@ } // 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 @@ } // 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 @@ } // 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 @@ } // 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 @@ } // 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 @@ //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 @@ }; } 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 @@ 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 @@ */ 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; Modified: ant/core/trunk/src/main/org/apache/tools/ant/Project.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/Project.java?view=diff&rev=554394&r1=554393&r2=554394 ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/Project.java (original) +++ ant/core/trunk/src/main/org/apache/tools/ant/Project.java Sun Jul 8 10:23:25 2007 @@ -526,8 +526,7 @@ * @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 @@ * @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 @@ * @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 @@ * @param value The property value. Must not be <code>null</code>. */ 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 @@ * or if a <code>null</code> 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 @@ * @exception BuildException if the given value has an unclosed * 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 @@ * or if a <code>null</code> 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 @@ * (including user properties). */ public Hashtable getProperties() { - PropertyHelper ph = PropertyHelper.getPropertyHelper(this); - return ph.getProperties(); + return PropertyHelper.getPropertyHelper(this).getProperties(); } /** @@ -637,8 +628,7 @@ * @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 @@ * @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 @@ * @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 @@ // 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; Modified: ant/core/trunk/src/main/org/apache/tools/ant/PropertyHelper.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/PropertyHelper.java?view=diff&rev=554394&r1=554393&r2=554394 ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/PropertyHelper.java (original) +++ ant/core/trunk/src/main/org/apache/tools/ant/PropertyHelper.java Sun Jul 8 10:23:25 2007 @@ -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 @@ 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 @@ */ 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() { + /** + * [EMAIL PROTECTED] + * @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 @@ * 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 @@ * 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 @@ * 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 @@ 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 @@ * @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 @@ 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 @@ * @exception BuildException if the string contains an opening * <code>${</code> without a closing * <code>}</code> + * @deprecated We can do better than this. */ public void parsePropertyString(String value, Vector fragments, Vector propertyRefs) throws BuildException { @@ -250,58 +349,150 @@ * <code>null</code> if the original string is <code>null</code>. */ 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; } - Vector fragments = new Vector(); - Vector propertyRefs = new Vector(); - parsePropertyString(value, fragments, propertyRefs); - - StringBuffer sb = new StringBuffer(); - Enumeration i = fragments.elements(); - Enumeration j = propertyRefs.elements(); - - while (i.hasMoreElements()) { - String fragment = (String) i.nextElement(); - if (fragment == null) { - String propertyName = (String) j.nextElement(); - Object replacement = 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 + "}"; + 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); } - sb.append(fragment); } return sb.toString(); } + /** + * 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; + } + + /** + * 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; + } + + 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; + } + return propertyName; + } + 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 @@ 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 @@ * @param value The new value of the property. * Must not be <code>null</code>. * @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)) { - 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 @@ * Must not be <code>null</code>. * @param value The new value of the property. * Must not be <code>null</code>. + * @deprecated namespaces are unnecessary. */ - public synchronized void setUserProperty(String ns, String name, - Object value) { - project.log("Setting ro project property: " + name + " -> " - + value, Project.MSG_DEBUG); + 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 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 @@ * Must not be <code>null</code>. * @param value The new value of the property. * Must not be <code>null</code>. + * @deprecated namespaces are unnecessary. + */ + 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 ns, String name, - Object value) { + 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 @@ * 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 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) { 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 @@ * 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 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 ns, String name) { + 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 @@ // 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) @@ -623,5 +892,54 @@ if (prev < value.length()) { 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; } } Modified: ant/core/trunk/src/main/org/apache/tools/ant/RuntimeConfigurable.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/RuntimeConfigurable.java?view=diff&rev=554394&r1=554393&r2=554394 ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/RuntimeConfigurable.java (original) +++ ant/core/trunk/src/main/org/apache/tools/ant/RuntimeConfigurable.java Sun Jul 8 10:23:25 2007 @@ -387,9 +387,9 @@ 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")) { Added: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java?view=auto&rev=554394 ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java (added) +++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java Sun Jul 8 10:23:25 2007 @@ -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); + } + } + +} Propchange: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/PropertyHelperTask.java ------------------------------------------------------------------------------ svn:eol-style = native Modified: ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/defaults.properties URL: http://svn.apache.org/viewvc/ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/defaults.properties?view=diff&rev=554394&r1=554393&r2=554394 ============================================================================== --- ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/defaults.properties (original) +++ ant/core/trunk/src/main/org/apache/tools/ant/taskdefs/defaults.properties Sun Jul 8 10:23:25 2007 @@ -57,6 +57,7 @@ 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 --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]