Rev 15 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | SVN | RSS feed
| Rev 15 | Rev 73 | ||
|---|---|---|---|
| 1 | /* |
1 | /* |
| 2 | * File created by Jimmy |
2 | * File created by Jimmy |
| 3 | * at 2004-jan-20 16:59:27 |
3 | * at 2004-jan-20 16:59:27 |
| 4 | */ |
4 | */ |
| 5 | package Macbeth.Utilities; |
5 | package Macbeth.Utilities; |
| 6 | 6 | ||
| 7 | import java.io.File; |
7 | import java.io.File; |
| 8 | import java.io.FileNotFoundException; |
8 | import java.io.FileNotFoundException; |
| 9 | import java.net.URL; |
9 | import java.net.URL; |
| 10 | import java.net.MalformedURLException; |
10 | import java.net.MalformedURLException; |
| 11 | import java.net.URLClassLoader; |
11 | import java.net.URLClassLoader; |
| 12 | import java.lang.reflect.Method; |
12 | import java.lang.reflect.Method; |
| 13 | import java.util.LinkedList; |
13 | import java.util.LinkedList; |
| 14 | import java.util.List; |
14 | import java.util.List; |
| 15 | 15 | ||
| 16 | /** |
16 | /** |
| 17 | * Dynamic class loader that can enumerate and load classes at runtime. |
17 | * Dynamic class loader that can enumerate and load classes at runtime. |
| 18 | * @author Jimmy |
18 | * @author Jimmy |
| 19 | */ |
19 | */ |
| 20 | public class DynamicClassLoader { |
20 | public class DynamicClassLoader { |
| 21 | 21 | ||
| 22 | /** |
22 | /** |
| 23 | * Examines a class to find outwhether or not it meets the set requirements. |
23 | * Examines a class to find outwhether or not it meets the set requirements. |
| 24 | * Requirements can be set by passing a list of Superclasses and/or interfaces |
24 | * Requirements can be set by passing a list of Superclasses and/or interfaces |
| 25 | * that the class must inherit/implement. |
25 | * that the class must inherit/implement. |
| 26 | * @param theClass The name of the class to examine. |
26 | * @param theClass The name of the class to examine. |
| 27 | * @param superClassRequirements A list of names of superclasses that the class must inherit. |
27 | * @param superClassRequirements A list of names of superclasses that the class must inherit. |
| 28 | * @param interfaceRequirements A list of names of interfaces that the class must implement. |
28 | * @param interfaceRequirements A list of names of interfaces that the class must implement. |
| 29 | * @return true if all the set requirements are met, false otherwise. |
29 | * @return true if all the set requirements are met, false otherwise. |
| 30 | */ |
30 | */ |
| 31 | private static boolean examineClass(Class theClass, String[] superClassRequirements, String[] interfaceRequirements) { |
31 | private static boolean examineClass(Class theClass, String[] superClassRequirements, String[] interfaceRequirements) { |
| 32 | //if there are requirements on superclasses |
32 | //if there are requirements on superclasses |
| 33 | if (superClassRequirements!=null) { |
33 | if (superClassRequirements!=null) { |
| 34 | //an array that indicates which superclass requirements have been met |
34 | //an array that indicates which superclass requirements have been met |
| 35 | boolean[] requirementsMet = new boolean[superClassRequirements.length]; |
35 | boolean[] requirementsMet = new boolean[superClassRequirements.length]; |
| 36 | for (int i=0; i<requirementsMet.length; i++) { |
36 | for (int i=0; i<requirementsMet.length; i++) { |
| 37 | requirementsMet[i] = false; |
37 | requirementsMet[i] = false; |
| 38 | } |
38 | } |
| 39 | //get the first superclass of our class |
39 | //get the first superclass of our class |
| 40 | Class sup = theClass.getSuperclass(); |
40 | Class sup = theClass.getSuperclass(); |
| 41 | //iterate through all superclass requirements |
41 | //iterate through all superclass requirements |
| 42 | for (int i=0; i<superClassRequirements.length; i++) { |
42 | for (int i=0; i<superClassRequirements.length; i++) { |
| 43 | String superClassRequirement = superClassRequirements[i]; |
43 | String superClassRequirement = superClassRequirements[i]; |
| 44 | //iterate through all superclasses of our class |
44 | //iterate through all superclasses of our class |
| 45 | while (sup != null) { |
45 | while (sup != null) { |
| 46 | //if this superclass equals the required superclass |
46 | //if this superclass equals the required superclass |
| 47 | if (sup.getName().toString().equals(superClassRequirement)) { |
47 | if (sup.getName().toString().equals(superClassRequirement)) { |
| 48 | //then this requirement has been met |
48 | //then this requirement has been met |
| 49 | requirementsMet[i] = true; |
49 | requirementsMet[i] = true; |
| 50 | //no need to search for this particular superclass anymore |
50 | //no need to search for this particular superclass anymore |
| 51 | break; |
51 | break; |
| 52 | } |
52 | } |
| 53 | //go to next superclass (if any) |
53 | //go to next superclass (if any) |
| 54 | else { |
54 | else { |
| 55 | sup = sup.getSuperclass(); |
55 | sup = sup.getSuperclass(); |
| 56 | } |
56 | } |
| 57 | } |
57 | } |
| 58 | } |
58 | } |
| 59 | //now, find out if all superclass requirements were met |
59 | //now, find out if all superclass requirements were met |
| 60 | for (int i=0; i<requirementsMet.length; i++) { |
60 | for (int i=0; i<requirementsMet.length; i++) { |
| 61 | //if any of the requirements were NOT met |
61 | //if any of the requirements were NOT met |
| 62 | if (!requirementsMet[i]) { |
62 | if (!requirementsMet[i]) { |
| 63 | //the whole method fails |
63 | //the whole method fails |
| 64 | return false; |
64 | return false; |
| 65 | } |
65 | } |
| 66 | } |
66 | } |
| 67 | } |
67 | } |
| 68 | //if there are requirements on interfaces |
68 | //if there are requirements on interfaces |
| 69 | if (interfaceRequirements != null) { |
69 | if (interfaceRequirements != null) { |
| 70 | //an array that indicates which interface requirements have been met |
70 | //an array that indicates which interface requirements have been met |
| 71 | boolean[] requirementsMet = new boolean[interfaceRequirements.length]; |
71 | boolean[] requirementsMet = new boolean[interfaceRequirements.length]; |
| 72 | for (int i = 0; i < requirementsMet.length; i++) { |
72 | for (int i = 0; i < requirementsMet.length; i++) { |
| 73 | requirementsMet[i] = false; |
73 | requirementsMet[i] = false; |
| 74 | } |
74 | } |
| 75 | //get all interface classes implemented by our class |
75 | //get all interface classes implemented by our class |
| 76 | Class[] interfaces = theClass.getInterfaces(); |
76 | Class[] interfaces = theClass.getInterfaces(); |
| 77 | //iterate through all interface requirements |
77 | //iterate through all interface requirements |
| 78 | for (int i = 0; i < interfaceRequirements.length; i++) { |
78 | for (int i = 0; i < interfaceRequirements.length; i++) { |
| 79 | String interfaceRequirement = interfaceRequirements[i]; |
79 | String interfaceRequirement = interfaceRequirements[i]; |
| 80 | //iterate through all the implemented interfaces |
80 | //iterate through all the implemented interfaces |
| 81 | for (int j=0; j<interfaces.length; j++) { |
81 | for (int j=0; j<interfaces.length; j++) { |
| 82 | //if this interface equals the required interface |
82 | //if this interface equals the required interface |
| 83 | if (interfaces[j].getName().toString().equals(interfaceRequirement)) { |
83 | if (interfaces[j].getName().toString().equals(interfaceRequirement)) { |
| 84 | //then this requirement has been met |
84 | //then this requirement has been met |
| 85 | requirementsMet[i] = true; |
85 | requirementsMet[i] = true; |
| 86 | //no need to search for this particular interface anymore |
86 | //no need to search for this particular interface anymore |
| 87 | break; |
87 | break; |
| 88 | } |
88 | } |
| 89 | } |
89 | } |
| 90 | } |
90 | } |
| 91 | //now, find out if all interface requirements were met |
91 | //now, find out if all interface requirements were met |
| 92 | for (int i = 0; i < requirementsMet.length; i++) { |
92 | for (int i = 0; i < requirementsMet.length; i++) { |
| 93 | //if any of the requirements were NOT met |
93 | //if any of the requirements were NOT met |
| 94 | if (!requirementsMet[i]) { |
94 | if (!requirementsMet[i]) { |
| 95 | //the whole method fails |
95 | //the whole method fails |
| 96 | return false; |
96 | return false; |
| 97 | } |
97 | } |
| 98 | } |
98 | } |
| 99 | } |
99 | } |
| 100 | //if this point is reached, the class met all requirements! |
100 | //if this point is reached, the class met all requirements! |
| 101 | return true; |
101 | return true; |
| 102 | } |
102 | } |
| 103 | 103 | ||
| 104 | 104 | ||
| 105 | /** |
105 | /** |
| 106 | * Searches a directory for classes that fulfill some specified requirements. |
106 | * Searches a directory for classes that fulfill some specified requirements. |
| 107 | * The class names of the classes that are found will be saved in a user |
107 | * The class names of the classes that are found will be saved in a user |
| 108 | * specified list. Subdirectories will be recursively searched as well, and the |
108 | * specified list. Subdirectories will be recursively searched as well, and the |
| 109 | * name of each found subdir will be appended to the class names of the classes |
109 | * name of each found subdir will be appended to the class names of the classes |
| 110 | * that are found inside that directory. For instance, if a class is found in |
110 | * that are found inside that directory. For instance, if a class is found in |
| 111 | * the base directory it is assumed to be part of the given base package (both |
111 | * the base directory it is assumed to be part of the given base package (both |
| 112 | * of which you specify with parameters), but if the class is found insude a |
112 | * of which you specify with parameters), but if the class is found insude a |
| 113 | * subdir with the relative path "Macbeth/Modules" it is assumed to be part of a |
113 | * subdir with the relative path "Macbeth/Modules" it is assumed to be part of a |
| 114 | * package called [base package].Macbeth.Modules. |
114 | * package called [base package].Macbeth.Modules. |
| 115 | * @param basedirectory The base directory to start looking in. |
115 | * @param basedirectory The base directory to start looking in. |
| 116 | * @param basepackage The corresponing package name for the given base directory. |
116 | * @param basepackage The corresponing package name for the given base directory. |
| 117 | * @param superClassRequirements A list of superclass names that all classes |
117 | * @param superClassRequirements A list of superclass names that all classes |
| 118 | * must inherit from (or else they will be ignored). |
118 | * must inherit from (or else they will be ignored). |
| 119 | * @param interfaceRequirements A list of interface names that all classes must |
119 | * @param interfaceRequirements A list of interface names that all classes must |
| 120 | * implement (or else they will be ignored). |
120 | * implement (or else they will be ignored). |
| 121 | * @param classList A list into which all class names will be put. |
121 | * @param classList A list into which all class names will be put. |
| 122 | * @throws FileNotFoundException if the specified directory isn't found. |
122 | * @throws FileNotFoundException if the specified directory isn't found. |
| 123 | */ |
123 | */ |
| 124 | public static void locateClasses(String basedirectory, String basepackage,String[] superClassRequirements, |
124 | public static void locateClasses(String basedirectory, String basepackage,String[] superClassRequirements, |
| 125 | String[] interfaceRequirements, List classList) |
125 | String[] interfaceRequirements, List classList) |
| 126 | throws FileNotFoundException { |
126 | throws FileNotFoundException { |
| 127 | //create file listing for given directory |
127 | //create file listing for given directory |
| 128 | File dir = new File(basedirectory); |
128 | File dir = new File(basedirectory); |
| 129 | File[] dirListing = dir.listFiles(); |
129 | File[] dirListing = dir.listFiles(); |
| 130 | //convert directory to an URL (so it can be used with URLClassLoader) |
130 | //convert directory to an URL (so it can be used with URLClassLoader) |
| 131 | URL dirURL; |
131 | URL dirURL; |
| 132 | URL[] urls; |
132 | URL[] urls; |
| 133 | try { |
133 | try { |
| 134 | dirURL = dir.toURL(); |
134 | dirURL = dir.toURL(); |
| 135 | urls = new URL[]{dirURL}; |
135 | urls = new URL[]{dirURL}; |
| 136 | } catch (MalformedURLException e) { |
136 | } catch (MalformedURLException e) { |
| 137 | throw new FileNotFoundException("Invalid directory"); |
137 | throw new FileNotFoundException("Invalid directory"); |
| 138 | } |
138 | } |
| 139 | //create an URLClassLoader that can load classes from our directory |
139 | //create an URLClassLoader that can load classes from our directory |
| 140 | ClassLoader cl = new URLClassLoader(urls); |
140 | ClassLoader cl = new URLClassLoader(urls); |
| 141 | //the object to temporarily hold loaded classes |
141 | //the object to temporarily hold loaded classes |
| 142 | Class loadedClass; |
142 | Class loadedClass; |
| 143 | //now, for each file in this dir... |
143 | //now, for each file in this dir... |
| 144 | for (int i=0; i<dirListing.length; i++) { |
144 | for (int i=0; i<dirListing.length; i++) { |
| 145 | //if this is a directory |
145 | //if this is a directory |
| 146 | if (dirListing[i].isDirectory()) { |
146 | if (dirListing[i].isDirectory()) { |
| 147 | //if no base package is specified |
147 | //if no base package is specified |
| 148 | if (basepackage.equals("")) { |
148 | if (basepackage.equals("")) { |
| 149 | //continue searching recursively through the subdirs |
149 | //continue searching recursively through the subdirs |
| 150 | locateClasses(basedirectory + System.getProperty("file.separator").toString() + |
150 | locateClasses(basedirectory + System.getProperty("file.separator").toString() + |
| 151 | dirListing[i].getName(), dirListing[i].getName(), |
151 | dirListing[i].getName(), dirListing[i].getName(), |
| 152 | superClassRequirements, interfaceRequirements, classList); |
152 | superClassRequirements, interfaceRequirements, classList); |
| 153 | } |
153 | } |
| 154 | //if base package is specified |
154 | //if base package is specified |
| 155 | else { |
155 | else { |
| 156 | //append a dot, and then continue searching recursively through the subdirs |
156 | //append a dot, and then continue searching recursively through the subdirs |
| 157 | locateClasses(basedirectory + System.getProperty("file.separator").toString() + |
157 | locateClasses(basedirectory + System.getProperty("file.separator").toString() + |
| 158 | dirListing[i].getName(), basepackage + "." + dirListing[i].getName(), |
158 | dirListing[i].getName(), basepackage + "." + dirListing[i].getName(), |
| 159 | superClassRequirements, interfaceRequirements, classList); |
159 | superClassRequirements, interfaceRequirements, classList); |
| 160 | } |
160 | } |
| 161 | } |
161 | } |
| 162 | //this wasn't a directory |
162 | //this wasn't a directory |
| 163 | else { |
163 | else { |
| 164 | //if this is not a file ending with .class |
164 | //if this is not a file ending with .class |
| 165 | if (!dirListing[i].getName().toString().endsWith(".class")) { |
165 | if (!dirListing[i].getName().toString().endsWith(".class")) { |
| 166 | //ignore it and go on |
166 | //ignore it and go on |
| 167 | continue; |
167 | continue; |
| 168 | } |
168 | } |
| 169 | //else, work out the class name (so we can try to load the class) |
169 | //else, work out the class name (so we can try to load the class) |
| 170 | String filename = dirListing[i].getName().toString(); |
170 | String filename = dirListing[i].getName().toString(); |
| 171 | String classname; |
171 | String classname; |
| 172 | //if a base package was given, we need to append a dot as a separator |
172 | //if a base package was given, we need to append a dot as a separator |
| 173 | if (basepackage.equals("")) { |
173 | if (basepackage.equals("")) { |
| 174 | classname = filename.substring(0, filename.length() - 6); |
174 | classname = filename.substring(0, filename.length() - 6); |
| 175 | } else { |
175 | } else { |
| 176 | classname = basepackage + "." + filename.substring(0, filename.length() - 6); |
176 | classname = basepackage + "." + filename.substring(0, filename.length() - 6); |
| 177 | } |
177 | } |
| 178 | //try to load class (so we can examine it) |
178 | //try to load class (so we can examine it) |
| 179 | try { |
179 | try { |
| 180 | //try to load class |
180 | //try to load class |
| 181 | loadedClass = cl.loadClass(classname); |
181 | loadedClass = cl.loadClass(classname); |
| 182 | //and check if class meets requirements |
182 | //and check if class meets requirements |
| 183 | if (examineClass(loadedClass,superClassRequirements,interfaceRequirements)) { |
183 | if (examineClass(loadedClass,superClassRequirements,interfaceRequirements)) { |
| 184 | //if it does, save this classname in class list |
184 | //if it does, save this classname in class list |
| 185 | classList.add(classname); |
185 | classList.add(classname); |
| 186 | } |
186 | } |
| 187 | } catch (ClassNotFoundException e) { |
187 | } catch (ClassNotFoundException e) { |
| 188 | //something went wrong, so ignore the current class and go on |
188 | //something went wrong, so ignore the current class and go on |
| 189 | continue; |
189 | continue; |
| 190 | } |
190 | } |
| 191 | } |
191 | } |
| 192 | } |
192 | } |
| 193 | } |
193 | } |
| 194 | 194 | ||
| 195 | /** |
195 | /** |
| 196 | * Loads a class from a directory at runtime. |
196 | * Loads a class from a directory at runtime. |
| 197 | * @param baseDirectory The base directory in which the class (or package) is located. |
197 | * @param baseDirectory The base directory in which the class (or package) is located. |
| 198 | * @param classname The full classname of the class you want to load. |
198 | * @param classname The full classname of the class you want to load. |
| 199 | * @return The loaded class. |
199 | * @return The loaded class. |
| 200 | * @throws FileNotFoundException If the base directory isn't found. |
200 | * @throws FileNotFoundException If the base directory isn't found. |
| 201 | * @throws ClassNotFoundException If the class isn't found. |
201 | * @throws ClassNotFoundException If the class isn't found. |
| 202 | */ |
202 | */ |
| 203 | public static Class loadClass(String baseDirectory, String classname) |
203 | public static Class loadClass(String baseDirectory, String classname) |
| 204 | throws FileNotFoundException, ClassNotFoundException { |
204 | throws FileNotFoundException, ClassNotFoundException { |
| 205 | File dir = new File(baseDirectory); |
205 | File dir = new File(baseDirectory); |
| 206 | //convert directory to an URL (so it can be used with URLClassLoader) |
206 | //convert directory to an URL (so it can be used with URLClassLoader) |
| 207 | URL dirURL; |
207 | URL dirURL; |
| 208 | URL[] urls; |
208 | URL[] urls; |
| 209 | try { |
209 | try { |
| 210 | dirURL = dir.toURL(); |
210 | dirURL = dir.toURL(); |
| 211 | urls = new URL[]{dirURL}; |
211 | urls = new URL[]{dirURL}; |
| 212 | } catch (MalformedURLException e) { |
212 | } catch (MalformedURLException e) { |
| 213 | throw new FileNotFoundException("Invalid directory"); |
213 | throw new FileNotFoundException("Invalid directory"); |
| 214 | } |
214 | } |
| 215 | //create an URLClassLoader that can load classes from our directory |
215 | //create an URLClassLoader that can load classes from our directory |
| 216 | ClassLoader cl = new URLClassLoader(urls); |
216 | ClassLoader cl = new URLClassLoader(urls); |
| 217 | //try to load and return the class |
217 | //try to load and return the class |
| 218 | return cl.loadClass(classname); |
218 | return cl.loadClass(classname); |
| 219 | } |
219 | } |
| 220 | 220 | ||
| 221 | } |
221 | } |
| 222 | 222 | ||