Browse Source

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
master
Conor MacNeill 23 years ago
parent
commit
792642344d
12 changed files with 1282 additions and 551 deletions
  1. +1
    -9
      build.xml
  2. +9
    -13
      src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java
  3. +116
    -101
      src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java
  4. +57
    -12
      src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java
  5. +107
    -84
      src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java
  6. +324
    -0
      src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java
  7. +0
    -272
      src/main/org/apache/tools/ant/util/depend/Dependencies.java
  8. +170
    -0
      src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java
  9. +0
    -60
      src/main/org/apache/tools/ant/util/depend/Filter.java
  10. +160
    -0
      src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java
  11. +182
    -0
      src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java
  12. +156
    -0
      src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java

+ 1
- 9
build.xml View File

@@ -212,15 +212,7 @@
<patternset id="needs.jakarta.bcel">
<exclude name="${ant.package}/filters/util/JavaClassHelper.java"
unless="bcel.present" />
<exclude name="${optional.type.package}/depend/*.java"
unless="bcel.present" />
<exclude name="${util.package}/depend/*.java"
unless="bcel.present" />
<exclude name="${optional.package}/ejb/EjbJar.java"
unless="bcel.present" />
<exclude name="${optional.package}/ejb/*DeploymentTool.java"
unless="bcel.present" />
<exclude name="${optional.package}/ejb/IPlanet*.java"
<exclude name="${util.package}/depend/bcel/*.java"
unless="bcel.present" />
</patternset>
<patternset id="needs.jakarta.log4j">


+ 9
- 13
src/main/org/apache/tools/ant/taskdefs/optional/ejb/EjbJar.java View File

@@ -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);


+ 116
- 101
src/main/org/apache/tools/ant/taskdefs/optional/ejb/GenericDeploymentTool.java View File

@@ -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 <code>DeploymentTool</code> 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);
}



+ 57
- 12
src/main/org/apache/tools/ant/types/optional/depend/ClassfileSet.java View File

@@ -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 <a href="mailto:hengels@innovidata.com">Holger Engels</a>
*/
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()));


+ 107
- 84
src/main/org/apache/tools/ant/types/optional/depend/DependScanner.java View File

@@ -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 <a href="mailto:conor@apache.org">Conor MacNeill</a>
* @author <a href="mailto:hengels@innovidata.com">Holger Engels</a>
*/
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) {
}
}

+ 324
- 0
src/main/org/apache/tools/ant/util/depend/AbstractAnalyzer.java View File

@@ -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
* <http://www.apache.org/>.
*/
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 <a href="mailto:conor@apache.org">Conor MacNeill</a>
* @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;
}
}


+ 0
- 272
src/main/org/apache/tools/ant/util/depend/Dependencies.java View File

@@ -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
* <http://www.apache.org/>.
*/
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();
}
}
}
}

+ 170
- 0
src/main/org/apache/tools/ant/util/depend/DependencyAnalyzer.java View File

@@ -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
* <http://www.apache.org/>.
*/
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
* &quot;root&quot; 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 <a href="mailto:conor@apache.org">Conor MacNeill</a>
* @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;
}


+ 0
- 60
src/main/org/apache/tools/ant/util/depend/Filter.java View File

@@ -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
* <http://www.apache.org/>.
*/
package org.apache.tools.ant.util.depend;



public interface Filter {
boolean accept(Object object);
}

+ 160
- 0
src/main/org/apache/tools/ant/util/depend/bcel/AncestorAnalyzer.java View File

@@ -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
* <http://www.apache.org/>.
*/
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 <a href="mailto:conor@apache.org">Conor MacNeill</a>
* @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;
}

}


+ 182
- 0
src/main/org/apache/tools/ant/util/depend/bcel/DependencyVisitor.java View File

@@ -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
* <http://www.apache.org/>.
*/
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 <a href="mailto:conor@apache.org">Conor MacNeill</a>
* @author <a href="mailto:hengels@innovidata.com">Holger Engels</a>
* @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('/', '.'));
}
}


+ 156
- 0
src/main/org/apache/tools/ant/util/depend/bcel/FullAnalyzer.java View File

@@ -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
* <http://www.apache.org/>.
*/
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 <a href="mailto:conor@apache.org">Conor MacNeill</a>
* @author <a href="mailto:hengels@innovidata.com">Holger Engels</a>
* @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;
}

}


Loading…
Cancel
Save