Java tutorial
/* * The contents of this file are subject to the Sapient Public License * Version 1.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://carbon.sf.net/License.html. * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for * the specific language governing rights and limitations under the License. * * The Original Code is The Carbon Component Framework. * * The Initial Developer of the Original Code is Sapient Corporation * * Copyright (C) 2003 Sapient Corporation. All Rights Reserved. */ import java.io.File; import java.util.Enumeration; import java.util.HashSet; import java.util.Iterator; import java.util.Set; import java.util.StringTokenizer; import java.util.zip.ZipEntry; import java.util.zip.ZipFile; /** * <p>This class implements the capability to search over the current classpath * retrieving classes that implement a certain interface.</p> * * Copyright 2001 Sapient * @since carbon 1.0 * @author Greg Hinkle, June 2001 * @version $Revision: 1.10 $($Author: dvoet $ / $Date: 2003/05/05 21:21:23 $) */ public class ClassFinder { /** * Tracks the count of classes found that match the * provided criteria. */ protected long foundClasses = 0; /** * The super class criteria */ protected Class superClass = null; /** * The required substring path criteria for this searcher */ protected String requiredPathSubstring = null; /** * The set of classes found matching the provided criteria. */ protected Set classes = new HashSet(2000); /** * <p>Instantiates the type of MBeanHarvester that will return all classes * in the entire classpath.</p> */ public ClassFinder() { } /** * <p>Instantiates the type of MBeanHarvester that will return all classes * that are assignable to the supplied class. This would include all * implementations of it, if it is an interface or it and all subclasses * of it if it's a class.</p> * * @param superClass the Class that should be searched for along with * implementations and subclasses */ public ClassFinder(Class superClass) { this.superClass = superClass; } /** * <p>Instantiates the type of MBeanHarvester that will return all classes * that are assignable to the supplied class and are part of the supplied * package. This would include all implementations of it, if it is an * interface or it and all subclasses of it if it's a class. The * supplied <code>requiredPathSubstring must be part of the fully * qualified classname.</p> * * @param superClass the Class that should be searched for along with * implementations and subclasses * @param requiredPathSubstring the String part that must be found in the * classes FQN */ public ClassFinder(Class superClass, String requiredPathSubstring) { this.superClass = superClass; this.requiredPathSubstring = requiredPathSubstring; } /** * <p>Adds a class name to the list of found classes if and only if it meets * the configured requirements.</p> * * @param className the FQN String name of the class to add */ protected void addClassName(String className) { // Only add this class if we're not checking for a particular // substring of the FQN or we find that substring if ((this.requiredPathSubstring == null) || (className.indexOf(this.requiredPathSubstring) >= 0)) { if (this.superClass == null) { this.classes.add(className); } else { try { // TODO: GH - add a way to search other classpaths and the // system classpath. Class theClass = Class.forName(className, false, this.getClass().getClassLoader()); if (this.superClass.isAssignableFrom(theClass)) { this.classes.add(className); } } catch (ClassNotFoundException cnfe) { // Used to catch mis-parsed classnames } catch (Throwable t) { // Used to catch JVM security and linkage errors } } } } /** * <p>Used to retrieve the results <code>Set</code> from this harvester's * search.</p> * * @return Set the set of classes that meet this harvester's requirements */ public Set getClasses() { // 1) tokenize classpath String classpath = System.getProperty("java.class.path"); String pathSeparator = System.getProperty("path.separator"); StringTokenizer st = new StringTokenizer(classpath, pathSeparator); // 2) for each element in the classpath while (st.hasMoreTokens()) { File currentDirectory = new File(st.nextToken()); processFile(currentDirectory.getAbsolutePath(), ""); } return this.classes; } /** * Recursively search through Directories with special checks to recognize * zip and jar files. (Zip and Jar files return true from * <File>.isDirectory()) * @param base the base file path to search * @param current the current recursively searched file path being searched */ private void processFile(String base, String current) { File currentDirectory = new File(base + File.separatorChar + current); // Handle special for archives if (isArchive(currentDirectory.getName())) { try { processZip(new ZipFile(currentDirectory)); } catch (Exception e) { // The directory was not found so the classpath was probably in // error or we don't have rights to it } return; } else { Set directories = new HashSet(); File[] children = currentDirectory.listFiles(); // if no children, return if (children == null || children.length == 0) { return; } // check for classfiles for (int i = 0; i < children.length; i++) { File child = children[i]; if (child.isDirectory()) { directories.add(children[i]); } else { if (child.getName().endsWith(".class")) { String className = getClassName( current + ((current == "") ? "" : File.separator) + child.getName()); addClassName(className); this.foundClasses++; } } } //call process file on each directory. This is an iterative call!! for (Iterator i = directories.iterator(); i.hasNext();) { processFile(base, current + ((current == "") ? "" : File.separator) + ((File) i.next()).getName()); } } } /** * <p>Looks at the name of a file to determine if it is an archive</p> * @param name the name of a file * @return true if a file in the classpath is an archive * such as a Jar or Zip file */ protected boolean isArchive(String name) { if ((name.endsWith(".jar") || (name.endsWith(".zip")))) { return true; } else { return false; } } /** * <p>Returns the Fully Qualified Class name of a class from it's path * @param fileName the full path to a class * @return the FQN of a class */ protected String getClassName(String fileName) { String newName = fileName.replace(File.separatorChar, '.'); // Because zipfiles don't have platform specific seperators newName = newName.replace('/', '.'); return newName.substring(0, fileName.length() - 6); } /** * <P>Iterates through the files in a zip looking for files that may be * classes. This is not recursive as zip's in zip's are not searched by the * classloader either.</p> * * @param file The ZipFile to be searched */ protected void processZip(ZipFile file) { Enumeration files = file.entries(); while (files.hasMoreElements()) { Object tfile = files.nextElement(); ZipEntry child = (ZipEntry) tfile; if (child.getName().endsWith(".class")) { addClassName(getClassName(child.getName())); this.foundClasses++; } } } }