All Downloads are FREE. Search and download functionalities are using the official Maven repository.

weka.core.ClassDiscovery Maven / Gradle / Ivy

/*
 *   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 3 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, see .
 */

/*
 * ClassDiscovery.java
 * Copyright (C) 2005-2012 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.ArrayList;
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: 10203 $
 * @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;

  /** the overall class cache. */
  protected static ClassCache m_ClassCache;

  /** 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;
          }
        }
        jarfile.close();
      }
    } 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;
  }

  /**
   * Find all classes that have the supplied matchText String in their suffix.
   * 
   * @param matchText the text to match
   * @return an array list of matching fully qualified class names.
   */
  public static ArrayList find(String matchText) {
    return m_ClassCache.find(matchText);
  }

  /**
   * 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;
    int i;
    Class clsNew;

    // already cached?
    result = getCache(cls, pkgname);

    if (result == null) {
      if (VERBOSE) {
        System.out.println("Searching for '" + cls.getName() + "' in '"
          + pkgname + "':");
      }

      result = new Vector();
      if (m_ClassCache.getClassnames(pkgname) != null) {
        result.addAll(m_ClassCache.getClassnames(pkgname));
      }

      // check classes
      i = 0;
      while (i < result.size()) {
        try {
          clsNew = Class.forName(result.get(i));

          // no abstract classes
          if (Modifier.isAbstract(clsNew.getModifiers())) {
            m_ClassCache.remove(result.get(i));
            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 (Exception e) {
          System.out.println("Accessing class '" + result.get(i)
            + "' resulted in error:");
          e.printStackTrace();
        }
      }

      // 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;
    Enumeration packages;

    initCache();

    result = new Vector();
    packages = m_ClassCache.packages();
    while (packages.hasMoreElements()) {
      result.add(packages.nextElement());
    }
    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>();
    }
    if (m_ClassCache == null) {
      m_ClassCache = new ClassCache();
    }
  }

  /**
   * 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 queries.
   */
  public static void clearCache() {
    initCache();
    m_Cache.clear();
  }

  /**
   * Calls clearCache() and resets the cache of classes on the classpath (i.e.
   * forces a rescan of the classpath).
   */
  public static void clearClassCache() {
    clearCache();
    // make sure that any new classes are picked up
    m_ClassCache = new ClassCache();
  }

  /**
   * Returns the revision string.
   * 
   * @return the revision
   */
  @Override
  public String getRevision() {
    return RevisionUtils.extract("$Revision: 10203 $");
  }

  /**
   * 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], 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 */ @Override public int compare(String o1, String 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 */ @Override public boolean equals(Object obj) { return (obj instanceof StringCompare); } /** * Returns the revision string. * * @return the revision */ @Override public String getRevision() { return RevisionUtils.extract("$Revision: 10203 $"); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy