=================================================================== RCS file: /home/cvspublic/ant/src/main/org/apache/tools/ant/PropertyHelper.java,v retrieving revision 1.15 diff -c -r1.15 PropertyHelper.java *** src/main/org/apache/tools/ant/PropertyHelper.java 14 Apr 2004 15:42:06 -0000 1.15 --- src/main/org/apache/tools/ant/PropertyHelper.java 3 Jun 2004 17:40:32 -0000 *************** *** 20,25 **** --- 20,26 ---- import java.util.Hashtable; import java.util.Vector; import java.util.Enumeration; + import org.apache.tools.ant.util.RecursivePropertyParser; /* ISSUES: *************** *** 45,57 **** * @since Ant 1.6 */ public class PropertyHelper { ! private Project project; private PropertyHelper next; ! /** Project properties map (usually String to String). */ private Hashtable properties = new Hashtable(); ! /** * Map of "user" properties (as created in the Ant task, for example). * Note that these key/value pairs are also always put into the --- 46,58 ---- * @since Ant 1.6 */ public class PropertyHelper { ! private Project project; private PropertyHelper next; ! /** Project properties map (usually String to String). */ private Hashtable properties = new Hashtable(); ! /** * Map of "user" properties (as created in the Ant task, for example). * Note that these key/value pairs are also always put into the *************** *** 59,65 **** * Mapping is String to String. */ private Hashtable userProperties = new Hashtable(); ! /** * Map of inherited "user" properties - that are those "user" * properties that have been created by tasks and not been set --- 60,66 ---- * Mapping is String to String. */ private Hashtable userProperties = new Hashtable(); ! /** * Map of inherited "user" properties - that are those "user" * properties that have been created by tasks and not been set *************** *** 67,81 **** * Mapping is String to String. */ private Hashtable inheritedProperties = new Hashtable(); ! /** * Default constructor. */ protected PropertyHelper() { } ! // -------------------- Hook management -------------------- ! /** * Set the project for which this helper is performing property resolution * --- 68,82 ---- * Mapping is String to String. */ private Hashtable inheritedProperties = new Hashtable(); ! /** * Default constructor. */ protected PropertyHelper() { } ! // -------------------- Hook management -------------------- ! /** * Set the project for which this helper is performing property resolution * *************** *** 84,90 **** public void setProject(Project p) { this.project = p; } ! /** 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 :-) --- 85,91 ---- public void setProject(Project p) { this.project = p; } ! /** 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 :-) *************** *** 98,104 **** public void setNext(PropertyHelper next) { this.next = next; } ! /** * Get the next property helper in the chain. * --- 99,105 ---- public void setNext(PropertyHelper next) { this.next = next; } ! /** * Get the next property helper in the chain. * *************** *** 107,113 **** public PropertyHelper getNext() { return next; } ! /** * Factory method to create a property processor. * Users can provide their own or replace it using "ant.PropertyHelper" --- 108,114 ---- public PropertyHelper getNext() { return next; } ! /** * Factory method to create a property processor. * Users can provide their own or replace it using "ant.PropertyHelper" *************** *** 119,139 **** * @return the project's property helper. */ public static synchronized ! PropertyHelper getPropertyHelper(Project project) { PropertyHelper helper ! = (PropertyHelper) project.getReference("ant.PropertyHelper"); if (helper != null) { return helper; } helper = new PropertyHelper(); helper.setProject(project); ! project.addReference("ant.PropertyHelper", helper); return helper; } ! // -------------------- Methods to override -------------------- ! /** * Sets a property. Any existing property of the same name * is overwritten, unless it is a user property. Will be called --- 120,140 ---- * @return the project's property helper. */ public static synchronized ! PropertyHelper getPropertyHelper(Project project) { PropertyHelper helper ! = (PropertyHelper) project.getReference("ant.PropertyHelper"); if (helper != null) { return helper; } helper = new PropertyHelper(); helper.setProject(project); ! project.addReference("ant.PropertyHelper", helper); return helper; } ! // -------------------- Methods to override -------------------- ! /** * Sets a property. Any existing property of the same name * is overwritten, unless it is a user property. Will be called *************** *** 151,171 **** * has a good reason not to). */ public boolean setPropertyHook(String ns, String name, ! Object value, ! boolean inherited, boolean user, ! boolean isNew) { if (getNext() != null) { boolean subst = getNext().setPropertyHook(ns, name, value, ! inherited, user, isNew); // If next has handled the property if (subst) { return true; } } ! return false; } ! /** Get a property. If all hooks return null, the default * tables will be used. * --- 152,172 ---- * has a good reason not to). */ public boolean setPropertyHook(String ns, String name, ! Object value, ! boolean inherited, boolean user, ! boolean isNew) { if (getNext() != null) { boolean subst = getNext().setPropertyHook(ns, name, value, ! inherited, user, isNew); // If next has handled the property if (subst) { return true; } } ! return false; } ! /** Get a property. If all hooks return null, the default * tables will be used. * *************** *** 189,205 **** } return v.toString(); } ! ! return null; } ! // -------------------- Optional methods -------------------- // You can override those methods if you want to optimize or // do advanced things (like support a special syntax). // The methods do not chain - you should use them when embedding ant // (by replacing the main helper) ! /** * Parses a string containing ${xxx} style property * references into two lists. The first list is a collection --- 190,206 ---- } return v.toString(); } ! ! return null; } ! // -------------------- Optional methods -------------------- // You can override those methods if you want to optimize or // do advanced things (like support a special syntax). // The methods do not chain - you should use them when embedding ant // (by replacing the main helper) ! /** * Parses a string containing ${xxx} style property * references into two lists. The first list is a collection *************** *** 220,230 **** * } */ public void parsePropertyString(String value, Vector fragments, ! Vector propertyRefs) ! throws BuildException { parsePropertyStringDefault(value, fragments, propertyRefs); } ! /** * Replaces ${xxx} style constructions in the given value * with the string value of the corresponding data types. --- 221,237 ---- * } */ public void parsePropertyString(String value, Vector fragments, ! Vector propertyRefs) ! throws BuildException { parsePropertyStringDefault(value, fragments, propertyRefs); } ! ! public String replaceProperties(String ns, String value, ! Hashtable keys) ! throws BuildException { ! return replacePropertiesRecursively(ns, value, keys); ! } ! /** * Replaces ${xxx} style constructions in the given value * with the string value of the corresponding data types. *************** *** 242,268 **** * @return the original string with the properties replaced, or * null if the original string is null. */ ! public String replaceProperties(String ns, String value, ! Hashtable keys) ! throws BuildException { if (value == null) { return null; } ! 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) { --- 249,275 ---- * @return the original string with the properties replaced, or * null if the original string is null. */ ! public String replacePropertiesBC(String ns, String value, ! Hashtable keys) ! throws BuildException { if (value == null) { return null; } ! 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) { *************** *** 271,329 **** 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 + "}"; } sb.append(fragment); } ! return sb.toString(); } ! // -------------------- 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. * This is the original 1.5 implementation, with calls to the hook * added. */ public synchronized boolean setProperty(String ns, String name, ! Object value, boolean verbose) { // user (CLI) properties take precedence if (null != userProperties.get(name)) { if (verbose) { project.log("Override ignored for user property " + name, ! Project.MSG_VERBOSE); } return false; } ! 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, ! Project.MSG_VERBOSE); } ! if (verbose) { project.log("Setting project property: " + name + " -> " ! + value, Project.MSG_DEBUG); } properties.put(name, value); return true; } ! /** * Sets a property if no value currently exists. If the property * exists already, a message is logged and the method returns with --- 278,403 ---- 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 + "}"; } sb.append(fragment); } ! return sb.toString(); } ! ! /** ! * Replaces ${xxx} style constructions RECURSIVELY in the given value ! * with the string value of the corresponding data types. That is, this function ! * keeps finding the next leftmost outermost expression and expands it recursively until no ! * instantiable expressions remain. ! * ! * ! * @param value The string to be scanned for property references. ! * May be null, in which case this ! * method returns immediately with no effect. ! * @param keys Mapping (String to String) of property names to their ! * values. If null, only project properties will ! * be used. ! * ! * @exception BuildException if the string contains an opening ! * ${ without a closing ! * } ! * @return the original string with the properties replaced, or ! * null if the original string is null. ! */ ! ! public String replacePropertiesRecursively(String ns, String value, ! Hashtable keys) ! throws BuildException { ! if (value == null) { ! return null; ! } ! StringBuffer sb = new StringBuffer(value); ! ! int nextOpenerIndex = RecursivePropertyParser.nextOpener(value, 0); ! ! while (nextOpenerIndex != RecursivePropertyParser.NO_MARKER) { // More inner expressions, recurse. ! // /* debug */ System.out.println("Head of replacePropertiesRecursively while loop, nextOpenerIndex = " + nextOpenerIndex + " sb=" + sb.toString()); ! int balancingCloserIndex = RecursivePropertyParser.balancingCloser(sb.toString(), nextOpenerIndex); ! if (balancingCloserIndex == RecursivePropertyParser.NO_MARKER) { ! // ??? Should this be logged first? ! throw new BuildException("Unbalanced property expression: " + sb.substring(nextOpenerIndex).toString()); ! } ! final String innerExpression = sb.substring(nextOpenerIndex + RecursivePropertyParser.OPENER_LENGTH , balancingCloserIndex); ! ! final String expansion = replacePropertiesRecursively(ns, innerExpression, keys); ! // /* debug */ System.out.println("replacePropertiesRecursively() innerExpression=" + innerExpression); ! Object replacement = null; ! ! // try to get it from the project or keys ! // Backward compatibility ! if (keys != null) { ! replacement = keys.get(expansion); ! } ! if (replacement == null) { ! replacement = getProperty(ns, expansion); ! } ! ! if (replacement == null) { ! project.log("Property ${" + expansion ! + "} has not been set", Project.MSG_VERBOSE); ! } ! final String fragment = (replacement != null) ! ? replacement.toString() ! : "${" + expansion + "}"; ! ! sb.replace(nextOpenerIndex, balancingCloserIndex + RecursivePropertyParser.CLOSER_LENGTH, fragment); ! nextOpenerIndex = RecursivePropertyParser.nextOpener(sb.toString(), balancingCloserIndex+1); ! } ! return sb.toString(); ! } ! // -------------------- 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. * This is the original 1.5 implementation, with calls to the hook * added. */ public synchronized boolean setProperty(String ns, String name, ! Object value, boolean verbose) { // user (CLI) properties take precedence if (null != userProperties.get(name)) { if (verbose) { project.log("Override ignored for user property " + name, ! Project.MSG_VERBOSE); } return false; } ! 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, ! Project.MSG_VERBOSE); } ! if (verbose) { project.log("Setting project property: " + name + " -> " ! + value, Project.MSG_DEBUG); } properties.put(name, value); return true; } ! /** * Sets a property if no value currently exists. If the property * exists already, a message is logged and the method returns with *************** *** 336,360 **** * @since Ant 1.6 */ public synchronized void setNewProperty(String ns, 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) { return; } ! project.log("Setting project property: " + name + " -> " ! + value, Project.MSG_DEBUG); if (name != null && value != null) { properties.put(name, value); } } ! /** * Sets a user property, which cannot be overwritten by * set/unset property calls. Any previous value is overwritten. --- 410,434 ---- * @since Ant 1.6 */ public synchronized void setNewProperty(String ns, 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) { return; } ! project.log("Setting project property: " + name + " -> " ! + value, Project.MSG_DEBUG); if (name != null && value != null) { properties.put(name, value); } } ! /** * Sets a user property, which cannot be overwritten by * set/unset property calls. Any previous value is overwritten. *************** *** 364,381 **** * Must not be null. */ public synchronized void setUserProperty(String ns, String name, ! Object value) { project.log("Setting ro project property: " + name + " -> " ! + value, Project.MSG_DEBUG); userProperties.put(name, value); ! boolean done = setPropertyHook(ns, name, value, false, true, false); if (done) { return; } properties.put(name, value); } ! /** * Sets a user property, which cannot be overwritten by set/unset * property calls. Any previous value is overwritten. Also marks --- 438,455 ---- * Must not be null. */ public synchronized void setUserProperty(String ns, String name, ! Object value) { project.log("Setting ro project property: " + name + " -> " ! + value, Project.MSG_DEBUG); userProperties.put(name, value); ! boolean done = setPropertyHook(ns, name, value, false, true, false); if (done) { return; } properties.put(name, value); } ! /** * Sets a user property, which cannot be overwritten by set/unset * property calls. Any previous value is overwritten. Also marks *************** *** 388,409 **** * Must not be null. */ public synchronized void setInheritedProperty(String ns, String name, ! Object value) { inheritedProperties.put(name, value); ! 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; } properties.put(name, value); } ! // -------------------- Getting properties -------------------- ! /** * Returns the value of a property, if it is set. You can override * this method in order to plug your own storage. --- 462,483 ---- * Must not be null. */ public synchronized void setInheritedProperty(String ns, String name, ! Object value) { inheritedProperties.put(name, value); ! 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; } properties.put(name, value); } ! // -------------------- Getting properties -------------------- ! /** * Returns the value of a property, if it is set. You can override * this method in order to plug your own storage. *************** *** 418,429 **** if (name == null) { return null; } ! Object o = getPropertyHook(ns, name, false); if (o != null) { return o; } ! return properties.get(name); } /** --- 492,503 ---- if (name == null) { return null; } ! Object o = getPropertyHook(ns, name, false); if (o != null) { return o; } ! return properties.get(name); } /** *************** *** 445,459 **** } return userProperties.get(name); } ! ! // -------------------- Access to property tables -------------------- // This is used to support ant call and similar tasks. It should be // deprecated, it is possible to use a better (more efficient) // mechanism to preserve the context. ! // TODO: do we need to delegate ? ! /** * Returns a copy of the properties table. * @return a hashtable containing all properties --- 519,533 ---- } return userProperties.get(name); } ! ! // -------------------- Access to property tables -------------------- // This is used to support ant call and similar tasks. It should be // deprecated, it is possible to use a better (more efficient) // mechanism to preserve the context. ! // TODO: do we need to delegate ? ! /** * Returns a copy of the properties table. * @return a hashtable containing all properties *************** *** 461,497 **** */ public Hashtable getProperties() { Hashtable propertiesCopy = new Hashtable(); ! Enumeration e = properties.keys(); while (e.hasMoreElements()) { Object name = e.nextElement(); Object value = properties.get(name); propertiesCopy.put(name, value); } ! // There is a better way to save the context. This shouldn't // delegate to next, it's for backward compatibility only. ! return propertiesCopy; } ! /** * Returns a copy of the user property hashtable * @return a hashtable containing just the user properties */ public Hashtable getUserProperties() { Hashtable propertiesCopy = new Hashtable(); ! Enumeration e = userProperties.keys(); while (e.hasMoreElements()) { Object name = e.nextElement(); Object value = properties.get(name); propertiesCopy.put(name, value); } ! return propertiesCopy; } ! /** * Copies all user properties that have not been set on the * command line or a GUI tool from this instance to the Project --- 535,571 ---- */ public Hashtable getProperties() { Hashtable propertiesCopy = new Hashtable(); ! Enumeration e = properties.keys(); while (e.hasMoreElements()) { Object name = e.nextElement(); Object value = properties.get(name); propertiesCopy.put(name, value); } ! // There is a better way to save the context. This shouldn't // delegate to next, it's for backward compatibility only. ! return propertiesCopy; } ! /** * Returns a copy of the user property hashtable * @return a hashtable containing just the user properties */ public Hashtable getUserProperties() { Hashtable propertiesCopy = new Hashtable(); ! Enumeration e = userProperties.keys(); while (e.hasMoreElements()) { Object name = e.nextElement(); Object value = properties.get(name); propertiesCopy.put(name, value); } ! return propertiesCopy; } ! /** * Copies all user properties that have not been set on the * command line or a GUI tool from this instance to the Project *************** *** 515,521 **** other.setInheritedProperty(arg, value.toString()); } } ! /** * Copies all user properties that have been set on the command * line or a GUI tool from this instance to the Project instance --- 589,595 ---- other.setInheritedProperty(arg, value.toString()); } } ! /** * Copies all user properties that have been set on the command * line or a GUI tool from this instance to the Project instance *************** *** 539,561 **** other.setUserProperty(arg.toString(), value.toString()); } } ! // -------------------- Property parsing -------------------- // Moved from ProjectHelper. You can override the static method - // 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 * for the static ProjectHelper.parsePropertyString(). */ static void parsePropertyStringDefault(String value, Vector fragments, ! Vector propertyRefs) ! throws BuildException { int prev = 0; int pos; //search for the next instance of $ from the 'prev' position while ((pos = value.indexOf("$", prev)) >= 0) { ! //if there was any text before this, add it as a fragment //TODO, this check could be modified to go if pos>prev; //seems like this current version could stick empty strings --- 613,635 ---- other.setUserProperty(arg.toString(), value.toString()); } } ! // -------------------- Property parsing -------------------- // Moved from ProjectHelper. You can override the static method - // 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 * for the static ProjectHelper.parsePropertyString(). */ static void parsePropertyStringDefault(String value, Vector fragments, ! Vector propertyRefs) ! throws BuildException { int prev = 0; int pos; //search for the next instance of $ from the 'prev' position while ((pos = value.indexOf("$", prev)) >= 0) { ! //if there was any text before this, add it as a fragment //TODO, this check could be modified to go if pos>prev; //seems like this current version could stick empty strings *************** *** 574,580 **** /* fragments.addElement(value.substring(pos + 1, pos + 2)); prev = pos + 2; ! */ if (value.charAt(pos + 1) == '$') { //backwards compatibility two $ map to one mode fragments.addElement("$"); --- 648,654 ---- /* fragments.addElement(value.substring(pos + 1, pos + 2)); prev = pos + 2; ! */ if (value.charAt(pos + 1) == '$') { //backwards compatibility two $ map to one mode fragments.addElement("$"); *************** *** 584,596 **** fragments.addElement(value.substring(pos, pos + 2)); prev = pos + 2; } ! } else { //property found, extract its name or bail on a typo int endName = value.indexOf('}', pos); if (endName < 0) { throw new BuildException("Syntax error in property: " ! + value); } String propertyName = value.substring(pos + 2, endName); fragments.addElement(null); --- 658,670 ---- fragments.addElement(value.substring(pos, pos + 2)); prev = pos + 2; } ! } else { //property found, extract its name or bail on a typo int endName = value.indexOf('}', pos); if (endName < 0) { throw new BuildException("Syntax error in property: " ! + value); } String propertyName = value.substring(pos + 2, endName); fragments.addElement(null); *************** *** 604,608 **** fragments.addElement(value.substring(prev)); } } ! } --- 678,682 ---- fragments.addElement(value.substring(prev)); } } ! }