KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > types > Path


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.types;
20
21 import java.io.File JavaDoc;
22 import java.util.Collections JavaDoc;
23 import java.util.Iterator JavaDoc;
24 import java.util.Locale JavaDoc;
25 import java.util.Stack JavaDoc;
26 import java.util.Vector JavaDoc;
27
28 import org.apache.tools.ant.BuildException;
29 import org.apache.tools.ant.PathTokenizer;
30 import org.apache.tools.ant.Project;
31 import org.apache.tools.ant.types.resources.Union;
32 import org.apache.tools.ant.types.resources.FileResourceIterator;
33 import org.apache.tools.ant.util.FileUtils;
34 import org.apache.tools.ant.util.JavaEnvUtils;
35
36 /**
37  * This object represents a path as used by CLASSPATH or PATH
38  * environment variable. A path might also be described as a collection
39  * of unique filesystem resources.
40  * <p>
41  * <code>
42  * &lt;sometask&gt;<br>
43  * &nbsp;&nbsp;&lt;somepath&gt;<br>
44  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file.jar" /&gt;<br>
45  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement
46  * path="/path/to/file2.jar:/path/to/class2;/path/to/class3" /&gt;
47  * <br>
48  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file3.jar" /&gt;<br>
49  * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file4.jar" /&gt;<br>
50  * &nbsp;&nbsp;&lt;/somepath&gt;<br>
51  * &lt;/sometask&gt;<br>
52  * </code>
53  * <p>
54  * The object implemention <code>sometask</code> must provide a method called
55  * <code>createSomepath</code> which returns an instance of <code>Path</code>.
56  * Nested path definitions are handled by the Path object and must be labeled
57  * <code>pathelement</code>.<p>
58  *
59  * The path element takes a parameter <code>path</code> which will be parsed
60  * and split into single elements. It will usually be used
61  * to define a path from an environment variable.
62  */

