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 | 216 |
1 files changed, 115 insertions, 101 deletions
diff --git a/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java b/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java index 29745335..856e5d3d 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) 2013 + Copyright (C) 2014 Ludwig-Maximilians-Universität München Lehr- und Forschungseinheit für Datenbanksysteme ELKI Development Team @@ -30,6 +30,7 @@ import java.io.IOException; import java.lang.reflect.Modifier; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLClassLoader; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -55,7 +56,7 @@ public class InspectionUtil { /** * Class loader */ - private static final ClassLoader CLASSLOADER = ClassLoader.getSystemClassLoader(); + private static final URLClassLoader CLASSLOADER = (URLClassLoader) ClassLoader.getSystemClassLoader(); /** * Default package ignores. @@ -83,28 +84,10 @@ public class InspectionUtil { }; /** - * If we have a non-static classpath, we do more extensive scanning for user - * extensions. - */ - 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")) { - hasnonstatic = true; - } - } - NONSTATIC_CLASSPATH = hasnonstatic; - } - /** * Weak hash map for class lookups */ @@ -123,12 +106,12 @@ 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) { - res = findAllImplementations(c, false); + if(res == null) { + res = findAllImplementations(c, false, true); CLASS_CACHE.put(c, res); } return res; @@ -141,42 +124,61 @@ public class InspectionUtil { * * @param c Class restriction * @param everything include interfaces, abstract and private classes + * @param parameterizable only return classes instantiable by the + * parameterizable API * @return List of found classes. */ - public static List<Class<?>> findAllImplementations(Class<?> c, boolean everything) { - ArrayList<Class<?>> list = new ArrayList<>(); + public static List<Class<?>> findAllImplementations(Class<?> c, boolean everything, boolean parameterizable) { + // For removing duplicates: + THashSet<Class<?>> dupes = new THashSet<>(); + ArrayList<Class<?>> known = new ArrayList<>(); // Add all from service files (i.e. jars) { Iterator<Class<?>> iter = new ELKIServiceLoader(c); - while (iter.hasNext()) { - list.add(iter.next()); + while(iter.hasNext()) { + known.add(iter.next()); } + dupes.addAll(known); + } + // Build cache on first use: + if(MASTER_CACHE == null) { + MASTER_CACHE = slowScan(); } - if (!InspectionUtil.NONSTATIC_CLASSPATH) { - if (list.size() == 0) { - LOG.warning("No implementations for " + c.getName() + " were found using index files."); + Iterator<Class<?>> iter = MASTER_CACHE.iterator(); + while(iter.hasNext()) { + Class<?> cls = iter.next(); + if(dupes.contains(cls)) { + continue; } - } else { - // Duplicate checking - THashSet<Class<?>> dupes = new THashSet<>(list); - // Build cache on first use: - if (MASTER_CACHE == null) { - MASTER_CACHE = slowScan(); + // skip abstract / private classes. + if(!everything && (Modifier.isInterface(cls.getModifiers()) || Modifier.isAbstract(cls.getModifiers()) || Modifier.isPrivate(cls.getModifiers()))) { + continue; } - 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()))) { - continue; + if(!c.isAssignableFrom(cls)) { + continue; + } + if(parameterizable) { + boolean instantiable = false; + try { + instantiable = cls.getConstructor() != null; } - if (c.isAssignableFrom(cls) && !dupes.contains(cls)) { - list.add(cls); - dupes.add(cls); + catch(Exception | Error e) { + // ignore + } + try { + instantiable = instantiable || ClassGenericsUtil.getParameterizer(cls) != null; + } + catch(Exception | Error e) { + // ignore + } + if(!instantiable) { + continue; } } + known.add(cls); + dupes.add(cls); } - return list; + return known; } /** @@ -192,33 +194,37 @@ public class InspectionUtil { // Try exact class factory first. try { return (Class<? extends C>) CLASSLOADER.loadClass(value + FACTORY_POSTFIX); - } catch (ClassNotFoundException e) { + } + catch(ClassNotFoundException e) { // Ignore, retry } try { return (Class<? extends C>) CLASSLOADER.loadClass(value); - } catch (ClassNotFoundException e) { + } + 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) { + } + catch(ClassNotFoundException e) { // Ignore, retry } // Last try: guessed name prefix only try { return (Class<? extends C>) CLASSLOADER.loadClass(completedName); - } catch (ClassNotFoundException e) { + } + catch(ClassNotFoundException e) { // Ignore, retry } // Try aliases: - for (Class<?> c : InspectionUtil.cachedFindAllImplementations(restrictionClass)) { - if (c.isAnnotationPresent(Alias.class)) { + 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)) { + for(String alias : aliases.value()) { + if(alias.equalsIgnoreCase(value) || alias.equalsIgnoreCase(completedName)) { return (Class<? extends C>) c; } } @@ -234,43 +240,51 @@ public class InspectionUtil { */ private static List<Class<?>> slowScan() { ArrayList<Class<?>> res = new ArrayList<>(); + Enumeration<URL> cps; try { - Enumeration<URL> cps = CLASSLOADER.getResources(""); - while (cps.hasMoreElements()) { - URL u = cps.nextElement(); - // Scan file sources only. - if ("file".equals(u.getProtocol())) { - File path; + cps = CLASSLOADER.getResources(""); + } + catch(IOException e) { + de.lmu.ifi.dbs.elki.logging.LoggingUtil.exception(e); + return res; + } + while(cps.hasMoreElements()) { + URL u = cps.nextElement(); + // Scan file sources only. + 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 { - 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 = CLASSLOADER.loadClass(classname); - // skip classes where we can't get a full name. - if (cls.getCanonicalName() == null) { - continue; - } - res.add(cls); - } catch (ClassNotFoundException e) { - continue; - } catch (NoClassDefFoundError e) { - continue; - } catch (Exception e) { - continue; - } catch (Error e) { + Class<?> cls = CLASSLOADER.loadClass(classname); + // skip classes where we can't get a full name. + if(cls.getCanonicalName() == null) { continue; } + res.add(cls); + } + catch(ClassNotFoundException e) { + continue; + } + catch(NoClassDefFoundError e) { + continue; + } + catch(Exception e) { + continue; + } + catch(Error e) { + continue; } } } - } catch (IOException e) { - LOG.exception(e); } Collections.sort(res, new ClassSorter()); return res; @@ -306,7 +320,7 @@ 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; } @@ -315,7 +329,7 @@ public class InspectionUtil { @Override public boolean hasNext() { - if (files.size() == 0) { + if(files.size() == 0) { findNext(); } return (files.size() > 0); @@ -325,19 +339,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; } } @@ -346,10 +360,10 @@ 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; } } @@ -362,10 +376,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; @@ -389,14 +403,14 @@ public class InspectionUtil { public int compare(Class<?> o1, Class<?> o2) { Package p1 = o1.getPackage(); Package p2 = o2.getPackage(); - if (p1 == null) { + if(p1 == null) { return -1; } - if (p2 == null) { + if(p2 == null) { return 1; } int pkgcmp = p1.getName().compareTo(p2.getName()); - if (pkgcmp != 0) { + if(pkgcmp != 0) { return pkgcmp; } return o1.getCanonicalName().compareTo(o2.getCanonicalName()); |