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]

Reply via email to