Browse Source

Some cleanups of property expansion

* 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-ffa450edef68
master
Peter Reilly 18 years ago
parent
commit
902b72ec83
7 changed files with 373 additions and 173 deletions
  1. +22
    -106
      src/main/org/apache/tools/ant/PropertyHelper.java
  2. +28
    -0
      src/main/org/apache/tools/ant/property/GetProperty.java
  3. +40
    -0
      src/main/org/apache/tools/ant/property/ParseNextProperty.java
  4. +153
    -0
      src/main/org/apache/tools/ant/property/ParseProperties.java
  5. +36
    -0
      src/main/org/apache/tools/ant/property/PropertyExpander.java
  6. +87
    -0
      src/main/org/apache/tools/ant/property/ResolvePropertyMap.java
  7. +7
    -67
      src/main/org/apache/tools/ant/taskdefs/Property.java

+ 22
- 106
src/main/org/apache/tools/ant/PropertyHelper.java View File

@@ -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;
}
}

+ 28
- 0
src/main/org/apache/tools/ant/property/GetProperty.java View File

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

+ 40
- 0
src/main/org/apache/tools/ant/property/ParseNextProperty.java View File

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

+ 153
- 0
src/main/org/apache/tools/ant/property/ParseProperties.java View File

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

+ 36
- 0
src/main/org/apache/tools/ant/property/PropertyExpander.java View File

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


+ 87
- 0
src/main/org/apache/tools/ant/property/ResolvePropertyMap.java View File

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

+ 7
- 67
src/main/org/apache/tools/ant/taskdefs/Property.java View File

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

}

Loading…
Cancel
Save