/*
 *  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.types;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Properties;
import java.util.Set;
import java.util.Stack;
import java.util.TreeMap;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.resources.MappedResource;
import org.apache.tools.ant.types.resources.PropertyResource;
import org.apache.tools.ant.util.FileNameMapper;
import org.apache.tools.ant.util.regexp.RegexpMatcher;
import org.apache.tools.ant.util.regexp.RegexpMatcherFactory;

A set of properties.
Since:Ant 1.6
/** * A set of properties. * * @since Ant 1.6 */
public class PropertySet extends DataType implements ResourceCollection { private boolean dynamic = true; private boolean negate = false; private Set<String> cachedNames; private List<PropertyRef> ptyRefs = new ArrayList<>(); private List<PropertySet> setRefs = new ArrayList<>(); private Mapper mapper;
This is a nested class containing a reference to some properties and optionally a source of properties.
/** * This is a nested class containing a reference to some properties * and optionally a source of properties. */
public static class PropertyRef { private int count; private String name; private String regex; private String prefix; private String builtin;
Set the name.
Params:
  • name – a String value.
/** * Set the name. * @param name a <code>String</code> value. */
public void setName(String name) { assertValid("name", name); this.name = name; }
Set the regular expression to use to filter the properties.
Params:
  • regex – a regular expression.
/** * Set the regular expression to use to filter the properties. * @param regex a regular expression. */
public void setRegex(String regex) { assertValid("regex", regex); this.regex = regex; }
Set the prefix to use.
Params:
  • prefix – a String value.
/** * Set the prefix to use. * @param prefix a <code>String</code> value. */
public void setPrefix(String prefix) { assertValid("prefix", prefix); this.prefix = prefix; }
Builtin property names - all, system or commandline.
Params:
  • b – an enumerated BuildinPropertySetName value.
/** * Builtin property names - all, system or commandline. * @param b an enumerated <code>BuildinPropertySetName</code> value. */
public void setBuiltin(BuiltinPropertySetName b) { String pBuiltIn = b.getValue(); assertValid("builtin", pBuiltIn); this.builtin = pBuiltIn; } private void assertValid(String attr, String value) { if (value == null || value.length() < 1) { throw new BuildException("Invalid attribute: " + attr); } if (++count != 1) { throw new BuildException("Attributes name, regex, and " + "prefix are mutually exclusive"); } }
A debug toString().
Returns:a string version of this object.
/** * A debug toString(). * @return a string version of this object. */
@Override public String toString() { return "name=" + name + ", regex=" + regex + ", prefix=" + prefix + ", builtin=" + builtin; } } //end nested class
Allow properties of a particular name in the set.
Params:
  • name – the property name to allow.
/** * Allow properties of a particular name in the set. * @param name the property name to allow. */
public void appendName(String name) { PropertyRef r = new PropertyRef(); r.setName(name); addPropertyref(r); }
Allow properties whose names match a regex in the set.
Params:
  • regex – the regular expression to use.
/** * Allow properties whose names match a regex in the set. * @param regex the regular expression to use. */
public void appendRegex(String regex) { PropertyRef r = new PropertyRef(); r.setRegex(regex); addPropertyref(r); }
Allow properties whose names start with a prefix in the set.
Params:
  • prefix – the prefix to use.
/** * Allow properties whose names start with a prefix in the set. * @param prefix the prefix to use. */
public void appendPrefix(String prefix) { PropertyRef r = new PropertyRef(); r.setPrefix(prefix); addPropertyref(r); }
Allow builtin (all, system or commandline) properties in the set.
Params:
  • b – the type of builtin properties.
/** * Allow builtin (all, system or commandline) properties in the set. * @param b the type of builtin properties. */
public void appendBuiltin(BuiltinPropertySetName b) { PropertyRef r = new PropertyRef(); r.setBuiltin(b); addPropertyref(r); }
Set a mapper to change property names.
Params:
  • type – mapper type.
  • from – source pattern.
  • to – output pattern.
/** * Set a mapper to change property names. * @param type mapper type. * @param from source pattern. * @param to output pattern. */
public void setMapper(String type, String from, String to) { Mapper m = createMapper(); Mapper.MapperType mapperType = new Mapper.MapperType(); mapperType.setValue(type); m.setType(mapperType); m.setFrom(from); m.setTo(to); }
Add a property reference (nested element) to the references to be used.
Params:
  • ref – a property reference.
/** * Add a property reference (nested element) to the references to be used. * @param ref a property reference. */
public void addPropertyref(PropertyRef ref) { assertNotReference(); setChecked(false); ptyRefs.add(ref); }
Add another property set to this set.
Params:
  • ref – another property set.
/** * Add another property set to this set. * @param ref another property set. */
public void addPropertyset(PropertySet ref) { assertNotReference(); setChecked(false); setRefs.add(ref); }
Create a mapper to map the property names.
Returns:a mapper to be configured.
/** * Create a mapper to map the property names. * @return a mapper to be configured. */
public Mapper createMapper() { assertNotReference(); if (mapper != null) { throw new BuildException("Too many <mapper>s!"); } mapper = new Mapper(getProject()); setChecked(false); return mapper; }
Add a nested FileNameMapper.
Params:
  • fileNameMapper – the mapper to add.
Since:Ant 1.6.3
/** * Add a nested FileNameMapper. * @param fileNameMapper the mapper to add. * @since Ant 1.6.3 */
public void add(FileNameMapper fileNameMapper) { createMapper().add(fileNameMapper); }
Set whether to reevaluate the set every time the set is used. Default is true.
Params:
  • dynamic – if true, reevaluate the property set each time the set is used. if false cache the property set the first time and use the cached set on subsequent occasions.
/** * Set whether to reevaluate the set every time the set is used. * Default is true. * * @param dynamic if true, reevaluate the property set each time * the set is used. if false cache the property set * the first time and use the cached set on subsequent * occasions. */
public void setDynamic(boolean dynamic) { assertNotReference(); this.dynamic = dynamic; }
Set whether to negate results. If "true", all properties not selected by nested elements will be returned. Default is "false".
Params:
  • negate – if true, negate the selection criteria.
/** * Set whether to negate results. * If "true", all properties not selected by nested elements will be returned. * Default is "false". * @param negate if true, negate the selection criteria. */
public void setNegate(boolean negate) { assertNotReference(); this.negate = negate; }
Get the dynamic attribute.
Returns:true if the property set is to be evaluated each time it is used.
/** * Get the dynamic attribute. * @return true if the property set is to be evaluated each time it is used. */
public boolean getDynamic() { if (isReference()) { return getRef().dynamic; } dieOnCircularReference(); return dynamic; }
Get the mapper attribute.
Returns:the mapper attribute.
/** * Get the mapper attribute. * @return the mapper attribute. */
public Mapper getMapper() { if (isReference()) { return getRef().mapper; } dieOnCircularReference(); return mapper; }
Convert the system properties to a Map. Use stringPropertyNames to get the list of properties (including default ones).
/** * Convert the system properties to a Map. * Use stringPropertyNames to get the list of properties (including * default ones). */
private Map<String, Object> getAllSystemProperties() { return System.getProperties().stringPropertyNames().stream() .collect(Collectors.toMap(name -> name, name -> System.getProperties().getProperty(name), (a, b) -> b)); }
This is the operation to get the existing or recalculated properties.
Returns:the properties for this propertyset.
/** * This is the operation to get the existing or recalculated properties. * @return the properties for this propertyset. */
public Properties getProperties() { final Properties result = new Properties(); result.putAll(getPropertyMap()); return result; }
Returns:Map
Since:1.9.0
/** * * @return Map * @since 1.9.0 */
private Map<String, Object> getPropertyMap() { if (isReference()) { return getRef().getPropertyMap(); } dieOnCircularReference(); final Mapper myMapper = getMapper(); final FileNameMapper m = myMapper == null ? null : myMapper.getImplementation(); final Map<String, Object> effectiveProperties = getEffectiveProperties(); final Set<String> propertyNames = getPropertyNames(effectiveProperties); final Map<String, Object> result = new HashMap<>(); //iterate through the names, get the matching values for (String name : propertyNames) { Object value = effectiveProperties.get(name); // TODO should we include null properties? // TODO should we query the PropertyHelper for property value to grab potentially shadowed values? if (value != null) { // may be null if a system property has been added // after the project instance has been initialized if (m != null) { //map the names String[] newname = m.mapFileName(name); if (newname != null) { name = newname[0]; } } result.put(name, value); } } return result; } private Map<String, Object> getEffectiveProperties() { final Project prj = getProject(); final Map<String, Object> result = prj == null ? getAllSystemProperties() : prj.getProperties(); //quick & dirty, to make nested mapped p-sets work: for (PropertySet set : setRefs) { result.putAll(set.getPropertyMap()); } return result; } private Set<String> getPropertyNames(Map<String, Object> props) { Set<String> names; if (getDynamic() || cachedNames == null) { names = new HashSet<>(); addPropertyNames(names, props); // Add this PropertySet's nested PropertySets' property names. for (PropertySet set : setRefs) { names.addAll(set.getPropertyMap().keySet()); } if (negate) { //make a copy... HashSet<String> complement = new HashSet<>(props.keySet()); complement.removeAll(names); names = complement; } if (!getDynamic()) { cachedNames = names; } } else { names = cachedNames; } return names; }
Params:
  • names – the output Set to fill with the property names matching this PropertySet selection criteria.
  • props – the current Project properties, passed in to avoid needless duplication of the Hashtable during recursion.
/** * @param names the output Set to fill with the property names * matching this PropertySet selection criteria. * @param props the current Project properties, passed in to * avoid needless duplication of the Hashtable during recursion. */
private void addPropertyNames(Set<String> names, Map<String, Object> props) { if (isReference()) { getRef().addPropertyNames(names, props); } dieOnCircularReference(); // Add this PropertySet's property names. for (PropertyRef r : ptyRefs) { if (r.name != null) { if (props.get(r.name) != null) { names.add(r.name); } } else if (r.prefix != null) { for (String name : props.keySet()) { if (name.startsWith(r.prefix)) { names.add(name); } } } else if (r.regex != null) { RegexpMatcherFactory matchMaker = new RegexpMatcherFactory(); RegexpMatcher matcher = matchMaker.newRegexpMatcher(); matcher.setPattern(r.regex); for (String name : props.keySet()) { if (matcher.matches(name)) { names.add(name); } } } else if (r.builtin != null) { switch (r.builtin) { case BuiltinPropertySetName.ALL: names.addAll(props.keySet()); break; case BuiltinPropertySetName.SYSTEM: names.addAll(getAllSystemProperties().keySet()); break; case BuiltinPropertySetName.COMMANDLINE: names.addAll(getProject().getUserProperties().keySet()); break; default: throw new BuildException("Impossible: Invalid builtin " + "attribute!"); } } else { throw new BuildException("Impossible: Invalid PropertyRef!"); } } }
Performs the check for circular references and returns the referenced PropertySet.
Returns:the referenced PropertySet.
/** * Performs the check for circular references and returns the * referenced PropertySet. * @return the referenced PropertySet. */
protected PropertySet getRef() { return getCheckedRef(PropertySet.class); }
Sets the value of the refid attribute.
Params:
  • r – the reference this datatype should point to.
Throws:
  • BuildException – if another attribute was set, since refid and all other attributes are mutually exclusive.
/** * Sets the value of the refid attribute. * * @param r the reference this datatype should point to. * @throws BuildException if another attribute was set, since * refid and all other attributes are mutually exclusive. */
@Override public final void setRefid(Reference r) { if (!noAttributeSet) { throw tooManyAttributes(); } super.setRefid(r); }
Ensures this data type is not a reference.

Calling this method as the first line of every bean method of this data type (setXyz, addXyz, createXyz) ensure proper handling of the refid attribute.

Throws:
  • BuildException – if the refid attribute was already set, since refid and all other attributes are mutually exclusive.
/** * Ensures this data type is not a reference. * * <p>Calling this method as the first line of every bean method of * this data type (setXyz, addXyz, createXyz) ensure proper handling * of the refid attribute.</p> * * @throws BuildException if the refid attribute was already set, since * refid and all other attributes are mutually exclusive. */
protected final void assertNotReference() { if (isReference()) { throw tooManyAttributes(); } noAttributeSet = false; }
Flag which tracks whether any attribute has been set; used by assertNotReference() and setRefid(Reference).
/** * Flag which tracks whether any attribute has been set; used by * {@link #assertNotReference()} and {@link #setRefid(Reference)}. */
private boolean noAttributeSet = true;
Used for propertyref's builtin attribute.
/** * Used for propertyref's builtin attribute. */
public static class BuiltinPropertySetName extends EnumeratedAttribute { static final String ALL = "all"; static final String SYSTEM = "system"; static final String COMMANDLINE = "commandline";
{@inheritDoc}.
/** {@inheritDoc}. */
@Override public String[] getValues() { return new String[] {ALL, SYSTEM, COMMANDLINE}; } }
A debug toString. This gets a comma separated list of key=value pairs for the properties in the set. The output order is sorted according to the keys' natural order.
Returns:a string rep of this object.
/** * A debug toString. * This gets a comma separated list of key=value pairs for * the properties in the set. * The output order is sorted according to the keys' <i>natural order</i>. * @return a string rep of this object. */
@Override public String toString() { if (isReference()) { return getRef().toString(); } dieOnCircularReference(); return new TreeMap<>(getPropertyMap()).entrySet().stream() .map(e -> e.getKey() + "=" + e.getValue()).collect(Collectors.joining(", ")); }
Fulfill the ResourceCollection interface.
Returns:an Iterator of Resources.
Since:Ant 1.7
/** * Fulfill the ResourceCollection interface. * @return an Iterator of Resources. * @since Ant 1.7 */
@Override public Iterator<Resource> iterator() { if (isReference()) { return getRef().iterator(); } dieOnCircularReference(); Stream<Resource> result = getPropertyNames(getEffectiveProperties()) .stream().map(name -> new PropertyResource(getProject(), name)); Optional<FileNameMapper> m = Optional.ofNullable(getMapper()).map(Mapper::getImplementation); if (m.isPresent()) { result = result.map(p -> new MappedResource(p, m.get())); } return result.iterator(); }
Fulfill the ResourceCollection contract.
Returns:the size of this ResourceCollection.
/** * Fulfill the ResourceCollection contract. * @return the size of this ResourceCollection. */
@Override public int size() { return isReference() ? getRef().size() : getProperties().size(); }
Fulfill the ResourceCollection contract.
Returns:whether this is a filesystem-only resource collection.
/** * Fulfill the ResourceCollection contract. * @return whether this is a filesystem-only resource collection. */
@Override public boolean isFilesystemOnly() { if (isReference()) { return getRef().isFilesystemOnly(); } dieOnCircularReference(); return false; } @Override protected synchronized void dieOnCircularReference(Stack<Object> stk, Project p) throws BuildException { if (isChecked()) { return; } if (isReference()) { super.dieOnCircularReference(stk, p); } else { if (mapper != null) { pushAndInvokeCircularReferenceCheck(mapper, stk, p); } for (PropertySet propertySet : setRefs) { pushAndInvokeCircularReferenceCheck(propertySet, stk, p); } setChecked(true); } } }