Created facilities for enumerating all of the classes available via the

classpath.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@158 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2001-06-14 20:57:15 +00:00
parent e31f0f2706
commit f273548b58
6 changed files with 570 additions and 0 deletions
@@ -0,0 +1,181 @@
//
// $Id: ClassEnumerator.java,v 1.1 2001/06/14 20:57:15 mdb Exp $
package com.samskivert.viztool.enum;
import java.util.ArrayList;
import java.util.StringTokenizer;
/**
* The class enumerator is supplied with a classpath which it decomposes
* and enumerates over all of the classes available via those classpath
* componenets. To do this it uses component enumerators which know how to
* enumerate all of the classes in a directory tree, in a jar file, etc.
* The component enumerators are structured so that new enumerators can be
* authored for new kinds of classpath component.
*/
public class ClassEnumerator
{
/**
* Constructs a class enumerator with the supplied classpath. A set of
* component enumerators will be chosen for each element and warnings
* will be generated for components that cannot be processed for some
* reason or other. Those will be available following the completion
* of the constructor via <code>getWarnings()</code>.
*
* @see #getWarnings
*/
public ClassEnumerator (String classpath)
{
// decompose the path and select enumerators for each component
StringTokenizer tok = new StringTokenizer(classpath, ":");
ArrayList warnings = new ArrayList();
while (tok.hasMoreTokens()) {
String component = tok.nextToken();
// locate an enumerator for this token
ComponentEnumerator enum = matchEnumerator(component);
if (enum == null) {
String wmsg = "Unable to match enumerator for " +
"component '" + component + "'.";
warnings.add(new Warning(wmsg));
} else {
try {
System.out.println("Adding [enum=" + enum +
", component=" + component + "].");
// construct an enumerator to enumerate this component
// and put it on our list
_enums.add(enum.enumerate(component));
} catch (EnumerationException ee) {
// if there was a problem creating an enumerator for
// said component, create a warning to that effect
warnings.add(new Warning(ee.getMessage()));
}
}
}
// convert the warnings into an array
_warnings = new Warning[warnings.size()];
warnings.toArray(_warnings);
// scan to the first class
scanToNextClass();
}
/**
* Locates an enumerator that matches the specified component and
* returns the prototype instance of that enumerator. Returns null if
* no enumerator could be matched.
*/
protected ComponentEnumerator matchEnumerator (String component)
{
for (int i = 0; i < _enumerators.size(); i++) {
ComponentEnumerator enum =
(ComponentEnumerator)_enumerators.get(i);
if (enum.matchesComponent(component)) {
return enum;
}
}
return null;
}
/**
* Returns the warnings generated in parsing the classpath and
* constructing enumerators for each component. For example, if a
* classpath component specified a directory that was non-existent or
* inaccessible, a warning would be generated for that component. If
* no warnings were generated, a zero length array will be returned.
*/
public Warning[] getWarnings ()
{
return _warnings;
}
public boolean hasMoreClasses ()
{
return (_nextClass != null);
}
public String nextClass ()
{
String clazz = _nextClass;
_nextClass = null;
scanToNextClass();
return clazz;
}
/**
* Queues up the next enumerator in the list or clears out our
* enumerator reference if we have no remaining enumerators.
*/
protected void scanToNextClass ()
{
if (_enums.size() > 0) {
// grab the first enumerator in the list
ComponentEnumerator enum = (ComponentEnumerator)_enums.get(0);
// if it has more classes
if (enum.hasMoreClasses()) {
// get the next one
_nextClass = enum.nextClass();
return;
} else {
// otherwise remove it from the list and try the next enum
_enums.remove(0);
scanToNextClass();
}
}
}
protected ArrayList _enums = new ArrayList();
protected String _nextClass;
protected Warning[] _warnings;
/**
* A warning is generated when a component of the classpath cannot be
* processed for some reason or other.
*/
public static class Warning
{
public String reason;
public Warning (String reason)
{
this.reason = reason;
}
}
public static void main (String[] args)
{
// run ourselves on the classpath
String classpath = System.getProperty("java.class.path");
ClassEnumerator enum = new ClassEnumerator(classpath);
// print out the warnings
Warning[] warnings = enum.getWarnings();
for (int i = 0; i < warnings.length; i++) {
System.out.println("Warning: " + warnings[i].reason);
}
// enumerate over whatever classes we can
while (enum.hasMoreClasses()) {
System.out.println("Class: " + enum.nextClass());
}
}
protected static ArrayList _enumerators = new ArrayList();
static {
// register our enumerators
_enumerators.add(new ZipFileEnumerator());
_enumerators.add(new JarFileEnumerator());
// the directory enumerator should always be last in the list
// because it picks up all stragglers and tries enumerating them
// as if they were directories
_enumerators.add(new DirectoryEnumerator());
}
}
@@ -0,0 +1,63 @@
//
// $Id: ComponentEnumerator.java,v 1.1 2001/06/14 20:57:15 mdb Exp $
package com.samskivert.viztool.enum;
import com.samskivert.util.StringUtil;
/**
* A component enumerator knows how to enumerate all of the classes in a
* particular classpath component. Examples include a zip file enumerator,
* a directory tree enumerator and a jar file enumerator.
*/
public abstract class ComponentEnumerator
{
/**
* To determine which component enumerator should be used for a given
* classpath component, one instance of each is maintained and used
* for the matching process.
*
* @return true if this enumerator should be used to enumerate the
* specified classpath component; false otherwise.
*/
public abstract boolean matchesComponent (String component);
/**
* Instantiates an instance of the underlying enumerator and
* configures it to enumerate the specified classpath component.
*
* @exception EnumerationException thrown if some problem (like file
* or directory not existing or being inaccessible) prevents the
* enumerator from enumerating the component.
*/
public abstract ComponentEnumerator enumerate (String component)
throws EnumerationException;
/**
* Returns true if there are more classes yet to be enumerated for
* this component.
*/
public abstract boolean hasMoreClasses ();
/**
* Returns the next class in this component's enumeration.
*/
public abstract String nextClass ();
/**
* Converts a classfile path to a class name (eg. foo/bar/Baz.class
* converts to foo.bar.Baz).
*/
protected String pathToClassName (String path)
{
// strip off the .class suffix
path = path.substring(0, path.length() - CLASS_SUFFIX.length());
// convert slashes to dots
return StringUtil.replace(path, "/", ".");
}
/**
* This is used to identify class files.
*/
protected static final String CLASS_SUFFIX = ".class";
}
@@ -0,0 +1,164 @@
//
// $Id: DirectoryEnumerator.java,v 1.1 2001/06/14 20:57:15 mdb Exp $
package com.samskivert.viztool.enum;
import java.io.*;
import java.util.ArrayList;
import com.samskivert.util.StringUtil;
import com.samskivert.viztool.Log;
/**
* The directory enumerator enumerates all of the classes in a directory
* hierarchy.
*/
public class DirectoryEnumerator extends ComponentEnumerator
{
/**
* Constructs a prototype enumerator that can be used for matching.
*/
public DirectoryEnumerator ()
{
}
/**
* Constructs a directory enumerator with the specified root directory
* for enumeration.
*/
public DirectoryEnumerator (String dirpath)
throws EnumerationException
{
_root = new File(dirpath);
_rootpath = _root.getAbsolutePath();
// make sure the specified component exists
if (!_root.exists()) {
String msg = "Can't enumerate '" + dirpath +
"': directory doesn't exist";
throw new EnumerationException(msg);
}
// make sure the specified component is a directory
if (!_root.isDirectory()) {
String msg = "Can't enumerate non-directory '" + dirpath + "'.";
throw new EnumerationException(msg);
}
// create a directory record for our root directory
addDirectory(_root);
// and scan to the first class
scanToNextClass();
}
// documentation inherited from interface
public boolean matchesComponent (String component)
{
// the directory enumerator picks up anything that falls through
// the zip or jar file enumerators
return true;
}
// documentation inherited from interface
public ComponentEnumerator enumerate (String component)
throws EnumerationException
{
return new DirectoryEnumerator(component);
}
// documentation inherited from interface
public boolean hasMoreClasses ()
{
return (_nextClass != null);
}
// documentation inherited from interface
public String nextClass ()
{
String clazz = _nextClass;
_nextClass = null;
scanToNextClass();
return clazz;
}
protected void scanToNextClass ()
{
// if we have no drecords, we have nothing to do
while (_drecords.size() > 0) {
// grab a reference to the last drecord in the list
DirRecord rec = (DirRecord)_drecords.get(_drecords.size()-1);
// grab the file related to our current position
File target = rec.kids[rec.kidpos];
// bump up the kidpos so that things are in place for the next
// iteration; if we just grabbed the last kid, pop this record
// off of the stack
if (++rec.kidpos >= rec.kids.length) {
_drecords.remove(_drecords.size()-1);
}
// if our target file is a directory, push another drecord
// onto the stack and recurse into it
if (target.isDirectory()) {
addDirectory(target);
continue;
}
// otherwise, process it as a file
String path = target.getAbsolutePath();
// make sure it's readable
if (!target.canRead()) {
Log.warning("Can't read file '" + path + "'.");
continue;
}
// check to see if it matches our filename pattern
if (path.endsWith(CLASS_SUFFIX)) {
// strip off the root path (plus one for the trailing slash)
path = path.substring(_rootpath.length()+1);
_nextClass = pathToClassName(path);
return;
}
// if we didn't match, we loop back through and process the
// next kid (potentially popping back up the directory record
// stack in the process)
}
}
protected void addDirectory (File dir)
{
DirRecord rec = new DirRecord(dir);
if (rec.kids == null) {
String path = dir.getAbsolutePath();
// complain if there was an error reading the directory
Log.warning("Unable to scan directory '" + path + "'.");
} else if (rec.kids.length > 0) {
// only add the record if the directory actually has some
// children that we can scan
_drecords.add(rec);
}
}
protected static class DirRecord
{
public File directory;
public File[] kids;
public int kidpos;
public DirRecord (File directory)
{
this.directory = directory;
kids = directory.listFiles();
}
}
protected File _root;
protected String _rootpath;
protected ArrayList _drecords = new ArrayList();
protected String _nextClass;
}
@@ -0,0 +1,18 @@
//
// $Id: EnumerationException.java,v 1.1 2001/06/14 20:57:15 mdb Exp $
package com.samskivert.viztool.enum;
/**
* An enumeration exception is thrown when some problem occurs while
* attempting to enumerate over a classpath component. This may be when
* initially attempting to read a zip or jar file, or during the process
* of enumeration.
*/
public class EnumerationException extends Exception
{
public EnumerationException (String message)
{
super(message);
}
}
@@ -0,0 +1,36 @@
//
// $Id: JarFileEnumerator.java,v 1.1 2001/06/14 20:57:15 mdb Exp $
package com.samskivert.viztool.enum;
/**
* The jar file enumerator enumerates all of the classes in a .jar class
* archive.
*/
public class JarFileEnumerator extends ZipFileEnumerator
{
/**
* Constructs a prototype enumerator that can be used for matching.
*/
public JarFileEnumerator ()
{
}
/**
* Constructs a jar file enumerator with the specified jar file for
* enumeration.
*/
public JarFileEnumerator (String jarpath)
throws EnumerationException
{
super(jarpath);
}
// documentation inherited from interface
public boolean matchesComponent (String component)
{
return component.endsWith(JAR_SUFFIX);
}
protected static final String JAR_SUFFIX = ".jar";
}
@@ -0,0 +1,108 @@
//
// $Id: ZipFileEnumerator.java,v 1.1 2001/06/14 20:57:15 mdb Exp $
package com.samskivert.viztool.enum;
import java.io.IOException;
import java.util.Enumeration;
import java.util.zip.*;
import com.samskivert.viztool.Log;
/**
* The zip file enumerator enumerates all of the classes in a .zip class
* archive.
*/
public class ZipFileEnumerator extends ComponentEnumerator
{
/**
* Constructs a prototype enumerator that can be used for matching.
*/
public ZipFileEnumerator ()
{
}
/**
* Constructs a zip file enumerator with the specified zip file for
* enumeration.
*/
public ZipFileEnumerator (String zippath)
throws EnumerationException
{
try {
_zipfile = new ZipFile(zippath);
_entenum = _zipfile.entries();
scanToNextClass();
} catch (IOException ioe) {
String msg = "Can't enumerate zip file '" + zippath + "': " +
ioe.getMessage();
throw new EnumerationException(msg);
}
}
// documentation inherited from interface
public boolean matchesComponent (String component)
{
return component.endsWith(ZIP_SUFFIX);
}
// documentation inherited from interface
public ComponentEnumerator enumerate (String component)
throws EnumerationException
{
return new ZipFileEnumerator(component);
}
// documentation inherited from interface
public boolean hasMoreClasses ()
{
return (_nextClass != null);
}
// documentation inherited from interface
public String nextClass ()
{
String clazz = _nextClass;
_nextClass = null;
scanToNextClass();
return clazz;
}
protected void scanToNextClass ()
{
// if we've already scanned to the end of our zipfile, we can bail
// immediately
if (_zipfile == null) {
return;
}
// otherwise scan through the zip contents for the next thing that
// looks like a class
while (_entenum.hasMoreElements()) {
ZipEntry entry = (ZipEntry)_entenum.nextElement();
String nextClass = entry.getName();
if (nextClass.endsWith(CLASS_SUFFIX)) {
_nextClass = pathToClassName(nextClass);
break;
}
}
// if we've reached the end of the zip file, we want to close
// things up
if (_zipfile != null && _nextClass == null) {
try {
_zipfile.close();
} catch (IOException ioe) {
Log.warning("Error closing archive: " + ioe.getMessage());
}
_zipfile = null;
}
}
protected ZipFile _zipfile;
protected Enumeration _entenum;
protected String _nextClass;
protected static final String ZIP_SUFFIX = ".zip";
}