source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/PropertyHelper.java@ 14627

Last change on this file since 14627 was 14627, checked in by oranfry, 17 years ago

initial import of the gs3-release-maker

File size: 22.7 KB
Line 
1/*
2 * Copyright 2002-2004 The Apache Software Foundation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 */
17
18package org.apache.tools.ant;
19
20import java.util.Hashtable;
21import java.util.Vector;
22import java.util.Enumeration;
23
24
25/* ISSUES:
26 - ns param. It could be used to provide "namespaces" for properties, which
27 may be more flexible.
28 - Object value. In ant1.5 String is used for Properties - but it would be nice
29 to support generic Objects (the property remains immutable - you can't change
30 the associated object). This will also allow JSP-EL style setting using the
31 Object if an attribute contains only the property (name="${property}" could
32 avoid Object->String->Object conversion)
33 - Currently we "chain" only for get and set property (probably most users
34 will only need that - if they need more they can replace the top helper).
35 Need to discuss this and find if we need more.
36 */
37
38/** NOT FINAL. API MAY CHANGE
39 *
40 * Deals with properties - substitution, dynamic properties, etc.
41 *
42 * This is the same code as in Ant1.5. The main addition is the ability
43 * to chain multiple PropertyHelpers and to replace the default.
44 *
45 * @since Ant 1.6
46 */
47public class PropertyHelper {
48
49 private Project project;
50 private PropertyHelper next;
51
52 /** Project properties map (usually String to String). */
53 private Hashtable properties = new Hashtable();
54
55 /**
56 * Map of "user" properties (as created in the Ant task, for example).
57 * Note that these key/value pairs are also always put into the
58 * project properties, so only the project properties need to be queried.
59 * Mapping is String to String.
60 */
61 private Hashtable userProperties = new Hashtable();
62
63 /**
64 * Map of inherited "user" properties - that are those "user"
65 * properties that have been created by tasks and not been set
66 * from the command line or a GUI tool.
67 * Mapping is String to String.
68 */
69 private Hashtable inheritedProperties = new Hashtable();
70
71 /**
72 * Default constructor.
73 */
74 protected PropertyHelper() {
75 }
76
77 // -------------------- Hook management --------------------
78
79 /**
80 * Set the project for which this helper is performing property resolution
81 *
82 * @param p the project instance.
83 */
84 public void setProject(Project p) {
85 this.project = p;
86 }
87
88 /** There are 2 ways to hook into property handling:
89 * - you can replace the main PropertyHelper. The replacement is required
90 * to support the same semantics (of course :-)
91 *
92 * - you can chain a property helper capable of storing some properties.
93 * Again, you are required to respect the immutability semantics (at
94 * least for non-dynamic properties)
95 *
96 * @param next the next property helper in the chain.
97 */
98 public void setNext(PropertyHelper next) {
99 this.next = next;
100 }
101
102 /**
103 * Get the next property helper in the chain.
104 *
105 * @return the next property helper.
106 */
107 public PropertyHelper getNext() {
108 return next;
109 }
110
111 /**
112 * Factory method to create a property processor.
113 * Users can provide their own or replace it using "ant.PropertyHelper"
114 * reference. User tasks can also add themselves to the chain, and provide
115 * dynamic properties.
116 *
117 * @param project the project fro which the property helper is required.
118 *
119 * @return the project's property helper.
120 */
121 public static synchronized
122 PropertyHelper getPropertyHelper(Project project) {
123 PropertyHelper helper
124 = (PropertyHelper) project.getReference("ant.PropertyHelper");
125 if (helper != null) {
126 return helper;
127 }
128 helper = new PropertyHelper();
129 helper.setProject(project);
130
131 project.addReference("ant.PropertyHelper", helper);
132 return helper;
133 }
134
135 // -------------------- Methods to override --------------------
136
137 /**
138 * Sets a property. Any existing property of the same name
139 * is overwritten, unless it is a user property. Will be called
140 * from setProperty().
141 *
142 * If all helpers return false, the property will be saved in
143 * the default properties table by setProperty.
144 *
145 * @param ns The namespace that the property is in (currently
146 * not used.
147 * @param name The name of property to set.
148 * Must not be <code>null</code>.
149 * @param value The new value of the property.
150 * Must not be <code>null</code>.
151 * @param inherited True if this property is inherited (an [sub]ant[call] property).
152 * @param user True if this property is a user property.
153 * @param isNew True is this is a new property.
154 * @return true if this helper has stored the property, false if it
155 * couldn't. Each helper should delegate to the next one (unless it
156 * has a good reason not to).
157 */
158 public boolean setPropertyHook(String ns, String name,
159 Object value,
160 boolean inherited, boolean user,
161 boolean isNew) {
162 if (getNext() != null) {
163 boolean subst = getNext().setPropertyHook(ns, name, value,
164 inherited, user, isNew);
165 // If next has handled the property
166 if (subst) {
167 return true;
168 }
169 }
170
171 return false;
172 }
173
174 /** Get a property. If all hooks return null, the default
175 * tables will be used.
176 *
177 * @param ns namespace of the sought property.
178 * @param name name of the sought property.
179 * @param user True if this is a user property.
180 * @return The property, if returned by a hook, or null if none.
181 */
182 public Object getPropertyHook(String ns, String name, boolean user) {
183 if (getNext() != null) {
184 Object o = getNext().getPropertyHook(ns, name, user);
185 if (o != null) {
186 return o;
187 }
188 }
189 // Experimental/Testing, will be removed
190 if (name.startsWith("toString:")) {
191 name = name.substring("toString:".length());
192 Object v = project.getReference(name);
193 if (v == null) {
194 return null;
195 }
196 return v.toString();
197 }
198
199
200 return null;
201 }
202
203 // -------------------- Optional methods --------------------
204 // You can override those methods if you want to optimize or
205 // do advanced things (like support a special syntax).
206 // The methods do not chain - you should use them when embedding ant
207 // (by replacing the main helper)
208
209 /**
210 * Parses a string containing <code>${xxx}</code> style property
211 * references into two lists. The first list is a collection
212 * of text fragments, while the other is a set of string property names.
213 * <code>null</code> entries in the first list indicate a property
214 * reference from the second list.
215 *
216 * It can be overridden with a more efficient or customized version.
217 *
218 * @param value Text to parse. Must not be <code>null</code>.
219 * @param fragments List to add text fragments to.
220 * Must not be <code>null</code>.
221 * @param propertyRefs List to add property names to.
222 * Must not be <code>null</code>.
223 *
224 * @exception BuildException if the string contains an opening
225 * <code>${</code> without a closing
226 * <code>}</code>
227 */
228 public void parsePropertyString(String value, Vector fragments,
229 Vector propertyRefs)
230 throws BuildException {
231 parsePropertyStringDefault(value, fragments, propertyRefs);
232 }
233
234 /**
235 * Replaces <code>${xxx}</code> style constructions in the given value
236 * with the string value of the corresponding data types.
237 *
238 * @param ns The namespace for the property.
239 * @param value The string to be scanned for property references.
240 * May be <code>null</code>, in which case this
241 * method returns immediately with no effect.
242 * @param keys Mapping (String to String) of property names to their
243 * values. If <code>null</code>, only project properties will
244 * be used.
245 *
246 * @exception BuildException if the string contains an opening
247 * <code>${</code> without a closing
248 * <code>}</code>
249 * @return the original string with the properties replaced, or
250 * <code>null</code> if the original string is <code>null</code>.
251 */
252 public String replaceProperties(String ns, String value,
253 Hashtable keys)
254 throws BuildException {
255 if (value == null) {
256 return null;
257 }
258
259 Vector fragments = new Vector();
260 Vector propertyRefs = new Vector();
261 parsePropertyString(value, fragments, propertyRefs);
262
263 StringBuffer sb = new StringBuffer();
264 Enumeration i = fragments.elements();
265 Enumeration j = propertyRefs.elements();
266
267 while (i.hasMoreElements()) {
268 String fragment = (String) i.nextElement();
269 if (fragment == null) {
270 String propertyName = (String) j.nextElement();
271 Object replacement = null;
272
273 // try to get it from the project or keys
274 // Backward compatibility
275 if (keys != null) {
276 replacement = keys.get(propertyName);
277 }
278 if (replacement == null) {
279 replacement = getProperty(ns, propertyName);
280 }
281
282 if (replacement == null) {
283 project.log("Property ${" + propertyName
284 + "} has not been set", Project.MSG_VERBOSE);
285 }
286 fragment = (replacement != null)
287 ? replacement.toString()
288 : "${" + propertyName + "}";
289 }
290 sb.append(fragment);
291 }
292
293 return sb.toString();
294 }
295
296 // -------------------- Default implementation --------------------
297 // Methods used to support the default behavior and provide backward
298 // compatibility. Some will be deprecated, you should avoid calling them.
299
300
301 /** Default implementation of setProperty. Will be called from Project.
302 * This is the original 1.5 implementation, with calls to the hook
303 * added.
304 * @param ns The namespace for the property (currently not used).
305 * @param name The name of the property.
306 * @param value The value to set the property to.
307 * @param verbose If this is true output extra log messages.
308 * @return true if the property is set.
309 */
310 public synchronized boolean setProperty(String ns, String name,
311 Object value, boolean verbose) {
312 // user (CLI) properties take precedence
313 if (null != userProperties.get(name)) {
314 if (verbose) {
315 project.log("Override ignored for user property " + name,
316 Project.MSG_VERBOSE);
317 }
318 return false;
319 }
320
321 boolean done = setPropertyHook(ns, name, value, false, false, false);
322 if (done) {
323 return true;
324 }
325
326 if (null != properties.get(name) && verbose) {
327 project.log("Overriding previous definition of property " + name,
328 Project.MSG_VERBOSE);
329 }
330
331 if (verbose) {
332 project.log("Setting project property: " + name + " -> "
333 + value, Project.MSG_DEBUG);
334 }
335 properties.put(name, value);
336 return true;
337 }
338
339 /**
340 * Sets a property if no value currently exists. If the property
341 * exists already, a message is logged and the method returns with
342 * no other effect.
343 *
344 * @param ns The namespace for the property (currently not used).
345 * @param name The name of property to set.
346 * Must not be <code>null</code>.
347 * @param value The new value of the property.
348 * Must not be <code>null</code>.
349 * @since Ant 1.6
350 */
351 public synchronized void setNewProperty(String ns, String name,
352 Object value) {
353 if (null != properties.get(name)) {
354 project.log("Override ignored for property " + name,
355 Project.MSG_VERBOSE);
356 return;
357 }
358
359 boolean done = setPropertyHook(ns, name, value, false, false, true);
360 if (done) {
361 return;
362 }
363
364 project.log("Setting project property: " + name + " -> "
365 + value, Project.MSG_DEBUG);
366 if (name != null && value != null) {
367 properties.put(name, value);
368 }
369 }
370
371 /**
372 * Sets a user property, which cannot be overwritten by
373 * set/unset property calls. Any previous value is overwritten.
374 * @param ns The namespace for the property (currently not used).
375 * @param name The name of property to set.
376 * Must not be <code>null</code>.
377 * @param value The new value of the property.
378 * Must not be <code>null</code>.
379 */
380 public synchronized void setUserProperty(String ns, String name,
381 Object value) {
382 project.log("Setting ro project property: " + name + " -> "
383 + value, Project.MSG_DEBUG);
384 userProperties.put(name, value);
385
386 boolean done = setPropertyHook(ns, name, value, false, true, false);
387 if (done) {
388 return;
389 }
390 properties.put(name, value);
391 }
392
393 /**
394 * Sets an inherited user property, which cannot be overwritten by set/unset
395 * property calls. Any previous value is overwritten. Also marks
396 * these properties as properties that have not come from the
397 * command line.
398 *
399 * @param ns The namespace for the property (currently not used).
400 * @param name The name of property to set.
401 * Must not be <code>null</code>.
402 * @param value The new value of the property.
403 * Must not be <code>null</code>.
404 */
405 public synchronized void setInheritedProperty(String ns, String name,
406 Object value) {
407 inheritedProperties.put(name, value);
408
409 project.log("Setting ro project property: " + name + " -> "
410 + value, Project.MSG_DEBUG);
411 userProperties.put(name, value);
412
413 boolean done = setPropertyHook(ns, name, value, true, false, false);
414 if (done) {
415 return;
416 }
417 properties.put(name, value);
418 }
419
420 // -------------------- Getting properties --------------------
421
422 /**
423 * Returns the value of a property, if it is set. You can override
424 * this method in order to plug your own storage.
425 *
426 * @param ns The namespace for the property (currently not used).
427 * @param name The name of the property.
428 * May be <code>null</code>, in which case
429 * the return value is also <code>null</code>.
430 * @return the property value, or <code>null</code> for no match
431 * or if a <code>null</code> name is provided.
432 */
433 public synchronized Object getProperty(String ns, String name) {
434 if (name == null) {
435 return null;
436 }
437
438 Object o = getPropertyHook(ns, name, false);
439 if (o != null) {
440 return o;
441 }
442
443 return properties.get(name);
444 }
445 /**
446 * Returns the value of a user property, if it is set.
447 *
448 * @param ns The namespace for the property (currently not used).
449 * @param name The name of the property.
450 * May be <code>null</code>, in which case
451 * the return value is also <code>null</code>.
452 * @return the property value, or <code>null</code> for no match
453 * or if a <code>null</code> name is provided.
454 */
455 public synchronized Object getUserProperty(String ns, String name) {
456 if (name == null) {
457 return null;
458 }
459 Object o = getPropertyHook(ns, name, true);
460 if (o != null) {
461 return o;
462 }
463 return userProperties.get(name);
464 }
465
466
467 // -------------------- Access to property tables --------------------
468 // This is used to support ant call and similar tasks. It should be
469 // deprecated, it is possible to use a better (more efficient)
470 // mechanism to preserve the context.
471
472 // TODO: do we need to delegate ?
473
474 /**
475 * Returns a copy of the properties table.
476 * @return a hashtable containing all properties
477 * (including user properties).
478 */
479 public Hashtable getProperties() {
480 return new Hashtable(properties);
481 // There is a better way to save the context. This shouldn't
482 // delegate to next, it's for backward compatibility only.
483 }
484
485 /**
486 * Returns a copy of the user property hashtable
487 * @return a hashtable containing just the user properties
488 */
489 public Hashtable getUserProperties() {
490 return new Hashtable(userProperties);
491 }
492
493 /**
494 * Copies all user properties that have not been set on the
495 * command line or a GUI tool from this instance to the Project
496 * instance given as the argument.
497 *
498 * <p>To copy all "user" properties, you will also have to call
499 * {@link #copyUserProperties copyUserProperties}.</p>
500 *
501 * @param other the project to copy the properties to. Must not be null.
502 *
503 * @since Ant 1.6
504 */
505 public void copyInheritedProperties(Project other) {
506 Enumeration e = inheritedProperties.keys();
507 while (e.hasMoreElements()) {
508 String arg = e.nextElement().toString();
509 if (other.getUserProperty(arg) != null) {
510 continue;
511 }
512 Object value = inheritedProperties.get(arg);
513 other.setInheritedProperty(arg, value.toString());
514 }
515 }
516
517 /**
518 * Copies all user properties that have been set on the command
519 * line or a GUI tool from this instance to the Project instance
520 * given as the argument.
521 *
522 * <p>To copy all "user" properties, you will also have to call
523 * {@link #copyInheritedProperties copyInheritedProperties}.</p>
524 *
525 * @param other the project to copy the properties to. Must not be null.
526 *
527 * @since Ant 1.6
528 */
529 public void copyUserProperties(Project other) {
530 Enumeration e = userProperties.keys();
531 while (e.hasMoreElements()) {
532 Object arg = e.nextElement();
533 if (inheritedProperties.containsKey(arg)) {
534 continue;
535 }
536 Object value = userProperties.get(arg);
537 other.setUserProperty(arg.toString(), value.toString());
538 }
539 }
540
541 // -------------------- Property parsing --------------------
542 // Moved from ProjectHelper. You can override the static method -
543 // this is used for backward compatibility (for code that calls
544 // the parse method in ProjectHelper).
545
546 /** Default parsing method. It is here only to support backward compatibility
547 * for the static ProjectHelper.parsePropertyString().
548 */
549 static void parsePropertyStringDefault(String value, Vector fragments,
550 Vector propertyRefs)
551 throws BuildException {
552 int prev = 0;
553 int pos;
554 //search for the next instance of $ from the 'prev' position
555 while ((pos = value.indexOf("$", prev)) >= 0) {
556
557 //if there was any text before this, add it as a fragment
558 //TODO, this check could be modified to go if pos>prev;
559 //seems like this current version could stick empty strings
560 //into the list
561 if (pos > 0) {
562 fragments.addElement(value.substring(prev, pos));
563 }
564 //if we are at the end of the string, we tack on a $
565 //then move past it
566 if (pos == (value.length() - 1)) {
567 fragments.addElement("$");
568 prev = pos + 1;
569 } else if (value.charAt(pos + 1) != '{') {
570 //peek ahead to see if the next char is a property or not
571 //not a property: insert the char as a literal
572 /*
573 fragments.addElement(value.substring(pos + 1, pos + 2));
574 prev = pos + 2;
575 */
576 if (value.charAt(pos + 1) == '$') {
577 //backwards compatibility two $ map to one mode
578 fragments.addElement("$");
579 prev = pos + 2;
580 } else {
581 //new behaviour: $X maps to $X for all values of X!='$'
582 fragments.addElement(value.substring(pos, pos + 2));
583 prev = pos + 2;
584 }
585
586 } else {
587 //property found, extract its name or bail on a typo
588 int endName = value.indexOf('}', pos);
589 if (endName < 0) {
590 throw new BuildException("Syntax error in property: "
591 + value);
592 }
593 String propertyName = value.substring(pos + 2, endName);
594 fragments.addElement(null);
595 propertyRefs.addElement(propertyName);
596 prev = endName + 1;
597 }
598 }
599 //no more $ signs found
600 //if there is any tail to the file, append it
601 if (prev < value.length()) {
602 fragments.addElement(value.substring(prev));
603 }
604 }
605
606}
Note: See TracBrowser for help on using the repository browser.