* 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); | |||
| } | |||
| } | |||