From 792642344dd1e5f6135f2b96020b1dd2cfe47bcf Mon Sep 17 00:00:00 2001 From: Conor MacNeill Date: Fri, 15 Mar 2002 14:15:53 +0000 Subject: [PATCH] Refactor dependency analysis. Add a DependencyAnalyzer interface Currently supports BCEL based analyzers Refactor ejbjar not to require BCEL to run. More to come ... git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271856 13f79535-47bb-0310-9956-ffa450edef68 --- build.xml | 10 +- .../ant/taskdefs/optional/ejb/EjbJar.java | 22 +- .../optional/ejb/GenericDeploymentTool.java | 217 ++++++------ .../types/optional/depend/ClassfileSet.java | 69 +++- .../types/optional/depend/DependScanner.java | 191 ++++++----- .../ant/util/depend/AbstractAnalyzer.java | 324 ++++++++++++++++++ .../tools/ant/util/depend/Dependencies.java | 272 --------------- .../ant/util/depend/DependencyAnalyzer.java | 170 +++++++++ .../apache/tools/ant/util/depend/Filter.java | 60 ---- .../util/depend/bcel/AncestorAnalyzer.java | 160 +++++++++ .../util/depend/bcel/DependencyVisitor.java | 182 ++++++++++ .../ant/util/depend/bcel/FullAnalyzer.java | 156 +++++++++ 12 files changed, 1282 insertions(+), 551 deletions(-) create mode 100644 src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java delete mode 100644 src/main/org/apache/tools/ant/util/depend/Dependencies.java create mode 100644 src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java delete mode 100644 src/main/org/apache/tools/ant/util/depend/Filter.java create mode 100644 src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java create mode 100644 src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java create mode 100644 src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java diff --git a/build.xml b/build.xml index 7df16e303..be44ea57a 100644 --- a/build.xml +++ b/build.xml @@ -212,15 +212,7 @@ - - - - - diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java index aa05ae31d..8e9672f86 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java @@ -210,25 +210,25 @@ public class EjbJar extends MatchingTask { * Naming scheme where generated jar is determined from the ejb-name in * the deployment descripor */ - public final static String EJB_NAME = "ejb-name"; + public static final String EJB_NAME = "ejb-name"; /** * Naming scheme where the generated jar name is based on the * name of the directory containing the deployment descriptor */ - public final static String DIRECTORY = "directory"; + public static final String DIRECTORY = "directory"; /** * Naming scheme where the generated jar name is based on the name of * the deployment descriptor file */ - public final static String DESCRIPTOR = "descriptor"; + public static final String DESCRIPTOR = "descriptor"; /** * Naming scheme where the generated jar is named by the basejarname * attribute */ - public final static String BASEJARNAME = "basejarname"; + public static final String BASEJARNAME = "basejarname"; /** * Gets the values of the NamingScheme @@ -451,8 +451,7 @@ public class EjbJar extends MatchingTask { if (config.namingScheme == null) { config.namingScheme = new NamingScheme(); config.namingScheme.setValue(NamingScheme.BASEJARNAME); - } - else if (!config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)) { + } else if (!config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME)) { throw new BuildException("The basejarname attribute is not compatible with the " + config.namingScheme.getValue() + " naming scheme"); } @@ -543,7 +542,7 @@ public class EjbJar extends MatchingTask { * * @throws BuildException if the config is not valid */ - private void validateConfig() { + private void validateConfig() throws BuildException { if (config.srcDir == null) { throw new BuildException("The srcDir attribute must be specified"); } @@ -555,8 +554,7 @@ public class EjbJar extends MatchingTask { if (config.namingScheme == null) { config.namingScheme = new NamingScheme(); config.namingScheme.setValue(NamingScheme.DESCRIPTOR); - } - else if (config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME) && + } else if (config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME) && config.baseJarName == null) { throw new BuildException("The basejarname attribute must be specified " + "with the basejarname naming scheme"); @@ -618,14 +616,12 @@ public class EjbJar extends MatchingTask { tool.processDescriptor(files[index], saxParser); } } - } - catch (SAXException se) { + } catch (SAXException se) { String msg = "SAXException while creating parser." + " Details: " + se.getMessage(); throw new BuildException(msg, se); - } - catch (ParserConfigurationException pce) { + } catch (ParserConfigurationException pce) { String msg = "ParserConfigurationException while creating parser. " + "Details: " + pce.getMessage(); throw new BuildException(msg, pce); diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java index 8f9578ad5..bbb9e95a2 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java @@ -68,20 +68,17 @@ import java.util.ArrayList; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; import java.util.zip.ZipEntry; +import java.util.Enumeration; import javax.xml.parsers.SAXParser; import org.xml.sax.InputSource; import org.xml.sax.SAXException; - - -import org.apache.tools.ant.util.depend.Dependencies; -import org.apache.tools.ant.util.depend.Filter; +import org.apache.tools.ant.util.depend.DependencyAnalyzer; import org.apache.bcel.classfile.JavaClass; import org.apache.bcel.classfile.ClassParser; - import org.apache.tools.ant.Task; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Location; @@ -99,16 +96,23 @@ import org.apache.tools.ant.types.FileSet; * This class is also used as a framework for the creation of vendor specific * deployment tools. A number of template methods are provided through which the * vendor specific tool can hook into the EJB creation process. + * + * @author Conor MacNeill */ public class GenericDeploymentTool implements EJBDeploymentTool { - /** Private constants that are used when constructing the standard jarfile */ - protected final static String META_DIR = "META-INF/"; - protected final static String EJB_DD = "ejb-jar.xml"; - + /** The standard META-INF directory in jar files */ + protected static final String META_DIR = "META-INF/"; + + /** Name for EJB Deployment descriptor within EJB jars */ + protected static final String EJB_DD = "ejb-jar.xml"; + + public static final String DEFAULT_ANALYZER_CLASS + = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer"; + /** - * The configuration from the containing task. This config combined with the - * settings of the individual attributes here constitues the complete config for - * this deployment tool. + * The configuration from the containing task. This config combined + * with the settings of the individual attributes here constitues the + * complete config for this deployment tool. */ private EjbJar.Config config; @@ -123,8 +127,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { private String genericJarSuffix = "-generic.jar"; /** - * The task to which this tool belongs. This is used to access services provided - * by the ant core, such as logging. + * The task to which this tool belongs. This is used to access services + * provided by the ant core, such as logging. */ private Task task; @@ -145,8 +149,25 @@ public class GenericDeploymentTool implements EJBDeploymentTool { private DescriptorHandler handler; /** - * Setter used to store the value of destination directory prior to execute() - * being called. + * Dependency analyzer used to collect class dependencies + */ + private DependencyAnalyzer dependencyAnalyzer; + + public GenericDeploymentTool() { + String analyzerClassName = DEFAULT_ANALYZER_CLASS; + try { + Class analyzerClass = Class.forName(analyzerClassName); + dependencyAnalyzer = (DependencyAnalyzer)analyzerClass.newInstance(); + } catch (Exception e) { + task.log("Unable to load dependency analyzer: " + analyzerClassName, + Project.MSG_VERBOSE); + } + } + + + /** + * Setter used to store the value of destination directory prior to + * execute() being called. * @param inDir the destination directory. */ public void setDestdir(File inDir) { @@ -155,6 +176,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { /** * Get the desitination directory. + * + * @return the destination directory into which EJB jars are to be written */ protected File getDestDir() { return destDir; @@ -163,6 +186,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { /** * Set the task which owns this tool + * + * @param task the Task to which this deployment tool is associated. */ public void setTask(Task task) { this.task = task; @@ -170,6 +195,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { /** * Get the task for this tool. + * + * @return the Task instance this tool is associated with. */ protected Task getTask() { return task; @@ -177,13 +204,18 @@ public class GenericDeploymentTool implements EJBDeploymentTool { /** * Get the basename terminator. + * + * @return an ejbjar task configuration */ protected EjbJar.Config getConfig() { return config; } /** - * Returns true, if the meta-inf dir is being explicitly set, false otherwise. + * Indicate if this build is using the base jar name. + * + * @return true if the name of the generated jar is coming from the + * basejarname attribute */ protected boolean usingBaseJarName() { return config.baseJarName != null; @@ -199,6 +231,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { /** * Add the classpath for the user classes + * + * @return a Path instance to be configured by Ant. */ public Path createClasspath() { if (classpath == null) { @@ -209,6 +243,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { /** * Set the classpath to be used for this compilation. + * + * @param classpath the classpath to be used for this build. */ public void setClasspath(Path classpath) { this.classpath = classpath; @@ -217,14 +253,15 @@ public class GenericDeploymentTool implements EJBDeploymentTool { /** * Get the classpath by combining the one from the surrounding task, if any * and the one from this tool. + * + * @return the combined classpath */ protected Path getCombinedClasspath() { Path combinedPath = classpath; if (config.classpath != null) { if (combinedPath == null) { combinedPath = config.classpath; - } - else { + } else { combinedPath.append(config.classpath); } } @@ -232,10 +269,21 @@ public class GenericDeploymentTool implements EJBDeploymentTool { return combinedPath; } + /** + * Log a message to the Ant output. + * + * @param message the message to be logged. + * @param level the severity of this message. + */ protected void log(String message, int level) { getTask().log(message, level); } + /** + * Get the build file location associated with this element's task. + * + * @return the task's location instance. + */ protected Location getLocation() { return getTask().getLocation(); } @@ -243,10 +291,15 @@ public class GenericDeploymentTool implements EJBDeploymentTool { /** * Configure this tool for use in the ejbjar task. + * + * @param config the configuration from the surrounding ejbjar task. */ public void configure(EjbJar.Config config) { this.config = config; - + dependencyAnalyzer.addClassPath(new Path(task.getProject(), + config.srcDir.getPath())); + dependencyAnalyzer.addClassPath(config.classpath); + classpathLoader = null; } @@ -271,7 +324,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { if (!addedfiles.contains(logicalFilename)) { iStream = new FileInputStream(inputFile); // Create the zip entry and add it to the jar file - ZipEntry zipEntry = new ZipEntry(logicalFilename.replace('\\','/')); + ZipEntry zipEntry = new ZipEntry(logicalFilename.replace('\\', '/')); jStream.putNextEntry(zipEntry); // Create the file input stream, and buffer everything over @@ -286,19 +339,16 @@ public class GenericDeploymentTool implements EJBDeploymentTool { //add it to list of files in jar addedfiles.add(logicalFilename); } - } - catch (IOException ioe) { + } catch (IOException ioe) { log("WARNING: IOException while adding entry " + logicalFilename + " to jarfile from " + inputFile.getPath() + " " + ioe.getClass().getName() + "-" + ioe.getMessage(), Project.MSG_WARN); - } - finally { + } finally { // Close up the file input stream for the class file if (iStream != null) { try { iStream.close(); - } - catch (IOException closeException) {} + } catch (IOException closeException) {} } } } @@ -383,23 +433,20 @@ public class GenericDeploymentTool implements EJBDeploymentTool { String publicId = getPublicId(); writeJar(baseName, jarFile, ejbFiles, publicId); - } - else { + } else { // Log that the file is up to date... log(jarFile.toString() + " is up to date.", Project.MSG_VERBOSE); } - } - catch (SAXException se) { + } catch (SAXException se) { String msg = "SAXException while parsing '" + descriptorFileName.toString() + "'. This probably indicates badly-formed XML." + " Details: " + se.getMessage(); throw new BuildException(msg, se); - } - catch (IOException ioe) { + } catch (IOException ioe) { String msg = "IOException while parsing'" + descriptorFileName.toString() + "'. This probably indicates that the descriptor" @@ -466,8 +513,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { if (descriptorStream != null) { try { descriptorStream.close(); - } - catch (IOException closeException) {} + } catch (IOException closeException) {} } } @@ -566,8 +612,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { int index = canonicalDescriptor.lastIndexOf('/'); if (index == -1) { ddPrefix = ""; - } - else { + } else { ddPrefix = descriptorFileName.substring(0, index + 1); } } @@ -622,7 +667,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { // Loop through the files seeing if any has been touched // more recently than the destination jar. - while(fileIter.hasNext()) { + while (fileIter.hasNext()) { File currentFile = (File) fileIter.next(); if (lastBuild < currentFile.lastModified()) { log("Build needed because " + currentFile.getPath() + " is out of date", @@ -641,7 +686,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { * every vendor-specific DeploymentTool will need to reference * this value or may want to determine this value in a vendor-specific way. * - * @return Public ID of the DTD specified in the EJB descriptor. + * @return Public ID of the DTD specified in the EJB descriptor. */ protected String getPublicId() { return handler.getPublicId(); @@ -677,15 +722,13 @@ public class GenericDeploymentTool implements EJBDeploymentTool { File manifestFile = new File(getConfig().descriptorDir, baseName + "-manifest.mf"); if (manifestFile.exists()) { in = new FileInputStream(manifestFile); - } - else if (config.manifest != null) { + } else if (config.manifest != null) { in = new FileInputStream(config.manifest); if ( in == null ) { throw new BuildException("Could not find manifest file: " + config.manifest, getLocation()); } - } - else { + } else { String defaultManifest = "/org/apache/tools/ant/defaultManifest.mf"; in = this.getClass().getResourceAsStream(defaultManifest); if ( in == null ) { @@ -695,11 +738,9 @@ public class GenericDeploymentTool implements EJBDeploymentTool { } manifest = new Manifest(in); - } - catch (IOException e) { + } catch (IOException e) { throw new BuildException ("Unable to read manifest", e, getLocation()); - } - finally { + } finally { if (in != null) { in.close(); } @@ -728,11 +769,10 @@ public class GenericDeploymentTool implements EJBDeploymentTool { for (int i = 0, n = innerfiles.length; i < n; i++) { //get and clean up innerclass name - int entryIndex = entryName.lastIndexOf(entryFile.getName()) -1; + int entryIndex = entryName.lastIndexOf(entryFile.getName()) - 1; if ( entryIndex < 0) { entryName = innerfiles[i]; - } - else { + } else { entryName = entryName.substring(0, entryIndex) + File.separatorChar + innerfiles[i]; } // link the file @@ -746,20 +786,17 @@ public class GenericDeploymentTool implements EJBDeploymentTool { } } } - } - catch(IOException ioe) { + } catch (IOException ioe) { String msg = "IOException while processing ejb-jar file '" + jarfile.toString() + "'. Details: " + ioe.getMessage(); throw new BuildException(msg, ioe); - } - finally { + } finally { if (jarStream != null) { try { jarStream.close(); - } - catch (IOException closeException) {} + } catch (IOException closeException) {} } } } // end of writeJar @@ -770,55 +807,35 @@ public class GenericDeploymentTool implements EJBDeploymentTool { * @param checkEntries files, that are extracted from the deployment descriptor */ protected void checkAndAddDependants(Hashtable checkEntries) - throws BuildException - { - Dependencies visitor = new Dependencies(); - Set set = new TreeSet(); - Set newSet = new HashSet(); - final String base = config.srcDir.getAbsolutePath() + File.separator; + throws BuildException { + dependencyAnalyzer.reset(); + Iterator i = checkEntries.keySet().iterator(); while (i.hasNext()) { String entryName = (String)i.next(); if (entryName.endsWith(".class")) { - newSet.add(entryName.substring(0, entryName.length() - ".class".length()).replace(File.separatorChar, '/')); + String className = entryName.substring(0, + entryName.length() - ".class".length()); + className = className.replace(File.separatorChar, '/'); + className = className.replace('/', '.'); + + dependencyAnalyzer.addRootClass(className); } } - set.addAll(newSet); - - do { - i = newSet.iterator(); - while (i.hasNext()) { - String fileName = base + ((String)i.next()).replace('/', File.separatorChar) + ".class"; - - try { - JavaClass javaClass = new ClassParser(fileName).parse(); - javaClass.accept(visitor); - } - catch (IOException e) { - log("exception: " + e.getMessage(), Project.MSG_INFO); - } + + Enumeration e = dependencyAnalyzer.getClassDependencies(); + + while (e.hasMoreElements()) { + String classname = (String)e.nextElement(); + String location + = classname.replace('.', File.separatorChar) + ".class"; + File classFile = new File(config.srcDir, location); + if (classFile.exists()) { + checkEntries.put(location, classFile); + log("dependent class: " + classname + " - " + classFile, + Project.MSG_VERBOSE); } - newSet.clear(); - newSet.addAll(visitor.getDependencies()); - visitor.clearDependencies(); - - Dependencies.applyFilter(newSet, new Filter() { - public boolean accept(Object object) { - String fileName = base + ((String)object).replace('/', File.separatorChar) + ".class"; - return new File(fileName).exists(); - } - }); - newSet.removeAll(set); - set.addAll(newSet); - } - while (newSet.size() > 0); - - i = set.iterator(); - while (i.hasNext()) { - String next = ((String)i.next()).replace('/', File.separatorChar); - checkEntries.put(next + ".class", new File(base + next + ".class")); - log("dependent class: " + next + ".class" + " - " + base + next + ".class", Project.MSG_VERBOSE); } } @@ -829,8 +846,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { * being added to the jar. * */ - protected ClassLoader getClassLoaderForBuild() - { + protected ClassLoader getClassLoaderForBuild() { if (classpathLoader != null) { return classpathLoader; } @@ -840,8 +856,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { // only generate a new ClassLoader if we have a classpath if (combinedClasspath == null) { classpathLoader = getClass().getClassLoader(); - } - else { + } else { classpathLoader = new AntClassLoader(getTask().getProject(), combinedClasspath); } diff --git a/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java b/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java index 775bd9db7..e0175a180 100644 --- a/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java +++ b/src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java @@ -55,52 +55,87 @@ package org.apache.tools.ant.types.optional.depend; -import java.util.List; -import java.util.ArrayList; -import org.apache.tools.ant.BuildException; +import java.util.Vector; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.Project; import org.apache.tools.ant.types.FileSet; /** - * A DepSet is a FileSet, that enlists all classes that depend on a - * certain class. + * A ClassfileSet is a FileSet, that enlists all classes that depend on a + * certain set of root classes. * - * A DependSet extends FileSets and uses another FileSet as input. The + * A ClassfileSet extends FileSets. The * nested FileSet attribute provides the domain, that is used for searching * for dependent classes * * @author Holger Engels */ public class ClassfileSet extends FileSet { - private List rootClasses = new ArrayList(); + /** + * The list of root classes for this class file set. These are the + * classes which must be included in the fileset and which are the + * starting point for the dependency search. + */ + private Vector rootClasses = new Vector(); + /** + * Inner class used to contain info about root classes + */ public static class ClassRoot { + /** The name of the root class */ private String rootClass; + /** + * Set the root class name + * + * @param name the name of the root class + */ public void setClassname(String name) { this.rootClass = name; } + /** + * Get the name of the root class + * + * @return the name of the root class. + */ public String getClassname() { return rootClass; } } + /** + * Default constructor + */ + public ClassfileSet() { + } + + /** + * Create a ClassfileSet from another ClassfileSet + * + * @param s the other classfileset + */ protected ClassfileSet(ClassfileSet s) { super(s); - rootClasses = s.rootClasses; + rootClasses = (Vector)s.rootClasses.clone(); } - public void setRootClass(String rootClass) - throws BuildException - { - rootClasses.add(rootClass); + /** + * Set the root class attribute + * + * @param rootClass the name of the root class. + */ + public void setRootClass(String rootClass) { + rootClasses.addElement(rootClass); } /** * Return the DirectoryScanner associated with this FileSet. + * + * @param p the project used to resolve dirs, etc. + * + * @return a dependency scanner. */ public DirectoryScanner getDirectoryScanner(Project p) { DependScanner scanner = new DependScanner(); @@ -110,10 +145,20 @@ public class ClassfileSet extends FileSet { return scanner; } + /** + * Add a nested root class definition to this class file set + * + * @param root the configured class root. + */ public void addConfiguredRoot(ClassRoot root) { rootClasses.add(root.getClassname()); } + /** + * Clone this data type. + * + * @return a clone of the class file set + */ public Object clone() { if (isReference()) { return new ClassfileSet((ClassfileSet) getRef(getProject())); diff --git a/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java b/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java index a3e82e75a..88c52bc41 100644 --- a/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java +++ b/src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java @@ -54,43 +54,43 @@ package org.apache.tools.ant.types.optional.depend; import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.LinkedList; -import java.util.Set; -import java.util.TreeSet; -import java.util.Iterator; -import java.util.HashSet; - -import org.apache.tools.ant.util.depend.Dependencies; -import org.apache.tools.ant.util.depend.Filter; +import java.util.Vector; +import java.util.Enumeration; +import org.apache.tools.ant.util.depend.DependencyAnalyzer; import org.apache.tools.ant.DirectoryScanner; - -import org.apache.bcel.classfile.JavaClass; -import org.apache.bcel.classfile.ClassParser; +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.types.Path; /** * An interface used to describe the actions required by any type of * directory scanner. + * + * @author Conor MacNeill + * @author Holger Engels */ public class DependScanner extends DirectoryScanner { - File basedir; - File baseClass; - List included = new LinkedList(); + /** + * The name of the analyzer to use by default. + */ + public static final String DEFAULT_ANALYZER_CLASS + = "org.apache.tools.ant.util.depend.bcel.FullAnalyzer"; - private List rootClasses; + /** + * The base directory for the scan + */ + private File basedir; /** - * Sets the basedir for scanning. This is the directory that is scanned - * recursively. - * - * @param basedir the (non-null) basedir for scanning + * The root classes to drive the search for dependent classes */ - public void setBasedir(String basedir) { - setBasedir(new File(basedir.replace('/',File.separatorChar).replace('\\',File.separatorChar))); - } - + private Vector rootClasses; + + /** + * The names of the classes to include in the fileset + */ + private Vector included; + /** * Sets the basedir for scanning. This is the directory that is scanned * recursively. @@ -100,6 +100,7 @@ public class DependScanner extends DirectoryScanner { public void setBasedir(File basedir) { this.basedir = basedir; } + /** * Gets the basedir that is used for scanning. * @@ -108,11 +109,11 @@ public class DependScanner extends DirectoryScanner { public File getBasedir() { return basedir; } /** - * Sets the domain, where dependant classes are searched + * Sets the root classes to be used to drive the scan. * - * @param domain the domain + * @param rootClasses the rootClasses to be used for this scan */ - public void setRootClasses(List rootClasses) { + public void setRootClasses(Vector rootClasses) { this.rootClasses = rootClasses; } @@ -125,7 +126,11 @@ public class DependScanner extends DirectoryScanner { int count = included.size(); String[] files = new String[count]; for (int i = 0; i < count; i++) { - files[i] = included.get(i) + ".class"; + String classname = (String)included.elementAt(i); + String filename = classname.replace('.', File.separatorChar); + filename = filename + ".class"; + File file = new File(basedir, filename); + files[i] = file.getPath(); //System.err.println(" " + files[i]); } return files; @@ -136,68 +141,86 @@ public class DependScanner extends DirectoryScanner { * * @exception IllegalStateException when basedir was set incorrecly */ - public void scan() { - Dependencies visitor = new Dependencies(); - - Set set = new TreeSet(); - - final String base; + public void scan() throws IllegalStateException { + String analyzerClassName = DEFAULT_ANALYZER_CLASS; + DependencyAnalyzer analyzer = null; try { - base = basedir.getCanonicalPath() + File.separator; + Class analyzerClass = Class.forName(analyzerClassName); + analyzer = (DependencyAnalyzer)analyzerClass.newInstance(); + } catch (Exception e) { + throw new BuildException("Unable to load dependency analyzer: " + + analyzerClassName, e); } - catch (Exception e) { - throw new IllegalArgumentException(e.getMessage()); + analyzer.addClassPath(new Path(null, basedir.getPath())); + + for (Enumeration e = rootClasses.elements(); e.hasMoreElements(); ) { + analyzer.addRootClass((String)e.nextElement()); } - for (Iterator rootClassIterator = rootClasses.iterator(); rootClassIterator.hasNext();) { - Set newSet = new HashSet(); - String start = (String)rootClassIterator.next(); - start = start.replace('.', '/'); + Enumeration e = analyzer.getClassDependencies(); - newSet.add(start); - set.add(start); - - do { - Iterator i = newSet.iterator(); - while (i.hasNext()) { - String fileName = base + ((String)i.next()).replace('/', File.separatorChar) + ".class"; - - try { - JavaClass javaClass = new ClassParser(fileName).parse(); - javaClass.accept(visitor); - } - catch (IOException e) { - System.err.println("exception: " + e.getMessage()); - } - } - newSet.clear(); - newSet.addAll(visitor.getDependencies()); - visitor.clearDependencies(); - - Dependencies.applyFilter(newSet, new Filter() { - public boolean accept(Object object) { - String fileName = base + ((String)object).replace('/', File.separatorChar) + ".class"; - return new File(fileName).exists(); - } - }); - newSet.removeAll(set); - set.addAll(newSet); - } - while (newSet.size() > 0); + included.removeAllElements(); + while (e.hasMoreElements()) { + included.addElement(e.nextElement()); } - - included.clear(); - included.addAll(set); } - public void addDefaultExcludes() {} - public String[] getExcludedDirectories() { return null; } - public String[] getExcludedFiles() { return null; } - public String[] getIncludedDirectories() { return new String[0]; } - public String[] getNotIncludedDirectories() { return null; } - public String[] getNotIncludedFiles() { return null; } + /** + * @see DirectoryScanner#addDefaultExcludes + */ + public void addDefaultExcludes() { + } + + /** + * @see DirectoryScanner#getExcludedDirectories + */ + public String[] getExcludedDirectories() { + return null; + } + + /** + * @see DirectoryScanner#getExcludedFiles + */ + public String[] getExcludedFiles() { + return null; + } + + /** + * @see DirectoryScanner#getIncludedDirectories + */ + public String[] getIncludedDirectories() { + return new String[0]; + } + + /** + * @see DirectoryScanner#getNotIncludedDirectories + */ + public String[] getNotIncludedDirectories() { + return null; + } + + /** + * @see DirectoryScanner#getNotIncludedFiles + */ + public String[] getNotIncludedFiles() { + return null; + } - public void setExcludes(String[] excludes) {} - public void setIncludes(String[] includes) {} - public void setCaseSensitive(boolean isCaseSensitive) {} + /** + * @see DirectoryScanner#setExcludes + */ + public void setExcludes(String[] excludes) { + } + + /** + * @see DirectoryScanner#setIncludes + */ + public void setIncludes(String[] includes) { + } + + /** + * @see DirectoryScanner#setCaseSensitive + */ + public void setCaseSensitive(boolean isCaseSensitive) { + } } diff --git a/src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java b/src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java new file mode 100644 index 000000000..e6510d082 --- /dev/null +++ b/src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java @@ -0,0 +1,324 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.util.depend; +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Vector; +import java.util.zip.ZipFile; +import org.apache.tools.ant.types.Path; + +/** + * An abstract implementation of the analyzer interface providing support + * for the bulk of interface methods. + * + * @author Conor MacNeill + * @created 13 March 2002 + */ +public abstract class AbstractAnalyzer implements DependencyAnalyzer { + /** Maximum number of loops for looking for indirect dependencies. */ + public static final int MAX_LOOPS = 1000; + + /** The source path for the source files */ + private Path sourcePath = new Path(null); + + /** The classpath containg dirs and jars of class files */ + private Path classPath = new Path(null); + + /** The list of root classes */ + private Vector rootClasses = new Vector(); + + /** true if dependencies have been determined */ + private boolean determined = false; + + /** the list of File objects that the root classes depend upon */ + private Vector fileDependencies; + /** the list of java classes the root classes depend upon */ + private Vector classDependencies; + + /** true if indirect dependencies should be gathered */ + private boolean closure = true; + + /** Setup the analyzer */ + protected AbstractAnalyzer() { + reset(); + } + + /** + * Set the closure flag. If this flag is true the analyzer will traverse + * all class relationships until it has collected the entire set of + * direct and indirect dependencies + * + * @param closure true if dependencies should be traversed to determine + * indirect dependencies. + */ + public void setClosure(boolean closure) { + this.closure = closure; + } + + /** + * Get the list of files in the file system upon which the root classes + * depend. The files will be either the classfiles or jar files upon + * which the root classes depend. + * + * @return an enumeration of File instances. + * @exception UnsupportedOperationException if the analyzer cannot + * determine file dependencies. + */ + public Enumeration getFileDependencies() + throws UnsupportedOperationException { + if (!supportsFileDependencies()) { + throw new UnsupportedOperationException(); + } + if (!determined) { + determineDependencies(fileDependencies, classDependencies); + } + return fileDependencies.elements(); + } + + /** + * Get the list of classes upon which root classes depend. This is a + * list of Java classnames in dot notation. + * + * @return an enumeration of Strings, each being the name of a Java + * class in dot notation. + */ + public Enumeration getClassDependencies() { + if (!determined) { + determineDependencies(fileDependencies, classDependencies); + } + return classDependencies.elements(); + } + + /** + * Get the file that contains the class definition + * + * @param classname the name of the required class + * @return the file instance, zip or class, containing the + * class or null if the class could not be found. + * @exception IOException if the files in the classpath cannot be read. + */ + public File getClassContainer(String classname) throws IOException { + String classLocation = classname.replace('.', '/') + ".class"; + // we look through the classpath elements. If the element is a dir + // we look for the file. IF it is a zip, we look for the zip entry + return getResourceContainer(classLocation, classPath.list()); + } + + /** + * Get the file that contains the class source. + * + * @param classname the name of the required class + * @return the file instance, zip or java, containing the + * source or null if the source for the class could not be found. + * @exception IOException if the files in the sourcepath cannot be read. + */ + public File getSourceContainer(String classname) throws IOException { + String sourceLocation = classname.replace('.', '/') + ".java"; + + // we look through the source path elements. If the element is a dir + // we look for the file. If it is a zip, we look for the zip entry. + // This isn't normal for source paths but we get it for free + return getResourceContainer(sourceLocation, sourcePath.list()); + } + + /** + * Add a source path to the source path used by this analyzer. The + * elements in the given path contain the source files for the classes + * being analyzed. Not all analyzers will use this information. + * + * @param sourcePath The Path instance specifying the source path + * elements. + */ + public void addSourcePath(Path sourcePath) { + if (sourcePath == null) { + return; + } + this.sourcePath.append(sourcePath); + this.sourcePath.setProject(sourcePath.getProject()); + } + + /** + * Add a classpath to the classpath being used by the analyzer. The + * classpath contains the binary classfiles for the classes being + * analyzed The elements may either be the directories or jar files.Not + * all analyzers will use this information. + * + * @param classPath the Path instance specifying the classpath elements + */ + public void addClassPath(Path classPath) { + if (classPath == null) { + return; + } + + this.classPath.append(classPath); + this.classPath.setProject(classPath.getProject()); + } + + /** + * Add a root class. The root classes are used to drive the + * determination of dependency information. The analyzer will start at + * the root classes and add dependencies from there. + * + * @param className the name of the class in Java dot notation. + */ + public void addRootClass(String className) { + if (className == null) { + return; + } + if (!rootClasses.contains(className)) { + rootClasses.addElement(className); + } + } + + /** + * Configure an aspect of the analyzer. The set of aspects that are + * supported is specific to each analyzer instance. + * + * @param name the name of the aspect being configured + * @param info the configuration info. + */ + public void config(String name, Object info) { + // do nothing by default + } + + /** + * Reset the dependency list. This will reset the determined + * dependencies and the also list of root classes. + */ + public void reset() { + rootClasses.clear(); + determined = false; + fileDependencies = new Vector(); + classDependencies = new Vector(); + } + + /** + * Get an enumeration of the root classes + * + * @return an enumeration of Strings, each of which is a class name + * for a root class. + */ + protected Enumeration getRootClasses() { + return rootClasses.elements(); + } + + /** + * Indicate if the analyzer is required to follow + * indirect class relationships. + * + * @return true if indirect relationships should be followed. + */ + protected boolean isClosureRequired() { + return closure; + } + + /** + * Determine the dependencies of the current set of root classes + * + * @param files a vector into which Files upon which the root classes + * depend should be placed. + * @param classes a vector of Strings into which the names of classes + * upon which the root classes depend should be placed. + */ + protected abstract void determineDependencies(Vector files, Vector classes); + + /** + * Indicate if the particular subclass supports file dependency + * information. + * + * @return true if file dependencies are supported. + */ + protected abstract boolean supportsFileDependencies(); + + /** + * Get the file that contains the resource + * + * @param resourceLocation the name of the required resource. + * @param paths the paths which will be searched for the resource. + * @return the file instance, zip or class, containing the + * class or null if the class could not be found. + * @exception IOException if the files in the given paths cannot be read. + */ + private File getResourceContainer(String resourceLocation, String[] paths) + throws IOException { + for (int i = 0; i < paths.length; ++i) { + File element = new File(paths[i]); + if (!element.exists()) { + continue; + } + if (element.isDirectory()) { + File resource = new File(element, resourceLocation); + if (resource.exists()) { + return resource; + } + } else { + // must be a zip of some sort + ZipFile zipFile = null; + try { + zipFile = new ZipFile(element); + if (zipFile.getEntry(resourceLocation) != null) { + return element; + } + } finally { + if (zipFile != null) { + zipFile.close(); + } + } + } + } + return null; + } +} + diff --git a/src/main/org/apache/tools/ant/util/depend/Dependencies.java b/src/main/org/apache/tools/ant/util/depend/Dependencies.java deleted file mode 100644 index 277cfedb4..000000000 --- a/src/main/org/apache/tools/ant/util/depend/Dependencies.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2001-2002 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowlegement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowlegement may appear in the software itself, - * if and wherever such third-party acknowlegements normally appear. - * - * 4. The names "The Jakarta Project", "Ant", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Group. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - */ -package org.apache.tools.ant.util.depend; - -import java.io.File; -import java.util.Set; -import java.util.HashSet; -import java.util.StringTokenizer; -import java.util.TreeSet; -import java.util.Iterator; -import java.util.Collection; -import org.apache.bcel.classfile.EmptyVisitor; -import org.apache.bcel.classfile.JavaClass; -import org.apache.bcel.classfile.ConstantPool; -import org.apache.bcel.classfile.Code; -import org.apache.bcel.classfile.CodeException; -import org.apache.bcel.classfile.ConstantClass; -import org.apache.bcel.classfile.ConstantDouble; -import org.apache.bcel.classfile.ConstantFieldref; -import org.apache.bcel.classfile.ConstantFloat; -import org.apache.bcel.classfile.ConstantInteger; -import org.apache.bcel.classfile.ConstantInterfaceMethodref; -import org.apache.bcel.classfile.ConstantLong; -import org.apache.bcel.classfile.ConstantMethodref; -import org.apache.bcel.classfile.ConstantNameAndType; -import org.apache.bcel.classfile.Constant; -import org.apache.bcel.classfile.ConstantString; -import org.apache.bcel.classfile.ConstantUtf8; -import org.apache.bcel.classfile.ConstantValue; -import org.apache.bcel.classfile.Deprecated; -import org.apache.bcel.classfile.ExceptionTable; -import org.apache.bcel.classfile.Field; -import org.apache.bcel.classfile.InnerClass; -import org.apache.bcel.classfile.InnerClasses; -import org.apache.bcel.classfile.Method; -import org.apache.bcel.classfile.LineNumber; -import org.apache.bcel.classfile.LineNumberTable; -import org.apache.bcel.classfile.LocalVariable; -import org.apache.bcel.classfile.LocalVariableTable; -import org.apache.bcel.classfile.SourceFile; -import org.apache.bcel.classfile.Synthetic; -import org.apache.bcel.classfile.Unknown; -import org.apache.bcel.classfile.StackMap; -import org.apache.bcel.classfile.StackMapEntry; -import org.apache.bcel.classfile.ClassParser; - - - -public class Dependencies extends EmptyVisitor { - private boolean verbose = false; - - private JavaClass javaClass; - private ConstantPool constantPool; - private Set dependencies = new HashSet(); - - public void clearDependencies() { - dependencies.clear(); - } - - public Set getDependencies() { - return dependencies; - } - - public void visitConstantClass(ConstantClass obj) { - if (verbose) { - System.out.println("visit ConstantClass"); - System.out.println(obj.getConstantValue(constantPool)); - } - dependencies.add("" + obj.getConstantValue(constantPool)); - } - - public void visitConstantPool(ConstantPool obj) { - if (verbose) { - System.out.println("visit ConstantPool"); - } - this.constantPool = obj; - - // visit constants - for(int idx = 0; idx < constantPool.getLength(); idx++) { - Constant c = constantPool.getConstant(idx); - if (c != null) { - c.accept(this); - } - } - } - - public void visitField(Field obj) { - if (verbose) { - System.out.println("visit Field"); - System.out.println(obj.getSignature()); - } - addClasses(obj.getSignature()); - } - - public void visitJavaClass(JavaClass obj) { - if (verbose) { - System.out.println("visit JavaClass"); - } - - this.javaClass = obj; - dependencies.add(javaClass.getClassName().replace('.', '/')); - - // visit constant pool - javaClass.getConstantPool().accept(this); - - // visit fields - Field[] fields = obj.getFields(); - for(int i=0; i < fields.length; i++) { - fields[i].accept(this); - } - - // visit methods - Method[] methods = obj.getMethods(); - for(int i=0; i < methods.length; i++) { - methods[i].accept(this); - } - } - - public void visitMethod(Method obj) { - if (verbose) { - System.out.println("visit Method"); - System.out.println(obj.getSignature()); - } - String signature = obj.getSignature(); - int pos = signature.indexOf(")"); - addClasses(signature.substring(1, pos)); - addClasses(signature.substring(pos + 1)); - } - - void addClasses(String string) { - StringTokenizer tokens = new StringTokenizer(string, ";"); - while (tokens.hasMoreTokens()) { - addClass(tokens.nextToken()); - } - } - - void addClass(String string) { - int pos = string.indexOf('L'); - if (pos != -1) { - dependencies.add(string.substring(pos+1)); - } - } - - public static void main(String[] args) { - try { - Dependencies visitor = new Dependencies(); - - Set set = new TreeSet(); - Set newSet = new HashSet(); - - int o=0; - String arg = null; - if ("-base".equals(args[0])) { - arg = args[1]; - if (!arg.endsWith(File.separator)) { - arg = arg + File.separator; - } - o=2; - } - final String base = arg; - - for (int i=o; i < args.length; i++) { - String fileName = args[i].substring(0, args[i].length() - ".class".length()); - if (base != null && fileName.startsWith(base)) { - fileName = fileName.substring(base.length()); - } - newSet.add(fileName); - } - set.addAll(newSet); - - do { - Iterator i = newSet.iterator(); - while (i.hasNext()) { - String fileName = i.next() + ".class"; - - if (base != null) { - fileName = base + fileName; - } - - JavaClass javaClass = new ClassParser(fileName).parse(); - javaClass.accept(visitor); - } - newSet.clear(); - newSet.addAll(visitor.getDependencies()); - visitor.clearDependencies(); - - applyFilter(newSet, new Filter() { - public boolean accept(Object object) { - String fileName = object + ".class"; - if (base != null) { - fileName = base + fileName; - } - return new File(fileName).exists(); - } - }); - newSet.removeAll(set); - set.addAll(newSet); - } - while (newSet.size() > 0); - - Iterator i = set.iterator(); - while (i.hasNext()) { - System.out.println(i.next()); - } - } - catch (Exception e) { - System.err.println(e.getMessage()); - e.printStackTrace(System.err); - } - } - - public static void applyFilter(Collection collection, Filter filter) { - Iterator i = collection.iterator(); - while (i.hasNext()) { - Object next = i.next(); - if (!filter.accept(next)) { - i.remove(); - } - } - } -} diff --git a/src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java b/src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java new file mode 100644 index 000000000..c6940b774 --- /dev/null +++ b/src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java @@ -0,0 +1,170 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.util.depend; +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; + +import org.apache.tools.ant.types.Path; + +/** + * A dependency analyzer analyzes dependencies between Java classes to + * determine the minimal set of classes which are required by a set of + * "root" classes. Different implementations of this interface can + * use different strategies and libraries to determine the required set. For + * example, some analyzers will use class files while others might use + * source files. Analyzer specific configuration is catered for through a + * generic configure method + * + * @author Conor MacNeill + * @created 13 March 2002 + */ +public interface DependencyAnalyzer { + /** + * Add a source path to the source path used by this analyzer. The + * elements in the given path contain the source files for the classes + * being analyzed. Not all analyzers will use this information. + * + * @param sourcePath The Path instance specifying the source path + * elements. + */ + void addSourcePath(Path sourcePath); + + /** + * Add a classpath to the classpath being used by the analyzer. The + * classpath contains the binary classfiles for the classes being + * analyzed The elements may either be the directories or jar files.Not + * all analyzers will use this information. + * + * @param classpath the Path instance specifying the classpath elements + */ + void addClassPath(Path classpath); + + /** + * Add a root class. The root classes are used to drive the + * determination of dependency information. The analyzer will start at + * the root classes and add dependencies from there. + * + * @param classname the name of the class in Java dot notation. + */ + void addRootClass(String classname); + + /** + * Get the list of files in the file system upon which the root classes + * depend. The files will be either the classfiles or jar files upon + * which the root classes depend. + * + * @return an enumeration of File instances. + * @exception UnsupportedOperationException if the analyzer cannot + * determine file dependencies. + */ + Enumeration getFileDependencies() throws UnsupportedOperationException; + + /** + * Get the list of classes upon which root classes depend. This is a + * list of Java classnames in dot notation. + * + * @return an enumeration of Strings, each being the name of a Java + * class in dot notation. + */ + Enumeration getClassDependencies(); + + + /** + * Reset the dependency list. This will reset the determined + * dependencies and the also list of root classes. + */ + void reset(); + + /** + * Configure an aspect of the analyzer. The set of aspects that are + * supported is specific to each analyzer instance. + * + * @param name the name of the aspect being configured + * @param info the configuration information. + */ + void config(String name, Object info); + + /** + * Set the closure flag. If this flag is true the analyzer will traverse + * all class relationships until it has collected the entire set of + * direct and indirect dependencies + * + * @param closure true if dependencies should be traversed to determine + * indirect dependencies. + */ + void setClosure(boolean closure); + + + /** + * Get the file that contains the class definition + * + * @param classname the name of the required class + * @return the file instance, zip or class, containing the + * class or null if the class could not be found. + * @exception IOException if the files in the classpath cannot be read. + */ + File getClassContainer(String classname) throws IOException; + + /** + * Get the file that contains the class source. + * + * @param classname the name of the required class + * @return the file instance, zip or java, containing the + * source or null if the source for the class could not be found. + * @exception IOException if the files in the sourcepath cannot be read. + */ + File getSourceContainer(String classname) throws IOException; +} + diff --git a/src/main/org/apache/tools/ant/util/depend/Filter.java b/src/main/org/apache/tools/ant/util/depend/Filter.java deleted file mode 100644 index ace12fbcb..000000000 --- a/src/main/org/apache/tools/ant/util/depend/Filter.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * The Apache Software License, Version 1.1 - * - * Copyright (c) 2001 The Apache Software Foundation. All rights - * reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * - * 3. The end-user documentation included with the redistribution, if - * any, must include the following acknowlegement: - * "This product includes software developed by the - * Apache Software Foundation (http://www.apache.org/)." - * Alternately, this acknowlegement may appear in the software itself, - * if and wherever such third-party acknowlegements normally appear. - * - * 4. The names "The Jakarta Project", "Ant", and "Apache Software - * Foundation" must not be used to endorse or promote products derived - * from this software without prior written permission. For written - * permission, please contact apache@apache.org. - * - * 5. Products derived from this software may not be called "Apache" - * nor may "Apache" appear in their names without prior written - * permission of the Apache Group. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES - * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR - * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, - * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT - * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * ==================================================================== - * - * This software consists of voluntary contributions made by many - * individuals on behalf of the Apache Software Foundation. For more - * information on the Apache Software Foundation, please see - * . - */ -package org.apache.tools.ant.util.depend; - - - -public interface Filter { - boolean accept(Object object); -} diff --git a/src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java b/src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java new file mode 100644 index 000000000..5ed8ac156 --- /dev/null +++ b/src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java @@ -0,0 +1,160 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.util.depend.bcel; +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; +import org.apache.bcel.classfile.ClassParser; +import org.apache.bcel.classfile.JavaClass; +import org.apache.tools.ant.util.depend.AbstractAnalyzer; + +/** + * A dependency analyzer which returns superclass and superinterface + * dependencies. + * + * @author Conor MacNeill + * @created 13 March 2002 + */ +public class AncestorAnalyzer extends AbstractAnalyzer { + /** + * Determine the dependencies of the configured root classes. + * + * @param files a vector to be populated with the files which contain + * the dependency classes + * @param classes a vector to be populated with the names of the + * depencency classes. + */ + protected void determineDependencies(Vector files, Vector classes) { + // we get the root classes and build up a set of + // classes upon which they depend + Hashtable dependencies = new Hashtable(); + Hashtable containers = new Hashtable(); + Hashtable toAnalyze = new Hashtable(); + Hashtable nextAnalyze = new Hashtable(); + for (Enumeration e = getRootClasses(); e.hasMoreElements(); ) { + String classname = (String)e.nextElement(); + toAnalyze.put(classname, classname); + } + + int count = 0; + int maxCount = isClosureRequired() ? MAX_LOOPS : 2; + while (toAnalyze.size() != 0 && count++ < maxCount) { + nextAnalyze.clear(); + for (Enumeration e = toAnalyze.keys(); e.hasMoreElements(); ) { + String classname = (String)e.nextElement(); + dependencies.put(classname, classname); + try { + File container = getClassContainer(classname); + if (container == null) { + continue; + } + containers.put(container, container); + + ClassParser parser = null; + if (container.getName().endsWith(".class")) { + parser = new ClassParser(container.getPath()); + } else { + parser = new ClassParser(container.getPath(), + classname.replace('.', '/') + ".class"); + } + + JavaClass javaClass = parser.parse(); + String[] interfaces = javaClass.getInterfaceNames(); + for (int i = 0; i < interfaces.length; ++i) { + String interfaceName = interfaces[i]; + if (!dependencies.containsKey(interfaceName)) { + nextAnalyze.put(interfaceName, interfaceName); + } + } + + if (javaClass.isClass()) { + String superClass = javaClass.getSuperclassName(); + if (!dependencies.containsKey(superClass)) { + nextAnalyze.put(superClass, superClass); + } + } + } catch (IOException ioe) { + // ignore + } + } + + Hashtable temp = toAnalyze; + toAnalyze = nextAnalyze; + nextAnalyze = temp; + } + + files.removeAllElements(); + for (Enumeration e = containers.keys(); e.hasMoreElements(); ) { + files.addElement((File)e.nextElement()); + } + + classes.removeAllElements(); + for (Enumeration e = dependencies.keys(); e.hasMoreElements(); ) { + classes.addElement((String)e.nextElement()); + } + } + + /** + * Indicate if this analyzer can determine dependent files. + * + * @return true if the analyzer provides dependency file information. + */ + protected boolean supportsFileDependencies() { + return true; + } + +} + diff --git a/src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java b/src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java new file mode 100644 index 000000000..984d221bf --- /dev/null +++ b/src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java @@ -0,0 +1,182 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2001-2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.util.depend.bcel; + +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.StringTokenizer; +import org.apache.bcel.classfile.ConstantClass; +import org.apache.bcel.classfile.ConstantPool; +import org.apache.bcel.classfile.EmptyVisitor; +import org.apache.bcel.classfile.Field; +import org.apache.bcel.classfile.JavaClass; +import org.apache.bcel.classfile.Method; + +/** + * A BCEL visitor implementation to collect class dependency information + * + * @author Conor MacNeill + * @author Holger Engels + * @created 15 March 2002 + */ +public class DependencyVisitor extends EmptyVisitor { + /** The collectd dependencies */ + private Hashtable dependencies = new Hashtable(); + /** + * The current class's constant pool - used to determine class names + * from class references. + */ + private ConstantPool constantPool; + + /** + * Get the dependencies collected by this visitor + * + * @return a Enumeration of classnames, being the classes upon which the + * visited classes depend. + */ + public Enumeration getDependencies() { + return dependencies.keys(); + } + + /** Clear the curretn set of collected dependencies. */ + public void clearDependencies() { + dependencies.clear(); + } + + /** + * Visit the constant pool of a class + * + * @param constantPool the constant pool of the class being visited. + */ + public void visitConstantPool(ConstantPool constantPool) { + this.constantPool = constantPool; + } + + /** + * Visit a class reference + * + * @param constantClass the constantClass entry for the class reference + */ + public void visitConstantClass(ConstantClass constantClass) { + String classname + = constantClass.getConstantValue(constantPool).toString(); + addSlashClass(classname); + } + + /** + * Visit a field of the class. + * + * @param field the field being visited + */ + public void visitField(Field field) { + addClasses(field.getSignature()); + } + + /** + * Visit a Java class + * + * @param javaClass the class being visited. + */ + public void visitJavaClass(JavaClass javaClass) { + addClass(javaClass.getClassName()); + } + + /** + * Visit a method of the current class + * + * @param method the method being visited. + */ + public void visitMethod(Method method) { + String signature = method.getSignature(); + int pos = signature.indexOf(")"); + addClasses(signature.substring(1, pos)); + addClasses(signature.substring(pos + 1)); + } + + /** + * Add a classname to the list of dependency classes + * + * @param classname the class to be added to the list of dependencies. + */ + void addClass(String classname) { + dependencies.put(classname, classname); + } + + /** + * Add all the classes from a descriptor string. + * + * @param string the descriptor string, being descriptors separated by + * ';' characters. + */ + private void addClasses(String string) { + StringTokenizer tokens = new StringTokenizer(string, ";"); + while (tokens.hasMoreTokens()) { + String descriptor = tokens.nextToken(); + int pos = descriptor.indexOf('L'); + if (pos != -1) { + addSlashClass(descriptor.substring(pos + 1)); + } + } + } + + /** + * Add a class name in slash format (e.g. org/apache/tools/ant/...) + * + * @param classname the class name in slash format + */ + private void addSlashClass(String classname) { + addClass(classname.replace('/', '.')); + } +} + diff --git a/src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java b/src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java new file mode 100644 index 000000000..f8041d4da --- /dev/null +++ b/src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java @@ -0,0 +1,156 @@ +/* + * The Apache Software License, Version 1.1 + * + * Copyright (c) 2002 The Apache Software Foundation. All rights + * reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * 3. The end-user documentation included with the redistribution, if + * any, must include the following acknowlegement: + * "This product includes software developed by the + * Apache Software Foundation (http://www.apache.org/)." + * Alternately, this acknowlegement may appear in the software itself, + * if and wherever such third-party acknowlegements normally appear. + * + * 4. The names "The Jakarta Project", "Ant", and "Apache Software + * Foundation" must not be used to endorse or promote products derived + * from this software without prior written permission. For written + * permission, please contact apache@apache.org. + * + * 5. Products derived from this software may not be called "Apache" + * nor may "Apache" appear in their names without prior written + * permission of the Apache Group. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR + * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * ==================================================================== + * + * This software consists of voluntary contributions made by many + * individuals on behalf of the Apache Software Foundation. For more + * information on the Apache Software Foundation, please see + * . + */ +package org.apache.tools.ant.util.depend.bcel; +import java.io.File; +import java.io.IOException; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Vector; +import org.apache.bcel.classfile.ClassParser; +import org.apache.bcel.classfile.DescendingVisitor; +import org.apache.bcel.classfile.JavaClass; +import org.apache.tools.ant.util.depend.AbstractAnalyzer; + +/** + * An analyzer capable fo traversing all class - class relationships. + * + * @author Conor MacNeill + * @author Holger Engels + * @created 13 March 2002 + */ +public class FullAnalyzer extends AbstractAnalyzer { + /** + * Determine the dependencies of the configured root classes. + * + * @param files a vector to be populated with the files which contain + * the dependency classes + * @param classes a vector to be populated with the names of the + * depencency classes. + */ + protected void determineDependencies(Vector files, Vector classes) { + // we get the root classes and build up a set of + // classes upon which they depend + Hashtable dependencies = new Hashtable(); + Hashtable containers = new Hashtable(); + Hashtable toAnalyze = new Hashtable(); + for (Enumeration e = getRootClasses(); e.hasMoreElements(); ) { + String classname = (String)e.nextElement(); + toAnalyze.put(classname, classname); + } + + int count = 0; + int maxCount = isClosureRequired() ? MAX_LOOPS : 2; + while (toAnalyze.size() != 0 && count++ < maxCount) { + DependencyVisitor dependencyVisitor = new DependencyVisitor(); + for (Enumeration e = toAnalyze.keys(); e.hasMoreElements(); ) { + String classname = (String)e.nextElement(); + dependencies.put(classname, classname); + try { + File container = getClassContainer(classname); + if (container == null) { + continue; + } + containers.put(container, container); + + ClassParser parser = null; + if (container.getName().endsWith(".class")) { + parser = new ClassParser(container.getPath()); + } else { + parser = new ClassParser(container.getPath(), + classname.replace('.', '/') + ".class"); + } + + JavaClass javaClass = parser.parse(); + DescendingVisitor traverser + = new DescendingVisitor(javaClass, dependencyVisitor); + traverser.visit(); + } catch (IOException ioe) { + // ignore + } + } + + toAnalyze.clear(); + + // now recover all the dependencies collected and add to the list. + Enumeration depsEnum = dependencyVisitor.getDependencies(); + while (depsEnum.hasMoreElements()) { + String className = (String)depsEnum.nextElement(); + if (!dependencies.containsKey(className)) { + toAnalyze.put(className, className); + } + } + } + + files.removeAllElements(); + for (Enumeration e = containers.keys(); e.hasMoreElements(); ) { + files.addElement((File)e.nextElement()); + } + + classes.removeAllElements(); + for (Enumeration e = dependencies.keys(); e.hasMoreElements(); ) { + classes.addElement((String)e.nextElement()); + } + } + + /** + * Indicate if this analyzer can determine dependent files. + * + * @return true if the analyzer provides dependency file information. + */ + protected boolean supportsFileDependencies() { + return true; + } + +} +