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