KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > AntClassLoader


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;
20
21 import java.io.ByteArrayOutputStream JavaDoc;
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.io.InputStreamReader JavaDoc;
27 import java.io.Reader JavaDoc;
28 import java.lang.reflect.Constructor JavaDoc;
29 import java.net.MalformedURLException JavaDoc;
30 import java.net.URL JavaDoc;
31 import java.util.Collections JavaDoc;
32 import java.util.Enumeration JavaDoc;
33 import java.util.HashMap JavaDoc;
34 import java.util.Hashtable JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.StringTokenizer JavaDoc;
37 import java.util.Vector JavaDoc;
38 import java.util.jar.Attributes JavaDoc;
39 import java.util.jar.Attributes.Name;
40 import java.util.jar.JarFile JavaDoc;
41 import java.util.jar.Manifest JavaDoc;
42 import java.util.zip.ZipEntry JavaDoc;
43 import java.util.zip.ZipFile JavaDoc;
44 import org.apache.tools.ant.types.Path;
45 import org.apache.tools.ant.util.CollectionUtils;
46 import org.apache.tools.ant.util.FileUtils;
47 import org.apache.tools.ant.util.JavaEnvUtils;
48 import org.apache.tools.ant.util.LoaderUtils;
49 import org.apache.tools.ant.launch.Locator;
50
51 /**
52  * Used to load classes within ant with a different classpath from
53  * that used to start ant. Note that it is possible to force a class
54  * into this loader even when that class is on the system classpath by
55  * using the forceLoadClass method. Any subsequent classes loaded by that
56  * class will then use this loader rather than the system class loader.
57  *
58  * <p>
59  * Note that this classloader has a feature to allow loading
60  * in reverse order and for "isolation".
61  * Due to the fact that a number of
62  * methods in java.lang.ClassLoader are final (at least
63  * in java 1.4 getResources) this means that the
64  * class has to fake the given parent.
65  * </p>
66  *
67  */

