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

com.elephantdrummer.container.ClassFinder Maven / Gradle / Ivy

The newest version!
package com.elephantdrummer.container;
/*
 * ClassFinder.java
 */

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.TreeMap;
import java.util.Vector;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * This utility class was based originally on Daniel Le Berre's 
 * RTSI class. This class can be called in different modes, but the principal use 
 * is to determine what subclasses/implementations of a given class/interface exist in the current 
 * runtime environment.
 * @author Daniel Le Berre, Elliott Wade
 */
public class ClassFinder
{
    private Class searchClass = null;
    private Map classpathLocations = new HashMap ();
    private Map, URL> results = new HashMap, URL> ();
    private List errors = new ArrayList ();
    private boolean working = false;
    
    public ClassFinder ()
    {
        refreshLocations ();
    }
    
    /**
     * Rescan the classpath, cacheing all possible file locations.
     */
    public final void refreshLocations ()
    {
        synchronized (classpathLocations)
        {
            classpathLocations = getClasspathLocations ();
        }
    }
    
    /**
     * @param fqcn Name of superclass/interface on which to search
     * @return Vector
     */
    public final Vector> findSubclasses (String fqcn)
    {
        synchronized (classpathLocations)
        {
        synchronized (results)
        {
            try
            {
                working = true;
                searchClass = null;
                errors = new ArrayList ();
                results = new TreeMap, URL> (CLASS_COMPARATOR);
                
                //
                // filter malformed FQCN
                //
                if (fqcn.startsWith (".") || fqcn.endsWith ("."))
                {
                    return new Vector> ();
                }
                
                //
                // Determine search class from fqcn
                //
                try
                {
                    searchClass = Class.forName (fqcn);
                }
                catch (ClassNotFoundException ex)
                {
                    // if class not found, let empty vector return...
                    errors.add (ex);
                    return new Vector> ();
                }
                
                return findSubclasses(searchClass, classpathLocations);
            }
            finally
            {
                working = false;
            }
        }
        }
    }
    
    public final List getErrors ()
    {
        return new ArrayList (errors);
    }
    
    /**
     * The result of the last search is cached in this object, along
     * with the URL that corresponds to each class returned. This 
     * method may be called to query the cache for the location at
     * which the given class was found. null will be
     * returned if the given class was not found during the last
     * search, or if the result cache has been cleared.
     * @param cls ff
     * @return URL
     */
    public final URL getLocationOf (Class cls)
    {
        if (results != null) return results.get (cls);
        else return null;
    }
    
    /**
     * Determine every URL location defined by the current classpath, and
     * it's associated package name.
     * @return Map
     */
    public final Map getClasspathLocations ()
    {
        Map map = new TreeMap (URL_COMPARATOR);
        File file = null;
        
        String pathSep = System.getProperty ("path.separator");
        String classpath = System.getProperty ("java.class.path");
        //System.out.println ("classpath=" + classpath);
        
        StringTokenizer st = new StringTokenizer (classpath, pathSep);
        while (st.hasMoreTokens ())
        {
            String path = st.nextToken ();
            file = new File (path);
            include (null, file, map);
        }
        
        Iterator it = map.keySet ().iterator ();
        while (it.hasNext ())
        {
            URL url = it.next ();
            //System.out.println (url + "-->" + map.get (url));
        }
        
        return map;
    }
    
    private final static FileFilter DIRECTORIES_ONLY = new FileFilter ()
    {
    	 @Override
        public boolean accept (File f)
        {
            if (f.exists () && f.isDirectory ()) return true;
            else return false;
        }
    };
    
    private final static Comparator URL_COMPARATOR = new Comparator ()
    {
    	 @Override
        public int compare (URL u1, URL u2)
        {
            return String.valueOf (u1).compareTo (String.valueOf (u2));
        }
    };
    
    private final static Comparator> CLASS_COMPARATOR = new Comparator> ()
    {
    	 @Override
        public int compare (Class c1, Class c2)
        {
            return String.valueOf (c1).compareTo (String.valueOf (c2));
        }
    };
    
