/* * This file is adapted from jstacs by Stefan Posch */ /* * This file is part of Jstacs. * * Jstacs 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. * * Jstacs 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 * Jstacs. If not, see <http://www.gnu.org/licenses/>. * * For more information on Jstacs, visit http://www.jstacs.de */ package de.unihalle.informatik.Alida.helpers; import java.io.File; import java.io.IOException; import java.lang.reflect.Modifier; import java.net.JarURLConnection; import java.net.URL; import java.util.AbstractList; import java.util.Enumeration; import java.util.Iterator; import java.util.LinkedList; import java.util.jar.JarEntry; import java.util.jar.JarFile; /** * Utility-class with static methods to * <ul> * <li>find all sub-classes of a certain class (or interface) within the scope * of the current class-loader</li> * <li>find all sub-classes of a certain class (or interface) within the scope * of the current class-loader that can be instantiated, i.e. that are neither * interfaces nor abstract</li> * <li>filter a set of classes by inheritance from a super-class</li> * <li>obtain the class of an {@link InstanceParameterSet} that can be used to * instantiate a sub-class of {@link InstantiableFromParameterSet}.</li> * <li>obtain a {@link CollectionParameter} using all possible * {@link InstanceParameterSet}s (for classes that are a subclass of a specified * superclass) as elements</li> * </ul> * * The methods may fail to find a certain sub-class, if * <ul> * <li>it was loaded by another {@link ClassLoader} than the caller</li> * <li>it was loaded from a physically other place than the super-class, e.g. * another jar-file. * </ul> * * @author Jan Grau, Jens Keilwagen */ public class SubclassFinder { /** * Returns all sub-classes of <code>T</code> that can be instantiated, i.e. * are neither an interface nor abstract, and that are located in a package * below <code>startPackage</code>. * * @param <T> * The class to obtain the sub-classes for * @param clazz * the {@link Class} object for T * @param startPackage * the package under which to search * @return the {@link Class} objects for the sub-classes * @throws ClassNotFoundException * if one of the classes is present in the file system or jar * but cannot be loaded by the class loader * @throws IOException * is thrown if the classes are searched for in a jar file, but * that file could not be accessed or read */ public static LinkedList<Class> findInstantiableSubclasses( Class clazz, String startPackage ) throws ClassNotFoundException, IOException { LinkedList<Class> list = findSubclasses( clazz, startPackage ); LinkedList<Class> list2 = new LinkedList<Class>(); Iterator<Class> it = list.iterator(); while( it.hasNext() ) { Class c = it.next(); if( !c.isInterface() && !Modifier.isAbstract( c.getModifiers() ) ) { list2.add( c ); } } return list2; } /** * Returns all sub-classes of <code>T</code> including interfaces and * abstract classes that are located in a package below * <code>startPackage</code>. * * @param <T> * The class to obtain the sub-classes for * @param clazz * the {@link Class} object for T * @param startPackage * the package under which to search * @return the {@link Class} objects for the sub-classes * @throws ClassNotFoundException * if one of the classes is present in the file system or jar * but cannot be loaded by the class loader * @throws IOException * is thrown if the classes are searched for in a jar file, but * that file could not be accessed or read */ public static LinkedList<Class> findSubclasses( Class clazz, String startPackage ) throws ClassNotFoundException, IOException { return findSubclasses( startPackage, clazz, startPackage); } public static LinkedList<Class> findSubclasses( String prefix, Class clazz, String startPackage ) throws ClassNotFoundException, IOException { String name = startPackage; if( !name.startsWith( "/" ) ) { name = "/" + name; } name = name.replace( ".", "/" ); URL url = clazz.getResource( name ); LinkedList<Class> list = new LinkedList<Class>(); if( url != null ) { File dir = new File( url.getFile() ); if( dir.exists() ) { File[] files = dir.listFiles(); for( int i = 0; i < files.length; i++ ) { if( files[i].isDirectory() ) { //System.out.println(startPackage+"."+files[i].getName()); list.addAll( findSubclasses( prefix, clazz, startPackage + "." + files[i].getName() ) ); } else if( files[i].isFile() && files[i].getName().endsWith( ".class" ) ) { add( clazz, list, startPackage + "." + files[i].getName().substring( 0, files[i].getName().lastIndexOf( "." ) ) ); } } } else { JarURLConnection con = (JarURLConnection)url.openConnection(); JarFile jar = con.getJarFile(); String starts = con.getEntryName(); Enumeration<JarEntry> en = jar.entries(); while( en.hasMoreElements() ) { JarEntry entry = en.nextElement(); String entryname = entry.getName(); if( entryname.startsWith( starts ) && entryname.endsWith( ".class" ) ) { String classname = entryname.substring( 0, entryname.length() - 6 ); if( classname.startsWith( "/" ) ) { classname.substring( 1 ); } add( clazz, list, classname.replace( "/", "." ) ); } } } } else { name = startPackage; JarFile jar; File dir = new File( name); if( dir.exists() && dir.isDirectory()) { File[] files = dir.listFiles(); for( int i = 0; i < files.length; i++ ) { if( files[i].isDirectory() ) { list.addAll( findSubclasses( prefix, clazz, startPackage + "." + files[i].getName() ) ); } else if( files[i].isFile() && files[i].getName().endsWith( ".class" ) ) { add( clazz, list, files[i].getAbsolutePath().substring( prefix.length()+1, files[i].getAbsolutePath().lastIndexOf( "." ) ).replace("/", ".") ); } } } else if ( (jar = new JarFile(dir)) != null){ Enumeration<JarEntry> en = jar.entries(); while( en.hasMoreElements() ) { JarEntry entry = en.nextElement(); String entryname = entry.getName(); if( entryname.endsWith( ".class" ) ) { String classname = entryname.substring( 0, entryname.length() - 6 ); if( classname.startsWith( "/" ) ) { classname.substring( 1 ); } add( clazz, list, classname.replace( "/", "." ) ); } } } } return list; } @SuppressWarnings( "unchecked" ) private static void add( Class clazz, AbstractList<Class> list, String className ) { try { Class c = Class.forName( className ); if( clazz.isAssignableFrom( c ) ) { list.add( c ); } } catch ( Exception e ) {} catch ( Error e ) {} } }