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-ffa450edef68master
@@ -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"> | |||
@@ -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); | |||
@@ -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); | |||
} | |||
@@ -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())); | |||
@@ -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) { | |||
} | |||
} |
@@ -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; | |||
} | |||
} | |||
@@ -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(); | |||
} | |||
} | |||
} | |||
} |
@@ -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 | |||
* "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 <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; | |||
} | |||
@@ -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); | |||
} |
@@ -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; | |||
} | |||
} | |||
@@ -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('/', '.')); | |||
} | |||
} | |||
@@ -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; | |||
} | |||
} | |||