* extact expand property from PropertyExpander * extact resolve property map from Property task * remove use of ThreadLocal from Property task * remove need of cloning of PropertyHelper when resolving properties in a map. git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@578769 13f79535-47bb-0310-9956-ffa450edef68master
@@ -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 <code>null</code>. | |||
*/ | |||
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 { | |||
* <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; | |||
} | |||
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 <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; | |||
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; | |||
} | |||
} |
@@ -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); | |||
} |
@@ -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); | |||
} |
@@ -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 <code>null</code>, in which case this | |||
* method returns immediately with no effect. | |||
* | |||
* @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) { | |||
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 <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; | |||
} | |||
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); | |||
} | |||
} |
@@ -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 <code>null</code>. | |||
*/ | |||
String parsePropertyName( | |||
String s, ParsePosition pos, ParseNextProperty parseNextProperty); | |||
} | |||
@@ -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); | |||
} | |||
} | |||
} |
@@ -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); | |||
} | |||
} |