weka.core.ClassDiscovery Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of weka-stable Show documentation
Show all versions of weka-stable Show documentation
The Waikato Environment for Knowledge Analysis (WEKA), a machine
learning workbench. This is the stable version. Apart from bugfixes, this version
does not receive any other updates.
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/*
* ClassDiscovery.java
* Copyright (C) 2005 University of Waikato, Hamilton, New Zealand
*
*/
package weka.core;
import java.io.File;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.StringTokenizer;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* This class is used for discovering classes that implement a certain
* interface or a derived from a certain class.
*
* @author FracPete (fracpete at waikato dot ac dot nz)
* @version $Revision: 5377 $
* @see StringCompare
*/
public class ClassDiscovery
implements RevisionHandler {
/** whether to output some debug information. */
public final static boolean VERBOSE = false;
/** for caching queries (classname+packagename <-> Vector with classnames). */
protected static Hashtable m_Cache;
/** notify if VERBOSE is still on */
static {
if (VERBOSE)
System.err.println(ClassDiscovery.class.getName() + ": VERBOSE ON");
}
/**
* Checks whether the "otherclass" is a subclass of the given "superclass".
*
* @param superclass the superclass to check against
* @param otherclass this class is checked whether it is a subclass
* of the the superclass
* @return TRUE if "otherclass" is a true subclass
*/
public static boolean isSubclass(String superclass, String otherclass) {
try {
return isSubclass(Class.forName(superclass), Class.forName(otherclass));
}
catch (Exception e) {
return false;
}
}
/**
* Checks whether the "otherclass" is a subclass of the given "superclass".
*
* @param superclass the superclass to check against
* @param otherclass this class is checked whether it is a subclass
* of the the superclass
* @return TRUE if "otherclass" is a true subclass
*/
public static boolean isSubclass(Class superclass, Class otherclass) {
Class currentclass;
boolean result;
result = false;
currentclass = otherclass;
do {
result = currentclass.equals(superclass);
// topmost class reached?
if (currentclass.equals(Object.class))
break;
if (!result)
currentclass = currentclass.getSuperclass();
}
while (!result);
return result;
}
/**
* Checks whether the given class implements the given interface.
*
* @param intf the interface to look for in the given class
* @param cls the class to check for the interface
* @return TRUE if the class contains the interface
*/
public static boolean hasInterface(String intf, String cls) {
try {
return hasInterface(Class.forName(intf), Class.forName(cls));
}
catch (Exception e) {
return false;
}
}
/**
* Checks whether the given class implements the given interface.
*
* @param intf the interface to look for in the given class
* @param cls the class to check for the interface
* @return TRUE if the class contains the interface
*/
public static boolean hasInterface(Class intf, Class cls) {
Class[] intfs;
int i;
boolean result;
Class currentclass;
result = false;
currentclass = cls;
do {
// check all the interfaces, this class implements
intfs = currentclass.getInterfaces();
for (i = 0; i < intfs.length; i++) {
if (intfs[i].equals(intf)) {
result = true;
break;
}
}
// get parent class
if (!result) {
currentclass = currentclass.getSuperclass();
// topmost class reached or no superclass?
if ( (currentclass == null) || (currentclass.equals(Object.class)) )
break;
}
}
while (!result);
return result;
}
/**
* If the given package can be found in this part of the classpath then
* an URL object is returned, otherwise null
.
*
* @param classpathPart the part of the classpath to look for the package
* @param pkgname the package to look for
* @return if found, the url as string, otherwise null
*/
protected static URL getURL(String classpathPart, String pkgname) {
String urlStr;
URL result;
File classpathFile;
File file;
JarFile jarfile;
Enumeration enm;
String pkgnameTmp;
result = null;
urlStr = null;
try {
classpathFile = new File(classpathPart);
// directory or jar?
if (classpathFile.isDirectory()) {
// does the package exist in this directory?
file = new File(classpathPart + pkgname);
if (file.exists())
urlStr = "file:" + classpathPart + pkgname;
}
else {
// is package actually included in jar?
jarfile = new JarFile(classpathPart);
enm = jarfile.entries();
pkgnameTmp = pkgname.substring(1); // remove the leading "/"
while (enm.hasMoreElements()) {
if (enm.nextElement().toString().startsWith(pkgnameTmp)) {
urlStr = "jar:file:" + classpathPart + "!" + pkgname;
break;
}
}
}
}
catch (Exception e) {
// ignore
}
// try to generate URL from url string
if (urlStr != null) {
try {
result = new URL(urlStr);
}
catch (Exception e) {
System.err.println(
"Trying to create URL from '" + urlStr
+ "' generates this exception:\n" + e);
result = null;
}
}
return result;
}
/**
* Checks the given packages for classes that inherited from the given class,
* in case it's a class, or implement this class, in case it's an interface.
*
* @param classname the class/interface to look for
* @param pkgnames the packages to search in
* @return a list with all the found classnames
*/
public static Vector find(String classname, String[] pkgnames) {
Vector result;
Class cls;
result = new Vector();
try {
cls = Class.forName(classname);
result = find(cls, pkgnames);
}
catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* Checks the given package for classes that inherited from the given class,
* in case it's a class, or implement this class, in case it's an interface.
*
* @param classname the class/interface to look for
* @param pkgname the package to search in
* @return a list with all the found classnames
*/
public static Vector find(String classname, String pkgname) {
Vector result;
Class cls;
result = new Vector();
try {
cls = Class.forName(classname);
result = find(cls, pkgname);
}
catch (Exception e) {
e.printStackTrace();
}
return result;
}
/**
* Checks the given packages for classes that inherited from the given class,
* in case it's a class, or implement this class, in case it's an interface.
*
* @param cls the class/interface to look for
* @param pkgnames the packages to search in
* @return a list with all the found classnames
*/
public static Vector find(Class cls, String[] pkgnames) {
Vector result;
int i;
HashSet names;
result = new Vector();
names = new HashSet();
for (i = 0; i < pkgnames.length; i++)
names.addAll(find(cls, pkgnames[i]));
// sort result
result.addAll(names);
Collections.sort(result, new StringCompare());
return result;
}
/**
* Checks the given package for classes that inherited from the given class,
* in case it's a class, or implement this class, in case it's an interface.
*
* @param cls the class/interface to look for
* @param pkgname the package to search in
* @return a list with all the found classnames
*/
public static Vector find(Class cls, String pkgname) {
Vector result;
StringTokenizer tok;
String part;
String pkgpath;
File dir;
File[] files;
URL url;
int i;
Class clsNew;
String classname;
JarFile jar;
JarEntry entry;
Enumeration enm;
// already cached?
result = getCache(cls, pkgname);
if (result == null) {
result = new Vector();
if (VERBOSE)
System.out.println(
"Searching for '" + cls.getName() + "' in '" + pkgname + "':");
// turn package into path
pkgpath = pkgname.replaceAll("\\.", "/");
// check all parts of the classpath, to include additional classes from
// "parallel" directories/jars, not just the first occurence
tok = new StringTokenizer(
System.getProperty("java.class.path"),
System.getProperty("path.separator"));
while (tok.hasMoreTokens()) {
part = tok.nextToken();
if (VERBOSE)
System.out.println("Classpath-part: " + part);
// does package exist in this part of the classpath?
url = getURL(part, "/" + pkgpath);
if (VERBOSE) {
if (url == null)
System.out.println(" " + pkgpath + " NOT FOUND");
else
System.out.println(" " + pkgpath + " FOUND");
}
if (url == null)
continue;
// find classes
dir = new File(part + "/" + pkgpath);
if (dir.exists()) {
files = dir.listFiles();
for (i = 0; i < files.length; i++) {
// only class files
if ( (!files[i].isFile())
|| (!files[i].getName().endsWith(".class")) )
continue;
try {
classname = pkgname + "."
+ files[i].getName().replaceAll(".*/", "")
.replaceAll("\\.class", "");
result.add(classname);
}
catch (Exception e) {
e.printStackTrace();
}
}
}
else {
try {
jar = new JarFile(part);
enm = jar.entries();
while (enm.hasMoreElements()) {
entry = (JarEntry) enm.nextElement();
// only class files
if ( (entry.isDirectory())
|| (!entry.getName().endsWith(".class")) )
continue;
classname = entry.getName().replaceAll("\\.class", "");
// only classes in the particular package
if (!classname.startsWith(pkgpath))
continue;
// no sub-package
if (classname.substring(pkgpath.length() + 1).indexOf("/") > -1)
continue;
result.add(classname.replaceAll("/", "."));
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
// check classes
i = 0;
while (i < result.size()) {
try {
clsNew = Class.forName((String) result.get(i));
// no abstract classes
if (Modifier.isAbstract(clsNew.getModifiers()))
result.remove(i);
// must implement interface
else if ( (cls.isInterface()) && (!hasInterface(cls, clsNew)) )
result.remove(i);
// must be derived from class
else if ( (!cls.isInterface()) && (!isSubclass(cls, clsNew)) )
result.remove(i);
else
i++;
}
catch (Throwable e) {
System.err.println("Checking class: " + result.get(i));
e.printStackTrace();
result.remove(i);
}
}
// sort result
Collections.sort(result, new StringCompare());
// add to cache
addCache(cls, pkgname, result);
}
return result;
}
/**
* adds all the sub-directories recursively to the list.
*
* @param prefix the path prefix
* @param dir the directory to look in for sub-dirs
* @param list the current list of sub-dirs
* @return the new list of sub-dirs
*/
protected static HashSet getSubDirectories(String prefix, File dir, HashSet list) {
File[] files;
int i;
String newPrefix;
// add directory to the list
if (prefix == null)
newPrefix = "";
else if (prefix.length() == 0)
newPrefix = dir.getName();
else
newPrefix = prefix + "." + dir.getName();
if (newPrefix.length() != 0)
list.add(newPrefix);
// search for sub-directories
files = dir.listFiles();
if (files != null) {
for (i = 0; i < files.length; i++) {
if (files[i].isDirectory())
list = getSubDirectories(newPrefix, files[i], list);
}
}
return list;
}
/**
* Lists all packages it can find in the classpath.
*
* @return a list with all the found packages
*/
public static Vector findPackages() {
Vector result;
StringTokenizer tok;
String part;
File file;
JarFile jar;
JarEntry entry;
Enumeration enm;
HashSet set;
result = new Vector();
set = new HashSet();
// check all parts of the classpath, to include additional classes from
// "parallel" directories/jars, not just the first occurence
tok = new StringTokenizer(
System.getProperty("java.class.path"),
System.getProperty("path.separator"));
while (tok.hasMoreTokens()) {
part = tok.nextToken();
if (VERBOSE)
System.out.println("Classpath-part: " + part);
// find classes
file = new File(part);
if (file.isDirectory()) {
set = getSubDirectories(null, file, set);
}
else if (file.exists()) {
try {
jar = new JarFile(part);
enm = jar.entries();
while (enm.hasMoreElements()) {
entry = (JarEntry) enm.nextElement();
// only directories
if (entry.isDirectory())
set.add(entry.getName().replaceAll("/", ".").replaceAll("\\.$", ""));
}
}
catch (Exception e) {
e.printStackTrace();
}
}
}
// sort result
set.remove("META-INF");
result.addAll(set);
Collections.sort(result, new StringCompare());
return result;
}
/**
* initializes the cache for the classnames.
*/
protected static void initCache() {
if (m_Cache == null)
m_Cache = new Hashtable();
}
/**
* adds the list of classnames to the cache.
*
* @param cls the class to cache the classnames for
* @param pkgname the package name the classes were found in
* @param classnames the list of classnames to cache
*/
protected static void addCache(Class cls, String pkgname, Vector classnames) {
initCache();
m_Cache.put(cls.getName() + "-" + pkgname, classnames);
}
/**
* returns the list of classnames associated with this class and package, if
* available, otherwise null.
*
* @param cls the class to get the classnames for
* @param pkgname the package name for the classes
* @return the classnames if found, otherwise null
*/
protected static Vector getCache(Class cls, String pkgname) {
initCache();
return m_Cache.get(cls.getName() + "-" + pkgname);
}
/**
* clears the cache for class/classnames relation.
*/
public static void clearCache() {
initCache();
m_Cache.clear();
}
/**
* Returns the revision string.
*
* @return the revision
*/
public String getRevision() {
return RevisionUtils.extract("$Revision: 5377 $");
}
/**
* Possible calls:
*
* -
* weka.core.ClassDiscovery <packages>
* Prints all the packages in the current classpath
*
* -
* weka.core.ClassDiscovery <classname> <packagename(s)>
* Prints the classes it found.
*
*
*
* @param args the commandline arguments
*/
public static void main(String[] args) {
Vector list;
Vector packages;
int i;
StringTokenizer tok;
if ((args.length == 1) && (args[0].equals("packages"))) {
list = findPackages();
for (i = 0; i < list.size(); i++)
System.out.println(list.get(i));
}
else if (args.length == 2) {
// packages
packages = new Vector();
tok = new StringTokenizer(args[1], ",");
while (tok.hasMoreTokens())
packages.add(tok.nextToken());
// search
list = ClassDiscovery.find(
args[0],
(String[]) packages.toArray(new String[packages.size()]));
// print result, if any
System.out.println(
"Searching for '" + args[0] + "' in '" + args[1] + "':\n"
+ " " + list.size() + " found.");
for (i = 0; i < list.size(); i++)
System.out.println(" " + (i+1) + ". " + list.get(i));
}
else {
System.out.println("\nUsage:");
System.out.println(
ClassDiscovery.class.getName() + " packages");
System.out.println("\tlists all packages in the classpath");
System.out.println(
ClassDiscovery.class.getName() + " ");
System.out.println("\tlists classes derived from/implementing 'classname' that");
System.out.println("\tcan be found in 'packagename(s)' (comma-separated list");
System.out.println();
System.exit(1);
}
}
/**
* compares two strings. The following order is used:
*
* - case insensitive
* - german umlauts (ä , ö etc.) or other non-ASCII letters
* are treated as special chars
* - special chars < numbers < letters
*
*/
public static class StringCompare
implements Comparator, RevisionHandler {
/**
* appends blanks to the string if its shorter than len
.
*
* @param s the string to pad
* @param len the minimum length for the string to have
* @return the padded string
*/
private String fillUp(String s, int len) {
while (s.length() < len)
s += " ";
return s;
}
/**
* returns the group of the character: 0=special char, 1=number, 2=letter.
*
* @param c the character to check
* @return the group
*/
private int charGroup(char c) {
int result;
result = 0;
if ( (c >= 'a') && (c <= 'z') )
result = 2;
else if ( (c >= '0') && (c <= '9') )
result = 1;
return result;
}
/**
* Compares its two arguments for order.
*
* @param o1 the first object
* @param o2 the second object
* @return -1 if o1<o2, 0 if o1=o2 and 1 if o1&;gt;o2
*/
public int compare(Object o1, Object o2) {
String s1;
String s2;
int i;
int result;
int v1;
int v2;
result = 0; // they're equal
// get lower case string
s1 = o1.toString().toLowerCase();
s2 = o2.toString().toLowerCase();
// same length
s1 = fillUp(s1, s2.length());
s2 = fillUp(s2, s1.length());
for (i = 0; i < s1.length(); i++) {
// same char?
if (s1.charAt(i) == s2.charAt(i)) {
result = 0;
}
else {
v1 = charGroup(s1.charAt(i));
v2 = charGroup(s2.charAt(i));
// different type (special, number, letter)?
if (v1 != v2) {
if (v1 < v2)
result = -1;
else
result = 1;
}
else {
if (s1.charAt(i) < s2.charAt(i))
result = -1;
else
result = 1;
}
break;
}
}
return result;
}
/**
* Indicates whether some other object is "equal to" this Comparator.
*
* @param obj the object to compare with this Comparator
* @return true if the object is a StringCompare object as well
*/
public boolean equals(Object obj) {
return (obj instanceof StringCompare);
}
/**
* Returns the revision string.
*
* @return the revision
*/
public String getRevision() {
return RevisionUtils.extract("$Revision: 5377 $");
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy