diff options
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java')
-rw-r--r-- | src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java | 286 |
1 files changed, 125 insertions, 161 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java index aedc4e7c..0d147420 100644 --- a/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java +++ b/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java @@ -4,7 +4,7 @@ package de.lmu.ifi.dbs.elki.utilities; This file is part of ELKI: Environment for Developing KDD-Applications Supported by Index-Structures - Copyright (C) 2012 + Copyright (C) 2013 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -28,6 +28,7 @@ import gnu.trove.set.hash.THashSet; import java.io.File; import java.io.IOException; import java.lang.reflect.Modifier; +import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; @@ -36,12 +37,8 @@ import java.util.Enumeration; import java.util.Iterator; import java.util.List; import java.util.WeakHashMap; -import java.util.jar.JarEntry; -import java.util.jar.JarFile; import de.lmu.ifi.dbs.elki.logging.Logging; -import de.lmu.ifi.dbs.elki.logging.LoggingUtil; -import de.lmu.ifi.dbs.elki.utilities.optionhandling.parameters.ClassParameter; import de.lmu.ifi.dbs.elki.utilities.pairs.Pair; /** @@ -58,6 +55,11 @@ public class InspectionUtil { private static final Logging LOG = Logging.getLogger(InspectionUtil.class); /** + * Class loader + */ + private static final ClassLoader CLASSLOADER = ClassLoader.getSystemClassLoader(); + + /** * Default package ignores. */ private static final String[] DEFAULT_IGNORES = { @@ -77,6 +79,8 @@ public class InspectionUtil { "spin.", "osxadapter.", "antlr.", "ca.odell.", "com.jgoodies.", "com.michaelbaranov.", "com.mysql.", "gnu.dtools.", "net.sf.ext.", "net.sf.jabref.", "org.antlr.", "org.gjt.", "org.java.plugin.", "org.jempbox.", "org.pdfbox.", "wsi.ra.", // GNU trove "gnu.trove.", + // Java OpenGL + "jogamp.", "com.jogamp.", "javax.media.", "jogl.util." // }; @@ -86,12 +90,17 @@ public class InspectionUtil { */ public static final boolean NONSTATIC_CLASSPATH; + /** + * Factory class postfix. + */ + public static final String FACTORY_POSTFIX = "$Factory"; + // Check for non-jar entries in classpath. static { String[] classpath = System.getProperty("java.class.path").split(System.getProperty("path.separator")); boolean hasnonstatic = false; - for(String path : classpath) { - if(!path.endsWith(".jar")) { + for (String path : classpath) { + if (!path.endsWith(".jar")) { hasnonstatic = true; } } @@ -101,7 +110,7 @@ public class InspectionUtil { /** * Weak hash map for class lookups */ - private static WeakHashMap<Class<?>, List<Class<?>>> CLASS_CACHE = new WeakHashMap<Class<?>, List<Class<?>>>(); + private static WeakHashMap<Class<?>, List<Class<?>>> CLASS_CACHE = new WeakHashMap<>(); /** * (Non-weak) cache for all "frequently scanned" classes. @@ -116,11 +125,11 @@ public class InspectionUtil { * @return Found implementations */ public static List<Class<?>> cachedFindAllImplementations(Class<?> c) { - if(c == null) { + if (c == null) { return Collections.emptyList(); } List<Class<?>> res = CLASS_CACHE.get(c); - if(res == null) { + if (res == null) { res = findAllImplementations(c, false); CLASS_CACHE.put(c, res); } @@ -137,38 +146,33 @@ public class InspectionUtil { * @return List of found classes. */ public static List<Class<?>> findAllImplementations(Class<?> c, boolean everything) { - ArrayList<Class<?>> list = new ArrayList<Class<?>>(); + ArrayList<Class<?>> list = new ArrayList<>(); // Add all from service files (i.e. jars) { Iterator<Class<?>> iter = new ELKIServiceLoader(c); - while(iter.hasNext()) { + while (iter.hasNext()) { list.add(iter.next()); } } - if(!InspectionUtil.NONSTATIC_CLASSPATH) { - if(list.size() == 0) { + if (!InspectionUtil.NONSTATIC_CLASSPATH) { + if (list.size() == 0) { LOG.warning("No implementations for " + c.getName() + " were found using index files."); } - } - else { + } else { // Duplicate checking - THashSet<Class<?>> dupes = new THashSet<Class<?>>(list); - // Scan for additional ones in class path - Iterator<Class<?>> iter; - // If possible, reuse an existing scan result - if(InspectionUtilFrequentlyScanned.class.isAssignableFrom(c)) { - iter = getFrequentScan(); - } - else { - iter = slowScan(c).iterator(); + THashSet<Class<?>> dupes = new THashSet<>(list); + // Build cache on first use: + if (MASTER_CACHE == null) { + MASTER_CACHE = slowScan(); } - while(iter.hasNext()) { + Iterator<Class<?>> iter = MASTER_CACHE.iterator(); + while (iter.hasNext()) { Class<?> cls = iter.next(); // skip abstract / private classes. - if(!everything && (Modifier.isInterface(cls.getModifiers()) || Modifier.isAbstract(cls.getModifiers()) || Modifier.isPrivate(cls.getModifiers()))) { + if (!everything && (Modifier.isInterface(cls.getModifiers()) || Modifier.isAbstract(cls.getModifiers()) || Modifier.isPrivate(cls.getModifiers()))) { continue; } - if(c.isAssignableFrom(cls) && !dupes.contains(cls)) { + if (c.isAssignableFrom(cls) && !dupes.contains(cls)) { list.add(cls); dupes.add(cls); } @@ -178,64 +182,96 @@ public class InspectionUtil { } /** - * Get (or create) the result of a scan for any "frequent scanned" class. + * Find an implementation of the given interface / super class, given a + * relative class name or alias name. * - * @return Scan result + * @param restrictionClass Restriction class + * @param value Class name, relative class name, or nickname. + * @return Class found or {@code null} */ - private static Iterator<Class<?>> getFrequentScan() { - if(MASTER_CACHE == null) { - MASTER_CACHE = slowScan(InspectionUtilFrequentlyScanned.class); + @SuppressWarnings("unchecked") + public static <C> Class<? extends C> findImplementation(Class<? super C> restrictionClass, String value) { + // Try exact class factory first. + try { + return (Class<? extends C>) CLASSLOADER.loadClass(value + FACTORY_POSTFIX); + } catch (ClassNotFoundException e) { + // Ignore, retry + } + try { + return (Class<? extends C>) CLASSLOADER.loadClass(value); + } catch (ClassNotFoundException e) { + // Ignore, retry + } + final String completedName = restrictionClass.getPackage().getName() + "." + value; + // Try factory for guessed name next + try { + return (Class<? extends C>) CLASSLOADER.loadClass(completedName + FACTORY_POSTFIX); + } catch (ClassNotFoundException e) { + // Ignore, retry + } + // Last try: guessed name prefix only + try { + return (Class<? extends C>) CLASSLOADER.loadClass(completedName); + } catch (ClassNotFoundException e) { + // Ignore, retry + } + // Try aliases: + for (Class<?> c : InspectionUtil.cachedFindAllImplementations(restrictionClass)) { + if (c.isAnnotationPresent(Alias.class)) { + Alias aliases = c.getAnnotation(Alias.class); + for (String alias : aliases.value()) { + if (alias.equalsIgnoreCase(value) || alias.equalsIgnoreCase(completedName)) { + return (Class<? extends C>) c; + } + } + } } - return MASTER_CACHE.iterator(); + return null; } /** * Perform a full (slow) scan for classes. * - * @param cond Class to include * @return List with the scan result */ - private static List<Class<?>> slowScan(Class<?> cond) { - ArrayList<Class<?>> res = new ArrayList<Class<?>>(); + private static List<Class<?>> slowScan() { + ArrayList<Class<?>> res = new ArrayList<>(); try { - ClassLoader cl = ClassLoader.getSystemClassLoader(); - Enumeration<URL> cps = cl.getResources(""); - while(cps.hasMoreElements()) { + Enumeration<URL> cps = CLASSLOADER.getResources(""); + while (cps.hasMoreElements()) { URL u = cps.nextElement(); // Scan file sources only. - if("file".equals(u.getProtocol())) { - Iterator<String> it = new DirClassIterator(new File(u.getFile()), DEFAULT_IGNORES); - while(it.hasNext()) { + if ("file".equals(u.getProtocol())) { + File path; + try { + path = new File(u.toURI()); + } catch (URISyntaxException e) { + LOG.exception("Error in classpath: " + u, e); + continue; + } + Iterator<String> it = new DirClassIterator(path, DEFAULT_IGNORES); + while (it.hasNext()) { String classname = it.next(); try { - Class<?> cls = cl.loadClass(classname); + Class<?> cls = CLASSLOADER.loadClass(classname); // skip classes where we can't get a full name. - if(cls.getCanonicalName() == null) { - continue; - } - // Implements the right interface? - if(cond != null && !cond.isAssignableFrom(cls)) { + if (cls.getCanonicalName() == null) { continue; } res.add(cls); - } - catch(ClassNotFoundException e) { + } catch (ClassNotFoundException e) { continue; - } - catch(NoClassDefFoundError e) { + } catch (NoClassDefFoundError e) { continue; - } - catch(Exception e) { + } catch (Exception e) { continue; - } - catch(Error e) { + } catch (Error e) { continue; } } } } - } - catch(IOException e) { + } catch (IOException e) { LOG.exception(e); } Collections.sort(res, new ClassSorter()); @@ -243,86 +279,6 @@ public class InspectionUtil { } /** - * Class to iterate over a Jar file. - * - * Note: this is currently unused, as we now require all jar files to include - * an index in the form of service-style files in META-INF/elki/ - * - * @author Erich Schubert - * - * @apiviz.exclude - */ - static class JarClassIterator implements Iterator<String> { - private Enumeration<JarEntry> jarentries; - - private String ne; - - private String[] ignorepackages; - - /** - * Constructor from Jar file. - * - * @param path Jar file entries to iterate over. - */ - public JarClassIterator(String path, String[] ignorepackages) { - this.ignorepackages = ignorepackages; - try { - JarFile jf = new JarFile(path); - this.jarentries = jf.entries(); - this.ne = findNext(); - } - catch(IOException e) { - LoggingUtil.exception("Error opening jar file: " + path, e); - this.jarentries = null; - this.ne = null; - } - } - - @Override - public boolean hasNext() { - // Do we have a next entry? - return (ne != null); - } - - /** - * Find the next entry, since we need to skip some jar file entries. - * - * @return next entry or null - */ - private String findNext() { - nextfile: while(jarentries.hasMoreElements()) { - JarEntry je = jarentries.nextElement(); - String name = je.getName(); - if(name.endsWith(".class")) { - String classname = name.substring(0, name.length() - ".class".length()).replace('/', '.'); - for(String pkg : ignorepackages) { - if(classname.startsWith(pkg)) { - continue nextfile; - } - } - if(classname.endsWith(ClassParameter.FACTORY_POSTFIX) || !classname.contains("$")) { - return classname.replace('/', '.'); - } - } - } - return null; - } - - @Override - public String next() { - // Return the previously stored entry. - String ret = ne; - ne = findNext(); - return ret; - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - } - - /** * Class to iterate over a directory tree. * * @author Erich Schubert @@ -332,15 +288,15 @@ public class InspectionUtil { static class DirClassIterator implements Iterator<String> { private static final String CLASS_EXT = ".class"; - private static final String FACTORY_FILE_EXT = ClassParameter.FACTORY_POSTFIX + CLASS_EXT; + private static final String FACTORY_FILE_EXT = FACTORY_POSTFIX + CLASS_EXT; private static final int CLASS_EXT_LENGTH = CLASS_EXT.length(); private String prefix; - private ArrayList<String> files = new ArrayList<String>(100); + private ArrayList<String> files = new ArrayList<>(100); - private ArrayList<Pair<File, String>> folders = new ArrayList<Pair<File, String>>(100); + private ArrayList<Pair<File, String>> folders = new ArrayList<>(100); private String[] ignorepackages; @@ -352,16 +308,16 @@ public class InspectionUtil { public DirClassIterator(File path, String[] ignorepackages) { this.ignorepackages = ignorepackages; this.prefix = path.getAbsolutePath(); - if(prefix.charAt(prefix.length() - 1) != File.separatorChar) { + if (prefix.charAt(prefix.length() - 1) != File.separatorChar) { prefix = prefix + File.separatorChar; } - this.folders.add(new Pair<File, String>(path, "")); + this.folders.add(new Pair<>(path, "")); } @Override public boolean hasNext() { - if(files.size() == 0) { + if (files.size() == 0) { findNext(); } return (files.size() > 0); @@ -371,19 +327,19 @@ public class InspectionUtil { * Find the next entry, since we need to skip some directories. */ private void findNext() { - while(folders.size() > 0) { + while (folders.size() > 0) { Pair<File, String> pair = folders.remove(folders.size() - 1); // recurse into directories - if(pair.first.isDirectory()) { - nextfile: for(String localname : pair.first.list()) { + if (pair.first.isDirectory()) { + nextfile: for (String localname : pair.first.list()) { // Ignore unix-hidden files/dirs - if(localname.charAt(0) == '.') { + if (localname.charAt(0) == '.') { continue; } // Classes - if(localname.endsWith(CLASS_EXT)) { - if(localname.indexOf('$') >= 0) { - if(!localname.endsWith(FACTORY_FILE_EXT)) { + if (localname.endsWith(CLASS_EXT)) { + if (localname.indexOf('$') >= 0) { + if (!localname.endsWith(FACTORY_FILE_EXT)) { continue; } } @@ -392,14 +348,14 @@ public class InspectionUtil { } // Recurse into directories File newf = new File(pair.first, localname); - if(newf.isDirectory()) { + if (newf.isDirectory()) { String newpref = pair.second + localname + '.'; - for(String ignore : ignorepackages) { - if(ignore.equals(newpref)) { + for (String ignore : ignorepackages) { + if (ignore.equals(newpref)) { continue nextfile; } } - folders.add(new Pair<File, String>(newf, newpref)); + folders.add(new Pair<>(newf, newpref)); } } } @@ -408,10 +364,10 @@ public class InspectionUtil { @Override public String next() { - if(files.size() == 0) { + if (files.size() == 0) { findNext(); } - if(files.size() > 0) { + if (files.size() > 0) { return files.remove(files.size() - 1); } return null; @@ -433,11 +389,19 @@ public class InspectionUtil { public static class ClassSorter implements Comparator<Class<?>> { @Override public int compare(Class<?> o1, Class<?> o2) { - int pkgcmp = o1.getPackage().getName().compareTo(o2.getPackage().getName()); - if(pkgcmp != 0) { + Package p1 = o1.getPackage(); + Package p2 = o2.getPackage(); + if (p1 == null) { + return -1; + } + if (p2 == null) { + return 1; + } + int pkgcmp = p1.getName().compareTo(p2.getName()); + if (pkgcmp != 0) { return pkgcmp; } return o1.getCanonicalName().compareTo(o2.getCanonicalName()); } } -}
\ No newline at end of file +} |