@@ -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 | |||
===================================== | |||
@@ -154,6 +154,20 @@ | |||
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> | |||
</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> | |||
<p>The default <code class="code">PropertyExpander</code> looks similar to:</p> | |||
@@ -123,6 +123,16 @@ | |||
<echoproperties regex=".*ant.*"/> | |||
</target> | |||
<target name="testEchoLocalPropertyset"> | |||
<local name="loc"/> | |||
<property name="loc" value="foo"/> | |||
<echoproperties> | |||
<propertyset> | |||
<propertyref name="loc"/> | |||
</propertyset> | |||
</echoproperties> | |||
</target> | |||
<target name="cleanup"> | |||
<delete file="test.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 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() { | |||
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 hashtable containing just the user properties. | |||
@@ -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<String> 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<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. | |||
* | |||
@@ -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(). | |||
@@ -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<LocalPropertyStack> | |||
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<String> getPropertyNames() { | |||
return get().getPropertyNames(); | |||
} | |||
} |
@@ -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<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) { | |||
synchronized (LOCK) { | |||
for (Map<String, Object> map : stack) { | |||
@@ -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<String, Object> getEffectiveProperties() { | |||
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: | |||
for (PropertySet set : setRefs) { | |||
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 + "=")); | |||
} | |||
@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); | |||