| @@ -7,6 +7,9 @@ Fixed bugs: | |||||
| * the ftp task could throw a NullPointerException if an error occured | * the ftp task could throw a NullPointerException if an error occured | ||||
| Bugzilla Report 64438 | 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 | Changes from Ant 1.10.7 TO Ant 1.10.8 | ||||
| ===================================== | ===================================== | ||||
| @@ -154,6 +154,20 @@ | |||||
| this would be <code class="code">org.apache.tools.ant.property.LocalProperties</code> which | this would be <code class="code">org.apache.tools.ant.property.LocalProperties</code> which | ||||
| implements storage for <a href="Tasks/local.html">local properties</a>.</p> | implements storage for <a href="Tasks/local.html">local properties</a>.</p> | ||||
| </li> | </li> | ||||
| <li><code class="code">org.apache.tools.ant.PropertyHelper$PropertyEnumerator</code> | |||||
| is responsible for enumerating property names. | |||||
| <p>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 <code class="code">org.apache.tools.ant.property.LocalProperties</code> | |||||
| which implements storage for <a href="Tasks/local.html">local | |||||
| properties</a>.</p> | |||||
| <p><em>This interface has been added with Ant 1.10.9.</em></p> | |||||
| </li> | |||||
| </ul> | </ul> | ||||
| <p>The default <code class="code">PropertyExpander</code> looks similar to:</p> | <p>The default <code class="code">PropertyExpander</code> looks similar to:</p> | ||||
| @@ -123,6 +123,16 @@ | |||||
| <echoproperties regex=".*ant.*"/> | <echoproperties regex=".*ant.*"/> | ||||
| </target> | </target> | ||||
| <target name="testEchoLocalPropertyset"> | |||||
| <local name="loc"/> | |||||
| <property name="loc" value="foo"/> | |||||
| <echoproperties> | |||||
| <propertyset> | |||||
| <propertyref name="loc"/> | |||||
| </propertyset> | |||||
| </echoproperties> | |||||
| </target> | |||||
| <target name="cleanup"> | <target name="cleanup"> | ||||
| <delete file="test.properties" failonerror="no" /> | <delete file="test.properties" failonerror="no" /> | ||||
| <delete file="test-prefix.properties" failonerror="no" /> | <delete file="test-prefix.properties" failonerror="no" /> | ||||
| @@ -642,13 +642,23 @@ public class Project implements ResourceFactory { | |||||
| /** | /** | ||||
| * Return a copy of the properties table. | * 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<String, Object> getProperties() { | public Hashtable<String, Object> getProperties() { | ||||
| return PropertyHelper.getPropertyHelper(this).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<String> getPropertyNames() { | |||||
| return PropertyHelper.getPropertyHelper(this).getPropertyNames(); | |||||
| } | |||||
| /** | /** | ||||
| * Return a copy of the user property hashtable. | * Return a copy of the user property hashtable. | ||||
| * @return a hashtable containing just the user properties. | * @return a hashtable containing just the user properties. | ||||
| @@ -161,7 +161,19 @@ public class PropertyHelper implements GetProperty { | |||||
| String property, Object value, PropertyHelper propertyHelper); | 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<String> getPropertyNames(); | |||||
| } | |||||
| // -------------------------------------------------------- | // -------------------------------------------------------- | ||||
| // | // | ||||
| @@ -842,6 +854,18 @@ public class PropertyHelper implements GetProperty { | |||||
| return properties.get(name); | return properties.get(name); | ||||
| } | } | ||||
| /** | |||||
| * Returns the names of all known properties. | |||||
| * @since 1.10.9 | |||||
| * @return the names of all known properties. | |||||
| */ | |||||
| public Set<String> getPropertyNames() { | |||||
| final Set<String> 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. | * 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 - | // Moved from ProjectHelper. You can override the static method - | ||||
| // this is used for backward compatibility (for code that calls | // this is used for backward compatibility (for code that calls | ||||
| // the parse method in ProjectHelper). | // the parse method in ProjectHelper). | ||||
| /** | /** | ||||
| * Default parsing method. It is here only to support backward compatibility | * Default parsing method. It is here only to support backward compatibility | ||||
| * for the static ProjectHelper.parsePropertyString(). | * for the static ProjectHelper.parsePropertyString(). | ||||
| @@ -21,6 +21,8 @@ import org.apache.tools.ant.MagicNames; | |||||
| import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
| import org.apache.tools.ant.PropertyHelper; | import org.apache.tools.ant.PropertyHelper; | ||||
| import java.util.Set; | |||||
| /** | /** | ||||
| * Thread local class containing local properties. | * Thread local class containing local properties. | ||||
| * @since Ant 1.8.0 | * @since Ant 1.8.0 | ||||
| @@ -28,7 +30,7 @@ import org.apache.tools.ant.PropertyHelper; | |||||
| public class LocalProperties | public class LocalProperties | ||||
| extends InheritableThreadLocal<LocalPropertyStack> | extends InheritableThreadLocal<LocalPropertyStack> | ||||
| implements PropertyHelper.PropertyEvaluator, | implements PropertyHelper.PropertyEvaluator, | ||||
| PropertyHelper.PropertySetter { | |||||
| PropertyHelper.PropertySetter, PropertyHelper.PropertyEnumerator { | |||||
| /** | /** | ||||
| * Get a localproperties for the given project. | * Get a localproperties for the given project. | ||||
| @@ -147,4 +149,9 @@ public class LocalProperties | |||||
| String property, Object value, PropertyHelper propertyHelper) { | String property, Object value, PropertyHelper propertyHelper) { | ||||
| return get().set(property, value, propertyHelper); | return get().set(property, value, propertyHelper); | ||||
| } | } | ||||
| @Override | |||||
| public Set<String> getPropertyNames() { | |||||
| return get().getPropertyNames(); | |||||
| } | |||||
| } | } | ||||
| @@ -17,10 +17,14 @@ | |||||
| */ | */ | ||||
| package org.apache.tools.ant.property; | package org.apache.tools.ant.property; | ||||
| import java.util.Collections; | |||||
| import java.util.Deque; | import java.util.Deque; | ||||
| import java.util.HashSet; | |||||
| import java.util.LinkedList; | import java.util.LinkedList; | ||||
| import java.util.Map; | import java.util.Map; | ||||
| import java.util.Set; | |||||
| import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
| import java.util.stream.Collector; | |||||
| import org.apache.tools.ant.PropertyHelper; | import org.apache.tools.ant.PropertyHelper; | ||||
| @@ -148,6 +152,20 @@ public class LocalPropertyStack { | |||||
| return true; | return true; | ||||
| } | } | ||||
| /** | |||||
| * Returns the names of all known local properties. | |||||
| * @since 1.10.9 | |||||
| * @return the names of all known local properties. | |||||
| */ | |||||
| public Set<String> getPropertyNames() { | |||||
| final Set<String> names = stack.stream().map(Map::keySet) | |||||
| .collect(Collector.of(() -> new HashSet<String>(), | |||||
| (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<String, Object> getMapForProperty(String property) { | private Map<String, Object> getMapForProperty(String property) { | ||||
| synchronized (LOCK) { | synchronized (LOCK) { | ||||
| for (Map<String, Object> map : stack) { | for (Map<String, Object> map : stack) { | ||||
| @@ -18,6 +18,7 @@ | |||||
| package org.apache.tools.ant.types; | package org.apache.tools.ant.types; | ||||
| import java.util.AbstractMap; | |||||
| import java.util.ArrayList; | import java.util.ArrayList; | ||||
| import java.util.HashMap; | import java.util.HashMap; | ||||
| import java.util.HashSet; | import java.util.HashSet; | ||||
| @@ -34,6 +35,7 @@ import java.util.stream.Stream; | |||||
| import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
| import org.apache.tools.ant.Project; | 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.MappedResource; | ||||
| import org.apache.tools.ant.types.resources.PropertyResource; | import org.apache.tools.ant.types.resources.PropertyResource; | ||||
| import org.apache.tools.ant.util.FileNameMapper; | import org.apache.tools.ant.util.FileNameMapper; | ||||
| @@ -334,7 +336,16 @@ public class PropertySet extends DataType implements ResourceCollection { | |||||
| private Map<String, Object> getEffectiveProperties() { | private Map<String, Object> getEffectiveProperties() { | ||||
| final Project prj = getProject(); | final Project prj = getProject(); | ||||
| final Map<String, Object> result = prj == null ? getAllSystemProperties() : prj.getProperties(); | |||||
| final Map<String, Object> 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: | //quick & dirty, to make nested mapped p-sets work: | ||||
| for (PropertySet set : setRefs) { | for (PropertySet set : setRefs) { | ||||
| result.putAll(set.getPropertyMap()); | result.putAll(set.getPropertyMap()); | ||||
| @@ -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")); | |||||
| } | |||||
| } | |||||
| @@ -186,6 +186,12 @@ public class EchoPropertiesTest { | |||||
| assertThat(buildRule.getFullLog(), containsString(MagicNames.ANT_VERSION + "=")); | 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 { | private void testEchoPrefixVarious(String target) throws Exception { | ||||
| buildRule.executeTarget(target); | buildRule.executeTarget(target); | ||||
| Properties props = loadPropFile(PREFIX_OUTFILE); | Properties props = loadPropFile(PREFIX_OUTFILE); | ||||