KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > PropertyHelper


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18
19 package org.apache.tools.ant;
20
21 import java.util.Hashtable JavaDoc;
22 import java.util.Vector JavaDoc;
23 import java.util.Enumeration JavaDoc;
24
25
26 /* ISSUES:
27  - ns param. It could be used to provide "namespaces" for properties, which
28  may be more flexible.
29  - Object value. In ant1.5 String is used for Properties - but it would be nice
30  to support generic Objects (the property remains immutable - you can't change
31  the associated object). This will also allow JSP-EL style setting using the
32  Object if an attribute contains only the property (name="${property}" could
33  avoid Object->String->Object conversion)
34  - Currently we "chain" only for get and set property (probably most users
35  will only need that - if they need more they can replace the top helper).
36  Need to discuss this and find if we need more.
37  */

38
39 /** NOT FINAL. API MAY CHANGE
40  *
41  * Deals with properties - substitution, dynamic properties, etc.
42  *
43  * This is the same code as in Ant1.5. The main addition is the ability
44  * to chain multiple PropertyHelpers and to replace the default.
45  *
46  * @since Ant 1.6
47  */

48 public class PropertyHelper {
49
50     private Project project;
51     private PropertyHelper next;
52
53     /** Project properties map (usually String to String). */
54     private Hashtable JavaDoc properties = new Hashtable JavaDoc();
55
56     /**
57      * Map of "user" properties (as created in the Ant task, for example).
58      * Note that these key/value pairs are also always put into the
59      * project properties, so only the project properties need to be queried.
60      * Mapping is String to String.
61      */

62     private Hashtable JavaDoc userProperties = new Hashtable JavaDoc();
63
64     /**
65      * Map of inherited "user" properties - that are those "user"
66      * properties that have been created by tasks and not been set
67      * from the command line or a GUI tool.
68      * Mapping is String to String.
69      */

70     private Hashtable JavaDoc inheritedProperties = new Hashtable JavaDoc();
71
72     /**
73      * Default constructor.
74      */

75     protected PropertyHelper() {
76     }
77
78     //override facility for subclasses to put custom hashtables in
79

80
81     // -------------------- Hook management --------------------
82

83     /**
84      * Set the project for which this helper is performing property resolution
85      *
86      * @param p the project instance.
87      */

88     public void setProject(Project p) {
89         this.project = p;
90     }
91
92     /** There are 2 ways to hook into property handling:
93      * - you can replace the main PropertyHelper. The replacement is required
94      * to support the same semantics (of course :-)
95      *
96      * - you can chain a property helper capable of storing some properties.
97      * Again, you are required to respect the immutability semantics (at
98      * least for non-dynamic properties)
99      *
100      * @param next the next property helper in the chain.
101      */

102     public void setNext(PropertyHelper next) {
103         this.next = next;
104     }
105
106     /**
107      * Get the next property helper in the chain.
108      *
109      * @return the next property helper.
110      */

111     public PropertyHelper getNext() {
112         return next;
113     }
114
115     /**
116      * Factory method to create a property processor.
117      * Users can provide their own or replace it using "ant.PropertyHelper"
118      * reference. User tasks can also add themselves to the chain, and provide
119      * dynamic properties.
120      *
121      * @param project the project fro which the property helper is required.
122      *
123      * @return the project's property helper.
124      */

125     public static synchronized
126         PropertyHelper getPropertyHelper(Project project) {
127         PropertyHelper helper
128             = (PropertyHelper) project.getReference(MagicNames.REFID_PROPERTY_HELPER);
129         if (helper != null) {
130             return helper;
131         }
132         helper = new PropertyHelper();
133         helper.setProject(project);
134
135         project.addReference(MagicNames.REFID_PROPERTY_HELPER, helper);
136         return helper;
137     }
138
139     // -------------------- Methods to override --------------------
140

141     /**
142      * Sets a property. Any existing property of the same name
143      * is overwritten, unless it is a user property. Will be called
144      * from setProperty().
145      *
146      * If all helpers return false, the property will be saved in
147      * the default properties table by setProperty.
148      *
149      * @param ns The namespace that the property is in (currently
150      * not used.
151      * @param name The name of property to set.
152      * Must not be <code>null</code>.
153      * @param value The new value of the property.
154      * Must not be <code>null</code>.
155      * @param inherited True if this property is inherited (an [sub]ant[call] property).
156      * @param user True if this property is a user property.
157      * @param isNew True is this is a new property.
158      * @return true if this helper has stored the property, false if it
159      * couldn't. Each helper should delegate to the next one (unless it
160      * has a good reason not to).
161      */

162     public boolean setPropertyHook(String JavaDoc ns, String JavaDoc name,
163                                    Object JavaDoc value,
164                                    boolean inherited, boolean user,
165                                    boolean isNew) {
166         if (getNext() != null) {
167             boolean subst = getNext().setPropertyHook(ns, name, value,
168                     inherited, user, isNew);
169             // If next has handled the property
170
if (subst) {
171                 return true;
172             }
173         }
174         return false;
175     }
176
177     /** Get a property. If all hooks return null, the default
178      * tables will be used.
179      *
180      * @param ns namespace of the sought property.
181      * @param name name of the sought property.
182      * @param user True if this is a user property.
183      * @return The property, if returned by a hook, or null if none.
184      */

185     public Object JavaDoc getPropertyHook(String JavaDoc ns, String JavaDoc name, boolean user) {
186         if (getNext() != null) {
187             Object JavaDoc o = getNext().getPropertyHook(ns, name, user);
188             if (o != null) {
189                 return o;
190             }
191         }
192         // Experimental/Testing, will be removed
193
if (name.startsWith("toString:")) {
194             name = name.substring("toString:".length());
195             Object JavaDoc v = project.getReference(name);
196             return (v == null) ? null : v.toString();
197         }
198         return null;
199     }
200
201     // -------------------- Optional methods --------------------
202
// You can override those methods if you want to optimize or
203
// do advanced things (like support a special syntax).
204
// The methods do not chain - you should use them when embedding ant
205
// (by replacing the main helper)
206

207     /**
208      * Parses a string containing <code>${xxx}</code> style property
209      * references into two lists. The first list is a collection
210      * of text fragments, while the other is a set of string property names.
211      * <code>null</code> entries in the first list indicate a property
212      * reference from the second list.
213      *
214      * It can be overridden with a more efficient or customized version.
215      *
216      * @param value Text to parse. Must not be <code>null</code>.
217      * @param fragments List to add text fragments to.
218      * Must not be <code>null</code>.
219      * @param propertyRefs List to add property names to.
220      * Must not be <code>null</code>.
221      *
222      * @exception BuildException if the string contains an opening
223      * <code>${</code> without a closing
224      * <code>}</code>
225      */

226     public void parsePropertyString(String JavaDoc value, Vector JavaDoc fragments,
227                                     Vector JavaDoc propertyRefs)
228         throws BuildException {
229         parsePropertyStringDefault(value, fragments, propertyRefs);
230     }
231
232     /**
233      * Replaces <code>${xxx}</code> style constructions in the given value
234      * with the string value of the corresponding data types.
235      *
236      * @param ns The namespace for the property.
237      * @param value The string to be scanned for property references.
238      * May be <code>null</code>, in which case this
239      * method returns immediately with no effect.
240      * @param keys Mapping (String to String) of property names to their
241      * values. If <code>null</code>, only project properties will
242      * be used.
243      *
244      * @exception BuildException if the string contains an opening
245      * <code>${</code> without a closing
246      * <code>}</code>
247      * @return the original string with the properties replaced, or
248      * <code>null</code> if the original string is <code>null</code>.
249      */

250     public String JavaDoc replaceProperties(String JavaDoc ns, String JavaDoc value, Hashtable JavaDoc keys)
251             throws BuildException {
252         if (value == null || value.indexOf('$') == -1) {
253             return value;
254         }
255         Vector JavaDoc fragments = new Vector JavaDoc();
256         Vector JavaDoc propertyRefs = new Vector JavaDoc();
257         parsePropertyString(value, fragments, propertyRefs);
258
259         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
260         Enumeration JavaDoc i = fragments.elements();
261         Enumeration JavaDoc j = propertyRefs.elements();
262
263         while (i.hasMoreElements()) {
264             String JavaDoc fragment = (String JavaDoc) i.nextElement();
265             if (fragment == null) {
266                 String JavaDoc propertyName = (String JavaDoc) j.nextElement();
267                 Object JavaDoc replacement = null;
268
269                 // try to get it from the project or keys
270
// Backward compatibility
271
if (keys != null) {
272                     replacement = keys.get(propertyName);
273                 }
274                 if (replacement == null) {
275                     replacement = getProperty(ns, propertyName);
276                 }
277
278                 if (replacement == null) {
279                     project.log("Property \"" + propertyName
280                             + "\" has not been set", Project.MSG_VERBOSE);
281                 }
282                 fragment = (replacement != null)
283                         ? replacement.toString()
284                         : "${" + propertyName + "}";
285             }
286             sb.append(fragment);
287         }
288         return sb.toString();
289     }
290
291     // -------------------- Default implementation --------------------
292
// Methods used to support the default behavior and provide backward
293
// compatibility. Some will be deprecated, you should avoid calling them.
294

295
296     /** Default implementation of setProperty. Will be called from Project.
297      * This is the original 1.5 implementation, with calls to the hook
298      * added.
299      * @param ns The namespace for the property (currently not used).
300      * @param name The name of the property.
301      * @param value The value to set the property to.
302      * @param verbose If this is true output extra log messages.
303      * @return true if the property is set.
304      */

305     public synchronized boolean setProperty(String JavaDoc ns, String JavaDoc name,
306                                             Object JavaDoc value, boolean verbose) {
307         // user (CLI) properties take precedence
308
if (null != userProperties.get(name)) {
309             if (verbose) {
310                 project.log("Override ignored for user property \"" + name
311                     + "\"", Project.MSG_VERBOSE);
312             }
313             return false;
314         }
315
316         boolean done = setPropertyHook(ns, name, value, false, false, false);
317         if (done) {
318             return true;
319         }
320
321         if (null != properties.get(name) && verbose) {
322             project.log("Overriding previous definition of property \"" + name
323                 + "\"", Project.MSG_VERBOSE);
324         }
325
326         if (verbose) {
327             project.log("Setting project property: " + name + " -> "
328                 + value, Project.MSG_DEBUG);
329         }
330         properties.put(name, value);
331         return true;
332     }
333
334     /**
335      * Sets a property if no value currently exists. If the property
336      * exists already, a message is logged and the method returns with
337      * no other effect.
338      *
339      * @param ns The namespace for the property (currently not used).
340      * @param name The name of property to set.
341      * Must not be <code>null</code>.
342      * @param value The new value of the property.
343      * Must not be <code>null</code>.
344      * @since Ant 1.6
345      */

346     public synchronized void setNewProperty(String JavaDoc ns, String JavaDoc name,
347                                             Object JavaDoc value) {
348         if (null != properties.get(name)) {
349             project.log("Override ignored for property \"" + name
350                 + "\"", Project.MSG_VERBOSE);
351             return;
352         }
353
354         boolean done = setPropertyHook(ns, name, value, false, false, true);
355         if (done) {
356             return;
357         }
358
359         project.log("Setting project property: " + name + " -> "
360             + value, Project.MSG_DEBUG);
361         if (name != null && value != null) {
362             properties.put(name, value);
363         }
364     }
365
366     /**
367      * Sets a user property, which cannot be overwritten by
368      * set/unset property calls. Any previous value is overwritten.
369      * @param ns The namespace for the property (currently not used).
370      * @param name The name of property to set.
371      * Must not be <code>null</code>.
372      * @param value The new value of the property.
373      * Must not be <code>null</code>.
374      */

375     public synchronized void setUserProperty(String JavaDoc ns, String JavaDoc name,
376                                              Object JavaDoc value) {
377         project.log("Setting ro project property: " + name + " -> "
378             + value, Project.MSG_DEBUG);
379         userProperties.put(name, value);
380
381         boolean done = setPropertyHook(ns, name, value, false, true, false);
382         if (done) {
383             return;
384         }
385         properties.put(name, value);
386     }
387
388     /**
389      * Sets an inherited user property, which cannot be overwritten by set/unset
390      * property calls. Any previous value is overwritten. Also marks
391      * these properties as properties that have not come from the
392      * command line.
393      *
394      * @param ns The namespace for the property (currently not used).
395      * @param name The name of property to set.
396      * Must not be <code>null</code>.
397      * @param value The new value of the property.
398      * Must not be <code>null</code>.
399      */

400     public synchronized void setInheritedProperty(String JavaDoc ns, String JavaDoc name,
401                                                   Object JavaDoc value) {
402         inheritedProperties.put(name, value);
403
404         project.log("Setting ro project property: " + name + " -> "
405             + value, Project.MSG_DEBUG);
406         userProperties.put(name, value);
407
408         boolean done = setPropertyHook(ns, name, value, true, false, false);
409         if (done) {
410             return;
411         }
412         properties.put(name, value);
413     }
414
415     // -------------------- Getting properties --------------------
416

417     /**
418      * Returns the value of a property, if it is set. You can override
419      * this method in order to plug your own storage.
420      *
421      * @param ns The namespace for the property (currently not used).
422      * @param name The name of the property.
423      * May be <code>null</code>, in which case
424      * the return value is also <code>null</code>.
425      * @return the property value, or <code>null</code> for no match
426      * or if a <code>null</code> name is provided.
427      */

428     public synchronized Object JavaDoc getProperty(String JavaDoc ns, String JavaDoc name) {
429         if (name == null) {
430             return null;
431         }
432
433         Object JavaDoc o = getPropertyHook(ns, name, false);
434         if (o != null) {
435             return o;
436         }
437
438         return properties.get(name);
439     }
440     /**
441      * Returns the value of a user property, if it is set.
442      *
443      * @param ns The namespace for the property (currently not used).
444      * @param name The name of the property.
445      * May be <code>null</code>, in which case
446      * the return value is also <code>null</code>.
447      * @return the property value, or <code>null</code> for no match
448      * or if a <code>null</code> name is provided.
449      */

450     public synchronized Object JavaDoc getUserProperty(String JavaDoc ns, String JavaDoc name) {
451         if (name == null) {
452             return null;
453         }
454         Object JavaDoc o = getPropertyHook(ns, name, true);
455         if (o != null) {
456             return o;
457         }
458         return userProperties.get(name);
459     }
460
461
462     // -------------------- Access to property tables --------------------
463
// This is used to support ant call and similar tasks. It should be
464
// deprecated, it is possible to use a better (more efficient)
465
// mechanism to preserve the context.
466

467     /**
468      * Returns a copy of the properties table.
469      * @return a hashtable containing all properties
470      * (including user properties).
471      */

472     public Hashtable JavaDoc getProperties() {
473         return new Hashtable JavaDoc(properties);
474         // There is a better way to save the context. This shouldn't
475
// delegate to next, it's for backward compatibility only.
476
}
477
478     /**
479      * Returns a copy of the user property hashtable
480      * @return a hashtable containing just the user properties
481      */

482     public Hashtable JavaDoc getUserProperties() {
483         return new Hashtable JavaDoc(userProperties);
484     }
485
486     /**
487      * special back door for subclasses, internal access to
488      * the hashtables
489      * @return the live hashtable of all properties
490      */

491     protected Hashtable JavaDoc getInternalProperties() {
492         return properties;
493     }
494
495     /**
496      * special back door for subclasses, internal access to
497      * the hashtables
498      *
499      * @return the live hashtable of user properties
500      */

501     protected Hashtable JavaDoc getInternalUserProperties() {
502         return userProperties;
503     }
504
505     /**
506      * special back door for subclasses, internal access to
507      * the hashtables
508      *
509      * @return the live hashtable inherited properties
510      */

511     protected Hashtable JavaDoc getInternalInheritedProperties() {
512         return inheritedProperties;
513     }
514
515
516     /**
517      * Copies all user properties that have not been set on the
518      * command line or a GUI tool from this instance to the Project
519      * instance given as the argument.
520      *
521      * <p>To copy all "user" properties, you will also have to call
522      * {@link #copyUserProperties copyUserProperties}.</p>
523      *
524      * @param other the project to copy the properties to. Must not be null.
525      *
526      * @since Ant 1.6
527      */

528     public void copyInheritedProperties(Project other) {
529         Enumeration JavaDoc e = inheritedProperties.keys();
530         while (e.hasMoreElements()) {
531             String JavaDoc arg = e.nextElement().toString();
532             if (other.getUserProperty(arg) != null) {
533                 continue;
534             }
535             Object JavaDoc value = inheritedProperties.get(arg);
536             other.setInheritedProperty(arg, value.toString());
537         }
538     }
539
540     /**
541      * Copies all user properties that have been set on the command
542      * line or a GUI tool from this instance to the Project instance
543      * given as the argument.
544      *
545      * <p>To copy all "user" properties, you will also have to call
546      * {@link #copyInheritedProperties copyInheritedProperties}.</p>
547      *
548      * @param other the project to copy the properties to. Must not be null.
549      *
550      * @since Ant 1.6
551      */

552     public void copyUserProperties(Project other) {
553         Enumeration JavaDoc e = userProperties.keys();
554         while (e.hasMoreElements()) {
555             Object JavaDoc arg = e.nextElement();
556             if (inheritedProperties.containsKey(arg)) {
557                 continue;
558             }
559             Object JavaDoc value = userProperties.get(arg);
560             other.setUserProperty(arg.toString(), value.toString());
561         }
562     }
563
564     // -------------------- Property parsing --------------------
565
// Moved from ProjectHelper. You can override the static method -
566
// this is used for backward compatibility (for code that calls
567
// the parse method in ProjectHelper).
568

569     /** Default parsing method. It is here only to support backward compatibility
570      * for the static ProjectHelper.parsePropertyString().
571      */

572     static void parsePropertyStringDefault(String JavaDoc value, Vector JavaDoc fragments,
573                                     Vector JavaDoc propertyRefs)
574         throws BuildException {
575         int prev = 0;
576         int pos;
577         //search for the next instance of $ from the 'prev' position
578
while ((pos = value.indexOf("$", prev)) >= 0) {
579
580             //if there was any text before this, add it as a fragment
581
//TODO, this check could be modified to go if pos>prev;
582
//seems like this current version could stick empty strings
583
//into the list
584
if (pos > 0) {
585                 fragments.addElement(value.substring(prev, pos));
586             }
587             //if we are at the end of the string, we tack on a $
588
//then move past it
589
if (pos == (value.length() - 1)) {
590                 fragments.addElement("$");
591                 prev = pos + 1;
592             } else if (value.charAt(pos + 1) != '{') {
593                 //peek ahead to see if the next char is a property or not
594
//not a property: insert the char as a literal
595
/*
596                 fragments.addElement(value.substring(pos + 1, pos + 2));
597                 prev = pos + 2;
598                 */

599                 if (value.charAt(pos + 1) == '$') {
600                     //backwards compatibility two $ map to one mode
601
fragments.addElement("$");
602                     prev = pos + 2;
603                 } else {
604                     //new behaviour: $X maps to $X for all values of X!='$'
605
fragments.addElement(value.substring(pos, pos + 2));
606                     prev = pos + 2;
607                 }
608
609             } else {
610                 //property found, extract its name or bail on a typo
611
int endName = value.indexOf('}', pos);
612                 if (endName < 0) {
613                     throw new BuildException("Syntax error in property: "
614                                                  + value);
615                 }
616                 String JavaDoc propertyName = value.substring(pos + 2, endName);
617                 fragments.addElement(null);
618                 propertyRefs.addElement(propertyName);
619                 prev = endName + 1;
620             }
621         }
622         //no more $ signs found
623
//if there is any tail to the file, append it
624
if (prev < value.length()) {
625             fragments.addElement(value.substring(prev));
626         }
627     }
628
629 }
630
Popular Tags