KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > module > api > support > TargetLister


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.apache.tools.ant.module.api.support;
21
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.InputStream JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.Collection JavaDoc;
28 import java.util.Collections JavaDoc;
29 import java.util.HashMap JavaDoc;
30 import java.util.HashSet JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.List JavaDoc;
33 import java.util.Locale JavaDoc;
34 import java.util.Map JavaDoc;
35 import java.util.Properties JavaDoc;
36 import java.util.Set JavaDoc;
37 import java.util.WeakHashMap JavaDoc;
38 import org.apache.tools.ant.module.api.AntProjectCookie;
39 import org.apache.tools.ant.module.xml.AntProjectSupport;
40 import org.openide.filesystems.FileObject;
41 import org.openide.filesystems.FileUtil;
42 import org.openide.loaders.DataObject;
43 import org.openide.loaders.DataObjectNotFoundException;
44 import org.openide.util.NbCollections;
45 import org.openide.util.TopologicalSortException;
46 import org.openide.util.Union2;
47 import org.openide.util.Utilities;
48 import org.w3c.dom.Element JavaDoc;
49 import org.w3c.dom.Node JavaDoc;
50 import org.w3c.dom.NodeList JavaDoc;
51
52 /**
53  * Provides a way to find targets from an Ant build script.
54  * <p>
55  * Note that scripts may import other scripts using
56  * the <code>&lt;import&gt;</code> pseudotask, so you may need
57  * to use {@link Target#getScript} to check which script a target came from.
58  * </p>
59  * <p>
60  * <strong>Warning:</strong> the current implementation does not attempt to handle
61  * import statements which use Ant properties in the imported file name, since
62  * it is not possible to determine what the value of the file path will actually
63  * be at runtime, at least not with complete accuracy. A future implementation
64  * may be enhanced to handle most such cases, based on property definitions found
65  * in the Ant script. Currently such imports are quietly ignored.
66  * </p>
67  * <p>
68  * The imported file path is considered relative to the project
69  * base directory, hopefully according to Ant's own rules.
70  * </p>
71  * <p>
72  * If an import statement is marked as optional, and the imported script cannot
73  * be found, it will be silently skipped (as Ant does). If it is marked as mandatory
74  * (the default), this situation will result in an {@link IOException}.
75  * </p>
76  * @author Jesse Glick
77  * @since org.apache.tools.ant.module/3 3.11
78  */

