001/** 002 * Copyright (c) 2011, The University of Southampton and the individual contributors. 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without modification, 006 * are permitted provided that the following conditions are met: 007 * 008 * * Redistributions of source code must retain the above copyright notice, 009 * this list of conditions and the following disclaimer. 010 * 011 * * Redistributions in binary form must reproduce the above copyright notice, 012 * this list of conditions and the following disclaimer in the documentation 013 * and/or other materials provided with the distribution. 014 * 015 * * Neither the name of the University of Southampton nor the names of its 016 * contributors may be used to endorse or promote products derived from this 017 * software without specific prior written permission. 018 * 019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 020 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 021 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 022 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 023 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 024 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 025 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 026 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 027 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 028 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 029 */ 030package org.openimaj.util.reflection; 031 032import java.io.File; 033import java.io.IOException; 034import java.net.URL; 035import java.util.ArrayList; 036import java.util.Enumeration; 037import java.util.List; 038import java.util.jar.JarEntry; 039import java.util.jar.JarFile; 040 041/** 042 * Utility methods for finding classes. 043 * 044 * @author Jonathon Hare (jsh2@ecs.soton.ac.uk) 045 */ 046public class ClassFinder { 047 /** 048 * Scans all classes accessible from the context class loader which belong 049 * to the given package and sub-packages. 050 * 051 * @param pkg 052 * The base package 053 * @return The classes 054 * @throws IOException 055 */ 056 public static List<Class<?>> findClasses(Package pkg) throws IOException { 057 return findClasses(pkg.getName()); 058 } 059 060 /** 061 * Scans all classes accessible from the context class loader which belong 062 * to the given package and subpackages. 063 * 064 * @param packageName 065 * The base package 066 * @return The classes 067 * @throws IOException 068 */ 069 public static List<Class<?>> findClasses(String packageName) throws IOException { 070 final List<Class<?>> classes = new ArrayList<Class<?>>(); 071 072 final ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); 073 074 final String path = packageName.replace('.', '/'); 075 final Enumeration<URL> resources = classLoader.getResources(path); 076 077 while (resources.hasMoreElements()) { 078 final URL resource = resources.nextElement(); 079 080 if (resource.getProtocol().equals("file")) { 081 classes.addAll(findClassesInDir(new File(resource.getFile()), packageName)); 082 } else if (resource.getProtocol().equals("jar")) { 083 final String rf = resource.getFile(); 084 final File file = new File(rf.substring(5, rf.indexOf("!"))); 085 classes.addAll(findClassesInJar(file, packageName)); 086 } 087 } 088 return classes; 089 } 090 091 /** 092 * Recursive method to find all classes in a given directory and subdirs. 093 * 094 * @param directory 095 * The base directory 096 * @param packageName 097 * The package name for classes found inside the base directory 098 * @return The classes 099 */ 100 public static List<Class<?>> findClassesInDir(File directory, String packageName) { 101 final List<Class<?>> classes = new ArrayList<Class<?>>(); 102 if (!directory.exists()) { 103 return classes; 104 } 105 final File[] files = directory.listFiles(); 106 for (final File file : files) { 107 if (file.isDirectory()) { 108 classes.addAll(findClassesInDir(file, packageName + "." + file.getName())); 109 } else if (file.getName().endsWith(".class")) { 110 try { 111 classes.add(Class.forName(packageName + '.' 112 + file.getName().substring(0, file.getName().length() - 6))); 113 } catch (final ClassNotFoundException e) { 114 // do nothing 115 } 116 } 117 } 118 return classes; 119 } 120 121 /** 122 * Finds all the classes in a given package or its subpackages within a jar 123 * file. 124 * 125 * @param jarFile 126 * The jar file 127 * @param packageName 128 * The package name 129 * @return The classes 130 * @throws IOException 131 */ 132 public static List<Class<?>> findClassesInJar(File jarFile, String packageName) throws IOException { 133 final List<Class<?>> classes = new ArrayList<Class<?>>(); 134 135 JarFile jar = null; 136 try { 137 jar = new JarFile(jarFile); 138 final Enumeration<JarEntry> enu = jar.entries(); 139 140 final String path = packageName.replace(".", "/"); 141 142 while (enu.hasMoreElements()) { 143 final JarEntry je = enu.nextElement(); 144 final String name = je.getName(); 145 146 if (name.startsWith(path) && name.endsWith(".class")) { 147 try { 148 classes.add(Class.forName(name.replace("/", ".").substring(0, name.length() - 6))); 149 } catch (final ClassNotFoundException e) { 150 // do nothing 151 } 152 } 153 } 154 } finally { 155 if (jar != null) { 156 try { 157 jar.close(); 158 } catch (final IOException e) { 159 } 160 } 161 } 162 163 return classes; 164 } 165}