diff --git a/projects/viztool/src/java/com/samskivert/viztool/enum/ClassEnumerator.java b/projects/viztool/src/java/com/samskivert/viztool/enum/ClassEnumerator.java new file mode 100644 index 00000000..e07fc2dd --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/enum/ClassEnumerator.java @@ -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 getWarnings(). + * + * @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()); + } +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/enum/ComponentEnumerator.java b/projects/viztool/src/java/com/samskivert/viztool/enum/ComponentEnumerator.java new file mode 100644 index 00000000..bd36efcb --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/enum/ComponentEnumerator.java @@ -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"; +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/enum/DirectoryEnumerator.java b/projects/viztool/src/java/com/samskivert/viztool/enum/DirectoryEnumerator.java new file mode 100644 index 00000000..1de63345 --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/enum/DirectoryEnumerator.java @@ -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; +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/enum/EnumerationException.java b/projects/viztool/src/java/com/samskivert/viztool/enum/EnumerationException.java new file mode 100644 index 00000000..d246bb4e --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/enum/EnumerationException.java @@ -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); + } +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/enum/JarFileEnumerator.java b/projects/viztool/src/java/com/samskivert/viztool/enum/JarFileEnumerator.java new file mode 100644 index 00000000..d3545e7e --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/enum/JarFileEnumerator.java @@ -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"; +} diff --git a/projects/viztool/src/java/com/samskivert/viztool/enum/ZipFileEnumerator.java b/projects/viztool/src/java/com/samskivert/viztool/enum/ZipFileEnumerator.java new file mode 100644 index 00000000..761163b9 --- /dev/null +++ b/projects/viztool/src/java/com/samskivert/viztool/enum/ZipFileEnumerator.java @@ -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"; +}