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; | |||
| } | |||
| } | |||