diff --git a/src/main/org/apache/tools/ant/PropertyHelper.java b/src/main/org/apache/tools/ant/PropertyHelper.java
index 38197b8df..c0f926d4c 100644
--- a/src/main/org/apache/tools/ant/PropertyHelper.java
+++ b/src/main/org/apache/tools/ant/PropertyHelper.java
@@ -27,8 +27,13 @@ import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.Enumeration;
+import java.util.Collection;
import org.apache.tools.ant.property.NullReturn;
+import org.apache.tools.ant.property.GetProperty;
+import org.apache.tools.ant.property.ParseNextProperty;
+import org.apache.tools.ant.property.PropertyExpander;
+import org.apache.tools.ant.property.ParseProperties;
/* ISSUES:
- ns param. It could be used to provide "namespaces" for properties, which
@@ -60,7 +65,7 @@ import org.apache.tools.ant.property.NullReturn;
*
* @since Ant 1.6
*/
-public class PropertyHelper implements Cloneable {
+public class PropertyHelper implements GetProperty {
// --------------------------------------------------------
//
@@ -89,22 +94,6 @@ public class PropertyHelper implements Cloneable {
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 null
.
- */
- String parsePropertyName(
- String s, ParsePosition pos, PropertyHelper propertyHelper);
- }
-
/**
* Describes an entity capable of setting a property to a value.
* @since Ant 1.8
@@ -151,7 +140,7 @@ public class PropertyHelper implements Cloneable {
private static final PropertyExpander DEFAULT_EXPANDER = new PropertyExpander() {
public String parsePropertyName(
- String s, ParsePosition pos, PropertyHelper propertyHelper) {
+ String s, ParsePosition pos, ParseNextProperty notUsed) {
int index = pos.getIndex();
if (s.indexOf("${", index) == index) {
int end = s.indexOf('}', index);
@@ -176,7 +165,8 @@ public class PropertyHelper implements Cloneable {
*/
// CheckStyle:LineLengthCheck ON
public String parsePropertyName(
- String s, ParsePosition pos, PropertyHelper propertyHelper) {
+ String s, ParsePosition pos, ParseNextProperty notUsed) {
+ //System.out.println("parseproperty " + s);
int index = pos.getIndex();
if (s.indexOf("$$", index) == index) {
pos.setIndex(++index);
@@ -331,6 +321,15 @@ public class PropertyHelper implements Cloneable {
return helper;
}
+ /**
+ * Get the expanders.
+ * @return the exapanders.
+ */
+ public Collection getExpanders() {
+ return getDelegates(PropertyExpander.class);
+ }
+
+
// -------------------- Methods to override --------------------
/**
@@ -483,31 +482,8 @@ public class PropertyHelper implements Cloneable {
* null
if the original string is null
.
*/
public Object parseProperties(String value) throws BuildException {
- if (value == null || "".equals(value)) {
- return value;
- }
- ParsePosition pos = new ParsePosition(0);
- Object o = parseNextProperty(value, pos);
- if (o != null && pos.getIndex() == value.length()) {
- return o;
- }
- StringBuffer sb = new StringBuffer(value.length() * 2);
- if (o == null) {
- sb.append(value.charAt(pos.getIndex()));
- pos.setIndex(pos.getIndex() + 1);
- } else {
- sb.append(o);
- }
- while (pos.getIndex() < value.length()) {
- o = parseNextProperty(value, pos);
- if (o == null) {
- sb.append(value.charAt(pos.getIndex()));
- pos.setIndex(pos.getIndex() + 1);
- } else {
- sb.append(o);
- }
- }
- return sb.toString();
+ return new ParseProperties(getProject(), getExpanders(), this)
+ .parseProperties(value);
}
/**
@@ -516,50 +492,8 @@ public class PropertyHelper implements Cloneable {
* @return true
if value
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;
+ return new ParseProperties(getProject(), getExpanders(), this)
+ .containsProperties(value);
}
// -------------------- Default implementation --------------------
@@ -1062,22 +996,4 @@ public class PropertyHelper implements Cloneable {
return result;
}
- /**
- * Make a clone of this PropertyHelper.
- * @return the cloned PropertyHelper.
- * @since Ant 1.8
- */
- public synchronized Object clone() {
- PropertyHelper result;
- try {
- result = (PropertyHelper) super.clone();
- result.delegates = (Hashtable) delegates.clone();
- result.properties = (Hashtable) properties.clone();
- result.userProperties = (Hashtable) userProperties.clone();
- result.inheritedProperties = (Hashtable) inheritedProperties.clone();
- } catch (CloneNotSupportedException e) {
- throw new BuildException(e);
- }
- return result;
- }
}
diff --git a/src/main/org/apache/tools/ant/property/GetProperty.java b/src/main/org/apache/tools/ant/property/GetProperty.java
new file mode 100644
index 000000000..2ca2baa12
--- /dev/null
+++ b/src/main/org/apache/tools/ant/property/GetProperty.java
@@ -0,0 +1,28 @@
+/*
+ * 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.property;
+
+/** Interface to a class (normally PropertyHelper) to get a property */
+public interface GetProperty {
+ /**
+ * Returns the value of a property if it is set.
+ * @param name name of the property.
+ * @return the property value, or null for no match or for name being null.
+ */
+ Object getProperty(String name);
+}
diff --git a/src/main/org/apache/tools/ant/property/ParseNextProperty.java b/src/main/org/apache/tools/ant/property/ParseNextProperty.java
new file mode 100644
index 000000000..72dda73e1
--- /dev/null
+++ b/src/main/org/apache/tools/ant/property/ParseNextProperty.java
@@ -0,0 +1,40 @@
+/*
+ * 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.property;
+
+import java.text.ParsePosition;
+
+import org.apache.tools.ant.Project;
+
+/** Interface to parse a property */
+public interface ParseNextProperty {
+ /**
+ * Get the current project.
+ * @return the current ant project.
+ */
+ Project getProject();
+
+ /**
+ * 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.
+ */
+ Object parseNextProperty(String value, ParsePosition pos);
+}
diff --git a/src/main/org/apache/tools/ant/property/ParseProperties.java b/src/main/org/apache/tools/ant/property/ParseProperties.java
new file mode 100644
index 000000000..afb258696
--- /dev/null
+++ b/src/main/org/apache/tools/ant/property/ParseProperties.java
@@ -0,0 +1,153 @@
+/*
+ * 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.property;
+
+import java.text.ParsePosition;
+import java.util.Collection;
+import java.util.Iterator;
+import org.apache.tools.ant.Project;
+
+/**
+ * Parse properties using a collection of expanders.
+ */
+public class ParseProperties implements ParseNextProperty {
+
+ private final Project project;
+ private final GetProperty getProperty;
+ private final Collection expanders;
+
+ /**
+ * Constructor with a getProperty.
+ * @param project the current ant project.
+ * @param expanders a sequence of exapanders
+ * @param getProperty property resolver.
+ */
+ public ParseProperties(
+ Project project, Collection expanders, GetProperty getProperty) {
+ this.project = project;
+ this.expanders = expanders;
+ this.getProperty = getProperty;
+ }
+
+ /**
+ * Get the project.
+ * @return the current ant project.
+ */
+ public Project getProject() {
+ return project;
+ }
+
+ /**
+ * 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 null
, in which case this
+ * method returns immediately with no effect.
+ *
+ * @return the original string with the properties replaced, or
+ * null
if the original string is null
.
+ */
+ public Object parseProperties(String value) {
+ if (value == null || "".equals(value) || value.indexOf('$') == -1) {
+ return value;
+ }
+ ParsePosition pos = new ParsePosition(0);
+ Object o = parseNextProperty(value, pos);
+ if (o != null && pos.getIndex() == value.length()) {
+ return o;
+ }
+ StringBuffer sb = new StringBuffer(value.length() * 2);
+ if (o == null) {
+ sb.append(value.charAt(pos.getIndex()));
+ pos.setIndex(pos.getIndex() + 1);
+ } else {
+ sb.append(o);
+ }
+ while (pos.getIndex() < value.length()) {
+ o = parseNextProperty(value, pos);
+ if (o == null) {
+ sb.append(value.charAt(pos.getIndex()));
+ pos.setIndex(pos.getIndex() + 1);
+ } else {
+ sb.append(o);
+ }
+ }
+ return sb.toString();
+ }
+
+ /**
+ * Learn whether a String contains replaceable properties.
+ * @param value the String to check.
+ * @return true
if value
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;
+ }
+ if (project != null) {
+ project.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 = expanders.iterator(); iter.hasNext();) {
+ String propertyName = ((PropertyExpander) iter.next())
+ .parsePropertyName(value, pos, this);
+ if (propertyName == null) {
+ continue;
+ }
+ return propertyName;
+ }
+ return null;
+ }
+
+ private Object getProperty(String propertyName) {
+ return getProperty.getProperty(propertyName);
+ }
+}
diff --git a/src/main/org/apache/tools/ant/property/PropertyExpander.java b/src/main/org/apache/tools/ant/property/PropertyExpander.java
new file mode 100644
index 000000000..0fe44ee06
--- /dev/null
+++ b/src/main/org/apache/tools/ant/property/PropertyExpander.java
@@ -0,0 +1,36 @@
+/*
+ * 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.property;
+
+import org.apache.tools.ant.PropertyHelper;
+
+import java.text.ParsePosition;
+
+/** Interface to a class (normally PropertyHelper) to get a property */
+public interface PropertyExpander extends PropertyHelper.Delegate {
+ /**
+ * Parse the next property name.
+ * @param s the String to parse.
+ * @param pos the ParsePosition in use.
+ * @param parseNextProperty parse next property
+ * @return parsed String if any, else null
.
+ */
+ String parsePropertyName(
+ String s, ParsePosition pos, ParseNextProperty parseNextProperty);
+}
+
diff --git a/src/main/org/apache/tools/ant/property/ResolvePropertyMap.java b/src/main/org/apache/tools/ant/property/ResolvePropertyMap.java
new file mode 100644
index 000000000..2ec972425
--- /dev/null
+++ b/src/main/org/apache/tools/ant/property/ResolvePropertyMap.java
@@ -0,0 +1,87 @@
+/*
+ * 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.property;
+
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.HashSet;
+import java.util.Collection;
+
+import org.apache.tools.ant.Project;
+import org.apache.tools.ant.BuildException;
+
+/**
+ * Class to resolve properties in a map.
+ */
+public class ResolvePropertyMap implements GetProperty {
+ private final Set seen = new HashSet();
+ private final ParseProperties parseProperties;
+ private final GetProperty master;
+ private Map map;
+
+ /**
+ * Constructor with a master getproperty and a collection of expanders.
+ * @param project the current ant project.
+ * @param master the master property holder (usually PropertyHelper)
+ * @param expanders a collection of expanders (usually from PropertyHelper).
+ */
+ public ResolvePropertyMap(
+ Project project, GetProperty master, Collection expanders) {
+ this.master = master;
+ this.parseProperties = new ParseProperties(project, expanders, this);
+ }
+
+ /**
+ * Returns the value of a property if it is set.
+ * @param name name of the property.
+ * @return the property value, or null for no match or for name being null.
+ */
+ public Object getProperty(String name) {
+ if (seen.contains(name)) {
+ throw new BuildException(
+ "Property " + name + " was circularly " + "defined.");
+ }
+ // Note: the master overrides (even if the name is subsequently
+ // prefixed)
+ Object masterProperty = master.getProperty(name);
+ if (masterProperty != null) {
+ return masterProperty;
+ }
+ try {
+ seen.add(name);
+ return parseProperties.parseProperties((String) map.get(name));
+ } finally {
+ seen.remove(name);
+ }
+ }
+
+ /**
+ * The action method - resolves all the properties in a map.
+ * @param map the map to resolve properties in.
+ */
+ public void resolveAllProperties(Map map) {
+ this.map = map; // The map gets used in the getProperty callback
+ for (Iterator i = map.keySet().iterator(); i.hasNext();) {
+ String key = (String) i.next();
+ Object result = getProperty(key);
+ String value = result == null ? "" : result.toString();
+ map.put(key, value);
+ }
+ }
+}
diff --git a/src/main/org/apache/tools/ant/taskdefs/Property.java b/src/main/org/apache/tools/ant/taskdefs/Property.java
index b50ff76eb..0eb3bb151 100644
--- a/src/main/org/apache/tools/ant/taskdefs/Property.java
+++ b/src/main/org/apache/tools/ant/taskdefs/Property.java
@@ -28,7 +28,6 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
-import java.util.Stack;
import java.util.Vector;
import org.apache.tools.ant.BuildException;
@@ -38,6 +37,7 @@ import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.FileUtils;
+import org.apache.tools.ant.property.ResolvePropertyMap;
/**
* Sets a property by name, or set of properties (from file or
@@ -73,62 +73,6 @@ import org.apache.tools.ant.util.FileUtils;
* @ant.task category="property"
*/
public class Property extends Task {
- private static class PropertyResolver implements PropertyHelper.PropertyEvaluator {
- private ThreadLocal getStack = new ThreadLocal() {
- protected Object initialValue() {
- return new Stack();
- }
- };
- private ThreadLocal replaceStack = new ThreadLocal() {
- protected Object initialValue() {
- return new Stack();
- }
- };
- private Map map;
-
- /**
- * Construct a new Property.PropertyResolver instance.
- */
- public PropertyResolver(Map map) {
- this.map = map;
- }
-
- // CheckStyle:LineLengthCheck OFF see to long
- /* (non-Javadoc)
- * @see org.apache.tools.ant.PropertyHelper.PropertyEvaluator#evaluate(java.lang.String, org.apache.tools.ant.PropertyHelper)
- */
- // CheckStyle:LineLengthCheck ON
- public Object evaluate(String property, PropertyHelper propertyHelper) {
- //our feeble properties don't matter if the PropertyHelper
- // can resolve the property without us:
- Stack stk = (Stack) getStack.get();
- if (stk.contains(property)) {
- return null;
- }
- stk.push(property);
- try {
- if (propertyHelper.getProperty(property) != null) {
- return null;
- }
- } finally {
- stk.pop();
- }
- Object value = map.get(property);
- if (!(value instanceof String)) {
- return null;
- }
- stk = (Stack) replaceStack.get();
- if (stk.contains(property)) {
- throw new BuildException("Property " + property + " was circularly defined.");
- }
- stk.push(property);
- try {
- return propertyHelper.replaceProperties((String) value);
- } finally {
- stk.pop();
- }
- }
- }
// CheckStyle:VisibilityModifier OFF - bc
protected String name;
@@ -696,16 +640,12 @@ public class Property extends Task {
* @param props properties object to resolve
*/
private void resolveAllProperties(Map props) throws BuildException {
- PropertyHelper propertyHelper = (PropertyHelper) PropertyHelper.getPropertyHelper(
- getProject()).clone();
- propertyHelper.add(new PropertyResolver(props));
- for (Iterator it = props.keySet().iterator(); it.hasNext();) {
- Object k = it.next();
- if (k instanceof String) {
- Object value = propertyHelper.getProperty((String) k);
- props.put(k, value);
- }
- }
+ PropertyHelper propertyHelper
+ = (PropertyHelper) PropertyHelper.getPropertyHelper(getProject());
+ new ResolvePropertyMap(
+ getProject(),
+ propertyHelper,
+ propertyHelper.getExpanders()).resolveAllProperties(props);
}
}