aboutsummaryrefslogtreecommitdiffstats
path: root/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PropertyHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PropertyHelper.java')
-rw-r--r--framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PropertyHelper.java1215
1 files changed, 1215 insertions, 0 deletions
diff --git a/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PropertyHelper.java b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PropertyHelper.java
new file mode 100644
index 00000000..1dbb280c
--- /dev/null
+++ b/framework/src/ant/apache-ant-1.9.6/src/main/org/apache/tools/ant/PropertyHelper.java
@@ -0,0 +1,1215 @@
+/*
+ * 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;
+
+import java.text.ParsePosition;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Set;
+import java.util.Vector;
+
+import org.apache.tools.ant.property.GetProperty;
+import org.apache.tools.ant.property.NullReturn;
+import org.apache.tools.ant.property.ParseNextProperty;
+import org.apache.tools.ant.property.ParseProperties;
+import org.apache.tools.ant.property.PropertyExpander;
+
+/* ISSUES:
+ - ns param. It could be used to provide "namespaces" for properties, which
+ may be more flexible.
+ - Object value. In ant1.5 String is used for Properties - but it would be nice
+ to support generic Objects (the property remains immutable - you can't change
+ the associated object). This will also allow JSP-EL style setting using the
+ Object if an attribute contains only the property (name="${property}" could
+ avoid Object->String->Object conversion)
+ - Currently we "chain" only for get and set property (probably most users
+ will only need that - if they need more they can replace the top helper).
+ Need to discuss this and find if we need more.
+ */
+
+/* update for impending Ant 1.8.0:
+
+ - 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.
+
+ */
+
+/**
+ * Deals with properties - substitution, dynamic properties, etc.
+ *
+ * <p>This code has been heavily restructured for Ant 1.8.0. It is
+ * expected that custom PropertyHelper implementation that used the
+ * older chaining mechanism of Ant 1.6 won't work in all cases, and
+ * its usage is deprecated. The preferred way to customize Ant's
+ * property handling is by {@link #add adding} {@link
+ * PropertyHelper.Delegate delegates} of the appropriate subinterface
+ * and have this implementation use them.</p>
+ *
+ * <p>When {@link #parseProperties expanding a string that may contain
+ * properties} this class will delegate the actual parsing to {@link
+ * org.apache.tools.ant.property.ParseProperties#parseProperties
+ * parseProperties} inside the ParseProperties class which in turn
+ * uses the {@link org.apache.tools.ant.property.PropertyExpander
+ * PropertyExpander delegates} to find properties inside the string
+ * and this class to expand the propertiy names found into the
+ * corresponding values.</p>
+ *
+ * <p>When {@link #getProperty looking up a property value} this class
+ * will first consult all {@link PropertyHelper.PropertyEvaluator
+ * PropertyEvaluator} delegates and fall back to an internal map of
+ * "project properties" if no evaluator matched the property name.</p>
+ *
+ * <p>When {@link #setProperty setting a property value} this class
+ * will first consult all {@link PropertyHelper.PropertySetter
+ * PropertySetter} delegates and fall back to an internal map of
+ * "project properties" if no setter matched the property name.</p>
+ *
+ * @since Ant 1.6
+ */
+public class PropertyHelper implements GetProperty {
+
+ // --------------------------------------------------------
+ //
+ // The property delegate interfaces
+ //
+ // --------------------------------------------------------
+
+ /**
+ * Marker interface for a PropertyHelper delegate.
+ * @since Ant 1.8.0
+ */
+ public interface Delegate {
+ }
+
+ /**
+ * Looks up a property's value based on its name.
+ *
+ * <p>Can be used to look up properties in a different storage
+ * than the project instance (like local properties for example)
+ * or to implement custom "protocols" like Ant's
+ * <code>${toString:refid}</code> syntax.</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public interface PropertyEvaluator extends Delegate {
+ /**
+ * Evaluate a property.
+ *
+ * @param property the property's String "identifier".
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return null if the property name could not be found, an
+ * instance of {@link org.apache.tools.ant.property.NullReturn
+ * NullReturn} to indicate a property with a name that can be
+ * matched but a value of <code>null</code> and the property's
+ * value otherwise.
+ */
+ Object evaluate(String property, PropertyHelper propertyHelper);
+ }
+
+ /**
+ * Sets or overrides a property.
+ *
+ * <p>Can be used to store properties in a different storage than
+ * the project instance (like local properties for example).</p>
+ *
+ * @since Ant 1.8.0
+ */
+ public interface PropertySetter extends Delegate {
+ /**
+ * Set a *new" property.
+ *
+ * <p>Should not replace the value of an existing property.</p>
+ *
+ * @param property the property's String "identifier".
+ * @param value the value to set.
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return true if this entity 'owns' the property.
+ */
+ boolean setNew(
+ String property, Object value, PropertyHelper propertyHelper);
+
+ /**
+ * Set a property.
+ *
+ * <p>May replace the value of an existing property.</p>
+ *
+ * @param property the property's String "identifier".
+ * @param value the value to set.
+ * @param propertyHelper the invoking PropertyHelper.
+ * @return true if this entity 'owns' the property.
+ */
+ boolean set(
+ String property, Object value, PropertyHelper propertyHelper);
+ }
+
+ //TODO PropertyEnumerator Delegate type, would improve PropertySet
+
+ // --------------------------------------------------------
+ //
+ // The predefined property delegates
+ //
+ // --------------------------------------------------------
+
+ private static final PropertyEvaluator TO_STRING = new PropertyEvaluator() {
+ private final String PREFIX = "toString:";
+ private final int PREFIX_LEN = PREFIX.length();
+
+ public Object evaluate(String property, PropertyHelper propertyHelper) {
+ Object o = null;
+ if (property.startsWith(PREFIX) && propertyHelper.getProject() != null) {
+ o = propertyHelper.getProject().getReference(property.substring(PREFIX_LEN));
+ }
+ return o == null ? null : o.toString();
+ }
+ };
+
+ private static final PropertyExpander DEFAULT_EXPANDER = new PropertyExpander() {
+ public String parsePropertyName(
+ String s, ParsePosition pos, ParseNextProperty notUsed) {
+ int index = pos.getIndex();
+ //directly check near, triggering characters:
+ if (s.length() - index >= 3
+ && '$' == s.charAt(index) && '{' == s.charAt(index + 1)) {
+ int start = index + 2;
+ //defer to String.indexOf() for protracted check:
+ int end = s.indexOf('}', start);
+ if (end < 0) {
+ throw new BuildException("Syntax error in property: "
+ + s.substring(index));
+ }
+ pos.setIndex(end + 1);
+ return start == end ? "" : s.substring(start, end);
+ }
+ return null;
+ }
+ };
+
+ /** dummy */
+ private static final PropertyExpander SKIP_DOUBLE_DOLLAR
+ = new PropertyExpander() {
+ // CheckStyle:LineLengthCheck OFF see too long
+ /**
+ * {@inheritDoc}
+ * @see org.apache.tools.ant.property.PropertyExpander#parsePropertyName(java.lang.String, java.text.ParsePosition, org.apache.tools.ant.PropertyHelper)
+ */
+ // CheckStyle:LineLengthCheck ON
+ public String parsePropertyName(
+ String s, ParsePosition pos, ParseNextProperty notUsed) {
+ int index = pos.getIndex();
+ if (s.length() - index >= 2) {
+ /* check for $$; if found, advance by one--
+ * this expander is at the bottom of the stack
+ * and will thus be the last consulted,
+ * so the next thing that ParseProperties will do
+ * is advance the parse position beyond the second $
+ */
+ if ('$' == s.charAt(index) && '$' == s.charAt(++index)) {
+ pos.setIndex(index);
+ }
+ }
+ return null;
+ }
+ };
+
+ /**
+ * @since Ant 1.8.0
+ */
+ private static final PropertyEvaluator FROM_REF = new PropertyEvaluator() {
+ private final String PREFIX = "ant.refid:";
+ private final int PREFIX_LEN = PREFIX.length();
+
+ public Object evaluate(String prop, PropertyHelper helper) {
+ return prop.startsWith(PREFIX) && helper.getProject() != null
+ ? helper.getProject().getReference(prop.substring(PREFIX_LEN))
+ : null;
+ }
+ };
+
+ private Project project;
+ private PropertyHelper next;
+ private final Hashtable<Class<? extends Delegate>, List<Delegate>> delegates = new Hashtable<Class<? extends Delegate>, List<Delegate>>();
+
+ /** Project properties map (usually String to String). */
+ private Hashtable<String, Object> properties = new Hashtable<String, Object>();
+
+ /**
+ * 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.
+ */
+ private Hashtable<String, Object> userProperties = new Hashtable<String, Object>();
+
+ /**
+ * 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.
+ */
+ private Hashtable<String, Object> inheritedProperties = new Hashtable<String, Object>();
+
+ /**
+ * Default constructor.
+ */
+ protected PropertyHelper() {
+ add(FROM_REF);
+ add(TO_STRING);
+ add(SKIP_DOUBLE_DOLLAR);
+ add(DEFAULT_EXPANDER);
+ }
+
+ // --------------------------------------------------------
+ //
+ // Some helper static methods to get and set properties
+ //
+ // --------------------------------------------------------
+
+ /**
+ * A helper static method to get a property
+ * from a particular project.
+ * @param project the project in question.
+ * @param name the property name
+ * @return the value of the property if present, null otherwise.
+ * @since Ant 1.8.0
+ */
+ public static Object getProperty(Project project, String name) {
+ return PropertyHelper.getPropertyHelper(project)
+ .getProperty(name);
+ }
+
+ /**
+ * A helper static method to set a property
+ * from a particular project.
+ * @param project the project in question.
+ * @param name the property name
+ * @param value the value to use.
+ * @since Ant 1.8.0
+ */
+ public static void setProperty(Project project, String name, Object value) {
+ PropertyHelper.getPropertyHelper(project)
+ .setProperty(name, value, true);
+ }
+
+ /**
+ * A helper static method to set a new property
+ * from a particular project.
+ * @param project the project in question.
+ * @param name the property name
+ * @param value the value to use.
+ * @since Ant 1.8.0
+ */
+ public static void setNewProperty(
+ Project project, String name, Object value) {
+ PropertyHelper.getPropertyHelper(project)
+ .setNewProperty(name, value);
+ }
+
+ //override facility for subclasses to put custom hashtables in
+
+ // -------------------- Hook management --------------------
+
+ /**
+ * Set the project for which this helper is performing property resolution.
+ *
+ * @param p the project instance.
+ */
+ public void setProject(Project p) {
+ this.project = p;
+ }
+
+ /**
+ * Get this PropertyHelper's Project.
+ * @return Project
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * Prior to Ant 1.8.0 there have been 2 ways to hook into property handling:
+ *
+ * - you can replace the main PropertyHelper. The replacement is required
+ * to support the same semantics (of course :-)
+ *
+ * - you can chain a property helper capable of storing some properties.
+ * Again, you are required to respect the immutability semantics (at
+ * least for non-dynamic properties)
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself.</p>
+ *
+ * @param next the next property helper in the chain.
+ * @deprecated use the delegate mechanism instead
+ */
+ public void setNext(PropertyHelper next) {
+ this.next = next;
+ }
+
+ /**
+ * Get the next property helper in the chain.
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself except the {@link #setPropertyHook
+ * setPropertyHook} and {@link #getPropertyHook getPropertyHook}
+ * methods in this class.</p>
+ *
+ * @return the next property helper.
+ * @deprecated use the delegate mechanism instead
+ */
+ public PropertyHelper getNext() {
+ return next;
+ }
+
+ /**
+ * Factory method to create a property processor.
+ * Users can provide their own or replace it using "ant.PropertyHelper"
+ * reference. User tasks can also add themselves to the chain, and provide
+ * dynamic properties.
+ *
+ * @param project the project for which the property helper is required.
+ *
+ * @return the project's property helper.
+ */
+ public static synchronized PropertyHelper getPropertyHelper(Project project) {
+ PropertyHelper helper = null;
+ if (project != null) {
+ helper = (PropertyHelper) project.getReference(MagicNames
+ .REFID_PROPERTY_HELPER);
+ }
+ if (helper != null) {
+ return helper;
+ }
+
+ helper = new PropertyHelper();
+ helper.setProject(project);
+
+ if (project != null) {
+ project.addReference(MagicNames.REFID_PROPERTY_HELPER, helper);
+ }
+
+ return helper;
+ }
+
+ /**
+ * Get the {@link PropertyExpander expanders}.
+ * @since Ant 1.8.0
+ * @return the expanders.
+ */
+ public Collection<PropertyExpander> getExpanders() {
+ return getDelegates(PropertyExpander.class);
+ }
+
+
+ // -------------------- Methods to override --------------------
+
+ /**
+ * Sets a property. Any existing property of the same name
+ * is overwritten, unless it is a user property.
+ *
+ * If all helpers return false, the property will be saved in
+ * the default properties table by setProperty.
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself.</p>
+ *
+ * @param ns The namespace that the property is in (currently
+ * not used.
+ * @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>.
+ * @param inherited True if this property is inherited (an [sub]ant[call] property).
+ * @param user True if this property is a user property.
+ * @param isNew True is this is a new property.
+ * @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,
+ 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.
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself.</p>
+ *
+ * @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) {
+ Object o = getNext().getPropertyHook(ns, name, user);
+ if (o != null) {
+ return o;
+ }
+ }
+ // Experimental/Testing, will be removed
+ if (project != null && name.startsWith("toString:")) {
+ name = name.substring("toString:".length());
+ Object v = project.getReference(name);
+ return (v == null) ? null : 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 <code>${xxx}</code> style property
+ * references into two lists. The first list is a collection
+ * of text fragments, while the other is a set of string property names.
+ * <code>null</code> entries in the first list indicate a property
+ * reference from the second list.
+ *
+ * <p>Delegates to {@link #parsePropertyStringDefault
+ * parsePropertyStringDefault}.</p>
+ *
+ * <p>As of Ant 1.8.0 this method is never invoked by any code
+ * inside of Ant itself except {ProjectHelper#parsePropertyString
+ * ProjectHelper.parsePropertyString}.</p>
+ *
+ * @param value Text to parse. Must not be <code>null</code>.
+ * @param fragments List to add text fragments to.
+ * Must not be <code>null</code>.
+ * @param propertyRefs List to add property names to.
+ * Must not be <code>null</code>.
+ *
+ * @exception BuildException if the string contains an opening
+ * <code>${</code> without a closing
+ * <code>}</code>
+ * @deprecated use the other mechanisms of this class instead
+ */
+ public void parsePropertyString(String value, Vector<String> fragments,
+ Vector<String> propertyRefs) throws BuildException {
+ parsePropertyStringDefault(value, fragments, propertyRefs);
+ }
+
+ /**
+ * Replaces <code>${xxx}</code> style constructions in the given value
+ * with the string value of the corresponding data types.
+ *
+ * <p>Delegates to the one-arg version, completely ignoring the ns
+ * and keys parameters.</p>
+ *
+ * @param ns The namespace for the property.
+ * @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.
+ * @param keys Mapping (String to Object) of property names to their
+ * values. If <code>null</code>, only project properties will
+ * be used.
+ *
+ * @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>.
+ */
+ //TODO deprecate? Recall why no longer using ns/keys params
+ public String replaceProperties(String ns, String value, Hashtable<String, Object> keys) throws BuildException {
+ 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 {
+ return new ParseProperties(getProject(), getExpanders(), this)
+ .parseProperties(value);
+ }
+
+ /**
+ * 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) {
+ return new ParseProperties(getProject(), getExpanders(), this)
+ .containsProperties(value);
+ }
+
+ // -------------------- 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.
+ *
+ * <p>Delegates to the three-arg version, completely ignoring the
+ * ns parameter.</p>
+ *
+ * @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.
+ * @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 boolean setProperty(String name, Object value, boolean verbose) {
+ for (PropertySetter setter : getDelegates(PropertySetter.class)) {
+ if (setter.set(name, value, this)) {
+ return true;
+ }
+ }
+ synchronized (this) {
+ // user (CLI) properties take precedence
+ if (userProperties.containsKey(name)) {
+ if (project != null && verbose) {
+ project.log("Override ignored for user property \""
+ + name + "\"", Project.MSG_VERBOSE);
+ }
+ return false;
+ }
+ if (project != null && verbose) {
+ if (properties.containsKey(name)) {
+ project.log("Overriding previous definition of property \""
+ + name + "\"", Project.MSG_VERBOSE);
+ }
+ project.log("Setting project property: " + name + " -> "
+ + value, Project.MSG_DEBUG);
+ }
+ if (name != null && value != null) {
+ 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
+ * no other effect.
+ *
+ * <p>Delegates to the two-arg version, completely ignoring the
+ * ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @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.6
+ * @deprecated namespaces are unnecessary.
+ */
+ 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.0
+ */
+ public void setNewProperty(String name, Object value) {
+ for (PropertySetter setter : getDelegates(PropertySetter.class)) {
+ if (setter.setNew(name, value, this)) {
+ return;
+ }
+ }
+ synchronized (this) {
+ if (project != null && properties.containsKey(name)) {
+ project.log("Override ignored for property \"" + name
+ + "\"", Project.MSG_VERBOSE);
+ return;
+ }
+ if (project != null) {
+ 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.
+ *
+ * <p>Delegates to the two-arg version, completely ignoring the
+ * ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @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>.
+ * @deprecated namespaces are unnecessary.
+ */
+ public void setUserProperty(String ns, String name, Object value) {
+ setUserProperty(name, value);
+ }
+
+ /**
+ * Sets a user property, which cannot be overwritten by
+ * set/unset property calls. Any previous value is overwritten.
+ *
+ * <p>Does <code>not</code> consult any delegates.</p>
+ *
+ * @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 void setUserProperty(String name, Object value) {
+ if (project != null) {
+ project.log("Setting ro project property: "
+ + name + " -> " + value, Project.MSG_DEBUG);
+ }
+ synchronized (this) {
+ userProperties.put(name, value);
+ properties.put(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.
+ *
+ * <p>Delegates to the two-arg version, completely ignoring the
+ * ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @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>.
+ * @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.
+ *
+ * <p>Does <code>not</code> consult any delegates.</p>
+ *
+ * @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 void setInheritedProperty(String name, Object value) {
+ if (project != null) {
+ project.log("Setting ro project property: " + name + " -> "
+ + value, Project.MSG_DEBUG);
+ }
+
+ synchronized (this) {
+ inheritedProperties.put(name, value);
+ userProperties.put(name, value);
+ 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.
+ *
+ * <p>Delegates to the one-arg version ignoring the ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @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 Object getProperty(String ns, String name) {
+ return getProperty(name);
+ }
+
+ /**
+ * Returns the value of a property, if it is set.
+ *
+ * <p>This is the method that is invoked by {Project#getProperty
+ * Project.getProperty}.</p>
+ *
+ * <p>You can override this method in order to plug your own
+ * storage but the recommended approach is to add your own
+ * implementation of {@link PropertyEvaluator PropertyEvaluator}
+ * instead.</p>
+ *
+ * @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 Object getProperty(String name) {
+ if (name == null) {
+ return null;
+ }
+ for (PropertyEvaluator evaluator : getDelegates(PropertyEvaluator.class)) {
+ final Object o = evaluator.evaluate(name, this);
+ if (o == null) {
+ continue;
+ }
+ return o instanceof NullReturn ? null : o;
+ }
+ return properties.get(name);
+ }
+
+ /**
+ * Returns the value of a user property, if it is set.
+ *
+ * <p>Delegates to the one-arg version ignoring the ns parameter.</p>
+ *
+ * @param ns The namespace for the property (currently not used).
+ * @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 Object getUserProperty(String ns, String name) {
+ return getUserProperty(name);
+ }
+
+ /**
+ * Returns the value of a user property, if it is set.
+ *
+ * <p>Does <code>not</code> consult any delegates.</p>
+ *
+ * @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 Object getUserProperty(String name) {
+ if (name == null) {
+ return null;
+ }
+ 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.
+
+ /**
+ * Returns a copy of the properties table.
+ *
+ * <p>Does not contain properties held by implementations of
+ * delegates (like local properties).</p>
+ *
+ * @return a hashtable containing all properties (including user properties).
+ */
+ public Hashtable<String, Object> getProperties() {
+ //avoid concurrent modification:
+ synchronized (properties) {
+ return new Hashtable<String, Object>(properties);
+ }
+ // There is a better way to save the context. This shouldn't
+ // delegate to next, it's for backward compatibility only.
+ }
+
+ /**
+ * Returns a copy of the user property hashtable
+ *
+ * <p>Does not contain properties held by implementations of
+ * delegates (like local properties).</p>
+ *
+ * @return a hashtable containing just the user properties
+ */
+ public Hashtable<String, Object> getUserProperties() {
+ //avoid concurrent modification:
+ synchronized (userProperties) {
+ return new Hashtable<String, Object>(userProperties);
+ }
+ }
+
+ /**
+ * Returns a copy of the inherited property hashtable
+ *
+ * <p>Does not contain properties held by implementations of
+ * delegates (like local properties).</p>
+ *
+ * @return a hashtable containing just the inherited properties
+ */
+ public Hashtable<String, Object> getInheritedProperties() {
+ //avoid concurrent modification:
+ synchronized (inheritedProperties) {
+ return new Hashtable<String, Object>(inheritedProperties);
+ }
+ }
+
+ /**
+ * special back door for subclasses, internal access to the hashtables
+ * @return the live hashtable of all properties
+ */
+ protected Hashtable<String, Object> getInternalProperties() {
+ return properties;
+ }
+
+ /**
+ * special back door for subclasses, internal access to the hashtables
+ *
+ * @return the live hashtable of user properties
+ */
+ protected Hashtable<String, Object> getInternalUserProperties() {
+ return userProperties;
+ }
+
+ /**
+ * special back door for subclasses, internal access to the hashtables
+ *
+ * @return the live hashtable inherited properties
+ */
+ protected Hashtable<String, Object> getInternalInheritedProperties() {
+ return inheritedProperties;
+ }
+
+ /**
+ * Copies all user properties that have not been set on the
+ * command line or a GUI tool from this instance to the Project
+ * instance given as the argument.
+ *
+ * <p>To copy all "user" properties, you will also have to call
+ * {@link #copyUserProperties copyUserProperties}.</p>
+ *
+ * <p>Does not copy properties held by implementations of
+ * delegates (like local properties).</p>
+ *
+ * @param other the project to copy the properties to. Must not be null.
+ *
+ * @since Ant 1.6
+ */
+ public void copyInheritedProperties(Project other) {
+ //avoid concurrent modification:
+ synchronized (inheritedProperties) {
+ Enumeration<String> e = inheritedProperties.keys();
+ while (e.hasMoreElements()) {
+ String arg = e.nextElement().toString();
+ if (other.getUserProperty(arg) != null) {
+ continue;
+ }
+ Object value = inheritedProperties.get(arg);
+ 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
+ * given as the argument.
+ *
+ * <p>To copy all "user" properties, you will also have to call
+ * {@link #copyInheritedProperties copyInheritedProperties}.</p>
+ *
+ * <p>Does not copy properties held by implementations of
+ * delegates (like local properties).</p>
+ *
+ * @param other the project to copy the properties to. Must not be null.
+ *
+ * @since Ant 1.6
+ */
+ public void copyUserProperties(Project other) {
+ //avoid concurrent modification:
+ synchronized (userProperties) {
+ Enumeration<String> e = userProperties.keys();
+ while (e.hasMoreElements()) {
+ Object arg = e.nextElement();
+ if (inheritedProperties.containsKey(arg)) {
+ continue;
+ }
+ Object value = userProperties.get(arg);
+ 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<String> fragments, Vector<String> 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
+ //into the list
+ if (pos > 0) {
+ fragments.addElement(value.substring(prev, pos));
+ }
+ //if we are at the end of the string, we tack on a $
+ //then move past it
+ if (pos == (value.length() - 1)) {
+ fragments.addElement("$");
+ prev = pos + 1;
+ } else if (value.charAt(pos + 1) != '{') {
+ //peek ahead to see if the next char is a property or not
+ //not a property: insert the char as a literal
+ /*
+ 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("$");
+ prev = pos + 2;
+ } else {
+ //new behaviour: $X maps to $X for all values of X!='$'
+ 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);
+ propertyRefs.addElement(propertyName);
+ prev = endName + 1;
+ }
+ }
+ //no more $ signs found
+ //if there is any tail to the file, append it
+ 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.0
+ */
+ public void add(Delegate delegate) {
+ synchronized (delegates) {
+ for (Class<? extends Delegate> key : getDelegateInterfaces(delegate)) {
+ List<Delegate> list = delegates.get(key);
+ if (list == null) {
+ list = new ArrayList<Delegate>();
+ } else {
+ //copy on write, top priority
+ list = new ArrayList<Delegate>(list);
+ list.remove(delegate);
+ }
+ list.add(0, delegate);
+ delegates.put(key, Collections.unmodifiableList(list));
+ }
+ }
+ }
+
+ /**
+ * Get the Collection of delegates of the specified type.
+ *
+ * @param type
+ * delegate type.
+ * @return Collection.
+ * @since Ant 1.8.0
+ */
+ protected <D extends Delegate> List<D> getDelegates(Class<D> type) {
+ @SuppressWarnings("unchecked")
+ final List<D> result = (List<D>) delegates.get(type);
+ return result == null ? Collections.<D> emptyList() : result;
+ }
+
+ /**
+ * Get all Delegate interfaces (excluding Delegate itself) from the specified Delegate.
+ * @param d the Delegate to inspect.
+ * @return Set&lt;Class&gt;
+ * @since Ant 1.8.0
+ */
+ protected static Set<Class<? extends Delegate>> getDelegateInterfaces(Delegate d) {
+ final HashSet<Class<? extends Delegate>> result = new HashSet<Class<? extends Delegate>>();
+ Class<?> c = d.getClass();
+ while (c != null) {
+ Class<?>[] ifs = c.getInterfaces();
+ for (int i = 0; i < ifs.length; i++) {
+ if (Delegate.class.isAssignableFrom(ifs[i])) {
+ @SuppressWarnings("unchecked")
+ final Class<? extends Delegate> delegateInterface = (Class<? extends Delegate>) ifs[i];
+ result.add(delegateInterface);
+ }
+ }
+ c = c.getSuperclass();
+ }
+ result.remove(Delegate.class);
+ return result;
+ }
+
+ /**
+ * If the given object can be interpreted as a true/false value,
+ * turn it into a matching Boolean - otherwise return null.
+ * @since Ant 1.8.0
+ */
+ public static Boolean toBoolean(Object value) {
+ if (value instanceof Boolean) {
+ return (Boolean) value;
+ }
+ if (value instanceof String) {
+ String s = (String) value;
+ if (Project.toBoolean(s)) {
+ return Boolean.TRUE;
+ }
+ if ("off".equalsIgnoreCase(s)
+ || "false".equalsIgnoreCase(s)
+ || "no".equalsIgnoreCase(s)) {
+ return Boolean.FALSE;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the object is null or an empty string.
+ *
+ * @since Ant 1.8.0
+ */
+ private static boolean nullOrEmpty(Object value) {
+ return value == null || "".equals(value);
+
+ }
+
+ /**
+ * Returns true if the value can be interpreted as a true value or
+ * cannot be interpreted as a false value and a property of the
+ * value's name exists.
+ * @since Ant 1.8.0
+ */
+ private boolean evalAsBooleanOrPropertyName(Object value) {
+ Boolean b = toBoolean(value);
+ if (b != null) {
+ return b.booleanValue();
+ }
+ return getProperty(String.valueOf(value)) != null;
+ }
+
+ /**
+ * Returns true if the value is null or an empty string, can be
+ * interpreted as a true value or cannot be interpreted as a false
+ * value and a property of the value's name exists.
+ * @since Ant 1.8.0
+ */
+ public boolean testIfCondition(Object value) {
+ return nullOrEmpty(value) || evalAsBooleanOrPropertyName(value);
+ }
+
+ /**
+ * Returns true if the value is null or an empty string, can be
+ * interpreted as a false value or cannot be interpreted as a true
+ * value and a property of the value's name doesn't exist.
+ * @since Ant 1.8.0
+ */
+ public boolean testUnlessCondition(Object value) {
+ return nullOrEmpty(value) || !evalAsBooleanOrPropertyName(value);
+ }
+}