/* * Copyright 2004-2010 Information & Software Engineering Group (188/1) * Institute of Software Technology and Interactive Systems * Vienna University of Technology, Austria * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.ifs.tuwien.ac.at/dm/somtoolbox/license.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package at.tuwien.ifs.somtoolbox.util; import java.io.File; import java.io.IOException; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.regex.Pattern; import org.apache.commons.lang.ArrayUtils; import at.tuwien.ifs.somtoolbox.visualization.BackgroundImageVisualizer; /** * Utility class to find subclasses of a class/interface. * * @author Jakob Frank * @version $Id: SubClassFinder.java 3658 2010-07-14 13:23:51Z frank $ * @see #findSubclassesOf(Class, boolean) */ public class SubClassFinder { /** * For testing: Searches and prints all subclasses of {@link BackgroundImageVisualizer}. * * @param args Superclasses */ public static void main(String[] args) { if (args.length == 0) { List<Class<? extends BackgroundImageVisualizer>> viss = findSubclassesOf(BackgroundImageVisualizer.class); System.out.println("Available Visualisations:"); for (Class<? extends BackgroundImageVisualizer> vis : viss) { if (!(Modifier.isAbstract(vis.getModifiers()) && Modifier.isInterface(vis.getModifiers()))) { System.out.println(" " + vis.getSimpleName()); } } } else { for (String cls : args) { try { @SuppressWarnings("unchecked") Class<Object> c = (Class<Object>) Class.forName(cls); List<Class<? extends Object>> viss = findSubclassesOf(c); System.out.println("Subclasses of " + cls); for (Class<?> vis : viss) { if (!(Modifier.isAbstract(vis.getModifiers()) && Modifier.isInterface(vis.getModifiers()))) { System.out.println("\t" + vis.getName()); } } System.out.println(); } catch (ClassNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } /** * Find all subclasses of the given <code>superClass</code>. Searches through the complete classpath. * * @param <E> superClass class * @param superClass the class * @return ArrayList containing all found subclasses. */ public static <E> ArrayList<Class<? extends E>> findSubclassesOf(Class<E> superClass) { return findSubclassesOf(superClass, false); } /** * Find all subclasses of the given <code>superClass</code>. Searches through the complete classpath. * * @param <E> superClass class * @param superClass the class (e.g. <code>Object.class</code>) * @param onlyDirect it <code>true</code> only <b>direct</b> subclasses are found, i.e. classes that directly * extend/implement <code>superClass</code> * @return ArrayList containing all found subclasses. * @see #checkClass(Class, Class, List, boolean) */ public static <E> ArrayList<Class<? extends E>> findSubclassesOf(Class<E> superClass, boolean onlyDirect) { ArrayList<Class<? extends E>> result = new ArrayList<Class<? extends E>>(); String[] classPathEntries = System.getProperty("java.class.path").split(System.getProperty("path.separator")); Pattern p = Pattern.compile("somtoolbox", Pattern.CASE_INSENSITIVE); for (String classPathEntry : classPathEntries) { File f = new File(classPathEntry); if (f.isDirectory()) { searchDir(f, superClass, result, onlyDirect); } else if (f.getName().endsWith(".jar")) { /* * FIXME: hard-coded classpath! For the future we should consider a "plugin" directory to search through, and a "lib" directory with * jars to ignore here... */ if (p.matcher(f.getName()).find()) { searchJarFile(f, superClass, result, onlyDirect); } } } return result; } /** * Recursivly search through the given directory for subclasses of <code>dir</code>. * * @param <E> superClass class * @param dir the Directory to search through. * @param superClass the class to search subclasses of. * @param resultList List where to add found subclasses to. * @param onlyDirect only consider direct subclasses. * @see #checkClass(Class, Class, List, boolean) */ private static <E> void searchDir(File dir, Class<E> superClass, List<Class<? extends E>> resultList, boolean onlyDirect) { searchDir(dir, superClass, resultList, "", onlyDirect); } /** * Recursivly search through the given directory for subclasses of <code>dir</code>. * * @param <E> superClass class * @param dir the Directory to search through. * @param superClass the class to search subclasses of. * @param resultList List where to add found subclasses to. * @param relPath relative path within dir to search from, needed for recursion. * @param onlyDirect only consider direct subclasses. * @see #searchDir(File, Class, List, boolean) */ private static <E> void searchDir(File dir, Class<E> superClass, List<Class<? extends E>> resultList, String relPath, boolean onlyDirect) { // System.out.println(f.getPath() + " -- " + relPath); File current = new File(dir, relPath); File[] entries = current.listFiles(); for (File entrie : entries) { if (entrie.isDirectory()) { searchDir(dir, superClass, resultList, relPath + entrie.getName() + "/", onlyDirect); } else if (entrie.getName().endsWith(".class")) { String cname = entrie.getName(); String e = relPath + cname.substring(0, cname.length() - 6); String fqClassName = e.replace('/', '.'); // System.out.println(" " + fqClassName); checkClass(fqClassName, superClass, resultList, onlyDirect); } } } /** * Check if the class <code>fqClassName</code> is a subclass of <code>c</code>. * * @param <E> superClass class * @param fqClassName the fully qualified class name to check * @param c the superClass * @param result ArrayList where to add <code>fqClassName</code> if it is a subclass * @param onlyDirect if <code>true</code> only consider direct subclasses, i.e. classes that directly * extend/implement <code>superClass</code> * @see #checkClass(Class, Class, List, boolean) */ private static <E> void checkClass(String fqClassName, Class<E> c, List<Class<? extends E>> result, boolean onlyDirect) { try { // if (fqClassName.contains("$")) return; Class<?> d = Class.forName(fqClassName, false, c.getClassLoader()); // Check if d is a subclass of / is implementing c checkClass(d, c, result, onlyDirect); } catch (ClassNotFoundException e1) { } catch (NoClassDefFoundError e2) { } } /** * Check if the class <code>fqClassName</code> is a subclass of <code>c</code>. * * @param <E> superClass class * @param subClass the class to check * @param superClass the superClass * @param resultList ArrayList where to add <code>fqClassName</code> if it is a subclass * @param onlyDirect if <code>true</code> only consider direct subclasses, i.e. classes that directly * extend/implement <code>superClass</code> * @see #checkClass(Class, Class, List, boolean) */ private static <E> void checkClass(Class<?> subClass, Class<E> superClass, List<Class<? extends E>> resultList, boolean onlyDirect) { if (superClass.isAssignableFrom(subClass)) { // System.err.println(fqClassName); @SuppressWarnings("unchecked") Class<? extends E> d2 = (Class<? extends E>) subClass; if (onlyDirect) { // Add only if d2 is a direct subclass of c if (ArrayUtils.contains(d2.getInterfaces(), superClass)) { resultList.add(d2); } } else { resultList.add(d2); } } } /** * Search through the jar-file <code>jarFile</code> for subclasses of <code>superClass</code>. * * @param <E> superClass class * @param jarFile the jar-file to search * @param superClass the superClass * @param resultList ArrayList where to add subClasses of <code>superClass</code> * @param onlyDirect if <code>true</code> only consider direct subclasses, i.e. classes that directly * extend/implement <code>superClass</code> * @see #checkClass(Class, Class, List, boolean) */ private static <E> void searchJarFile(File jarFile, Class<E> superClass, List<Class<? extends E>> resultList, boolean onlyDirect) { try { JarFile jFile = new JarFile(jarFile); Enumeration<JarEntry> entries = jFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (entry.toString().endsWith(".class")) { String fname = entry.toString(); String cpath = fname.substring(0, fname.length() - 6); String fqClassName = cpath.replace('/', '.'); checkClass(fqClassName, superClass, resultList, onlyDirect); } } jFile.close(); } catch (IOException e) { } } }