Initial Commit
[packages] / xemacs-packages / jde / java / src / jde / util / DynamicClassLoader.java
1 /*
2  *    DynamicClassLoader.java
3  *    Copyright (C) 2001-2004 Javier Lopez (jslopez@forumsys.com)
4  *
5  *    $Revision: 1.7 $
6  *
7  *    This program is free software; you can redistribute it and/or modify
8  *    it under the terms of the GNU General Public License as published by
9  *    the Free Software Foundation; either version 2 of the License, or
10  *    (at your option) any later version.
11  *
12  *    This program is distributed in the hope that it will be useful,
13  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *    GNU General Public License for more details.
16  *
17  *    You should have received a copy of the GNU General Public License
18  *    along with this program; if not, write to the Free Software
19  *    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20  */
21 package jde.util;
22
23 import java.io.File;
24 import java.io.FileInputStream;
25 import java.io.FileNotFoundException;
26 import java.io.IOException;
27 import java.io.InputStream;
28 import java.util.StringTokenizer;
29 import java.util.zip.ZipEntry;
30 import java.util.zip.ZipFile;
31
32
33 /**
34  * The class <code>DynamicClassLoader</code> extends the 
35  * abstract class <code>ClassLoader</code>.
36  * This class loads the class binaries from the file system
37  * all the time, it does not catch the class information.
38  * There is caveat to this, classes that come with the JDK
39  * such as java.lang.*, are loaded using the standard class loader.
40  * The rest of the class are always reloaded from the file system.
41  *
42  * Created: Sun Jul 01 08:11:12 2001
43  *
44  * @author <a href="mailto:jlopez@cellexchange.com"></a>
45  * @version 1.0
46  * @since jde2.2.8beta2
47  */
48 public class DynamicClassLoader extends ClassLoader {
49   
50   /**
51    * Class path.
52    *
53    */
54   public static final String CLASS_PATH
55       = System.getProperty("java.class.path");
56   
57   /**
58    * Platform dependent file separator.
59    *
60    */
61   public static final String FILE_SEPARATOR
62       = System.getProperty("file.separator");
63     
64   /**
65    * Platform dependent path separator. i.e. in Win32 is ';' in Unix is ':'.
66    *
67    */
68   public static final String PATH_SEPARATOR
69       = System.getProperty("path.separator"); 
70     
71   /**
72    * Char use to separate packages. i.e. '.'
73    *
74    */
75   public static final char PACKAGE_SEPARATOR = '.'; 
76   
77   /**
78    * Classes file type. i.e. class
79    *
80    */
81   public static final String CLASS_FILE_TYPE = "class";
82   
83   /**
84    * Loads a class information from the file system,
85    * if it fails it tries Class.forName(argClassName)
86    *
87    * @param argClassName name of the class to be loaded.
88    * @return Class of the type argClassName
89    * @exception ClassNotFoundException if the class cannot be found.
90    */
91   public Class loadClass(String argClassName) throws ClassNotFoundException {
92     File file;
93     byte[] classBytes = null;
94     Class c;
95
96     //Checking if the class belong to either java.* or javax.*
97     if ((argClassName.startsWith("java.")) || 
98         (argClassName.startsWith("javax."))) {
99       return Class.forName(argClassName);
100     } // end of if ()
101     
102     //First convert the class name from java.lang.String to java/lang/String
103     //where '/' is platform dependent.
104     String className = argClassName.replace(PACKAGE_SEPARATOR,
105                                             FILE_SEPARATOR.charAt(0));
106
107     //Then add the class file termination i.e. from java/lang/String
108     //to java/lang/String.class
109     className += PACKAGE_SEPARATOR + CLASS_FILE_TYPE;
110
111     //Look for the class file in the current project classfile or in
112     //the system class path if there is no current classpath
113     ProjectClasses pc = JdeUtilities.getCurrentProjectClass();
114     String classpath = null;
115     if (pc != null) {
116         classpath = pc.getClassPath();
117     } // end of if (pc != null)
118     
119     if (classpath == null || classpath.equals("")) {
120         classpath = CLASS_PATH;
121     } // end of if (classpath == null )
122     
123     StringTokenizer st = new StringTokenizer(classpath, PATH_SEPARATOR);
124     ZipFile zf;
125     while (st.hasMoreTokens()) {
126       file = new File(st.nextToken());  
127
128       //Check if the file is a directory if is not 
129       //assume it is a jar or zip file
130       try {
131         if (file.isDirectory()) {
132           //if the file is a directory try to locate the class file 
133           //and load it.
134           file = new File(file, className);
135           classBytes = loadFile(file);
136           if (classBytes != null) {
137             break;
138           } // end of if (classBytes != null)
139         } else {
140           zf = new ZipFile(file);
141           classBytes = loadFile(zf, className);
142           if (classBytes != null) {
143             break;
144           } // end of if (classBytes != null)
145         }// end of if-else
146       } catch (IOException e) {
147         //ignore
148       } // end of try-catch
149     } // end of while (st.hasMoreTokens())
150     
151     if (classBytes != null) {
152       try {
153         c = defineClass(argClassName, classBytes, 0, classBytes.length);
154       } catch (SecurityException e) {
155         //basic packages such as java.lang.* can't be loaded directly
156         c = Class.forName(argClassName);
157       } catch (ClassFormatError e) { 
158         c = Class.forName(argClassName);
159       } catch (NoClassDefFoundError e) {
160         c = Class.forName(argClassName);
161       }
162       return c;
163     } else {
164       try {
165         return Class.forName(argClassName);
166       } catch (ClassNotFoundException e) {
167         throw new ClassNotFoundException(argClassName);
168       } // end of try-catch
169     } // end of else
170   }//end of loadClass
171
172   private byte[] loadFile(File argFile) {
173     byte[] b = null;
174     InputStream in = null;
175     if (argFile.exists()) {
176       try {
177         in = new FileInputStream(argFile);
178         b = read(in, (int)argFile.length());
179       } catch (FileNotFoundException e) {
180         b = null;
181       } catch (IOException e) {
182         b = null;
183       } finally {
184         try {
185           in.close();
186         } catch (IOException e) {
187         } // end of try-catch
188       }//end of try-finally
189     }// end of if
190     
191     return b;
192   }
193
194   private byte[] loadFile(ZipFile argFile, String argClassName) {
195     //zip and jar files seems to always be separated by a '/'
196     argClassName = argClassName.replace(FILE_SEPARATOR.charAt(0), '/');
197     byte[] b = null;
198     ZipEntry ze;
199     InputStream in;
200     try {
201       ze = argFile.getEntry(argClassName);
202       if (ze != null) {
203         in = argFile.getInputStream(ze);
204         b = read(in, (int) ze.getSize());
205       }
206     } catch (IOException e) {
207       b = null;
208     } finally {
209       try {
210         argFile.close();
211       } catch (IOException e) {
212       } 
213     }
214     return b;
215   }
216
217   private static byte[] read (InputStream is, int size) throws IOException {
218     int len = 0;
219     byte [] b = new byte[size];
220     try {
221       while (true) {
222         int n = is.read(b, len, size - len);
223         if (n == -1 || n == 0) {
224           if (len < size) {
225                     // ignore
226           }
227           break;
228         } else
229           len += n;
230       }
231     } finally {
232       try {
233         is.close();
234       } catch (IOException e) {} // ignore
235     }        
236     return b;
237   }
238
239 }// DynamicClassLoader
240
241 /*
242  * $Log: DynamicClassLoader.java,v $
243  * Revision 1.7  2004/10/15 03:28:09  paulk
244  * Fixed bug caused by assuming that only one read operation is needed to
245  * read a class from the file system. Thanks to Suraj Acharya.
246  *
247  * Revision 1.6  2002/02/21 12:26:45  jslopez
248  * Updates the DynamicClassLoader to use the current project
249  * classpath stored in JdeUtilities.
250  *
251  * Revision 1.5  2001/07/21 04:01:21  paulk
252  * Now loads classes in java and javax packages instead of waiting for an exception to be thrown The purpose is two-fold, one is performance and the other one is to get rid of a nasty LinkageError. Contributed by Javier Lopez.
253  *
254  * Revision 1.4  2001/07/18 01:51:37  paulk
255  * Handles ClassFormatError and NoClassDefFoundErrror exceptions that can occur when trying to load classes. Thanks to Javier Lopez.
256  *
257  * Revision 1.3  2001/07/17 05:27:53  paulk
258  * Fixed to search vm startup classpath if classes not found on user classpath. Thanks to Javier.
259  *
260  * Revision 1.2  2001/07/07 04:49:43  paulk
261  * Removed DOS line endings.
262  *
263  * Revision 1.1  2001/07/06 01:59:02  paulk
264  * Initial revision.
265  *
266  */
267
268 // End of DynamicClassLoader.java