63
64 public class Path extends DataType implements Cloneable JavaDoc, ResourceCollection {
65     // CheckStyle:VisibilityModifier OFF - bc
66

67     /** The system classpath as a Path object */
68     public static Path systemClasspath =
69         new Path(null, System.getProperty("java.class.path"));
70
71
72     /**
73      * The system bootclasspath as a Path object.
74      *
75      * @since Ant 1.6.2
76      */

77     public static Path systemBootClasspath =
78         new Path(null, System.getProperty("sun.boot.class.path"));
79
80     private static final Iterator JavaDoc EMPTY_ITERATOR
81         = Collections.EMPTY_SET.iterator();
82
83     // CheckStyle:VisibilityModifier OFF - bc
84

85     /**
86      * Helper class, holds the nested <code>&lt;pathelement&gt;</code> values.
87      */

88     public class PathElement implements ResourceCollection {
89         private String JavaDoc[] parts;
90
91         /**
92          * Set the location.
93          *
94          * @param loc a <code>File</code> value
95          */

96         public void setLocation(File JavaDoc loc) {
97             parts = new String JavaDoc[] {translateFile(loc.getAbsolutePath())};
98         }
99
100         /**
101          * Set the path.
102          *
103          * @param path a <code>String</code> value
104          */

105         public void setPath(String JavaDoc path) {
106             parts = Path.translatePath(getProject(), path);
107         }
108
109         /**
110          * Return the converted pathelements.
111          *
112          * @return a <code>String[]</code> value
113          */

114         public String JavaDoc[] getParts() {
115             return parts;
116         }
117
118         /**
119          * Create an iterator.
120          * @return an iterator.
121          */

122         public Iterator JavaDoc iterator() {
123             return new FileResourceIterator(null, parts);
124         }
125
126         /**
127          * Check if this resource is only for filesystems.
128          * @return true.
129          */

130         public boolean isFilesystemOnly() {
131             return true;
132         }
133
134         /**
135          * Get the number of resources.
136          * @return the number of parts.
137          */

138         public int size() {
139             return parts == null ? 0 : parts.length;
140         }
141
142     }
143
144     private Union union = null;
145
146     /**
147      * Invoked by IntrospectionHelper for <code>setXXX(Path p)</code>
148      * attribute setters.
149      * @param p the <code>Project</code> for this path.
150      * @param path the <code>String</code> path definition.
151      */

152     public Path(Project p, String JavaDoc path) {
153         this(p);
154         createPathElement().setPath(path);
155     }
156
157     /**
158      * Construct an empty <code>Path</code>.
159      * @param project the <code>Project</code> for this path.
160      */

161     public Path(Project project) {
162         setProject(project);
163     }
164
165     /**
166      * Adds a element definition to the path.
167      * @param location the location of the element to add (must not be
168      * <code>null</code> nor empty.
169      * @throws BuildException on error
170      */

171     public void setLocation(File JavaDoc location) throws BuildException {
172         checkAttributesAllowed();
173         createPathElement().setLocation(location);
174     }
175
176     /**
177      * Parses a path definition and creates single PathElements.
178      * @param path the <code>String</code> path definition.
179      * @throws BuildException on error
180      */

181     public void setPath(String JavaDoc path) throws BuildException {
182         checkAttributesAllowed();
183         createPathElement().setPath(path);
184     }
185
186     /**
187      * Makes this instance in effect a reference to another Path instance.
188      *
189      * <p>You must not set another attribute or nest elements inside
190      * this element if you make it a reference.</p>
191      * @param r the reference to another Path
192      * @throws BuildException on error
193      */

194     public void setRefid(Reference r) throws BuildException {
195         if (union != null) {
196             throw tooManyAttributes();
197         }
198         super.setRefid(r);
199     }
200
201     /**
202      * Creates the nested <code>&lt;pathelement&gt;</code> element.
203      * @return the <code>PathElement</code> to be configured
204      * @throws BuildException on error
205      */

206     public PathElement createPathElement() throws BuildException {
207         if (isReference()) {
208             throw noChildrenAllowed();
209         }
210         PathElement pe = new PathElement();
211         add(pe);
212         return pe;
213     }
214
215     /**
216      * Adds a nested <code>&lt;fileset&gt;</code> element.
217      * @param fs a <code>FileSet</code> to be added to the path
218      * @throws BuildException on error
219      */

220     public void addFileset(FileSet fs) throws BuildException {
221         if (fs.getProject() == null) {
222             fs.setProject(getProject());
223         }
224         add(fs);
225     }
226
227     /**
228      * Adds a nested <code>&lt;filelist&gt;</code> element.
229      * @param fl a <code>FileList</code> to be added to the path
230      * @throws BuildException on error
231      */

232     public void addFilelist(FileList fl) throws BuildException {
233         if (fl.getProject() == null) {
234             fl.setProject(getProject());
235         }
236         add(fl);
237     }
238
239     /**
240      * Adds a nested <code>&lt;dirset&gt;</code> element.
241      * @param dset a <code>DirSet</code> to be added to the path
242      * @throws BuildException on error
243      */

244     public void addDirset(DirSet dset) throws BuildException {
245         if (dset.getProject() == null) {
246             dset.setProject(getProject());
247         }
248         add(dset);
249     }
250
251     /**
252      * Adds a nested path
253      * @param path a <code>Path</code> to be added to the path
254      * @throws BuildException on error
255      * @since Ant 1.6
256      */

257     public void add(Path path) throws BuildException {
258         if (path == this) {
259             throw circularReference();
260         }
261         if (path.getProject() == null) {
262             path.setProject(getProject());
263         }
264         add((ResourceCollection) path);
265     }
266
267     /**
268      * Add a nested <code>ResourceCollection</code>.
269      * @param c the ResourceCollection to add.
270      * @since Ant 1.7
271      */

272     public void add(ResourceCollection c) {
273         checkChildrenAllowed();
274         if (c == null) {
275             return;
276         }
277         if (union == null) {
278             union = new Union();
279             union.setProject(getProject());
280             union.setCache(false);
281         }
282         union.add(c);
283         setChecked(false);
284     }
285
286     /**
287      * Creates a nested <code>&lt;path&gt;</code> element.
288      * @return a <code>Path</code> to be configured
289      * @throws BuildException on error
290      */

291     public Path createPath() throws BuildException {
292         Path p = new Path(getProject());
293         add(p);
294         return p;
295     }
296
297     /**
298      * Append the contents of the other Path instance to this.
299      * @param other a <code>Path</code> to be added to the path
300      */

301     public void append(Path other) {
302         if (other == null) {
303             return;
304         }
305         add(other);
306     }
307
308     /**
309      * Adds the components on the given path which exist to this
310      * Path. Components that don't exist aren't added.
311      *
312      * @param source - source path whose components are examined for existence
313      */

314      public void addExisting(Path source) {
315          addExisting(source, false);
316      }
317
318     /**
319      * Same as addExisting, but support classpath behavior if tryUserDir
320      * is true. Classpaths are relative to user dir, not the project base.
321      * That used to break jspc test
322      *
323      * @param source the source path
324      * @param tryUserDir if true try the user directory if the file is not present
325      */

326     public void addExisting(Path source, boolean tryUserDir) {
327         String JavaDoc[] list = source.list();
328         File JavaDoc userDir = (tryUserDir) ? new File JavaDoc(System.getProperty("user.dir"))
329                 : null;
330
331         for (int i = 0; i < list.length; i++) {
332             File JavaDoc f = resolveFile(getProject(), list[i]);
333
334             // probably not the best choice, but it solves the problem of
335
// relative paths in CLASSPATH
336
if (tryUserDir && !f.exists()) {
337                 f = new File JavaDoc(userDir, list[i]);
338             }
339             if (f.exists()) {
340                 setLocation(f);
341             } else {
342                 log("dropping " + f + " from path as it doesn't exist",
343                     Project.MSG_VERBOSE);
344             }
345         }
346     }
347
348     /**
349      * Returns all path elements defined by this and nested path objects.
350      * @return list of path elements.
351      */

352     public String JavaDoc[] list() {
353         if (isReference()) {
354             return ((Path) getCheckedRef()).list();
355         }
356         return assertFilesystemOnly(union) == null
357             ? new String JavaDoc[0] : union.list();
358     }
359
360     /**
361      * Returns a textual representation of the path, which can be used as
362      * CLASSPATH or PATH environment variable definition.
363      * @return a textual representation of the path.
364      */

365     public String JavaDoc toString() {
366         return isReference() ? getCheckedRef().toString()
367             : union == null ? "" : union.toString();
368     }
369
370     /**
371      * Splits a PATH (with : or ; as separators) into its parts.
372      * @param project the project to use
373      * @param source a <code>String</code> value
374      * @return an array of strings, one for each path element
375      */

376     public static String JavaDoc[] translatePath(Project project, String JavaDoc source) {
377         final Vector JavaDoc result = new Vector JavaDoc();
378         if (source == null) {
379             return new String JavaDoc[0];
380         }
381         PathTokenizer tok = new PathTokenizer(source);
382         StringBuffer JavaDoc element = new StringBuffer JavaDoc();
383         while (tok.hasMoreTokens()) {
384             String JavaDoc pathElement = tok.nextToken();
385             try {
386                 element.append(resolveFile(project, pathElement).getPath());
387             } catch (BuildException e) {
388                 project.log("Dropping path element " + pathElement
389                     + " as it is not valid relative to the project",
390                     Project.MSG_VERBOSE);
391             }
392             for (int i = 0; i < element.length(); i++) {
393                 translateFileSep(element, i);
394             }
395             result.addElement(element.toString());
396             element = new StringBuffer JavaDoc();
397         }
398         String JavaDoc[] res = new String JavaDoc[result.size()];
399         result.copyInto(res);
400         return res;
401     }
402
403     /**
404      * Returns its argument with all file separator characters
405      * replaced so that they match the local OS conventions.
406      * @param source the path to convert
407      * @return the converted path
408      */

409     public static String JavaDoc translateFile(String JavaDoc source) {
410         if (source == null) {
411           return "";
412         }
413         final StringBuffer JavaDoc result = new StringBuffer JavaDoc(source);
414         for (int i = 0; i < result.length(); i++) {
415             translateFileSep(result, i);
416         }
417         return result.toString();
418     }
419
420     /**
421      * Translates occurrences at a position of / or \ to correct separator of the
422      * current platform and returns whether it had to do a
423      * replacement.
424      * @param buffer a buffer containing a string
425      * @param pos the position in the string buffer to convert
426      * @return true if the character was a / or \
427      */

428     protected static boolean translateFileSep(StringBuffer JavaDoc buffer, int pos) {
429         if (buffer.charAt(pos) == '/' || buffer.charAt(pos) == '\\') {
430             buffer.setCharAt(pos, File.separatorChar);
431             return true;
432         }
433         return false;
434     }
435
436     /**
437      * Fulfill the ResourceCollection contract.
438      * @return number of elements as int.
439      */

440     public synchronized int size() {
441         if (isReference()) {
442             return ((Path) getCheckedRef()).size();
443         }
444         dieOnCircularReference();
445         return union == null ? 0 : assertFilesystemOnly(union).size();
446     }
447
448     /**
449      * Clone this Path.
450      * @return Path with shallowly cloned Resource children.
451      */

452     public Object JavaDoc clone() {
453         try {
454             Path result = (Path) super.clone();
455             result.union = union == null ? union : (Union) union.clone();
456             return result;
457         } catch (CloneNotSupportedException JavaDoc e) {
458             throw new BuildException(e);
459         }
460     }
461
462     /**
463      * Overrides the version of DataType to recurse on all DataType
464      * child elements that may have been added.
465      * @param stk the stack of data types to use (recursively).
466      * @param p the project to use to dereference the references.
467      * @throws BuildException on error.
468      */

469     protected synchronized void dieOnCircularReference(Stack JavaDoc stk, Project p)
470         throws BuildException {
471         if (isChecked()) {
472             return;
473         }
474         if (isReference()) {
475             super.dieOnCircularReference(stk, p);
476         } else {
477             if (union != null) {
478                 stk.push(union);
479                 invokeCircularReferenceCheck(union, stk, p);
480                 stk.pop();
481             }
482             setChecked(true);
483         }
484     }
485
486     /**
487      * Resolve a filename with Project's help - if we know one that is.
488      */

489     private static File JavaDoc resolveFile(Project project, String JavaDoc relativeName) {
490         return FileUtils.getFileUtils().resolveFile(
491             (project == null) ? null : project.getBaseDir(), relativeName);
492     }
493
494     /**
495      * Concatenates the system class path in the order specified by
496      * the ${build.sysclasspath} property - using &quot;last&quot; as
497      * default value.
498      * @return the concatenated path
499      */

500     public Path concatSystemClasspath() {
501         return concatSystemClasspath("last");
502     }
503
504     /**
505      * Concatenates the system class path in the order specified by
506      * the ${build.sysclasspath} property - using the supplied value
507      * if ${build.sysclasspath} has not been set.
508      * @param defValue the order ("first", "last", "only")
509      * @return the concatenated path
510      */

511     public Path concatSystemClasspath(String JavaDoc defValue) {
512         return concatSpecialPath(defValue, Path.systemClasspath);
513     }
514
515     /**
516      * Concatenates the system boot class path in the order specified
517      * by the ${build.sysclasspath} property - using the supplied
518      * value if ${build.sysclasspath} has not been set.
519      * @param defValue the order ("first", "last", "only")
520      * @return the concatenated path
521      */

522     public Path concatSystemBootClasspath(String JavaDoc defValue) {
523         return concatSpecialPath(defValue, Path.systemBootClasspath);
524     }
525
526     /**
527      * Concatenates a class path in the order specified by the
528      * ${build.sysclasspath} property - using the supplied value if
529      * ${build.sysclasspath} has not been set.
530      */

531     private Path concatSpecialPath(String JavaDoc defValue, Path p) {
532         Path result = new Path(getProject());
533
534         String JavaDoc order = defValue;
535         if (getProject() != null) {
536             String JavaDoc o = getProject().getProperty("build.sysclasspath");
537             if (o != null) {
538                 order = o;
539             }
540         }
541         if (order.equals("only")) {
542             // only: the developer knows what (s)he is doing
543
result.addExisting(p, true);
544
545         } else if (order.equals("first")) {
546             // first: developer could use a little help
547
result.addExisting(p, true);
548             result.addExisting(this);
549
550         } else if (order.equals("ignore")) {
551             // ignore: don't trust anyone
552
result.addExisting(this);
553
554         } else {
555             // last: don't trust the developer
556
if (!order.equals("last")) {
557                 log("invalid value for build.sysclasspath: " + order,
558                     Project.MSG_WARN);
559             }
560             result.addExisting(this);
561             result.addExisting(p, true);
562         }
563         return result;
564     }
565
566     /**
567      * Add the Java Runtime classes to this Path instance.
568      */

569     public void addJavaRuntime() {
570         if (JavaEnvUtils.isKaffe()) {
571             // newer versions of Kaffe (1.1.1+) won't have this,
572
// but this will be sorted by FileSet anyway.
573
File JavaDoc kaffeShare = new File JavaDoc(System.getProperty("java.home")
574                                        + File.separator + "share"
575                                        + File.separator + "kaffe");
576             if (kaffeShare.isDirectory()) {
577                 FileSet kaffeJarFiles = new FileSet();
578                 kaffeJarFiles.setDir(kaffeShare);
579                 kaffeJarFiles.setIncludes("*.jar");
580                 addFileset(kaffeJarFiles);
581             }
582         } else if ("GNU libgcj".equals(System.getProperty("java.vm.name"))) {
583             addExisting(systemBootClasspath);
584         }
585
586         if (System.getProperty("java.vendor").toLowerCase(Locale.US).indexOf("microsoft") >= 0) {
587             // XXX is this code still necessary? is there any 1.2+ port?
588
// Pull in *.zip from packages directory
589
FileSet msZipFiles = new FileSet();
590             msZipFiles.setDir(new File JavaDoc(System.getProperty("java.home")
591                 + File.separator + "Packages"));
592             msZipFiles.setIncludes("*.ZIP");
593             addFileset(msZipFiles);
594         } else {
595             // JDK 1.2+ seems to set java.home to the JRE directory.
596
addExisting(new Path(null,
597                                  System.getProperty("java.home")
598                                  + File.separator + "lib"
599                                  + File.separator + "rt.jar"));
600             // Just keep the old version as well and let addExisting
601
// sort it out.
602
addExisting(new Path(null,
603                                  System.getProperty("java.home")
604                                  + File.separator + "jre"
605                                  + File.separator + "lib"
606                                  + File.separator + "rt.jar"));
607
608             // Sun's and Apple's 1.4 have JCE and JSSE in separate jars.
609
String JavaDoc[] secJars = {"jce", "jsse"};
610             for (int i = 0; i < secJars.length; i++) {
611                 addExisting(new Path(null,
612                                      System.getProperty("java.home")
613                                      + File.separator + "lib"
614                                      + File.separator + secJars[i] + ".jar"));
615                 addExisting(new Path(null,
616                                      System.getProperty("java.home")
617                                      + File.separator + ".."
618                                      + File.separator + "Classes"
619                                      + File.separator + secJars[i] + ".jar"));
620             }
621
622             // IBM's 1.4 has rt.jar split into 4 smaller jars and a combined
623
// JCE/JSSE in security.jar.
624
String JavaDoc[] ibmJars
625                 = {"core", "graphics", "security", "server", "xml"};
626             for (int i = 0; i < ibmJars.length; i++) {
627                 addExisting(new Path(null,
628                                      System.getProperty("java.home")
629                                      + File.separator + "lib"
630                                      + File.separator + ibmJars[i] + ".jar"));
631             }
632
633             // Added for MacOS X
634
addExisting(new Path(null,
635                                  System.getProperty("java.home")
636                                  + File.separator + ".."
637                                  + File.separator + "Classes"
638                                  + File.separator + "classes.jar"));
639             addExisting(new Path(null,
640                                  System.getProperty("java.home")
641                                  + File.separator + ".."
642                                  + File.separator + "Classes"
643                                  + File.separator + "ui.jar"));
644         }
645     }
646
647     /**
648      * Emulation of extdirs feature in java >= 1.2.
649      * This method adds all files in the given
650      * directories (but not in sub-directories!) to the classpath,
651      * so that you don't have to specify them all one by one.
652      * @param extdirs - Path to append files to
653      */

654     public void addExtdirs(Path extdirs) {
655         if (extdirs == null) {
656             String JavaDoc extProp = System.getProperty("java.ext.dirs");
657             if (extProp != null) {
658                 extdirs = new Path(getProject(), extProp);
659             } else {
660                 return;
661             }
662         }
663
664         String JavaDoc[] dirs = extdirs.list();
665         for (int i = 0; i < dirs.length; i++) {
666             File JavaDoc dir = resolveFile(getProject(), dirs[i]);
667             if (dir.exists() && dir.isDirectory()) {
668                 FileSet fs = new FileSet();
669                 fs.setDir(dir);
670                 fs.setIncludes("*");
671                 addFileset(fs);
672             }
673         }
674     }
675
676     /**
677      * Fulfill the ResourceCollection contract. The Iterator returned
678      * will throw ConcurrentModificationExceptions if ResourceCollections
679      * are added to this container while the Iterator is in use.
680      * @return a "fail-fast" Iterator.
681      */

682     public final synchronized Iterator JavaDoc iterator() {
683         if (isReference()) {
684             return ((Path) getCheckedRef()).iterator();
685         }
686         dieOnCircularReference();
687         return union == null ? EMPTY_ITERATOR
688             : assertFilesystemOnly(union).iterator();
689     }
690
691     /**
692      * Fulfill the ResourceCollection contract.
693      * @return whether this is a filesystem-only resource collection.
694      */

695     public synchronized boolean isFilesystemOnly() {
696         if (isReference()) {
697             return ((Path) getCheckedRef()).isFilesystemOnly();
698         }
699         dieOnCircularReference();
700         assertFilesystemOnly(union);
701         return true;
702     }
703
704     /**
705      * Verify the specified ResourceCollection is filesystem-only.
706      * @param rc the ResourceCollection to check.
707      * @throws BuildException if <code>rc</code> is not filesystem-only.
708      * @return the passed in ResourceCollection.
709      */

710     protected ResourceCollection assertFilesystemOnly(ResourceCollection rc) {
711         if (rc != null && !(rc.isFilesystemOnly())) {
712             throw new BuildException(getDataTypeName()
713                 + " allows only filesystem resources.");
714         }
715         return rc;
716     }
717 }
718
Popular Tags