001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 2000-2003 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution, if
020: * any, must include the following acknowlegement:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowlegement may appear in the software itself,
024: * if and wherever such third-party acknowlegements normally appear.
025: *
026: * 4. The names "Ant" and "Apache Software
027: * Foundation" must not be used to endorse or promote products derived
028: * from this software without prior written permission. For written
029: * permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache"
032: * nor may "Apache" appear in their names without prior written
033: * permission of the Apache Group.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: */
054:
055: package org.acm.seguin.project;
056:
057: import java.io.File;
058: import java.util.Enumeration;
059: import java.util.Locale;
060: import java.util.Stack;
061: import java.util.Vector;
062:
063: /**
064: * This object represents a path as used by CLASSPATH or PATH
065: * environment variable.
066: * <p>
067: * <code>
068: * <sometask><br>
069: * <somepath><br>
070: * <pathelement location="/path/to/file.jar" /><br>
071: * <pathelement path="/path/to/file2.jar:/path/to/class2;/path/to/class3" /><br>
072: * <pathelement location="/path/to/file3.jar" /><br>
073: * <pathelement location="/path/to/file4.jar" /><br>
074: * </somepath><br>
075: * </sometask><br>
076: * </code>
077: * <p>
078: * The object implemention <code>sometask</code> must provide a method called
079: * <code>createSomepath</code> which returns an instance of <code>Path</code>.
080: * Nested path definitions are handled by the Path object and must be labeled
081: * <code>pathelement</code>.<p>
082: *
083: * The path element takes a parameter <code>path</code> which will be parsed
084: * and split into single elements. It will usually be used
085: * to define a path from an environment variable.
086: *
087: * @author Thomas.Haas@softwired-inc.com
088: * @author <a href="mailto:stefan.bodewig@epost.de">Stefan Bodewig</a>
089: * @author Mike Atkinson
090: * @version $Id: Path.java,v 1.4 2004/03/25 20:24:28 mikeatkinson Exp $
091: * @since 2.8.01
092: */
093:
094: public class Path implements Cloneable {
095:
096: private Project currentProject = null;
097: private boolean checked = false;
098: private Vector elements;
099:
100: /** The system classspath as a Path object */
101: public static Path systemClasspath = new Path(null, System
102: .getProperty("java.class.path"));
103:
104: /**
105: * Helper class, holds the nested <code><pathelement></code> values.
106: */
107: public class PathElement {
108: private String[] parts;
109:
110: public void setLocation(File loc) {
111: parts = new String[] { translateFile(loc.getAbsolutePath()) };
112: }
113:
114: public void setPath(String path) {
115: parts = Path.translatePath(getProject(), path);
116: }
117:
118: public String[] getParts() {
119: return parts;
120: }
121: }
122:
123: /**
124: * Invoked by IntrospectionHelper for <code>setXXX(Path p)</code>
125: * attribute setters.
126: */
127: public Path(Project p, String path) {
128: this (p);
129: createPathElement().setPath(path);
130: }
131:
132: public Path(Project project) {
133: setProject(project);
134: elements = new Vector();
135: }
136:
137: /**
138: * Adds a element definition to the path.
139: * @param location the location of the element to add (must not be
140: * <code>null</code> nor empty.
141: */
142: public void setLocation(File location) throws ProjectException {
143: //if (isReference()) {
144: // throw tooManyAttributes();
145: //}
146: createPathElement().setLocation(location);
147: }
148:
149: /**
150: * Parses a path definition and creates single PathElements.
151: * @param path the path definition.
152: */
153: public void setPath(String path) throws ProjectException {
154: //if (isReference()) {
155: // throw tooManyAttributes();
156: //}
157: createPathElement().setPath(path);
158: }
159:
160: /**
161: * Makes this instance in effect a reference to another Path instance.
162: *
163: * <p>You must not set another attribute or nest elements inside
164: * this element if you make it a reference.</p>
165: */
166: //public void setRefid(Reference r) throws ProjectException {
167: // if (!elements.isEmpty()) {
168: // throw tooManyAttributes();
169: // }
170: // elements.addElement(r);
171: // super.setRefid(r);
172: //}
173: /**
174: * Creates the nested <code><pathelement></code> element.
175: */
176: public PathElement createPathElement() { //throws ProjectException {
177: //if (isReference()) {
178: // throw noChildrenAllowed();
179: //}
180: PathElement pe = new PathElement();
181: elements.addElement(pe);
182: return pe;
183: }
184:
185: /**
186: * Adds a nested <code><fileset></code> element.
187: */
188: //public void addFileset(FileSet fs) throws ProjectException {
189: // if (isReference()) {
190: // throw noChildrenAllowed();
191: // }
192: // elements.addElement(fs);
193: // checked = false;
194: //}
195: /**
196: * Adds a nested <code><filelist></code> element.
197: */
198: //public void addFilelist(FileList fl) throws ProjectException {
199: // if (isReference()) {
200: // throw noChildrenAllowed();
201: // }
202: // elements.addElement(fl);
203: // checked = false;
204: //}
205: /**
206: * Adds a nested <code><dirset></code> element.
207: */
208: //public void addDirset(DirSet dset) throws ProjectException {
209: // if (isReference()) {
210: // throw noChildrenAllowed();
211: // }
212: // elements.addElement(dset);
213: // checked = false;
214: //}
215: /**
216: * Creates a nested <code><path></code> element.
217: */
218: public Path createPath() throws ProjectException {
219: //if (isReference()) {
220: // throw noChildrenAllowed();
221: //}
222: Path p = new Path(getProject());
223: elements.addElement(p);
224: checked = false;
225: return p;
226: }
227:
228: /**
229: * Append the contents of the other Path instance to this.
230: */
231: public void append(Path other) {
232: if (other == null) {
233: return;
234: }
235: try {
236: String[] l = other.list();
237: for (int i = 0; i < l.length; i++) {
238: if (elements.indexOf(l[i]) == -1) {
239: elements.addElement(l[i]);
240: }
241: }
242: } catch (ProjectException e) {
243: e.printStackTrace();
244: }
245: }
246:
247: /**
248: * Adds the components on the given path which exist to this
249: * Path. Components that don't exist, aren't added.
250: *
251: * @param source - source path whose components are examined for existence
252: */
253: public void addExisting(Path source) {
254: try {
255: String[] list = source.list();
256: for (int i = 0; i < list.length; i++) {
257: File f = null;
258: if (getProject() != null) {
259: f = getProject().resolveFile(list[i]);
260: } else {
261: f = new File(list[i]);
262: }
263:
264: if (f.exists()) {
265: setLocation(f);
266: } else {
267: log("dropping " + f
268: + " from path as it doesn't exist");
269: }
270: }
271: } catch (ProjectException e) {
272: e.printStackTrace();
273: }
274: }
275:
276: /**
277: * Returns all path elements defined by this and nested path objects.
278: * @return list of path elements.
279: */
280: public String[] list() throws ProjectException {
281: if (!checked) {
282: // make sure we don't have a circular reference here
283: Stack stk = new Stack();
284: stk.push(this );
285: dieOnCircularReference(stk, getProject());
286: }
287:
288: Vector result = new Vector(2 * elements.size());
289: for (int i = 0; i < elements.size(); i++) {
290: Object o = elements.elementAt(i);
291: //if (o instanceof Reference) {
292: // Reference r = (Reference) o;
293: // o = r.getReferencedObject(getProject());
294: // // we only support references to paths right now
295: // if (!(o instanceof Path)) {
296: // String msg = r.getRefId() + " doesn\'t denote a path";
297: // throw new ProjectException(msg);
298: // }
299: //}
300:
301: if (o instanceof String) {
302: // obtained via append
303: addUnlessPresent(result, (String) o);
304: } else if (o instanceof PathElement) {
305: String[] parts = ((PathElement) o).getParts();
306: if (parts == null) {
307: throw new ProjectException(
308: "You must either set location or"
309: + " path on <pathelement>");
310: }
311: for (int j = 0; j < parts.length; j++) {
312: addUnlessPresent(result, parts[j]);
313: }
314: } else if (o instanceof Path) {
315: Path p = (Path) o;
316: if (p.getProject() == null) {
317: p.setProject(getProject());
318: }
319: String[] parts = p.list();
320: for (int j = 0; j < parts.length; j++) {
321: addUnlessPresent(result, parts[j]);
322: }
323: //} else if (o instanceof DirSet) {
324: // DirSet dset = (DirSet) o;
325: // DirectoryScanner ds = dset.getDirectoryScanner(getProject());
326: // String[] s = ds.getIncludedDirectories();
327: // File dir = dset.getDir(getProject());
328: // addUnlessPresent(result, dir, s);
329: //} else if (o instanceof FileSet) {
330: // FileSet fs = (FileSet) o;
331: // DirectoryScanner ds = fs.getDirectoryScanner(getProject());
332: // String[] s = ds.getIncludedFiles();
333: // File dir = fs.getDir(getProject());
334: // addUnlessPresent(result, dir, s);
335: //} else if (o instanceof FileList) {
336: // FileList fl = (FileList) o;
337: // String[] s = fl.getFiles(getProject());
338: // File dir = fl.getDir(getProject());
339: // addUnlessPresent(result, dir, s);
340: }
341: }
342: String[] res = new String[result.size()];
343: result.copyInto(res);
344: return res;
345: }
346:
347: /**
348: * Returns a textual representation of the path, which can be used as
349: * CLASSPATH or PATH environment variable definition.
350: * @return a textual representation of the path.
351: */
352: public String toString() {
353: try {
354: final String[] list = list();
355:
356: // empty path return empty string
357: if (list.length == 0) {
358: return "";
359: }
360:
361: // path containing one or more elements
362: final StringBuffer result = new StringBuffer(list[0]
363: .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: } catch (ProjectException e) {
371: e.printStackTrace();
372: }
373: return "";
374: }
375:
376: /**
377: * Splits a PATH (with : or ; as separators) into its parts.
378: */
379: public static String[] translatePath(Project project, String source) {
380: final Vector result = new Vector();
381: if (source == null) {
382: return new String[0];
383: }
384:
385: PathTokenizer tok = new PathTokenizer(source);
386: StringBuffer element = new StringBuffer();
387: while (tok.hasMoreTokens()) {
388: element.setLength(0);
389: String pathElement = tok.nextToken();
390: try {
391: element.append(resolveFile(project, pathElement));
392: } catch (ProjectException e) {
393: project
394: .log("Dropping path element "
395: + pathElement
396: + " as it is not valid relative to the project");
397: }
398: for (int i = 0; i < element.length(); i++) {
399: translateFileSep(element, i);
400: }
401: result.addElement(element.toString());
402: }
403: String[] res = new String[result.size()];
404: result.copyInto(res);
405: return res;
406: }
407:
408: /**
409: * Returns its argument with all file separator characters
410: * replaced so that they match the local OS conventions.
411: */
412: public static String translateFile(String source) {
413: if (source == null) {
414: return "";
415: }
416:
417: final StringBuffer result = new StringBuffer(source);
418: for (int i = 0; i < result.length(); i++) {
419: translateFileSep(result, i);
420: }
421:
422: return result.toString();
423: }
424:
425: /**
426: * Translates all occurrences of / or \ to correct separator of the
427: * current platform and returns whether it had to do any
428: * replacements.
429: */
430: protected static boolean translateFileSep(StringBuffer buffer,
431: int pos) {
432: if (buffer.charAt(pos) == '/' || buffer.charAt(pos) == '\\') {
433: buffer.setCharAt(pos, File.separatorChar);
434: return true;
435: }
436: return false;
437: }
438:
439: /**
440: * How many parts does this Path instance consist of.
441: */
442: public int size() {
443: try {
444: return list().length;
445: } catch (ProjectException e) {
446: e.printStackTrace();
447: }
448: return 0;
449: }
450:
451: /**
452: * Return a Path that holds the same elements as this instance.
453: */
454: public Object clone() {
455: Path p = new Path(getProject());
456: p.append(this );
457: return p;
458: }
459:
460: /**
461: * Overrides the version of DataType to recurse on all DataType
462: * child elements that may have been added.
463: */
464: protected void dieOnCircularReference(Stack stk, Project p)
465: throws ProjectException {
466:
467: if (checked) {
468: return;
469: }
470:
471: Enumeration enumx = elements.elements();
472: while (enumx.hasMoreElements()) {
473: Object o = enumx.nextElement();
474: //if (o instanceof Reference) {
475: // o = ((Reference) o).getReferencedObject(p);
476: //}
477:
478: if (o instanceof Path) {
479: if (stk.contains(o)) {
480: throw new ProjectException("circularReference");
481: } else {
482: stk.push(o);
483: ((Path) o).dieOnCircularReference(stk, p);
484: stk.pop();
485: }
486: }
487: }
488: checked = true;
489: }
490:
491: /**
492: * Resolve a filename with Project's help - if we know one that is.
493: *
494: * <p>Assume the filename is absolute if project is null.</p>
495: */
496: private static String resolveFile(Project project,
497: String relativeName) throws ProjectException {
498: if (project != null) {
499: File f = project.resolveFile(relativeName);
500: return f.getAbsolutePath();
501: }
502: return relativeName;
503: }
504:
505: /**
506: * Adds a String to the Vector if it isn't already included.
507: */
508: private static void addUnlessPresent(Vector v, String s) {
509: if (v.indexOf(s) == -1) {
510: v.addElement(s);
511: }
512: }
513:
514: /**
515: * Adds absolute path names of listed files in the given directory
516: * to the Vector if they are not already included.
517: */
518: private static void addUnlessPresent(Vector v, File dir, String[] s) {
519: for (int j = 0; j < s.length; j++) {
520: File d = new File(dir, s[j]);
521: String absolutePath = d.getAbsolutePath();
522: addUnlessPresent(v, translateFile(absolutePath));
523: }
524: }
525:
526: /**
527: * Concatenates the system class path in the order specified by
528: * the ${build.sysclasspath} property - using "last" as
529: * default value.
530: */
531: public Path concatSystemClasspath() {
532: return concatSystemClasspath("last");
533: }
534:
535: /**
536: * Concatenates the system class path in the order specified by
537: * the ${build.sysclasspath} property - using the supplied value
538: * if ${build.sysclasspath} has not been set.
539: */
540: public Path concatSystemClasspath(String defValue) {
541:
542: Path result = new Path(getProject());
543:
544: String order = defValue;
545: if (getProject() != null) {
546: String o = getProject().getProperty("build.sysclasspath",
547: null);
548: if (o != null) {
549: order = o;
550: }
551: }
552:
553: if (order.equals("only")) {
554: // only: the developer knows what (s)he is doing
555: result.addExisting(Path.systemClasspath);
556:
557: } else if (order.equals("first")) {
558: // first: developer could use a little help
559: result.addExisting(Path.systemClasspath);
560: result.addExisting(this );
561:
562: } else if (order.equals("ignore")) {
563: // ignore: don't trust anyone
564: result.addExisting(this );
565:
566: } else {
567: // last: don't trust the developer
568: if (!order.equals("last")) {
569: log("invalid value for build.sysclasspath: " + order);
570: }
571:
572: result.addExisting(this );
573: result.addExisting(Path.systemClasspath);
574: }
575:
576: return result;
577:
578: }
579:
580: /**
581: * Add the Java Runtime classes to this Path instance.
582: */
583: public void addJavaRuntime() {
584: //if (System.getProperty("java.vendor").toLowerCase(Locale.US).indexOf("microsoft") >= 0) {
585: // // Pull in *.zip from packages directory
586: // FileSet msZipFiles = new FileSet();
587: // msZipFiles.setDir(new File(System.getProperty("java.home")
588: // + File.separator + "Packages"));
589: // msZipFiles.setIncludes("*.ZIP");
590: // addFileset(msZipFiles);
591: //} else if ("Kaffe".equals(System.getProperty("java.vm.name"))) {
592: // FileSet kaffeJarFiles = new FileSet();
593: // kaffeJarFiles.setDir(new File(System.getProperty("java.home")
594: // + File.separator + "share"
595: // + File.separator + "kaffe"));
596: //
597: // kaffeJarFiles.setIncludes("*.jar");
598: // addFileset(kaffeJarFiles);
599: //} else if (JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
600: // addExisting(new Path(null,
601: // System.getProperty("java.home")
602: // + File.separator + "lib"
603: // + File.separator
604: // + "classes.zip"));
605: //} else {
606: // JDK > 1.1 seems to set java.home to the JRE directory.
607: addExisting(new Path(null, System.getProperty("java.home")
608: + File.separator + "lib" + File.separator + "rt.jar"));
609: // Just keep the old version as well and let addExisting
610: // sort it out.
611: addExisting(new Path(null, System.getProperty("java.home")
612: + File.separator + "jre" + File.separator + "lib"
613: + File.separator + "rt.jar"));
614:
615: // Sun's 1.4 has JCE and JSSE in separate jars.
616: String[] secJars = { "jce", "jsse" };
617: for (int i = 0; i < secJars.length; i++) {
618: addExisting(new Path(null, System.getProperty("java.home")
619: + File.separator + "lib" + File.separator
620: + secJars[i] + ".jar"));
621: }
622:
623: // IBM's 1.4 has rt.jar split into 4 smaller jars and a combined
624: // JCE/JSSE in security.jar.
625: String[] ibmJars = { "core", "graphics", "security", "server",
626: "xml" };
627: for (int i = 0; i < ibmJars.length; i++) {
628: addExisting(new Path(null, System.getProperty("java.home")
629: + File.separator + "lib" + File.separator
630: + ibmJars[i] + ".jar"));
631: }
632:
633: // Added for MacOS X
634: addExisting(new Path(null, System.getProperty("java.home")
635: + File.separator + ".." + File.separator + "Classes"
636: + File.separator + "classes.jar"));
637: addExisting(new Path(null, System.getProperty("java.home")
638: + File.separator + ".." + File.separator + "Classes"
639: + File.separator + "ui.jar"));
640: //}
641: }
642:
643: /**
644: * Emulation of extdirs feature in java >= 1.2.
645: * This method adds all files in the given
646: * directories (but not in sub-directories!) to the classpath,
647: * so that you don't have to specify them all one by one.
648: * @param extdirs - Path to append files to
649: */
650: //public void addExtdirs(Path extdirs) {
651: // if (extdirs == null) {
652: // String extProp = System.getProperty("java.ext.dirs");
653: // if (extProp != null) {
654: // extdirs = new Path(getProject(), extProp);
655: // } else {
656: // return;
657: // }
658: // }
659: //
660: // String[] dirs = extdirs.list();
661: // for (int i = 0; i < dirs.length; i++) {
662: // File dir = getProject().resolveFile(dirs[i]);
663: // if (dir.exists() && dir.isDirectory()) {
664: // FileSet fs = new FileSet();
665: // fs.setDir(dir);
666: // fs.setIncludes("*");
667: // addFileset(fs);
668: // }
669: // }
670: //}
671: private Project getProject() {
672: return currentProject;
673: }
674:
675: private void setProject(Project currentProject) {
676: this .currentProject = currentProject;
677: }
678:
679: /**
680: * Logs a message through the project object if one has been provided.
681: *
682: * @param message The message to log.
683: * Should not be <code>null</code>.
684: *
685: * @param priority The logging priority of the message.
686: */
687: protected void log(String message) {
688: if (currentProject != null) {
689: currentProject.log(message);
690: }
691: // else {
692: // System.out.println(message);
693: // }
694: }
695: }
|