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