summaryrefslogtreecommitdiff
path: root/src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java')
-rw-r--r--src/de/lmu/ifi/dbs/elki/utilities/InspectionUtil.java286
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
+}