68 public class AntClassLoader extends ClassLoader JavaDoc implements SubBuildListener {
69
70     private static final FileUtils FILE_UTILS = FileUtils.getFileUtils();
71
72     /**
73      * An enumeration of all resources of a given name found within the
74      * classpath of this class loader. This enumeration is used by the
75      * ClassLoader.findResources method, which is in
76      * turn used by the ClassLoader.getResources method.
77      *
78      * @see AntClassLoader#findResources(String)
79      * @see java.lang.ClassLoader#getResources(String)
80      */

81     private class ResourceEnumeration implements Enumeration JavaDoc {
82         /**
83          * The name of the resource being searched for.
84          */

85         private String JavaDoc resourceName;
86
87         /**
88          * The index of the next classpath element to search.
89          */

90         private int pathElementsIndex;
91
92         /**
93          * The URL of the next resource to return in the enumeration. If this
94          * field is <code>null</code> then the enumeration has been completed,
95          * i.e., there are no more elements to return.
96          */

97         private URL JavaDoc nextResource;
98
99         /**
100          * Constructs a new enumeration of resources of the given name found
101          * within this class loader's classpath.
102          *
103          * @param name the name of the resource to search for.
104          */

105         ResourceEnumeration(String JavaDoc name) {
106             this.resourceName = name;
107             this.pathElementsIndex = 0;
108             findNextResource();
109         }
110
111         /**
112          * Indicates whether there are more elements in the enumeration to
113          * return.
114          *
115          * @return <code>true</code> if there are more elements in the
116          * enumeration; <code>false</code> otherwise.
117          */

118         public boolean hasMoreElements() {
119             return (this.nextResource != null);
120         }
121
122         /**
123          * Returns the next resource in the enumeration.
124          *
125          * @return the next resource in the enumeration
126          */

127         public Object JavaDoc nextElement() {
128             URL JavaDoc ret = this.nextResource;
129             findNextResource();
130             return ret;
131         }
132
133         /**
134          * Locates the next resource of the correct name in the classpath and
135          * sets <code>nextResource</code> to the URL of that resource. If no
136          * more resources can be found, <code>nextResource</code> is set to
137          * <code>null</code>.
138          */

139         private void findNextResource() {
140             URL JavaDoc url = null;
141             while ((pathElementsIndex < pathComponents.size())
142                    && (url == null)) {
143                 try {
144                     File JavaDoc pathComponent
145                         = (File JavaDoc) pathComponents.elementAt(pathElementsIndex);
146                     url = getResourceURL(pathComponent, this.resourceName);
147                     pathElementsIndex++;
148                 } catch (BuildException e) {
149                     // ignore path elements which are not valid relative to the
150
// project
151
}
152             }
153             this.nextResource = url;
154         }
155     }
156
157     /**
158      * The size of buffers to be used in this classloader.
159      */

160     private static final int BUFFER_SIZE = 8192;
161     /**
162      * Number of array elements in a test array of strings
163      */

164     private static final int NUMBER_OF_STRINGS = 256;
165
166     /**
167      * The components of the classpath that the classloader searches
168      * for classes.
169      */

170     private Vector JavaDoc pathComponents = new Vector JavaDoc();
171
172     /**
173      * The project to which this class loader belongs.
174      */

175     private Project project;
176
177     /**
178      * Indicates whether the parent class loader should be
179      * consulted before trying to load with this class loader.
180      */

181     private boolean parentFirst = true;
182
183     /**
184      * These are the package roots that are to be loaded by the parent class
185      * loader regardless of whether the parent class loader is being searched
186      * first or not.
187      */

188     private Vector JavaDoc systemPackages = new Vector JavaDoc();
189
190     /**
191      * These are the package roots that are to be loaded by this class loader
192      * regardless of whether the parent class loader is being searched first
193      * or not.
194      */

195     private Vector JavaDoc loaderPackages = new Vector JavaDoc();
196
197     /**
198      * Whether or not this classloader will ignore the base
199      * classloader if it can't find a class.
200      *
201      * @see #setIsolated(boolean)
202      */

203     private boolean ignoreBase = false;
204
205     /**
206      * The parent class loader, if one is given or can be determined.
207      */

208     private ClassLoader JavaDoc parent = null;
209
210     /**
211      * A hashtable of zip files opened by the classloader (File to ZipFile).
212      */

213     private Hashtable JavaDoc zipFiles = new Hashtable JavaDoc();
214
215     /** Static map of jar file/time to manifiest class-path entries */
216     private static Map JavaDoc/*<String,String>*/ pathMap = Collections.synchronizedMap(new HashMap JavaDoc());
217
218     /**
219      * The context loader saved when setting the thread's current
220      * context loader.
221      */

222     private ClassLoader JavaDoc savedContextLoader = null;
223     /**
224      * Whether or not the context loader is currently saved.
225      */

226     private boolean isContextLoaderSaved = false;
227
228     /**
229      * Create an Ant ClassLoader for a given project, with
230      * a parent classloader and an initial classpath.
231      * @since Ant 1.7.
232      * @param parent the parent for this classloader.
233      * @param project The project to which this classloader is to
234      * belong.
235      * @param classpath The classpath to use to load classes.
236      */

237     public AntClassLoader(
238         ClassLoader JavaDoc parent, Project project, Path classpath) {
239         setParent(parent);
240         setClassPath(classpath);
241         setProject(project);
242     }
243
244     /**
245      * Create an Ant Class Loader
246      */

247     public AntClassLoader() {
248         setParent(null);
249     }
250
251     /**
252      * Creates a classloader for the given project using the classpath given.
253      *
254      * @param project The project to which this classloader is to belong.
255      * Must not be <code>null</code>.
256      * @param classpath The classpath to use to load the classes. This
257      * is combined with the system classpath in a manner
258      * determined by the value of ${build.sysclasspath}.
259      * May be <code>null</code>, in which case no path
260      * elements are set up to start with.
261      */

262     public AntClassLoader(Project project, Path classpath) {
263         setParent(null);
264         setProject(project);
265         setClassPath(classpath);
266     }
267
268     /**
269      * Creates a classloader for the given project using the classpath given.
270      *
271      * @param parent The parent classloader to which unsatisfied loading
272      * attempts are delegated. May be <code>null</code>,
273      * in which case the classloader which loaded this
274      * class is used as the parent.
275      * @param project The project to which this classloader is to belong.
276      * Must not be <code>null</code>.
277      * @param classpath the classpath to use to load the classes.
278      * May be <code>null</code>, in which case no path
279      * elements are set up to start with.
280      * @param parentFirst If <code>true</code>, indicates that the parent
281      * classloader should be consulted before trying to
282      * load the a class through this loader.
283      */

284     public AntClassLoader(ClassLoader JavaDoc parent, Project project, Path classpath,
285                           boolean parentFirst) {
286         this(project, classpath);
287         if (parent != null) {
288             setParent(parent);
289         }
290         setParentFirst(parentFirst);
291         addJavaLibraries();
292     }
293
294
295     /**
296      * Creates a classloader for the given project using the classpath given.
297      *
298      * @param project The project to which this classloader is to belong.
299      * Must not be <code>null</code>.
300      * @param classpath The classpath to use to load the classes. May be
301      * <code>null</code>, in which case no path
302      * elements are set up to start with.
303      * @param parentFirst If <code>true</code>, indicates that the parent
304      * classloader should be consulted before trying to
305      * load the a class through this loader.
306      */

307     public AntClassLoader(Project project, Path classpath,
308                           boolean parentFirst) {
309         this(null, project, classpath, parentFirst);
310     }
311
312     /**
313      * Creates an empty class loader. The classloader should be configured
314      * with path elements to specify where the loader is to look for
315      * classes.
316      *
317      * @param parent The parent classloader to which unsatisfied loading
318      * attempts are delegated. May be <code>null</code>,
319      * in which case the classloader which loaded this
320      * class is used as the parent.
321      * @param parentFirst If <code>true</code>, indicates that the parent
322      * classloader should be consulted before trying to
323      * load the a class through this loader.
324      */

325     public AntClassLoader(ClassLoader JavaDoc parent, boolean parentFirst) {
326         setParent(parent);
327         project = null;
328         this.parentFirst = parentFirst;
329     }
330
331     /**
332      * Set the project associated with this class loader
333      *
334      * @param project the project instance
335      */

336     public void setProject(Project project) {
337         this.project = project;
338         if (project != null) {
339             project.addBuildListener(this);
340         }
341     }
342
343     /**
344      * Set the classpath to search for classes to load. This should not be
345      * changed once the classloader starts to server classes
346      *
347      * @param classpath the search classpath consisting of directories and
348      * jar/zip files.
349      */

350     public void setClassPath(Path classpath) {
351         pathComponents.removeAllElements();
352         if (classpath != null) {
353             Path actualClasspath = classpath.concatSystemClasspath("ignore");
354             String JavaDoc[] pathElements = actualClasspath.list();
355             for (int i = 0; i < pathElements.length; ++i) {
356                 try {
357                     addPathElement(pathElements[i]);
358                 } catch (BuildException e) {
359                     // ignore path elements which are invalid
360
// relative to the project
361
}
362             }
363         }
364     }
365
366     /**
367      * Set the parent for this class loader. This is the class loader to which
368      * this class loader will delegate to load classes
369      *
370      * @param parent the parent class loader.
371      */

372     public void setParent(ClassLoader JavaDoc parent) {
373         if (parent == null) {
374             this.parent = AntClassLoader.class.getClassLoader();
375         } else {
376             this.parent = parent;
377         }
378     }
379
380     /**
381      * Control whether class lookup is delegated to the parent loader first
382      * or after this loader. Use with extreme caution. Setting this to
383      * false violates the class loader hierarchy and can lead to Linkage errors
384      *
385      * @param parentFirst if true, delegate initial class search to the parent
386      * classloader.
387      */

388     public void setParentFirst(boolean parentFirst) {
389         this.parentFirst = parentFirst;
390     }
391
392
393     /**
394      * Logs a message through the project object if one has been provided.
395      *
396      * @param message The message to log.
397      * Should not be <code>null</code>.
398      *
399      * @param priority The logging priority of the message.
400      */

401     protected void log(String JavaDoc message, int priority) {
402         if (project != null) {
403             project.log(message, priority);
404         }
405         // else {
406
// System.out.println(message);
407
// }
408
}
409
410     /**
411      * Sets the current thread's context loader to this classloader, storing
412      * the current loader value for later resetting.
413      */

414     public void setThreadContextLoader() {
415         if (isContextLoaderSaved) {
416             throw new BuildException("Context loader has not been reset");
417         }
418         if (LoaderUtils.isContextLoaderAvailable()) {
419             savedContextLoader = LoaderUtils.getContextClassLoader();
420             ClassLoader JavaDoc loader = this;
421             if (project != null
422                 && "only".equals(project.getProperty("build.sysclasspath"))) {
423                 loader = this.getClass().getClassLoader();
424             }
425             LoaderUtils.setContextClassLoader(loader);
426             isContextLoaderSaved = true;
427         }
428     }
429
430     /**
431      * Resets the current thread's context loader to its original value.
432      */

433     public void resetThreadContextLoader() {
434         if (LoaderUtils.isContextLoaderAvailable()
435             && isContextLoaderSaved) {
436             LoaderUtils.setContextClassLoader(savedContextLoader);
437             savedContextLoader = null;
438             isContextLoaderSaved = false;
439         }
440     }
441
442
443     /**
444      * Adds an element to the classpath to be searched.
445      *
446      * @param pathElement The path element to add. Must not be
447      * <code>null</code>.
448      *
449      * @exception BuildException if the given path element cannot be resolved
450      * against the project.
451      */

452     public void addPathElement(String JavaDoc pathElement) throws BuildException {
453         File JavaDoc pathComponent
454             = project != null ? project.resolveFile(pathElement)
455             : new File JavaDoc(pathElement);
456         try {
457             addPathFile(pathComponent);
458         } catch (IOException JavaDoc e) {
459             throw new BuildException(e);
460         }
461     }
462
463     /**
464      * Add a file to the path.
465      * Reads the manifest, if available, and adds any additional class path jars
466      * specified in the manifest.
467      *
468      * @param pathComponent the file which is to be added to the path for
469      * this class loader
470      *
471      * @throws IOException if data needed from the file cannot be read.
472      */

473     protected void addPathFile(File JavaDoc pathComponent) throws IOException JavaDoc {
474         pathComponents.addElement(pathComponent);
475         if (pathComponent.isDirectory()) {
476             return;
477         }
478
479         String JavaDoc absPathPlusTimeAndLength =
480             pathComponent.getAbsolutePath() + pathComponent.lastModified() + "-"
481             + pathComponent.length();
482         String JavaDoc classpath = (String JavaDoc) pathMap.get(absPathPlusTimeAndLength);
483         if (classpath == null) {
484             ZipFile JavaDoc jarFile = null;
485             InputStream JavaDoc manifestStream = null;
486             try {
487                 jarFile = new ZipFile JavaDoc(pathComponent);
488                 manifestStream
489                     = jarFile.getInputStream(new ZipEntry JavaDoc("META-INF/MANIFEST.MF"));
490
491                 if (manifestStream == null) {
492                     return;
493                 }
494                 Reader JavaDoc manifestReader
495                     = new InputStreamReader JavaDoc(manifestStream, "UTF-8");
496                 org.apache.tools.ant.taskdefs.Manifest manifest
497                     = new org.apache.tools.ant.taskdefs.Manifest(manifestReader);
498                 classpath
499                     = manifest.getMainSection().getAttributeValue("Class-Path");
500
501             } catch (org.apache.tools.ant.taskdefs.ManifestException e) {
502                 // ignore
503
} finally {
504                 if (manifestStream != null) {
505                     manifestStream.close();
506                 }
507                 if (jarFile != null) {
508                     jarFile.close();
509                 }
510             }
511             if (classpath == null) {
512                 classpath = "";
513             }
514             pathMap.put(absPathPlusTimeAndLength, classpath);
515         }
516
517         if (!"".equals(classpath)) {
518             URL JavaDoc baseURL = FILE_UTILS.getFileURL(pathComponent);
519             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(classpath);
520             while (st.hasMoreTokens()) {
521                 String JavaDoc classpathElement = st.nextToken();
522                 URL JavaDoc libraryURL = new URL JavaDoc(baseURL, classpathElement);
523                 if (!libraryURL.getProtocol().equals("file")) {
524                     log("Skipping jar library " + classpathElement
525                         + " since only relative URLs are supported by this"
526                         + " loader", Project.MSG_VERBOSE);
527                     continue;
528                 }
529                 String JavaDoc decodedPath = Locator.decodeUri(libraryURL.getFile());
530                 File JavaDoc libraryFile = new File JavaDoc(decodedPath);
531                 if (libraryFile.exists() && !isInPath(libraryFile)) {
532                     addPathFile(libraryFile);
533                 }
534             }
535         }
536     }
537
538     /**
539      * Returns the classpath this classloader will consult.
540      *
541      * @return the classpath used for this classloader, with elements
542      * separated by the path separator for the system.
543      */

544     public String JavaDoc getClasspath() {
545         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
546         boolean firstPass = true;
547         Enumeration JavaDoc componentEnum = pathComponents.elements();
548         while (componentEnum.hasMoreElements()) {
549             if (!firstPass) {
550                 sb.append(System.getProperty("path.separator"));
551             } else {
552                 firstPass = false;
553             }
554             sb.append(((File JavaDoc) componentEnum.nextElement()).getAbsolutePath());
555         }
556         return sb.toString();
557     }
558
559     /**
560      * Sets whether this classloader should run in isolated mode. In
561      * isolated mode, classes not found on the given classpath will
562      * not be referred to the parent class loader but will cause a
563      * ClassNotFoundException.
564      *
565      * @param isolated Whether or not this classloader should run in
566      * isolated mode.
567      */

568     public synchronized void setIsolated(boolean isolated) {
569         ignoreBase = isolated;
570     }
571
572     /**
573      * Forces initialization of a class in a JDK 1.1 compatible, albeit hacky
574      * way.
575      *
576      * @param theClass The class to initialize.
577      * Must not be <code>null</code>.
578      *
579      * @deprecated since 1.6.x.
580      * Use Class.forName with initialize=true instead.
581      */

582     public static void initializeClass(Class JavaDoc theClass) {
583         // ***HACK*** We ask the VM to create an instance
584
// by voluntarily providing illegal arguments to force
585
// the VM to run the class' static initializer, while
586
// at the same time not running a valid constructor.
587

588         final Constructor JavaDoc[] cons = theClass.getDeclaredConstructors();
589         //At least one constructor is guaranteed to be there, but check anyway.
590
if (cons != null) {
591             if (cons.length > 0 && cons[0] != null) {
592                 final String JavaDoc[] strs = new String JavaDoc[NUMBER_OF_STRINGS];
593                 try {
594                     cons[0].newInstance((Object JavaDoc[]) strs);
595                     // Expecting an exception to be thrown by this call:
596
// IllegalArgumentException: wrong number of Arguments
597
} catch (Exception JavaDoc e) {
598                     // Ignore - we are interested only in the side
599
// effect - that of getting the static initializers
600
// invoked. As we do not want to call a valid
601
// constructor to get this side effect, an
602
// attempt is made to call a hopefully
603
// invalid constructor - come on, nobody
604
// would have a constructor that takes in
605
// 256 String arguments ;-)
606
// (In fact, they can't - according to JVM spec
607
// section 4.10, the number of method parameters is limited
608
// to 255 by the definition of a method descriptor.
609
// Constructors count as methods here.)
610
}
611             }
612         }
613     }
614
615     /**
616      * Adds a package root to the list of packages which must be loaded on the
617      * parent loader.
618      *
619      * All subpackages are also included.
620      *
621      * @param packageRoot The root of all packages to be included.
622      * Should not be <code>null</code>.
623      */

624     public void addSystemPackageRoot(String JavaDoc packageRoot) {
625         systemPackages.addElement(packageRoot
626                                   + (packageRoot.endsWith(".") ? "" : "."));
627     }
628
629     /**
630      * Adds a package root to the list of packages which must be loaded using
631      * this loader.
632      *
633      * All subpackages are also included.
634      *
635      * @param packageRoot The root of all packages to be included.
636      * Should not be <code>null</code>.
637      */

638     public void addLoaderPackageRoot(String JavaDoc packageRoot) {
639         loaderPackages.addElement(packageRoot
640                                   + (packageRoot.endsWith(".") ? "" : "."));
641     }
642
643     /**
644      * Loads a class through this class loader even if that class is available
645      * on the parent classpath.
646      *
647      * This ensures that any classes which are loaded by the returned class
648      * will use this classloader.
649      *
650      * @param classname The name of the class to be loaded.
651      * Must not be <code>null</code>.
652      *
653      * @return the required Class object
654      *
655      * @exception ClassNotFoundException if the requested class does not exist
656      * on this loader's classpath.
657      */

658     public Class JavaDoc forceLoadClass(String JavaDoc classname)
659         throws ClassNotFoundException JavaDoc {
660         log("force loading " + classname, Project.MSG_DEBUG);
661
662         Class JavaDoc theClass = findLoadedClass(classname);
663
664         if (theClass == null) {
665             theClass = findClass(classname);
666         }
667
668         return theClass;
669     }
670
671     /**
672      * Loads a class through this class loader but defer to the parent class
673      * loader.
674      *
675      * This ensures that instances of the returned class will be compatible
676      * with instances which have already been loaded on the parent
677      * loader.
678      *
679      * @param classname The name of the class to be loaded.
680      * Must not be <code>null</code>.
681      *
682      * @return the required Class object
683      *
684      * @exception ClassNotFoundException if the requested class does not exist
685      * on this loader's classpath.
686      */

687     public Class JavaDoc forceLoadSystemClass(String JavaDoc classname)
688         throws ClassNotFoundException JavaDoc {
689         log("force system loading " + classname, Project.MSG_DEBUG);
690
691         Class JavaDoc theClass = findLoadedClass(classname);
692
693         if (theClass == null) {
694             theClass = findBaseClass(classname);
695         }
696
697         return theClass;
698     }
699
700     /**
701      * Returns a stream to read the requested resource name.
702      *
703      * @param name The name of the resource for which a stream is required.
704      * Must not be <code>null</code>.
705      *
706      * @return a stream to the required resource or <code>null</code> if the
707      * resource cannot be found on the loader's classpath.
708      */

709     public InputStream JavaDoc getResourceAsStream(String JavaDoc name) {
710
711         InputStream JavaDoc resourceStream = null;
712         if (isParentFirst(name)) {
713             resourceStream = loadBaseResource(name);
714             if (resourceStream != null) {
715                 log("ResourceStream for " + name
716                     + " loaded from parent loader", Project.MSG_DEBUG);
717
718             } else {
719                 resourceStream = loadResource(name);
720                 if (resourceStream != null) {
721                     log("ResourceStream for " + name
722                         + " loaded from ant loader", Project.MSG_DEBUG);
723                 }
724             }
725         } else {
726             resourceStream = loadResource(name);
727             if (resourceStream != null) {
728                 log("ResourceStream for " + name
729                     + " loaded from ant loader", Project.MSG_DEBUG);
730
731             } else {
732                 resourceStream = loadBaseResource(name);
733                 if (resourceStream != null) {
734                     log("ResourceStream for " + name
735                         + " loaded from parent loader", Project.MSG_DEBUG);
736                 }
737             }
738         }
739
740         if (resourceStream == null) {
741             log("Couldn't load ResourceStream for " + name,
742                 Project.MSG_DEBUG);
743         }
744
745         return resourceStream;
746     }
747
748     /**
749      * Returns a stream to read the requested resource name from this loader.
750      *
751      * @param name The name of the resource for which a stream is required.
752      * Must not be <code>null</code>.
753      *
754      * @return a stream to the required resource or <code>null</code> if
755      * the resource cannot be found on the loader's classpath.
756      */

757     private InputStream JavaDoc loadResource(String JavaDoc name) {
758         // we need to search the components of the path to see if we can
759
// find the class we want.
760
InputStream JavaDoc stream = null;
761
762         Enumeration JavaDoc e = pathComponents.elements();
763         while (e.hasMoreElements() && stream == null) {
764             File JavaDoc pathComponent = (File JavaDoc) e.nextElement();
765             stream = getResourceStream(pathComponent, name);
766         }
767         return stream;
768     }
769
770     /**
771      * Finds a system resource (which should be loaded from the parent
772      * classloader).
773      *
774      * @param name The name of the system resource to load.
775      * Must not be <code>null</code>.
776      *
777      * @return a stream to the named resource, or <code>null</code> if
778      * the resource cannot be found.
779      */

780     private InputStream JavaDoc loadBaseResource(String JavaDoc name) {
781         if (parent == null) {
782             return getSystemResourceAsStream(name);
783         } else {
784             return parent.getResourceAsStream(name);
785         }
786     }
787
788     /**
789      * Returns an inputstream to a given resource in the given file which may
790      * either be a directory or a zip file.
791      *
792      * @param file the file (directory or jar) in which to search for the
793      * resource. Must not be <code>null</code>.
794      * @param resourceName The name of the resource for which a stream is
795      * required. Must not be <code>null</code>.
796      *
797      * @return a stream to the required resource or <code>null</code> if
798      * the resource cannot be found in the given file.
799      */

800     private InputStream JavaDoc getResourceStream(File JavaDoc file, String JavaDoc resourceName) {
801         try {
802             if (!file.exists()) {
803                 return null;
804             }
805
806             if (file.isDirectory()) {
807                 File JavaDoc resource = new File JavaDoc(file, resourceName);
808
809                 if (resource.exists()) {
810                     return new FileInputStream JavaDoc(resource);
811                 }
812             } else {
813                 // is the zip file in the cache
814
ZipFile JavaDoc zipFile = (ZipFile JavaDoc) zipFiles.get(file);
815                 if (zipFile == null) {
816                     zipFile = new ZipFile JavaDoc(file);
817                     zipFiles.put(file, zipFile);
818                 }
819                 ZipEntry JavaDoc entry = zipFile.getEntry(resourceName);
820                 if (entry != null) {
821                     return zipFile.getInputStream(entry);
822                 }
823             }
824         } catch (Exception JavaDoc e) {
825             log("Ignoring Exception " + e.getClass().getName()
826                 + ": " + e.getMessage() + " reading resource " + resourceName
827                 + " from " + file, Project.MSG_VERBOSE);
828         }
829
830         return null;
831     }
832
833    &n