diff --git a/WHATSNEW b/WHATSNEW index b085be75e..96920b736 100644 --- a/WHATSNEW +++ b/WHATSNEW @@ -7,6 +7,9 @@ Fixed bugs: * the ftp task could throw a NullPointerException if an error occured Bugzilla Report 64438 + * propertyset now also sees in-scope local properties + Bugzilla Report 50179 + Changes from Ant 1.10.7 TO Ant 1.10.8 ===================================== diff --git a/manual/properties.html b/manual/properties.html index c058af9d3..22476c4a0 100644 --- a/manual/properties.html +++ b/manual/properties.html @@ -154,6 +154,20 @@ this would be org.apache.tools.ant.property.LocalProperties which implements storage for local properties.

+ +
  • org.apache.tools.ant.PropertyHelper$PropertyEnumerator + is responsible for enumerating property names. + +

    This is the interface you'd implement if you want to provide + your own storage independent of Ant's project + instance—the interface represents part of the reading + end. An example for this would + be org.apache.tools.ant.property.LocalProperties + which implements storage for local + properties.

    + +

    This interface has been added with Ant 1.10.9.

    +
  • The default PropertyExpander looks similar to:

    diff --git a/src/etc/testcases/taskdefs/optional/echoproperties.xml b/src/etc/testcases/taskdefs/optional/echoproperties.xml index 449f1716e..661df13f7 100644 --- a/src/etc/testcases/taskdefs/optional/echoproperties.xml +++ b/src/etc/testcases/taskdefs/optional/echoproperties.xml @@ -123,6 +123,16 @@ + + + + + + + + + + diff --git a/src/main/org/apache/tools/ant/Project.java b/src/main/org/apache/tools/ant/Project.java index 3f3473651..6104e53e6 100644 --- a/src/main/org/apache/tools/ant/Project.java +++ b/src/main/org/apache/tools/ant/Project.java @@ -642,13 +642,23 @@ public class Project implements ResourceFactory { /** * Return a copy of the properties table. - * @return a hashtable containing all properties - * (including user properties). + * @return a hashtable containing all properties (including user + * properties) known to the project directly, does not + * contain local properties. */ public Hashtable getProperties() { return PropertyHelper.getPropertyHelper(this).getProperties(); } + /** + * Returns the names of all known properties. + * @since 1.10.9 + * @return the names of all known properties including local user and local properties. + */ + public Set getPropertyNames() { + return PropertyHelper.getPropertyHelper(this).getPropertyNames(); + } + /** * Return a copy of the user property hashtable. * @return a hashtable containing just the user properties. diff --git a/src/main/org/apache/tools/ant/PropertyHelper.java b/src/main/org/apache/tools/ant/PropertyHelper.java index 154ffdfd9..53ec6ee7a 100644 --- a/src/main/org/apache/tools/ant/PropertyHelper.java +++ b/src/main/org/apache/tools/ant/PropertyHelper.java @@ -161,7 +161,19 @@ public class PropertyHelper implements GetProperty { String property, Object value, PropertyHelper propertyHelper); } - //TODO PropertyEnumerator Delegate type, would improve PropertySet + /** + * Obtains the names of all known properties. + * + * @since 1.10.9 + */ + public interface PropertyEnumerator extends Delegate { + /** + * Returns the names of all properties known to this delegate. + * + * @return the names of all properties known to this delegate. + */ + Set getPropertyNames(); + } // -------------------------------------------------------- // @@ -842,6 +854,18 @@ public class PropertyHelper implements GetProperty { return properties.get(name); } + /** + * Returns the names of all known properties. + * @since 1.10.9 + * @return the names of all known properties. + */ + public Set getPropertyNames() { + final Set names = new HashSet<>(properties.keySet()); + getDelegates(PropertyEnumerator.class) + .forEach(e -> names.addAll(e.getPropertyNames())); + return Collections.unmodifiableSet(names); + } + /** * Returns the value of a user property, if it is set. * @@ -1014,7 +1038,7 @@ public class PropertyHelper implements GetProperty { // 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(). diff --git a/src/main/org/apache/tools/ant/property/LocalProperties.java b/src/main/org/apache/tools/ant/property/LocalProperties.java index 1dcbc2667..327f978ba 100644 --- a/src/main/org/apache/tools/ant/property/LocalProperties.java +++ b/src/main/org/apache/tools/ant/property/LocalProperties.java @@ -21,6 +21,8 @@ import org.apache.tools.ant.MagicNames; import org.apache.tools.ant.Project; import org.apache.tools.ant.PropertyHelper; +import java.util.Set; + /** * Thread local class containing local properties. * @since Ant 1.8.0 @@ -28,7 +30,7 @@ import org.apache.tools.ant.PropertyHelper; public class LocalProperties extends InheritableThreadLocal implements PropertyHelper.PropertyEvaluator, - PropertyHelper.PropertySetter { + PropertyHelper.PropertySetter, PropertyHelper.PropertyEnumerator { /** * Get a localproperties for the given project. @@ -147,4 +149,9 @@ public class LocalProperties String property, Object value, PropertyHelper propertyHelper) { return get().set(property, value, propertyHelper); } + + @Override + public Set getPropertyNames() { + return get().getPropertyNames(); + } } diff --git a/src/main/org/apache/tools/ant/property/LocalPropertyStack.java b/src/main/org/apache/tools/ant/property/LocalPropertyStack.java index 3b80b43f2..95b301f42 100644 --- a/src/main/org/apache/tools/ant/property/LocalPropertyStack.java +++ b/src/main/org/apache/tools/ant/property/LocalPropertyStack.java @@ -17,10 +17,14 @@ */ package org.apache.tools.ant.property; +import java.util.Collections; import java.util.Deque; +import java.util.HashSet; import java.util.LinkedList; import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collector; import org.apache.tools.ant.PropertyHelper; @@ -148,6 +152,20 @@ public class LocalPropertyStack { return true; } + /** + * Returns the names of all known local properties. + * @since 1.10.9 + * @return the names of all known local properties. + */ + public Set getPropertyNames() { + final Set names = stack.stream().map(Map::keySet) + .collect(Collector.of(() -> new HashSet(), + (ns, ks) -> ns.addAll(ks), + (ns1, ns2) -> { ns1.addAll(ns2); return ns1; }, + Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH)); + return Collections.unmodifiableSet(names); + } + private Map getMapForProperty(String property) { synchronized (LOCK) { for (Map map : stack) { diff --git a/src/main/org/apache/tools/ant/types/PropertySet.java b/src/main/org/apache/tools/ant/types/PropertySet.java index 21d59fe27..b1b7d7934 100644 --- a/src/main/org/apache/tools/ant/types/PropertySet.java +++ b/src/main/org/apache/tools/ant/types/PropertySet.java @@ -18,6 +18,7 @@ package org.apache.tools.ant.types; +import java.util.AbstractMap; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -34,6 +35,7 @@ import java.util.stream.Stream; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Project; +import org.apache.tools.ant.PropertyHelper; import org.apache.tools.ant.types.resources.MappedResource; import org.apache.tools.ant.types.resources.PropertyResource; import org.apache.tools.ant.util.FileNameMapper; @@ -334,7 +336,16 @@ public class PropertySet extends DataType implements ResourceCollection { private Map getEffectiveProperties() { final Project prj = getProject(); - final Map result = prj == null ? getAllSystemProperties() : prj.getProperties(); + final Map result; + if (prj == null) { + result = getAllSystemProperties(); + } else { + final PropertyHelper ph = PropertyHelper.getPropertyHelper(prj); + result = prj.getPropertyNames().stream() + .map(n -> new AbstractMap.SimpleImmutableEntry<>(n, ph.getProperty(n))) + .filter(kv -> kv.getValue() != null) + .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + } //quick & dirty, to make nested mapped p-sets work: for (PropertySet set : setRefs) { result.putAll(set.getPropertyMap()); diff --git a/src/tests/junit/org/apache/tools/ant/PropertyHelperTest.java b/src/tests/junit/org/apache/tools/ant/PropertyHelperTest.java new file mode 100644 index 000000000..883e5f148 --- /dev/null +++ b/src/tests/junit/org/apache/tools/ant/PropertyHelperTest.java @@ -0,0 +1,55 @@ +/* + * 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 + * + * https://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 org.apache.tools.ant.property.LocalProperties; +import org.junit.Test; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class PropertyHelperTest { + + @Test + public void findsPropertyNamesSetDirectly() { + Project p = new Project(); + p.setNewProperty("foo", "bar"); + assertTrue(p.getPropertyNames().contains("foo")); + } + + @Test + public void findsPropertyNamesSetForLocalProperties() { + Project p = new Project(); + p.setNewProperty("foo", "bar"); + + LocalProperties localProperties = LocalProperties.get(p); + localProperties.enterScope(); + localProperties.addLocal("baz"); + p.setNewProperty("baz", "xyzzy"); + + assertTrue(p.getPropertyNames().contains("foo")); + assertTrue(p.getPropertyNames().contains("baz")); + assertTrue(p.getProperties().keySet().contains("foo")); + assertFalse(p.getProperties().keySet().contains("baz")); + localProperties.exitScope(); + + assertTrue(p.getPropertyNames().contains("foo")); + assertFalse(p.getPropertyNames().contains("baz")); + } +} diff --git a/src/tests/junit/org/apache/tools/ant/taskdefs/optional/EchoPropertiesTest.java b/src/tests/junit/org/apache/tools/ant/taskdefs/optional/EchoPropertiesTest.java index 0c561a4b3..b2c30541e 100644 --- a/src/tests/junit/org/apache/tools/ant/taskdefs/optional/EchoPropertiesTest.java +++ b/src/tests/junit/org/apache/tools/ant/taskdefs/optional/EchoPropertiesTest.java @@ -186,6 +186,12 @@ public class EchoPropertiesTest { assertThat(buildRule.getFullLog(), containsString(MagicNames.ANT_VERSION + "=")); } + @Test + public void testLocalPropertyset() { + buildRule.executeTarget("testEchoLocalPropertyset"); + assertThat(buildRule.getLog(), containsString("loc=foo")); + } + private void testEchoPrefixVarious(String target) throws Exception { buildRule.executeTarget(target); Properties props = loadPropFile(PREFIX_OUTFILE);