source: other-projects/trunk/gs3-release-maker/apache-ant-1.6.5/src/main/org/apache/tools/ant/types/Path.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: 23.2 KB
Line 
1/*
2 * Copyright 2000-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.types;
19
20import java.io.File;
21import java.util.Enumeration;
22import java.util.Locale;
23import java.util.Stack;
24import java.util.Vector;
25import org.apache.tools.ant.BuildException;
26import org.apache.tools.ant.DirectoryScanner;
27import org.apache.tools.ant.PathTokenizer;
28import org.apache.tools.ant.Project;
29import org.apache.tools.ant.util.JavaEnvUtils;
30
31
32
33/**
34 * This object represents a path as used by CLASSPATH or PATH
35 * environment variable.
36 * <p>
37 * <code>
38 * &lt;sometask&gt;<br>
39 * &nbsp;&nbsp;&lt;somepath&gt;<br>
40 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file.jar" /&gt;<br>
41 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement path="/path/to/file2.jar:/path/to/class2;/path/to/class3" /&gt;<br>
42 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file3.jar" /&gt;<br>
43 * &nbsp;&nbsp;&nbsp;&nbsp;&lt;pathelement location="/path/to/file4.jar" /&gt;<br>
44 * &nbsp;&nbsp;&lt;/somepath&gt;<br>
45 * &lt;/sometask&gt;<br>
46 * </code>
47 * <p>
48 * The object implemention <code>sometask</code> must provide a method called
49 * <code>createSomepath</code> which returns an instance of <code>Path</code>.
50 * Nested path definitions are handled by the Path object and must be labeled
51 * <code>pathelement</code>.<p>
52 *
53 * The path element takes a parameter <code>path</code> which will be parsed
54 * and split into single elements. It will usually be used
55 * to define a path from an environment variable.
56 *
57 */
58
59public class Path extends DataType implements Cloneable {
60
61 private Vector elements;
62
63 /** The system classspath as a Path object */
64 public static Path systemClasspath =
65 new Path(null, System.getProperty("java.class.path"));
66
67
68 /**
69 * The system bootclassspath as a Path object.
70 *
71 * @since Ant 1.6.2
72 */
73 public static Path systemBootClasspath =
74 new Path(null, System.getProperty("sun.boot.class.path"));
75
76
77 /**
78 * Helper class, holds the nested <code>&lt;pathelement&gt;</code> values.
79 */
80 public class PathElement {
81 private String[] parts;
82
83 public void setLocation(File loc) {
84 parts = new String[] {translateFile(loc.getAbsolutePath())};
85 }
86
87 public void setPath(String path) {
88 parts = Path.translatePath(getProject(), path);
89 }
90
91 public String[] getParts() {
92 return parts;
93 }
94 }
95
96 /**
97 * Invoked by IntrospectionHelper for <code>setXXX(Path p)</code>
98 * attribute setters.
99 * @param project the <CODE>Project</CODE> for this path.
100 * @param path the <CODE>String</CODE> path definition.
101 */
102 public Path(Project p, String path) {
103 this(p);
104 createPathElement().setPath(path);
105 }
106
107 /**
108 * Construct an empty <CODE>Path</CODE>.
109 * @param project the <CODE>Project</CODE> for this path.
110 */
111 public Path(Project project) {
112 setProject(project);
113 elements = new Vector();
114 }
115
116 /**
117 * Adds a element definition to the path.
118 * @param location the location of the element to add (must not be
119 * <code>null</code> nor empty.
120 */
121 public void setLocation(File location) throws BuildException {
122 if (isReference()) {
123 throw tooManyAttributes();
124 }
125 createPathElement().setLocation(location);
126 }
127
128
129 /**
130 * Parses a path definition and creates single PathElements.
131 * @param path the <CODE>String</CODE> path definition.
132 */
133 public void setPath(String path) throws BuildException {
134 if (isReference()) {
135 throw tooManyAttributes();
136 }
137 createPathElement().setPath(path);
138 }
139
140 /**
141 * Makes this instance in effect a reference to another Path instance.
142 *
143 * <p>You must not set another attribute or nest elements inside
144 * this element if you make it a reference.</p>
145 */
146 public void setRefid(Reference r) throws BuildException {
147 if (!elements.isEmpty()) {
148 throw tooManyAttributes();
149 }
150 elements.addElement(r);
151 super.setRefid(r);
152 }
153
154 /**
155 * Creates the nested <code>&lt;pathelement&gt;</code> element.
156 */
157 public PathElement createPathElement() throws BuildException {
158 if (isReference()) {
159 throw noChildrenAllowed();
160 }
161 PathElement pe = new PathElement();
162 elements.addElement(pe);
163 return pe;
164 }
165
166 /**
167 * Adds a nested <code>&lt;fileset&gt;</code> element.
168 */
169 public void addFileset(FileSet fs) throws BuildException {
170 if (isReference()) {
171 throw noChildrenAllowed();
172 }
173 elements.addElement(fs);
174 setChecked(false);
175 }
176
177 /**
178 * Adds a nested <code>&lt;filelist&gt;</code> element.
179 */
180 public void addFilelist(FileList fl) throws BuildException {
181 if (isReference()) {
182 throw noChildrenAllowed();
183 }
184 elements.addElement(fl);
185 setChecked(false);
186 }
187
188 /**
189 * Adds a nested <code>&lt;dirset&gt;</code> element.
190 */
191 public void addDirset(DirSet dset) throws BuildException {
192 if (isReference()) {
193 throw noChildrenAllowed();
194 }
195 elements.addElement(dset);
196 setChecked(false);
197 }
198
199 /**
200 * Adds a nested path
201 * @since Ant 1.6
202 */
203 public void add(Path path) throws BuildException {
204 if (isReference()) {
205 throw noChildrenAllowed();
206 }
207 elements.addElement(path);
208 setChecked(false);
209
210 }
211
212 /**
213 * Creates a nested <code>&lt;path&gt;</code> element.
214 */
215 public Path createPath() throws BuildException {
216 if (isReference()) {
217 throw noChildrenAllowed();
218 }
219 Path p = new Path(getProject());
220 elements.addElement(p);
221 setChecked(false);
222 return p;
223 }
224
225 /**
226 * Append the contents of the other Path instance to this.
227 */
228 public void append(Path other) {
229 if (other == null) {
230 return;
231 }
232 String[] l = other.list();
233 for (int i = 0; i < l.length; i++) {
234 if (elements.indexOf(l[i]) == -1) {
235 elements.addElement(l[i]);
236 }
237 }
238 }
239
240 /**
241 * Adds the components on the given path which exist to this
242 * Path. Components that don't exist, aren't added.
243 *
244 * @param source - source path whose components are examined for existence
245 */
246 public void addExisting(Path source) {
247 addExisting(source, false);
248 }
249
250 /** Same as addExisting, but support classpath behavior if tryUserDir
251 * is true. Classpaths are relative to user dir, not the project base.
252 * That used to break jspc test
253 *
254 * @param source
255 * @param tryUserDir
256 */
257 public void addExisting(Path source, boolean tryUserDir) {
258 String[] list = source.list();
259 File userDir = (tryUserDir) ? new File(System.getProperty("user.dir"))
260 : null;
261
262 for (int i = 0; i < list.length; i++) {
263 File f = null;
264 if (getProject() != null) {
265 f = getProject().resolveFile(list[i]);
266 } else {
267 f = new File(list[i]);
268 }
269 // probably not the best choice, but it solves the problem of
270 // relative paths in CLASSPATH
271 if (tryUserDir && !f.exists()) {
272 f = new File(userDir, list[i]);
273 }
274 if (f.exists()) {
275 setLocation(f);
276 } else {
277 log("dropping " + f + " from path as it doesn't exist",
278 Project.MSG_VERBOSE);
279 }
280 }
281 }
282
283 /**
284 * Returns all path elements defined by this and nested path objects.
285 * @return list of path elements.
286 */
287 public String[] list() {
288 if (!isChecked()) {
289 // make sure we don't have a circular reference here
290 Stack stk = new Stack();
291 stk.push(this);
292 dieOnCircularReference(stk, getProject());
293 }
294
295 Vector result = new Vector(2 * elements.size());
296 for (int i = 0; i < elements.size(); i++) {
297 Object o = elements.elementAt(i);
298 if (o instanceof Reference) {
299 Reference r = (Reference) o;
300 o = r.getReferencedObject(getProject());
301 // we only support references to paths right now
302 if (!(o instanceof Path)) {
303 String msg = r.getRefId() + " doesn\'t denote a path " + o;
304 throw new BuildException(msg);
305 }
306 }
307
308 if (o instanceof String) {
309 // obtained via append
310 addUnlessPresent(result, (String) o);
311 } else if (o instanceof PathElement) {
312 String[] parts = ((PathElement) o).getParts();
313 if (parts == null) {
314 throw new BuildException("You must either set location or"
315 + " path on <pathelement>");
316 }
317 for (int j = 0; j < parts.length; j++) {
318 addUnlessPresent(result, parts[j]);
319 }
320 } else if (o instanceof Path) {
321 Path p = (Path) o;
322 if (p.getProject() == null) {
323 p.setProject(getProject());
324 }
325 String[] parts = p.list();
326 for (int j = 0; j < parts.length; j++) {
327 addUnlessPresent(result, parts[j]);
328 }
329 } else if (o instanceof DirSet) {
330 DirSet dset = (DirSet) o;
331 addUnlessPresent(result, dset.getDir(getProject()),
332 dset.getDirectoryScanner(getProject()).getIncludedDirectories());
333 } else if (o instanceof FileSet) {
334 FileSet fs = (FileSet) o;
335 addUnlessPresent(result, fs.getDir(getProject()),
336 fs.getDirectoryScanner(getProject()).getIncludedFiles());
337 } else if (o instanceof FileList) {
338 FileList fl = (FileList) o;
339 addUnlessPresent(result,
340 fl.getDir(getProject()), fl.getFiles(getProject()));
341 }
342 }
343 String[] res = new String[result.size()];
344 result.copyInto(res);
345 return res;
346 }
347
348
349 /**
350 * Returns a textual representation of the path, which can be used as
351 * CLASSPATH or PATH environment variable definition.
352 * @return a textual representation of the path.
353 */
354 public String toString() {
355 final String[] list = list();
356
357 // empty path return empty string
358 if (list.length == 0) {
359 return "";
360 }
361
362 // path containing one or more elements
363 final StringBuffer result = new StringBuffer(list[0].toString());
364 for (int i = 1; i < list.length; i++) {
365 result.append(File.pathSeparatorChar);
366 result.append(list[i]);
367 }
368
369 return result.toString();
370 }
371
372 /**
373 * Splits a PATH (with : or ; as separators) into its parts.
374 */
375 public static String[] translatePath(Project project, String source) {
376 final Vector result = new Vector();
377 if (source == null) {
378 return new String[0];
379 }
380
381 PathTokenizer tok = new PathTokenizer(source);
382 StringBuffer element = new StringBuffer();
383 while (tok.hasMoreTokens()) {
384 String pathElement = tok.nextToken();
385 try {
386 element.append(resolveFile(project, pathElement));
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();
397 }
398 String[] res = new String[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 */
407 public static String translateFile(String source) {
408 if (source == null) {
409 return "";
410 }
411
412 final StringBuffer result = new StringBuffer(source);
413 for (int i = 0; i < result.length(); i++) {
414 translateFileSep(result, i);
415 }
416
417 return result.toString();
418 }
419
420 /**
421 * Translates all occurrences of / or \ to correct separator of the
422 * current platform and returns whether it had to do any
423 * replacements.
424 */
425 protected static boolean translateFileSep(StringBuffer buffer, int pos) {
426 if (buffer.charAt(pos) == '/' || buffer.charAt(pos) == '\\') {
427 buffer.setCharAt(pos, File.separatorChar);
428 return true;
429 }
430 return false;
431 }
432
433 /**
434 * How many parts does this Path instance consist of.
435 */
436 public int size() {
437 return list().length;
438 }
439
440 /**
441 * Return a Path that holds the same elements as this instance.
442 */
443 public Object clone() {
444 try {
445 Path p = (Path) super.clone();
446 p.elements = (Vector) elements.clone();
447 return p;
448 } catch (CloneNotSupportedException e) {
449 throw new BuildException(e);
450 }
451 }
452
453 /**
454 * Overrides the version of DataType to recurse on all DataType
455 * child elements that may have been added.
456 */
457 protected void dieOnCircularReference(Stack stk, Project p)
458 throws BuildException {
459
460 if (isChecked()) {
461 return;
462 }
463
464 Enumeration e = elements.elements();
465 while (e.hasMoreElements()) {
466 Object o = e.nextElement();
467 if (o instanceof Reference) {
468 o = ((Reference) o).getReferencedObject(p);
469 }
470
471 if (o instanceof DataType) {
472 if (stk.contains(o)) {
473 throw circularReference();
474 } else {
475 stk.push(o);
476 ((DataType) o).dieOnCircularReference(stk, p);
477 stk.pop();
478 }
479 }
480 }
481 setChecked(true);
482 }
483
484 /**
485 * Resolve a filename with Project's help - if we know one that is.
486 *
487 * <p>Assume the filename is absolute if project is null.</p>
488 */
489 private static String resolveFile(Project project, String relativeName) {
490 if (project != null) {
491 File f = project.resolveFile(relativeName);
492 return f.getAbsolutePath();
493 }
494 return relativeName;
495 }
496
497 /**
498 * Adds a String to the Vector if it isn't already included.
499 */
500 private static void addUnlessPresent(Vector v, String s) {
501 if (v.indexOf(s) == -1) {
502 v.addElement(s);
503 }
504 }
505
506 /**
507 * Adds absolute path names of listed files in the given directory
508 * to the Vector if they are not already included.
509 */
510 private static void addUnlessPresent(Vector v, File dir, String[] s) {
511 for (int j = 0; j < s.length; j++) {
512 File d = new File(dir, s[j]);
513 String absolutePath = d.getAbsolutePath();
514 addUnlessPresent(v, translateFile(absolutePath));
515 }
516 }
517
518 /**
519 * Concatenates the system class path in the order specified by
520 * the ${build.sysclasspath} property - using &quot;last&quot; as
521 * default value.
522 */
523 public Path concatSystemClasspath() {
524 return concatSystemClasspath("last");
525 }
526
527 /**
528 * Concatenates the system class path in the order specified by
529 * the ${build.sysclasspath} property - using the supplied value
530 * if ${build.sysclasspath} has not been set.
531 */
532 public Path concatSystemClasspath(String defValue) {
533
534 Path result = new Path(getProject());
535
536 String order = defValue;
537 if (getProject() != null) {
538 String o = getProject().getProperty("build.sysclasspath");
539 if (o != null) {
540 order = o;
541 }
542 }
543
544 if (order.equals("only")) {
545 // only: the developer knows what (s)he is doing
546 result.addExisting(Path.systemClasspath, true);
547
548 } else if (order.equals("first")) {
549 // first: developer could use a little help
550 result.addExisting(Path.systemClasspath, true);
551 result.addExisting(this);
552
553 } else if (order.equals("ignore")) {
554 // ignore: don't trust anyone
555 result.addExisting(this);
556
557 } else {
558 // last: don't trust the developer
559 if (!order.equals("last")) {
560 log("invalid value for build.sysclasspath: " + order,
561 Project.MSG_WARN);
562 }
563
564 result.addExisting(this);
565 result.addExisting(Path.systemClasspath, true);
566 }
567
568
569 return result;
570
571 }
572
573 /**
574 * Add the Java Runtime classes to this Path instance.
575 */
576 public void addJavaRuntime() {
577 if ("Kaffe".equals(System.getProperty("java.vm.name"))) {
578 // newer versions of Kaffe (1.1.1+) won't have this,
579 // but this will be sorted by FileSet anyway.
580 File kaffeShare = new File(System.getProperty("java.home")
581 + File.separator + "share"
582 + File.separator + "kaffe");
583 if (kaffeShare.isDirectory()) {
584 FileSet kaffeJarFiles = new FileSet();
585 kaffeJarFiles.setDir(kaffeShare);
586 kaffeJarFiles.setIncludes("*.jar");
587 addFileset(kaffeJarFiles);
588 }
589 } else if ("GNU libgcj".equals(System.getProperty("java.vm.name"))) {
590 addExisting(systemBootClasspath);
591 }
592
593 if (System.getProperty("java.vendor").toLowerCase(Locale.US).indexOf("microsoft") >= 0) {
594 // Pull in *.zip from packages directory
595 FileSet msZipFiles = new FileSet();
596 msZipFiles.setDir(new File(System.getProperty("java.home")
597 + File.separator + "Packages"));
598 msZipFiles.setIncludes("*.ZIP");
599 addFileset(msZipFiles);
600 } else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
601 addExisting(new Path(null,
602 System.getProperty("java.home")
603 + File.separator + "lib"
604 + File.separator
605 + "classes.zip"));
606 } else {
607 // JDK > 1.1 seems to set java.home to the JRE directory.
608 addExisting(new Path(null,
609 System.getProperty("java.home")
610 + File.separator + "lib"
611 + File.separator + "rt.jar"));
612 // Just keep the old version as well and let addExisting
613 // sort it out.
614 addExisting(new Path(null,
615 System.getProperty("java.home")
616 + File.separator + "jre"
617 + File.separator + "lib"
618 + File.separator + "rt.jar"));
619
620 // Sun's and Apple's 1.4 have JCE and JSSE in separate jars.
621 String[] secJars = {"jce", "jsse"};
622 for (int i = 0; i < secJars.length; i++) {
623 addExisting(new Path(null,
624 System.getProperty("java.home")
625 + File.separator + "lib"
626 + File.separator + secJars[i] + ".jar"));
627 addExisting(new Path(null,
628 System.getProperty("java.home")
629 + File.separator + ".."
630 + File.separator + "Classes"
631 + File.separator + secJars[i] + ".jar"));
632 }
633
634 // IBM's 1.4 has rt.jar split into 4 smaller jars and a combined
635 // JCE/JSSE in security.jar.
636 String[] ibmJars
637 = {"core", "graphics", "security", "server", "xml"};
638 for (int i = 0; i < ibmJars.length; i++) {
639 addExisting(new Path(null,
640 System.getProperty("java.home")
641 + File.separator + "lib"
642 + File.separator + ibmJars[i] + ".jar"));
643 }
644
645 // Added for MacOS X
646 addExisting(new Path(null,
647 System.getProperty("java.home")
648 + File.separator + ".."
649 + File.separator + "Classes"
650 + File.separator + "classes.jar"));
651 addExisting(new Path(null,
652 System.getProperty("java.home")
653 + File.separator + ".."
654 + File.separator + "Classes"
655 + File.separator + "ui.jar"));
656 }
657 }
658
659 /**
660 * Emulation of extdirs feature in java >= 1.2.
661 * This method adds all files in the given
662 * directories (but not in sub-directories!) to the classpath,
663 * so that you don't have to specify them all one by one.
664 * @param extdirs - Path to append files to
665 */
666 public void addExtdirs(Path extdirs) {
667 if (extdirs == null) {
668 String extProp = System.getProperty("java.ext.dirs");
669 if (extProp != null) {
670 extdirs = new Path(getProject(), extProp);
671 } else {
672 return;
673 }
674 }
675
676 String[] dirs = extdirs.list();
677 for (int i = 0; i < dirs.length; i++) {
678 File dir = getProject().resolveFile(dirs[i]);
679 if (dir.exists() && dir.isDirectory()) {
680 FileSet fs = new FileSet();
681 fs.setDir(dir);
682 fs.setIncludes("*");
683 addFileset(fs);
684 }
685 }
686 }
687}
Note: See TracBrowser for help on using the repository browser.