79 public class TargetLister {
80     
81     private TargetLister() {}
82     
83     /**
84      * Gets all targets in an Ant script.
85      * Some may come from imported scripts.
86      * There is no guarantee that the actual {@link Target} objects will be
87      * the same from call to call.
88      * @param script an Ant build script
89      * @return an immutable, unchanging set of {@link Target}s; may be empty
90      * @throws IOException in case there is a problem reading the script (or a subscript)
91      */

92     public static Set JavaDoc<Target> getTargets(AntProjectCookie script) throws IOException JavaDoc {
93         Set JavaDoc<File JavaDoc> alreadyImported = new HashSet JavaDoc<File JavaDoc>();
94         Map JavaDoc<String JavaDoc,String JavaDoc> properties = NbCollections.checkedMapByFilter(System.getProperties(), String JavaDoc.class, String JavaDoc.class, false);
95         Script main = new Script(null, script, alreadyImported, properties, Collections.<String JavaDoc,Element JavaDoc>emptyMap());
96         Set JavaDoc<Target> targets = new HashSet JavaDoc<Target>();
97         Set JavaDoc<AntProjectCookie> visitedScripts = new HashSet JavaDoc<AntProjectCookie>();
98         traverseScripts(main, targets, visitedScripts);
99         return targets;
100     }
101     
102     /**
103      * Walk import tree in a depth-first search.
104      * At each node, collect the targets.
105      * Skip over nodes representing scripts which were already imported via a different path.
106      */

107     private static void traverseScripts(Script script, Set JavaDoc<Target> targets, Set JavaDoc<AntProjectCookie> visitedScripts) throws IOException JavaDoc {
108         if (!visitedScripts.add(script.getScript())) {
109             return;
110         }
111         targets.addAll(script.getTargets());
112         for (Script imported : script.getImports()) {
113             traverseScripts(imported, targets, visitedScripts);
114         }
115     }
116     
117     /**
118      * Representation of a target from an Ant script.
119      */

120     public static final class Target {
121         
122         private final Script script;
123         private final Element JavaDoc el;
124         private final String JavaDoc name;
125         
126         Target(Script script, Element JavaDoc el, String JavaDoc name) {
127             this.script = script;
128             this.el = el;
129             this.name = name;
130         }
131         
132         /**
133          * Gets the simple name of the target.
134          * This is just whatever is declared in the <code>name</code> attribute.
135          * @return the target name
136          */

137         public String JavaDoc getName() {
138             return name;
139         }
140         
141         /**
142          * Gets the qualified name of the target.
143          * This consists of the name of the project followed by a dot (<samp>.</samp>)
144          * followed by the simple target name.
145          * (Or just the simple target name in case the project has no defined name;
146          * questionable whether this is even legal.)
147          * The qualified name may be used in a <code>depends</code> attribute to
148          * distinguish an imported target from a target of the same name in the
149          * importing script.
150          * @return the qualified name
151          */

152         public String JavaDoc getQualifiedName() {
153             String JavaDoc n = script.getName();
154             if (n != null) {
155                 return n + '.' + getName();
156             } else {
157                 return getName();
158             }
159         }
160         
161         /**
162          * Gets the XML element that defines the target.
163          * @return an element with local name <code>target</code>
164          */

165         public Element JavaDoc getElement() {
166             return el;
167         }
168         
169         /**
170          * Gets the actual Ant script this target was found in.
171          * {@link #getElement} should be owned by {@link AntProjectCookie#getDocument}.
172          * @return the script which defines this target
173          */

174         public AntProjectCookie getScript() {
175             return script.getScript();
176         }
177         
178         /**
179          * Tests whether this target has a description.
180          * This is the <code>description</code> attribute in XML.
181          * Typically, targets with descriptions are intended to be exposed to the
182          * user of the script, whereas undescribed targets may not be intended
183          * for general use. However not all script authors use descriptions, so
184          * described targets should only be given UI precedence.
185          * @return true if the target has a description
186          */

187         public boolean isDescribed() {
188             return el.getAttribute("description").length() > 0;
189         }
190         
191         /**
192          * Tests whether a target is marked as internal to the script.
193          * Currently this means that the target name begins with a hyphen (<samp>-</samp>),
194          * though the precise semantics may be changed according to changes in Ant.
195          * Conventionally, internal targets are not intended to be run directly, and only
196          * exist to be called from other targets. As such, they should not normally
197          * be presented in the context of targets you might want to run.
198          * @return true if this is marked as an internal target, false for a regular target
199          * @see <a HREF="http://issues.apache.org/bugzilla/show_bug.cgi?id=22020">Ant issue #22020</a>
200          */

201         public boolean isInternal() {
202             String JavaDoc n = getName();
203             return n.length() > 0 && n.charAt(0) == '-';
204         }
205         
206         /**
207          * Tests whether this target is overridden in an importing script.
208          * If an importing script has a target of the same name as a target
209          * in an imported script, the latter is considered overridden, and may
210          * not be called directly (though it may be used as a dependency, if
211          * qualified via {@link #getQualifiedName}).
212          * Note that this flag may be true when asked of a {@link Target} gotten
213          * via the importing script, while false when asked of the same target
214          * gotten directly from the imported script, since the meaning is dependent
215          * on the import chain.
216          * @return true if the target is overridden
217          */

218         public boolean isOverridden() {
219             return !script.defines(getName());
220         }
221         
222         /**
223          * Tests whether this target is the default for the main script.
224          * Note that a set of targets will have at most one default target;
225          * any <code>default</code> attribute in an imported script is ignored.
226          * However the default target might come from an imported script.
227          * @return true if the target is the default target
228          */

229         public boolean isDefault() {
230             return !isOverridden() && getName().equals(script.getMainScript().getDefaultTargetName());
231         }
232         
233         @Override JavaDoc
234         public String JavaDoc toString() {
235             return "Target " + getName() + " in " + getScript(); // NOI18N
236
}
237         
238     }
239     
240     /**
241      * Representation of one script full of targets.
242      */

243     private static final class Script {
244         
245         private final AntProjectCookie apc;
246         private final Script importingScript;
247         private final Map JavaDoc<String JavaDoc,Target> targets;
248         private final String JavaDoc defaultTarget;
249         private final List JavaDoc<Script> imports;
250         private final String JavaDoc name;
251         
252         private static final Set JavaDoc<String JavaDoc> TRUE_VALS = new HashSet JavaDoc<String JavaDoc>(5);
253         static {
254             TRUE_VALS.add("true"); // NOI18N
255
TRUE_VALS.add("yes"); // NOI18N
256
TRUE_VALS.add("on"); // NOI18N
257
}
258         
259         public Script(Script importingScript, AntProjectCookie apc, Set JavaDoc<File JavaDoc> alreadyImported, Map JavaDoc<String JavaDoc,String JavaDoc> inheritedPropertyDefs, Map JavaDoc<String JavaDoc,Element JavaDoc> inheritedMacroDefs) throws IOException JavaDoc {
260             this.importingScript = importingScript;
261             this.apc = apc;
262             Element JavaDoc prj = apc.getProjectElement();
263             if (prj == null) {
264                 throw new IOException JavaDoc("Could not parse " + apc); // NOI18N
265
}
266             File JavaDoc prjFile = apc.getFile();
267             if (prjFile != null) {
268                 alreadyImported.add(prjFile);
269             }
270             String JavaDoc _defaultTarget = prj.getAttribute("default"); // NOI18N
271
defaultTarget = _defaultTarget.length() > 0 ? _defaultTarget : null;
272             String JavaDoc _name = prj.getAttribute("name"); // NOI18N
273
name = _name.length() > 0 ? _name : null;
274             // Treat basedir as relative to the project file, regardless
275
// of import context.
276
String JavaDoc basedirS = prj.getAttribute("basedir"); // NOI18N
277
if (basedirS.length() == 0) {
278                 basedirS = "."; // NOI18N
279
} else {
280                 basedirS = basedirS.replace('/', File.separatorChar).replace('\\', File.separatorChar);
281             }
282             File JavaDoc _basedir = new File JavaDoc(basedirS);
283             File JavaDoc basedir;
284             if (_basedir.isAbsolute()) {
285                 basedir = _basedir;
286             } else {
287                 if (prjFile != null) {
288                     basedir = new File JavaDoc(prjFile.getParentFile(), basedirS);
289                 } else {
290                     // Script not on disk.
291
basedir = null;
292                 }
293             }
294             // Go through top-level elements and look for <target> and <import>.
295
targets = new HashMap JavaDoc<String JavaDoc,Target>();
296             Map JavaDoc<String JavaDoc,String JavaDoc> propertyDefs = new HashMap JavaDoc<String JavaDoc,String JavaDoc>(inheritedPropertyDefs);
297             if (basedir != null && !propertyDefs.containsKey("basedir")) { // NOI18N
298
propertyDefs.put("basedir", basedir.getAbsolutePath()); // NOI18N
299
}
300             Map JavaDoc<String JavaDoc,Element JavaDoc> macroDefs = new HashMap JavaDoc<String JavaDoc,Element JavaDoc>(inheritedMacroDefs);
301             // Keep imported scripts in definition order so result is deterministic
302
// if a subsubscript is imported via two different paths: first one (DFS)
303
// takes precedence.
304
imports = new ArrayList JavaDoc<Script>();
305             interpretTasks(alreadyImported, prj, basedir, propertyDefs, macroDefs, null);
306         }
307         
308         private void interpretTasks(Set JavaDoc<File JavaDoc> alreadyImported, Element JavaDoc container, File JavaDoc basedir, Map JavaDoc<String JavaDoc,String JavaDoc> propertyDefs, Map JavaDoc<String JavaDoc,Element JavaDoc> macroDefs, Map JavaDoc<String JavaDoc,String JavaDoc> macroParams) throws IOException JavaDoc {
309             //System.err.println("interpretTasks: propertyDefs=" + propertyDefs + " macroParams=" + macroParams + " macroDefs=" + macroDefs.keySet());
310
NodeList JavaDoc nl = container.getChildNodes();
311             int len = nl.getLength();
312             for (int i = 0; i < len; i++) {
313                 Node JavaDoc n = nl.item(i);
314                 if (n.getNodeType() != Node.ELEMENT_NODE) {
315                     continue;
316                 }
317                 Element JavaDoc el = (Element JavaDoc)n;
318                 String JavaDoc elName = el.getLocalName();
319                 String JavaDoc fullname = elName;
320                 // Check for a macro definition.
321
// XXX Does not handle <customize>.
322
String JavaDoc uri = el.getNamespaceURI();
323                 if (uri != null) {
324                     fullname = uri + '#' + fullname;
325                 }
326                 Element JavaDoc macro = macroDefs.get(fullname);
327                 if (macro != null) {
328                     Map JavaDoc<String JavaDoc,String JavaDoc> newMacroParams = new HashMap JavaDoc<String JavaDoc,String JavaDoc>();
329                     NodeList JavaDoc macroKids = macro.getChildNodes();
330                     for (int j = 0; j < macroKids.getLength(); j++) {
331                         if (macroKids.item(j).getNodeType() != Node.ELEMENT_NODE) {
332                             continue;
333                         }
334                         Element JavaDoc el2 = (Element JavaDoc) macroKids.item(j);
335                         String JavaDoc elName2 = el2.getLocalName();
336                         if (elName2.equals("attribute")) { // NOI18N
337
String JavaDoc attrName = el2.getAttribute("name"); // NOI18N
338
if (attrName.length() == 0) {
339                                 continue;
340                             }
341                             String JavaDoc attrVal = el.getAttribute(attrName);
342                             String JavaDoc attrValSubst = replaceAntProperties(attrVal, propertyDefs);
343                             if (attrValSubst == null) {
344                                 continue;
345                             }
346                             newMacroParams.put(attrName, attrValSubst);
347                         } else if (elName2.equals("sequential")) { // NOI18N
348
interpretTasks(alreadyImported, el2, basedir, propertyDefs, macroDefs, newMacroParams);
349                         }
350                     }
351                 } else if (macroParams == null && elName.equals("target")) { // NOI18N
352
String JavaDoc name = el.getAttribute("name"); // NOI18N
353
targets.put(name, new Target(this, el, name));
354                 } else if (macroParams == null && elName.equals("import")) { // NOI18N
355
String JavaDoc fileS = el.getAttribute("file").replace('/', File.separatorChar).replace('\\', File.separatorChar); // NOI18N
356
String JavaDoc fileSubstituted = replaceAntProperties(fileS, propertyDefs);
357                     if (fileSubstituted.indexOf("${") != -1) { // NOI18N
358
// Too complex a substitution to handle.
359
// #45066: throwing an IOException might be more correct, but is undesirable in practice.
360
//System.err.println("cannot import " + fileSubstituted);
361
continue;
362                     }
363                     File JavaDoc _file = new File JavaDoc(fileSubstituted);
364                     File JavaDoc file;
365                     if (_file.isAbsolute()) {
366                         file = _file;
367                     } else {
368                         if (apc.getFile() == null) {
369                             //throw new IOException("Cannot import relative path " + fileS + " from a diskless script"); // NOI18N
370
// Can happen when refreshing Navigator or the like after deleting a script with imports. Just ignore.
371
continue;
372                         }
373                         // #50087: <import> resolves file against the script, *not* the basedir.
374
file = new File JavaDoc(apc.getFile().getParentFile(), fileSubstituted);
375                     }
376                     if (alreadyImported.contains(file)) {
377                         // #55263: avoid a stack overflow on a recursive import.
378
continue;
379                     }
380                     if (file.canRead()) {
381                         FileObject fileObj = FileUtil.toFileObject(FileUtil.normalizeFile(file));
382                         assert fileObj != null : file;
383                         AntProjectCookie importedApc = getAntProjectCookie(fileObj);
384                         imports.add(new Script(this, importedApc, alreadyImported, propertyDefs, macroDefs));
385                     } else {
386                         String JavaDoc optionalS = el.getAttribute("optional"); // NOI18N
387
boolean optional = TRUE_VALS.contains(optionalS.toLowerCase(Locale.US));
388                         if (!optional) {
389                             throw new IOException JavaDoc("Cannot find import " + file + " from " + apc); // NOI18N
390
}
391                     }
392                 } else if (elName.equals("property")) { // NOI18N
393
if (el.hasAttribute("value")) { // NOI18N
394
String JavaDoc name = replaceMacroParams(el.getAttribute("name"), macroParams); // NOI18N
395
if (name.length() == 0) {
396                             continue;
397                         }
398                         if (propertyDefs.containsKey(name)) {
399                             continue;
400                         }
401                         String JavaDoc value = replaceMacroParams(el.getAttribute("value"), macroParams); // NOI18N
402
String JavaDoc valueSubst = replaceAntProperties(value, propertyDefs);
403                         propertyDefs.put(name, valueSubst);
404                         continue;
405                     }
406                     String JavaDoc file = replaceMacroParams(el.getAttribute("file"), macroParams); // NOI18N
407
if (file.length() > 0) {
408                         String JavaDoc fileSubst = replaceAntProperties(file, propertyDefs);
409                         File JavaDoc propertyFile = new File JavaDoc(fileSubst);
410                         if (!propertyFile.isAbsolute() && basedir != null) {
411                             propertyFile = new File JavaDoc(basedir, fileSubst.replace('/', File.separatorChar).replace('\\', File.separatorChar));
412                         }
413                         if (!propertyFile.canRead()) {
414                             //System.err.println("cannot read from " + propertyFile);
415
continue;
416                         }
417                         Properties JavaDoc p = new Properties JavaDoc();
418                         InputStream JavaDoc is = new FileInputStream JavaDoc(propertyFile);
419                         try {
420                             p.load(is);
421                         } finally {
422                             is.close();
423                         }
424                         Map JavaDoc<String JavaDoc,String JavaDoc> evaluatedProperties = evaluateAll(propertyDefs, Collections.singletonList(NbCollections.checkedMapByFilter(p, String JavaDoc.class, String JavaDoc.class, true)));
425                         //System.err.println("loaded properties: " + evaluatedProperties);
426
if (evaluatedProperties == null) {
427                             continue;
428                         }
429                         Iterator JavaDoc it = evaluatedProperties.entrySet().iterator();
430                         while (it.hasNext()) {
431                             Map.Entry JavaDoc entry = (Map.Entry JavaDoc) it.next();
432                             String JavaDoc k = (String JavaDoc) entry.getKey();
433                             if (!propertyDefs.containsKey(k)) {
434                                 propertyDefs.put(k, (String JavaDoc) entry.getValue());
435                             }
436                         }
437                     }
438                 } else if (elName.equals("macrodef")) { // NOI18N
439
String JavaDoc name = el.getAttribute("name");
440                     if (name.length() == 0) {
441                         continue;
442                     }
443                     uri = el.getAttribute("uri"); // NOI18N
444
if (uri.length() > 0) {
445                         name = uri + '#' + name;
446                     }
447                     if (!macroDefs.containsKey(name)) {
448                         macroDefs.put(name, el);
449                     }
450                 }
451             }
452         }
453         
454         private static String JavaDoc replaceMacroParams(String JavaDoc rawval, Map JavaDoc<String JavaDoc,String JavaDoc> defs) {
455             if (rawval.indexOf('@') == -1) {
456                 // Shortcut:
457
return rawval;
458             }
459             int idx = 0;
460             StringBuffer JavaDoc val = new StringBuffer JavaDoc();
461             while (true) {
462                 int monkey = rawval.indexOf('@', idx);
463                 if (monkey == -1 || monkey == rawval.length() - 1) {
464                     val.append(rawval.substring(idx));
465                     return val.toString();
466                 }
467                 char c = rawval.charAt(monkey + 1);
468                 if (c == '{') {
469                     int end = rawval.indexOf('}', monkey + 2);
470                     if (end != -1) {
471                         String JavaDoc otherprop = rawval.substring(monkey + 2, end);
472                         if (defs.containsKey(otherprop)) {
473                             val.append(rawval.substring(idx, monkey));
474                             val.append(defs.get(otherprop));
475                         } else {
476                             val.append(rawval.substring(idx, end + 1));
477                         }
478                         idx = end + 1;
479                     } else {
480                         val.append(rawval.substring(idx));
481                         return val.toString();
482                     }
483                 } else {
484                     val.append(rawval.substring(idx, idx + 2));
485                     idx += 2;
486                 }
487             }
488         }
489         
490         private static String JavaDoc replaceAntProperties(String JavaDoc rawval, Map JavaDoc<String JavaDoc,String JavaDoc> defs) {
491             return subst(rawval, defs, Collections.<String JavaDoc>emptySet()).first();
492         }
493         
494         // Copied from org.netbeans.spi.project.support.ant.PropertyUtils.
495
private static Map JavaDoc<String JavaDoc,String JavaDoc> evaluateAll(Map JavaDoc<String JavaDoc,String JavaDoc> predefs, List JavaDoc<Map JavaDoc<String JavaDoc,String JavaDoc>> defs) {
496             Map JavaDoc<String JavaDoc,String JavaDoc> m = new HashMap JavaDoc<String JavaDoc,String JavaDoc>(predefs);
497             for (Map JavaDoc<String JavaDoc,String JavaDoc> curr : defs) {
498                 // Set of properties which we are deferring because they subst sibling properties:
499
Map JavaDoc<String JavaDoc,Set JavaDoc<String JavaDoc>> dependOnSiblings = new HashMap JavaDoc<String JavaDoc,Set JavaDoc<String JavaDoc>>();
500                 for (Map.Entry JavaDoc<String JavaDoc,String JavaDoc> entry : curr.entrySet()) {
501                     String JavaDoc prop = entry.getKey();
502                     if (!m.containsKey(prop)) {
503                         String JavaDoc rawval = entry.getValue();
504                         //System.err.println("subst " + prop + "=" + rawval + " with " + m);
505
Union2<String JavaDoc,Set JavaDoc<String JavaDoc>> r = subst(rawval, m, curr.keySet());
506                         if (r.hasFirst()) {
507                             m.put(prop, r.first());
508                         } else {
509                             dependOnSiblings.put(prop, r.second());
510                         }
511                     }
512                 }
513                 Set JavaDoc<String JavaDoc> toSort = new HashSet JavaDoc<String JavaDoc>(dependOnSiblings.keySet());
514                 for (Set JavaDoc<String JavaDoc> sibs : dependOnSiblings.values()) {
515                     toSort.addAll(sibs);
516                 }
517                 List JavaDoc<String JavaDoc> sorted;
518                 try {
519                     sorted = Utilities.topologicalSort(toSort, dependOnSiblings);
520                 } catch (TopologicalSortException e) {
521                     //System.err.println("Cyclic property refs: " + Arrays.asList(e.unsortableSets()));
522
return null;
523                 }
524                 Collections.reverse(sorted);
525                 for (String JavaDoc prop : sorted) {
526                     if (!m.containsKey(prop)) {
527                         String JavaDoc rawval = curr.get(prop);
528                         m.put(prop, subst(rawval, m, /*Collections.EMPTY_SET*/curr.keySet()).first());
529                     }
530                 }
531             }
532             return m;
533         }
534         private static Union2<String JavaDoc,Set JavaDoc<String JavaDoc>> subst(String JavaDoc rawval, Map JavaDoc<String JavaDoc,String JavaDoc> predefs, Set JavaDoc<String JavaDoc> siblingProperties) {
535             assert rawval != null : "null rawval passed in";
536             if (rawval.indexOf('$') == -1) {
537                 // Shortcut:
538
//System.err.println("shortcut");
539
return Union2.createFirst(rawval);
540             }
541             // May need to subst something.
542
int idx = 0;
543             // Result in progress, if it is to be a String:
544
StringBuffer JavaDoc val = new StringBuffer JavaDoc();
545             // Or, result in progress, if it is to be a Set<String>:
546
Set JavaDoc<String JavaDoc> needed = new HashSet JavaDoc<String JavaDoc>();
547             while (true) {
548                 int shell = rawval.indexOf('$', idx);
549                 if (shell == -1 || shell == rawval.length() - 1) {
550                     // No more $, or only as last char -> copy all.
551
//System.err.println("no more $");
552
if (needed.isEmpty()) {
553                         val.append(rawval.substring(idx));
554                         return Union2.createFirst(val.toString());
555                     } else {
556                         return Union2.createSecond(needed);
557                     }
558                 }
559                 char c = rawval.charAt(shell + 1);
560                 if (c == '$') {
561                     // $$ -> $
562
//System.err.println("$$");
563
if (needed.isEmpty()) {
564                         val.append('$');
565                     }
566                     idx += 2;
567                 } else if (c == '{') {
568                     // Possibly a property ref.
569
int end = rawval.indexOf('}', shell + 2);
570                     if (end != -1) {
571                         // Definitely a property ref.
572
String JavaDoc otherprop = rawval.substring(shell + 2, end);
573                         //System.err.println("prop ref to " + otherprop);
574
if (predefs.containsKey(otherprop)) {
575                             // Well-defined.
576
if (needed.isEmpty()) {
577                                 val.append(rawval.substring(idx, shell));
578                                 val.append(predefs.get(otherprop));
579                             }
580                             idx = end + 1;
581                         } else if (siblingProperties.contains(otherprop)) {
582                             needed.add(otherprop);
583                             // don't bother updating val, it will not be used anyway
584
idx = end + 1;
585                         } else {
586                             // No def, leave as is.
587
if (needed.isEmpty()) {
588                                 val.append(rawval.substring(idx, end + 1));
589                             }
590                             idx = end + 1;
591                         }
592                     } else {
593                         // Unclosed ${ sequence, leave as is.
594
if (needed.isEmpty()) {
595                             val.append(rawval.substring(idx));
596                             return Union2.createFirst(val.toString());
597                         } else {
598                             return Union2.createSecond(needed);
599                         }
600                     }
601                 } else {
602                     // $ followed by some other char, leave as is.
603
// XXX is this actually right?
604
if (needed.isEmpty()) {
605                         val.append(rawval.substring(idx, idx + 2));
606                     }
607                     idx += 2;
608                 }
609             }
610         }
611         
612         /** Get the associated script. */
613         public AntProjectCookie getScript() {
614             return apc;
615         }
616         
617         /** Get project name (or null). */
618         public String JavaDoc getName() {
619             return name;
620         }
621         
622         /** Get targets defined in this script. */
623         public Collection JavaDoc<Target> getTargets() {
624             return targets.values();
625         }
626         
627         /** Get name of default target (or null). */
628         public String JavaDoc getDefaultTargetName() {
629             return defaultTarget;
630         }
631         
632         /** Get imported scripts. */
633         public Collection JavaDoc<Script> getImports() {
634             return imports;
635         }
636         
637         /** Get the script importing this one (or null). */
638         public Script getImportingScript() {
639             return importingScript;
640         }
641         
642         /** Get the main script (never null). */
643         public Script getMainScript() {
644             if (importingScript != null) {
645                 return importingScript.getMainScript();
646             } else {
647                 return this;
648             }
649         }
650         
651         /** Test whether this script is the one to define a given target name. */
652         public boolean defines(String JavaDoc targetName) {
653             if (!targets.containsKey(targetName)) {
654                 return false;
655             }
656             for (Script s = importingScript; s != null; s = s.importingScript) {
657                 if (s.targets.containsKey(targetName)) {
658                     return false;
659                 }
660             }
661             return true;
662         }
663         
664     }
665     
666     /**
667      * Try to find an AntProjectCookie for a file.
668      */

669     static AntProjectCookie getAntProjectCookie(FileObject fo) {
670         try {
671             DataObject d = DataObject.find(fo);
672             AntProjectCookie apc = d.getCookie(AntProjectCookie.class);
673             if (apc != null) {
674                 return apc;
675             }
676         } catch (DataObjectNotFoundException e) {
677             assert false : e;
678         }
679         // AntProjectDataLoader probably not installed, e.g. from a unit test.
680
synchronized (antProjectCookies) {
681             AntProjectCookie apc = antProjectCookies.get(fo);
682             if (apc == null) {
683                 apc = new AntProjectSupport(fo);
684                 antProjectCookies.put(fo, apc);
685             }
686             return apc;
687         }
688     }
689     private static final Map JavaDoc<FileObject,AntProjectCookie> antProjectCookies = new WeakHashMap JavaDoc<FileObject,AntProjectCookie>();
690     
691 }
692
Popular Tags