001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.types;
020:
021: import java.util.Enumeration;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.HashSet;
025: import java.util.Set;
026: import java.util.TreeMap;
027: import java.util.Hashtable;
028: import java.util.Properties;
029: import java.util.Vector;
030:
031: import org.apache.tools.ant.BuildException;
032: import org.apache.tools.ant.Project;
033: import org.apache.tools.ant.types.resources.PropertyResource;
034: import org.apache.tools.ant.util.FileNameMapper;
035: import org.apache.tools.ant.util.regexp.RegexpMatcher;
036: import org.apache.tools.ant.util.regexp.RegexpMatcherFactory;
037:
038: /**
039: * A set of properties.
040: *
041: * @since Ant 1.6
042: */
043: public class PropertySet extends DataType implements ResourceCollection {
044:
045: private boolean dynamic = true;
046: private boolean negate = false;
047: private Set cachedNames;
048: private Vector ptyRefs = new Vector();
049: private Vector setRefs = new Vector();
050: private Mapper mapper;
051:
052: /**
053: * This is a nested class containing a reference to some properties
054: * and optionally a source of properties.
055: */
056: public static class PropertyRef {
057:
058: private int count;
059: private String name;
060: private String regex;
061: private String prefix;
062: private String builtin;
063:
064: /**
065: * Set the name.
066: * @param name a <code>String</code> value.
067: */
068: public void setName(String name) {
069: assertValid("name", name);
070: this .name = name;
071: }
072:
073: /**
074: * Set the regular expression to use to filter the properties.
075: * @param regex a regular expression.
076: */
077: public void setRegex(String regex) {
078: assertValid("regex", regex);
079: this .regex = regex;
080: }
081:
082: /**
083: * Set the prefix to use.
084: * @param prefix a <code>String</code> value.
085: */
086: public void setPrefix(String prefix) {
087: assertValid("prefix", prefix);
088: this .prefix = prefix;
089: }
090:
091: /**
092: * Builtin property names - all, system or commandline.
093: * @param b an enumerated <code>BuildinPropertySetName</code> value.
094: */
095: public void setBuiltin(BuiltinPropertySetName b) {
096: String pBuiltIn = b.getValue();
097: assertValid("builtin", pBuiltIn);
098: this .builtin = pBuiltIn;
099: }
100:
101: private void assertValid(String attr, String value) {
102: if (value == null || value.length() < 1) {
103: throw new BuildException("Invalid attribute: " + attr);
104: }
105:
106: if (++count != 1) {
107: throw new BuildException("Attributes name, regex, and "
108: + "prefix are mutually exclusive");
109: }
110: }
111:
112: /**
113: * A debug toString().
114: * @return a string version of this object.
115: */
116: public String toString() {
117: return "name=" + name + ", regex=" + regex + ", prefix="
118: + prefix + ", builtin=" + builtin;
119: }
120:
121: } //end nested class
122:
123: /**
124: * Allow properties of a particular name in the set.
125: * @param name the property name to allow.
126: */
127: public void appendName(String name) {
128: PropertyRef r = new PropertyRef();
129: r.setName(name);
130: addPropertyref(r);
131: }
132:
133: /**
134: * Allow properties whose names match a regex in the set.
135: * @param regex the regular expression to use.
136: */
137: public void appendRegex(String regex) {
138: PropertyRef r = new PropertyRef();
139: r.setRegex(regex);
140: addPropertyref(r);
141: }
142:
143: /**
144: * Allow properties whose names start with a prefix in the set.
145: * @param prefix the prefix to use.
146: */
147: public void appendPrefix(String prefix) {
148: PropertyRef r = new PropertyRef();
149: r.setPrefix(prefix);
150: addPropertyref(r);
151: }
152:
153: /**
154: * Allow builtin (all, system or commandline) properties in the set.
155: * @param b the type of builtin properties.
156: */
157: public void appendBuiltin(BuiltinPropertySetName b) {
158: PropertyRef r = new PropertyRef();
159: r.setBuiltin(b);
160: addPropertyref(r);
161: }
162:
163: /**
164: * Set a mapper to change property names.
165: * @param type mapper type.
166: * @param from source pattern.
167: * @param to output pattern.
168: */
169: public void setMapper(String type, String from, String to) {
170: Mapper m = createMapper();
171: Mapper.MapperType mapperType = new Mapper.MapperType();
172: mapperType.setValue(type);
173: m.setType(mapperType);
174: m.setFrom(from);
175: m.setTo(to);
176: }
177:
178: /**
179: * Add a property reference (nested element) to the references to be used.
180: * @param ref a property reference.
181: */
182: public void addPropertyref(PropertyRef ref) {
183: assertNotReference();
184: ptyRefs.addElement(ref);
185: }
186:
187: /**
188: * Add another property set to this set.
189: * @param ref another property set.
190: */
191: public void addPropertyset(PropertySet ref) {
192: assertNotReference();
193: setRefs.addElement(ref);
194: }
195:
196: /**
197: * Create a mapper to map the property names.
198: * @return a mapper to be configured.
199: */
200: public Mapper createMapper() {
201: assertNotReference();
202: if (mapper != null) {
203: throw new BuildException("Too many <mapper>s!");
204: }
205: mapper = new Mapper(getProject());
206: return mapper;
207: }
208:
209: /**
210: * Add a nested FileNameMapper.
211: * @param fileNameMapper the mapper to add.
212: * @since Ant 1.6.3
213: */
214: public void add(FileNameMapper fileNameMapper) {
215: createMapper().add(fileNameMapper);
216: }
217:
218: /**
219: * Set whether to reevaluate the set everytime the set is used.
220: * Default is true.
221: *
222: * @param dynamic if true, reevaluate the property set each time
223: * the set is used. if false cache the property set
224: * the first time and use the cached set on subsequent
225: * occasions.
226: */
227: public void setDynamic(boolean dynamic) {
228: assertNotReference();
229: this .dynamic = dynamic;
230: }
231:
232: /**
233: * Set whether to negate results.
234: * If "true", all properties not selected by nested elements will be returned.
235: * Default is "false".
236: * @param negate if true, negate the selection criteria.
237: */
238: public void setNegate(boolean negate) {
239: assertNotReference();
240: this .negate = negate;
241: }
242:
243: /**
244: * Get the dynamic attribute.
245: * @return true if the property set is to be evalulated each time it is used.
246: */
247: public boolean getDynamic() {
248: return isReference() ? getRef().dynamic : dynamic;
249: }
250:
251: /**
252: * Get the mapper attribute.
253: * @return the mapper attribute.
254: */
255: public Mapper getMapper() {
256: return isReference() ? getRef().mapper : mapper;
257: }
258:
259: /**
260: * Convert the system properties to a hashtable.
261: * Use propertynames to get the list of properties (including
262: * default ones).
263: */
264: private Hashtable getAllSystemProperties() {
265: Hashtable ret = new Hashtable();
266: for (Enumeration e = System.getProperties().propertyNames(); e
267: .hasMoreElements();) {
268: String name = (String) e.nextElement();
269: ret.put(name, System.getProperties().getProperty(name));
270: }
271: return ret;
272: }
273:
274: /**
275: * This is the operation to get the existing or recalculated properties.
276: * @return the properties for this propertyset.
277: */
278: public Properties getProperties() {
279: if (isReference()) {
280: return getRef().getProperties();
281: }
282: Set names = null;
283: Project prj = getProject();
284: Hashtable props = prj == null ? getAllSystemProperties() : prj
285: .getProperties();
286:
287: //quick & dirty, to make nested mapped p-sets work:
288: for (Enumeration e = setRefs.elements(); e.hasMoreElements();) {
289: PropertySet set = (PropertySet) e.nextElement();
290: props.putAll(set.getProperties());
291: }
292:
293: if (getDynamic() || cachedNames == null) {
294: names = new HashSet();
295: addPropertyNames(names, props);
296: // Add this PropertySet's nested PropertySets' property names.
297: for (Enumeration e = setRefs.elements(); e
298: .hasMoreElements();) {
299: PropertySet set = (PropertySet) e.nextElement();
300: names.addAll(set.getProperties().keySet());
301: }
302: if (negate) {
303: //make a copy...
304: HashSet complement = new HashSet(props.keySet());
305: complement.removeAll(names);
306: names = complement;
307: }
308: if (!getDynamic()) {
309: cachedNames = names;
310: }
311: } else {
312: names = cachedNames;
313: }
314: FileNameMapper m = null;
315: Mapper myMapper = getMapper();
316: if (myMapper != null) {
317: m = myMapper.getImplementation();
318: }
319: Properties properties = new Properties();
320: //iterate through the names, get the matching values
321: for (Iterator iter = names.iterator(); iter.hasNext();) {
322: String name = (String) iter.next();
323: String value = (String) props.get(name);
324: if (value != null) {
325: // may be null if a system property has been added
326: // after the project instance has been initialized
327: if (m != null) {
328: //map the names
329: String[] newname = m.mapFileName(name);
330: if (newname != null) {
331: name = newname[0];
332: }
333: }
334: properties.setProperty(name, value);
335: }
336: }
337: return properties;
338: }
339:
340: /**
341: * @param names the output Set to fill with the property names
342: * matching this PropertySet selection criteria.
343: * @param properties the current Project properties, passed in to
344: * avoid needless duplication of the Hashtable during recursion.
345: */
346: private void addPropertyNames(Set names, Hashtable properties) {
347: // Add this PropertySet's property names.
348: for (Enumeration e = ptyRefs.elements(); e.hasMoreElements();) {
349: PropertyRef r = (PropertyRef) e.nextElement();
350: if (r.name != null) {
351: if (properties.get(r.name) != null) {
352: names.add(r.name);
353: }
354: } else if (r.prefix != null) {
355: for (Enumeration p = properties.keys(); p
356: .hasMoreElements();) {
357: String name = (String) p.nextElement();
358: if (name.startsWith(r.prefix)) {
359: names.add(name);
360: }
361: }
362: } else if (r.regex != null) {
363: RegexpMatcherFactory matchMaker = new RegexpMatcherFactory();
364: RegexpMatcher matcher = matchMaker.newRegexpMatcher();
365: matcher.setPattern(r.regex);
366: for (Enumeration p = properties.keys(); p
367: .hasMoreElements();) {
368: String name = (String) p.nextElement();
369: if (matcher.matches(name)) {
370: names.add(name);
371: }
372: }
373: } else if (r.builtin != null) {
374:
375: if (r.builtin.equals(BuiltinPropertySetName.ALL)) {
376: names.addAll(properties.keySet());
377: } else if (r.builtin
378: .equals(BuiltinPropertySetName.SYSTEM)) {
379: names.addAll(System.getProperties().keySet());
380: } else if (r.builtin
381: .equals(BuiltinPropertySetName.COMMANDLINE)) {
382: names.addAll(getProject().getUserProperties()
383: .keySet());
384: } else {
385: throw new BuildException(
386: "Impossible: Invalid builtin "
387: + "attribute!");
388: }
389: } else {
390: throw new BuildException(
391: "Impossible: Invalid PropertyRef!");
392: }
393: }
394: }
395:
396: /**
397: * Performs the check for circular references and returns the
398: * referenced PropertySet.
399: * @return the referenced PropertySet.
400: */
401: protected PropertySet getRef() {
402: return (PropertySet) getCheckedRef(PropertySet.class,
403: "propertyset");
404: }
405:
406: /**
407: * Sets the value of the refid attribute.
408: *
409: * @param r the reference this datatype should point to.
410: * @throws BuildException if another attribute was set, since
411: * refid and all other attributes are mutually exclusive.
412: */
413: public final void setRefid(Reference r) {
414: if (!noAttributeSet) {
415: throw tooManyAttributes();
416: }
417: super .setRefid(r);
418: }
419:
420: /**
421: * Ensures this data type is not a reference.
422: *
423: * <p>Calling this method as the first line of every bean method of
424: * this data type (setXyz, addXyz, createXyz) ensure proper handling
425: * of the refid attribute.</p>
426: *
427: * @throws BuildException if the refid attribute was already set, since
428: * refid and all other attributes are mutually exclusive.
429: */
430: protected final void assertNotReference() {
431: if (isReference()) {
432: throw tooManyAttributes();
433: }
434: noAttributeSet = false;
435: }
436:
437: /**
438: * Flag which tracks whether any attribute has been set; used by
439: * {@link #assertNotReference()} and {@link #setRefid(Reference)}.
440: */
441: private boolean noAttributeSet = true;
442:
443: /**
444: * Used for propertyref's builtin attribute.
445: */
446: public static class BuiltinPropertySetName extends
447: EnumeratedAttribute {
448: static final String ALL = "all";
449: static final String SYSTEM = "system";
450: static final String COMMANDLINE = "commandline";
451:
452: /** {@inheritDoc}. */
453: public String[] getValues() {
454: return new String[] { ALL, SYSTEM, COMMANDLINE };
455: }
456: }
457:
458: /**
459: * A debug toString.
460: * This gets a comma separated list of key=value pairs for
461: * the properties in the set.
462: * The output order is sorted according to the keys' <i>natural order</i>.
463: * @return a string rep of this object.
464: */
465: public String toString() {
466: StringBuffer b = new StringBuffer();
467: TreeMap sorted = new TreeMap(getProperties());
468: for (Iterator i = sorted.entrySet().iterator(); i.hasNext();) {
469: Map.Entry e = (Map.Entry) i.next();
470: if (b.length() != 0) {
471: b.append(", ");
472: }
473: b.append(e.getKey().toString());
474: b.append("=");
475: b.append(e.getValue().toString());
476: }
477: return b.toString();
478: }
479:
480: /**
481: * Fulfill the ResourceCollection interface.
482: * @return an Iterator of Resources.
483: * @since Ant 1.7
484: */
485: public Iterator iterator() {
486: final Enumeration e = getProperties().propertyNames();
487: return new Iterator() {
488: public boolean hasNext() {
489: return e.hasMoreElements();
490: }
491:
492: public Object next() {
493: return new PropertyResource(getProject(), (String) e
494: .nextElement());
495: }
496:
497: public void remove() {
498: throw new UnsupportedOperationException();
499: }
500: };
501: }
502:
503: /**
504: * Fulfill the ResourceCollection contract.
505: * @return the size of this ResourceCollection.
506: */
507: public int size() {
508: return isReference() ? getRef().size() : getProperties().size();
509: }
510:
511: /**
512: * Fulfill the ResourceCollection contract.
513: * @return whether this is a filesystem-only resource collection.
514: */
515: public boolean isFilesystemOnly() {
516: return isReference() && getRef().isFilesystemOnly();
517: }
518:
519: }
|