    private final void include (String name, File file, Map map)
    {
        if (!file.exists ()) return;
        if (!file.isDirectory ())
        {
            // could be a JAR file
            includeJar (file, map);
            return;
        }
        
        if (name == null)
            name = "";
        else
            name += ".";
        
        // add subpackages
        File [] dirs = file.listFiles (DIRECTORIES_ONLY);
        for (int i=0; i map)
    {
        if (file.isDirectory ()) return;
        
        URL jarURL = null;
        JarFile jar = null;
        try
        {
            jarURL = new URL ("file:/" + file.getCanonicalPath ());
            jarURL = new URL ("jar:" + jarURL.toExternalForm () + "!/");
            JarURLConnection conn = (JarURLConnection) jarURL.openConnection ();
            jar = conn.getJarFile();
        }
        catch (Exception e)
        {
            // not a JAR or disk I/O error
            // either way, just skip
            return;
        }
        
        if (jar == null || jarURL == null) return;
        
        // include the jar's "default" package (i.e. jar's root)
        map.put (jarURL, "");
        
        Enumeration e = jar.entries();
        while (e.hasMoreElements())
        {
            JarEntry entry = e.nextElement ();
            
            if (entry.isDirectory ())
            {
                if (entry.getName ().toUpperCase ().equals ("META-INF/")) continue;
                
                try
                {
                    map.put
                    (
                        new URL (jarURL.toExternalForm () + entry.getName ()), 
                        packageNameFor (entry)
                    );
                }
                catch (MalformedURLException murl)
                {
                    // whacky entry?
                    continue;
                }
            }
        }
    }
    
    private static String packageNameFor (JarEntry entry)
    {
        if (entry == null) return "";
        String s = entry.getName ();
        if (s == null) return "";
        if (s.length () == 0) return s;
        if (s.startsWith ("/")) s = s.substring (1, s.length ());
        if (s.endsWith ("/")) s = s.substring (0, s.length ()-1);
        return s.replace ('/', '.');
    }
    
    private final void includeResourceLocations (String packageName, Map map)
    {
        try
        {
            Enumeration resourceLocations =
                ClassFinder.class.getClassLoader ().getResources (getPackagePath(packageName));
            
            while (resourceLocations.hasMoreElements ())
            {
                map.put
                (
                    resourceLocations.nextElement (), 
                    packageName
                );
            }
        }
        catch (Exception e)
        {
            // well, we tried
            errors.add (e);
            return;
        }
    }
    
    
    
    private final Vector> findSubclasses
    (
        Class superClass, Map locations
    )
    {
        Vector> v = new Vector> ();
        
        Vector> w = null; //new Vector> ();
        
        //Package [] packages = Package.getPackages ();
        //for (int i=0;i it = locations.keySet ().iterator ();
        while (it.hasNext ())
        {
            URL url = it.next ();
            //System.out.println (url + "-->" + locations.get (url));
            
            w = findSubclasses (url, locations.get (url), superClass);
            if (w != null && (w.size () > 0)) v.addAll (w);
        }
        
        return v;
    }

    private final Vector> findSubclasses
    (
        URL location, String packageName, Class superClass
    )
    {
        //System.out.println ("looking in package:" + packageName);
        //System.out.println ("looking for  class:" + superClass);
        
        synchronized (results)
        {
        
        // hash guarantees unique names...
        Map, URL> thisResult = new TreeMap, URL> (CLASS_COMPARATOR);
        Vector> v = new Vector> (); // ...but return a vector
        
        // TODO: double-check for null search class
        String fqcn = searchClass.getName ();
        
        List knownLocations = new ArrayList ();
        knownLocations.add(location);
        // TODO: add getResourceLocations() to this list
        
        // iterate matching package locations...
        for (int loc=0; loc c = Class.forName (packageName + "." + classname);
                            if (superClass.isAssignableFrom (c) &&
                                !fqcn.equals (packageName + "." + classname))
                            {
                                thisResult.put (c, url);
                            }
                        }
                        catch (ClassNotFoundException cnfex)
                        {
                            errors.add (cnfex);
                            //System.err.println(cnfex);
                        }
                        catch (Exception ex)
                        {
                            errors.add (ex);
                            //System.err.println (ex);
                        }
                    }
                }
            }
            else
            {   
                try
                {
                    // It does not work with the filesystem: we must
                    // be in the case of a package contained in a jar file.
                    JarURLConnection conn = (JarURLConnection)url.openConnection();
                    //String starts = conn.getEntryName();
                    JarFile jarFile = conn.getJarFile();
                    
                    //System.out.println ("starts=" + starts);
                    //System.out.println ("JarFile=" + jarFile);
                    
                    Enumeration e = jarFile.entries();
                    while (e.hasMoreElements())
                    {
                        JarEntry entry = e.nextElement();
                        String entryname = entry.getName();
                        
                        //System.out.println ("\tconsidering entry: " + entryname);
                        
                        if (!entry.isDirectory () && entryname.endsWith(".class"))
                        {
                            String classname = entryname.substring(0,entryname.length()-6);
                            if (classname.startsWith("/")) 
                                classname = classname.substring(1);
                            classname = classname.replace('/','.');
                            
                            //System.out.println ("\t\ttesting classname: " + classname);
                            
                            try
                            {
                                // TODO: verify this block
                                Class c = Class.forName (classname);
                                
                                if (superClass.isAssignableFrom (c) &&
                                    !fqcn.equals (classname))
                                {
                                    thisResult.put (c, url);
                                }
                            }catch (ClassNotFoundException cnfex){
                                // that's strange since we're scanning
                                // the same classpath the classloader's
                                // using... oh, well
                               // errors.add (cnfex);
                            }
                            catch (NoClassDefFoundError ncdfe)
                            {
                                // dependency problem... class is
                                // unusable anyway, so just ignore it
                                errors.add (ncdfe);
                            }
                            catch (UnsatisfiedLinkError ule)
                            {
                                // another dependency problem... class is
                                // unusable anyway, so just ignore it
                                errors.add (ule);
                            }
                            catch (Exception exception)
                            {
                                // unexpected problem
                                //System.err.println (ex);
                                errors.add (exception);
                            }
                            catch (Error error)
                            {
                                // lots of things could go wrong
                                // that we'll just ignore since
                                // they're so rare...
                                errors.add (error);
                            }
                        }
                    }
                }
                catch (IOException ioex)
                {
                    //System.err.println(ioex);
                    errors.add (ioex);
                }   
            }
        } // while
        
        //System.out.println ("results = " + thisResult);
        
        results.putAll (thisResult);
        
        Iterator> it = thisResult.keySet ().iterator ();
        while (it.hasNext ())
        {
            v.add (it.next ());
        }
        return v;
        
        } // synch results
    }
    
    private final static String getPackagePath (String packageName)
    {
        // Translate the package name into an "absolute" path
        String path = new String (packageName);
        if (!path.startsWith ("/"))
        {
            path = "/" + path;
        }   
        path = path.replace ('.','/');
        
        // ending with "/" indicates a directory to the classloader
        if (!path.endsWith ("/")) path += "/";
        
        // for actual classloader interface (NOT Class.getResource() which
        //  hacks up the request string!) a resource beginning with a "/"
        //  will never be found!!! (unless it's at the root, maybe?)
        if (path.startsWith ("/")) path = path.substring (1, path.length ());
        
        //System.out.println ("package path=" + path);
        
        return path;
    }
    
    public static void main(String []args)
    {
        //args = new String[] {"rcm.core.Any_String"};
        
        ClassFinder finder = null;
        Vector> v = null;
        List errors = null;
        
        if (args.length==1)
        {
            finder = new ClassFinder ();
            v = finder.findSubclasses (args[0]);
            errors = finder.getErrors ();
        }



    }
 
}
   




© 2015 - 2024 Weber Informatics LLC | Privacy Policy