diff options
Diffstat (limited to 'JLanguageTool/src/java/de/danielnaber/languagetool/tools/ReflectionUtils.java')
-rw-r--r-- | JLanguageTool/src/java/de/danielnaber/languagetool/tools/ReflectionUtils.java | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/JLanguageTool/src/java/de/danielnaber/languagetool/tools/ReflectionUtils.java b/JLanguageTool/src/java/de/danielnaber/languagetool/tools/ReflectionUtils.java new file mode 100644 index 0000000..9735cac --- /dev/null +++ b/JLanguageTool/src/java/de/danielnaber/languagetool/tools/ReflectionUtils.java @@ -0,0 +1,232 @@ +/* ReflectionUtils, helper methods to load classes dynamically + * Copyright (C) 2007 Andriy Rysin, Marcin Milkowski, Daniel Naber + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 + * USA + */ +package de.danielnaber.languagetool.tools; + +import java.io.File; +import java.io.IOException; +import java.lang.reflect.Modifier; +import java.net.JarURLConnection; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.*; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +public final class ReflectionUtils { + + private ReflectionUtils() { + // a static singleton class + } + + /** + * @param classLoader + * Classloader to use for loading classes + * @param packageName + * Package name to check classes in + * @param classNameRegEx + * If not null limit class names to this regexp. This parameter is + * checked before class is loaded so use it to improve performance by + * skipping loading extra classes + * @param subdirLevel + * If more than 0 all subdirectories/subpackages up to + * <code>dirLevel</code> will be traversed This parameter is checked + * before class is loaded - use it to improve performance by skipping + * loading extra classes + * @param classExtends + * If not null return only classes which extend this class + * @param interfaceImplements + * If not null return only classes which implement this interface + * @return Returns all classes inside given package + * @throws ClassNotFoundException + */ + public static Class[] findClasses(final ClassLoader classLoader, + final String packageName, final String classNameRegEx, + final int subdirLevel, final Class classExtends, + final Class interfaceImplements) throws ClassNotFoundException { + final Map<Class,String> foundClasses = new HashMap<Class,String>(); + + try { + final String packagePath = packageName.replace('.', '/'); + final Enumeration<URL> resources_ = classLoader.getResources(packagePath); + + final Set<URI> uniqResources = new HashSet<URI>(); + while (resources_.hasMoreElements()) { + final URI resource = resources_.nextElement().toURI(); + uniqResources.add(resource); + } + + for (final URI res : uniqResources) { + final URL resource = res.toURL(); + // System.err.println("trying resource: " + resource); + // jars and directories are treated differently + if (resource.getProtocol().startsWith("jar")) { + findClassesInJar(packageName, classNameRegEx, subdirLevel, + classExtends, interfaceImplements, foundClasses, resource); + } else { + findClassesInDirectory(classLoader, packageName, classNameRegEx, + subdirLevel, classExtends, interfaceImplements, foundClasses, + resource); + } + } + } catch (final Exception ex) { + throw new ClassNotFoundException("Loading rules failed: " + + ex.getMessage(), ex); + } + + return foundClasses.keySet().toArray(new Class[foundClasses.size()]); + } + + private static void findClassesInDirectory(final ClassLoader classLoader, + final String packageName, final String classNameRegEx, + final int subdirLevel, final Class classExtends, + final Class interfaceImplements, final Map<Class,String> foundClasses, + final URL resource) throws Exception { + final File directory = new File(resource.toURI()); + + if (!directory.exists() && !directory.isDirectory()) { + throw new Exception("directory does not exist: " + + directory.getAbsolutePath()); + } + + // read classes + for (final File file : directory.listFiles()) { + if (file.isFile() && file.getName().endsWith(".class")) { + final String classShortNm = file.getName().substring(0, + file.getName().lastIndexOf('.')); + if (classNameRegEx == null || classShortNm.matches(classNameRegEx)) { + final Class clazz = Class.forName(packageName + "." + classShortNm); + + if (!isMaterial(clazz)) { + continue; + } + + if (classExtends == null + || isExtending(clazz, classExtends.getName()) + && interfaceImplements == null + || isImplementing(clazz, interfaceImplements)) { + foundClasses.put(clazz, file.getAbsolutePath()); + // System.err.println("Added rule from dir: " + classShortNm); + } + } + } + } + + // then subdirectories if we're traversing + if (subdirLevel > 0) { + for (final File dir : directory.listFiles()) { + if (dir.isDirectory()) { + final Class[] subLevelClasses = findClasses(classLoader, packageName + + "." + dir.getName(), classNameRegEx, subdirLevel - 1, + classExtends, interfaceImplements); + for (Class tmpClass : subLevelClasses) { + foundClasses.put(tmpClass, "dir:" + dir.getAbsolutePath()); + } + } + } + } + } + + private static void findClassesInJar(final String packageName, + final String classNameRegEx, final int subdirLevel, + final Class classExtends, final Class interfaceImplements, + final Map<Class,String> foundClasses, final URL resource) throws IOException, + URISyntaxException, ClassNotFoundException { + final JarURLConnection conn = (JarURLConnection) resource.openConnection(); + final JarFile currentFile = conn.getJarFile(); // new JarFile(new + // File(resource.toURI())); + // jars are flat containers: + for (final Enumeration<JarEntry> e = currentFile.entries(); e + .hasMoreElements();) { + final JarEntry current = e.nextElement(); + final String name = current.getName(); + // System.err.println("jar entry: " + name); + + if (name.endsWith(".class")) { + final String classNm = name.replaceAll("/", ".").replace(".class", ""); + final int pointIdx = classNm.lastIndexOf('.'); + final String classShortNm = pointIdx == -1 ? classNm : classNm + .substring(pointIdx + 1); + + if (classNm.startsWith(packageName) + && (classNameRegEx == null || classShortNm.matches(classNameRegEx))) { + final String subName = classNm.substring(packageName.length() + 1); + + if (countOccurrences(subName, '.') > subdirLevel) { + continue; + } + + final Class clazz = Class.forName(classNm); + if (foundClasses.containsKey(clazz)) { + throw new RuntimeException("Duplicate class definition:\n" + + clazz.getName() + ", found in\n" + currentFile.getName() + " and\n" + + foundClasses.get(clazz)); + } + + if (!isMaterial(clazz)) { + continue; + } + + if (classExtends == null + || isExtending(clazz, classExtends.getName()) + && interfaceImplements == null + || isImplementing(clazz, interfaceImplements)) { + foundClasses.put(clazz, currentFile.getName()); + // System.err.println("Added class from jar: " + name); + } + } + } + } + } + + private static int countOccurrences(final String str, final char ch) { + int i = 0; + int pos = str.indexOf(ch, 0); + while (pos != -1) { + i++; + pos = str.indexOf(ch, pos + 1); + } + return i; + } + + private static boolean isMaterial(final Class clazz) { + final int mod = clazz.getModifiers(); + return !Modifier.isAbstract(mod) && !Modifier.isInterface(mod); + } + + /** + * @return Returns true if clazz extends superClassName + */ + private static boolean isExtending(final Class clazz, + final String superClassName) { + Class tmpSuperClass = clazz.getSuperclass(); + while (tmpSuperClass != null) { + if (superClassName.equals(tmpSuperClass.getName())) { + return true; + } + tmpSuperClass = tmpSuperClass.getSuperclass(); + } + return false; + } + + private static boolean isImplementing(final Class clazz, final Class interfaze) { + return Arrays.asList(clazz.getInterfaces()).contains(interfaze); + } + +} |