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"> | <patternset id="needs.jakarta.bcel"> | ||||
| <exclude name="${ant.package}/filters/util/JavaClassHelper.java" | <exclude name="${ant.package}/filters/util/JavaClassHelper.java" | ||||
| unless="bcel.present" /> | 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" /> | unless="bcel.present" /> | ||||
| </patternset> | </patternset> | ||||
| <patternset id="needs.jakarta.log4j"> | <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 | * Naming scheme where generated jar is determined from the ejb-name in | ||||
| * the deployment descripor | * 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 | * Naming scheme where the generated jar name is based on the | ||||
| * name of the directory containing the deployment descriptor | * 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 | * Naming scheme where the generated jar name is based on the name of | ||||
| * the deployment descriptor file | * 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 | * Naming scheme where the generated jar is named by the basejarname | ||||
| * attribute | * attribute | ||||
| */ | */ | ||||
| public final static String BASEJARNAME = "basejarname"; | |||||
| public static final String BASEJARNAME = "basejarname"; | |||||
| /** | /** | ||||
| * Gets the values of the NamingScheme | * Gets the values of the NamingScheme | ||||
| @@ -451,8 +451,7 @@ public class EjbJar extends MatchingTask { | |||||
| if (config.namingScheme == null) { | if (config.namingScheme == null) { | ||||
| config.namingScheme = new NamingScheme(); | config.namingScheme = new NamingScheme(); | ||||
| config.namingScheme.setValue(NamingScheme.BASEJARNAME); | 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 " + | throw new BuildException("The basejarname attribute is not compatible with the " + | ||||
| config.namingScheme.getValue() + " naming scheme"); | config.namingScheme.getValue() + " naming scheme"); | ||||
| } | } | ||||
| @@ -543,7 +542,7 @@ public class EjbJar extends MatchingTask { | |||||
| * | * | ||||
| * @throws BuildException if the config is not valid | * @throws BuildException if the config is not valid | ||||
| */ | */ | ||||
| private void validateConfig() { | |||||
| private void validateConfig() throws BuildException { | |||||
| if (config.srcDir == null) { | if (config.srcDir == null) { | ||||
| throw new BuildException("The srcDir attribute must be specified"); | throw new BuildException("The srcDir attribute must be specified"); | ||||
| } | } | ||||
| @@ -555,8 +554,7 @@ public class EjbJar extends MatchingTask { | |||||
| if (config.namingScheme == null) { | if (config.namingScheme == null) { | ||||
| config.namingScheme = new NamingScheme(); | config.namingScheme = new NamingScheme(); | ||||
| config.namingScheme.setValue(NamingScheme.DESCRIPTOR); | config.namingScheme.setValue(NamingScheme.DESCRIPTOR); | ||||
| } | |||||
| else if (config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME) && | |||||
| } else if (config.namingScheme.getValue().equals(NamingScheme.BASEJARNAME) && | |||||
| config.baseJarName == null) { | config.baseJarName == null) { | ||||
| throw new BuildException("The basejarname attribute must be specified " + | throw new BuildException("The basejarname attribute must be specified " + | ||||
| "with the basejarname naming scheme"); | "with the basejarname naming scheme"); | ||||
| @@ -618,14 +616,12 @@ public class EjbJar extends MatchingTask { | |||||
| tool.processDescriptor(files[index], saxParser); | tool.processDescriptor(files[index], saxParser); | ||||
| } | } | ||||
| } | } | ||||
| } | |||||
| catch (SAXException se) { | |||||
| } catch (SAXException se) { | |||||
| String msg = "SAXException while creating parser." | String msg = "SAXException while creating parser." | ||||
| + " Details: " | + " Details: " | ||||
| + se.getMessage(); | + se.getMessage(); | ||||
| throw new BuildException(msg, se); | throw new BuildException(msg, se); | ||||
| } | |||||
| catch (ParserConfigurationException pce) { | |||||
| } catch (ParserConfigurationException pce) { | |||||
| String msg = "ParserConfigurationException while creating parser. " | String msg = "ParserConfigurationException while creating parser. " | ||||
| + "Details: " + pce.getMessage(); | + "Details: " + pce.getMessage(); | ||||
| throw new BuildException(msg, pce); | throw new BuildException(msg, pce); | ||||
| @@ -68,20 +68,17 @@ import java.util.ArrayList; | |||||
| import java.util.jar.JarOutputStream; | import java.util.jar.JarOutputStream; | ||||
| import java.util.jar.Manifest; | import java.util.jar.Manifest; | ||||
| import java.util.zip.ZipEntry; | import java.util.zip.ZipEntry; | ||||
| import java.util.Enumeration; | |||||
| import javax.xml.parsers.SAXParser; | import javax.xml.parsers.SAXParser; | ||||
| import org.xml.sax.InputSource; | import org.xml.sax.InputSource; | ||||
| import org.xml.sax.SAXException; | 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.JavaClass; | ||||
| import org.apache.bcel.classfile.ClassParser; | import org.apache.bcel.classfile.ClassParser; | ||||
| import org.apache.tools.ant.Task; | import org.apache.tools.ant.Task; | ||||
| import org.apache.tools.ant.BuildException; | import org.apache.tools.ant.BuildException; | ||||
| import org.apache.tools.ant.Location; | 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 | * 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 | * deployment tools. A number of template methods are provided through which the | ||||
| * vendor specific tool can hook into the EJB creation process. | * vendor specific tool can hook into the EJB creation process. | ||||
| * | |||||
| * @author Conor MacNeill | |||||
| */ | */ | ||||
| public class GenericDeploymentTool implements EJBDeploymentTool { | 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; | private EjbJar.Config config; | ||||
| @@ -123,8 +127,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| private String genericJarSuffix = "-generic.jar"; | 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; | private Task task; | ||||
| @@ -145,8 +149,25 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| private DescriptorHandler handler; | 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. | * @param inDir the destination directory. | ||||
| */ | */ | ||||
| public void setDestdir(File inDir) { | public void setDestdir(File inDir) { | ||||
| @@ -155,6 +176,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| /** | /** | ||||
| * Get the desitination directory. | * Get the desitination directory. | ||||
| * | |||||
| * @return the destination directory into which EJB jars are to be written | |||||
| */ | */ | ||||
| protected File getDestDir() { | protected File getDestDir() { | ||||
| return destDir; | return destDir; | ||||
| @@ -163,6 +186,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| /** | /** | ||||
| * Set the task which owns this tool | * Set the task which owns this tool | ||||
| * | |||||
| * @param task the Task to which this deployment tool is associated. | |||||
| */ | */ | ||||
| public void setTask(Task task) { | public void setTask(Task task) { | ||||
| this.task = task; | this.task = task; | ||||
| @@ -170,6 +195,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| /** | /** | ||||
| * Get the task for this tool. | * Get the task for this tool. | ||||
| * | |||||
| * @return the Task instance this tool is associated with. | |||||
| */ | */ | ||||
| protected Task getTask() { | protected Task getTask() { | ||||
| return task; | return task; | ||||
| @@ -177,13 +204,18 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| /** | /** | ||||
| * Get the basename terminator. | * Get the basename terminator. | ||||
| * | |||||
| * @return an ejbjar task configuration | |||||
| */ | */ | ||||
| protected EjbJar.Config getConfig() { | protected EjbJar.Config getConfig() { | ||||
| return config; | 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() { | protected boolean usingBaseJarName() { | ||||
| return config.baseJarName != null; | return config.baseJarName != null; | ||||
| @@ -199,6 +231,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| /** | /** | ||||
| * Add the classpath for the user classes | * Add the classpath for the user classes | ||||
| * | |||||
| * @return a Path instance to be configured by Ant. | |||||
| */ | */ | ||||
| public Path createClasspath() { | public Path createClasspath() { | ||||
| if (classpath == null) { | if (classpath == null) { | ||||
| @@ -209,6 +243,8 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| /** | /** | ||||
| * Set the classpath to be used for this compilation. | * Set the classpath to be used for this compilation. | ||||
| * | |||||
| * @param classpath the classpath to be used for this build. | |||||
| */ | */ | ||||
| public void setClasspath(Path classpath) { | public void setClasspath(Path classpath) { | ||||
| this.classpath = 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 | * Get the classpath by combining the one from the surrounding task, if any | ||||
| * and the one from this tool. | * and the one from this tool. | ||||
| * | |||||
| * @return the combined classpath | |||||
| */ | */ | ||||
| protected Path getCombinedClasspath() { | protected Path getCombinedClasspath() { | ||||
| Path combinedPath = classpath; | Path combinedPath = classpath; | ||||
| if (config.classpath != null) { | if (config.classpath != null) { | ||||
| if (combinedPath == null) { | if (combinedPath == null) { | ||||
| combinedPath = config.classpath; | combinedPath = config.classpath; | ||||
| } | |||||
| else { | |||||
| } else { | |||||
| combinedPath.append(config.classpath); | combinedPath.append(config.classpath); | ||||
| } | } | ||||
| } | } | ||||
| @@ -232,10 +269,21 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| return combinedPath; | 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) { | protected void log(String message, int level) { | ||||
| getTask().log(message, 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() { | protected Location getLocation() { | ||||
| return getTask().getLocation(); | return getTask().getLocation(); | ||||
| } | } | ||||
| @@ -243,10 +291,15 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| /** | /** | ||||
| * Configure this tool for use in the ejbjar task. | * Configure this tool for use in the ejbjar task. | ||||
| * | |||||
| * @param config the configuration from the surrounding ejbjar task. | |||||
| */ | */ | ||||
| public void configure(EjbJar.Config config) { | public void configure(EjbJar.Config config) { | ||||
| this.config = config; | this.config = config; | ||||
| dependencyAnalyzer.addClassPath(new Path(task.getProject(), | |||||
| config.srcDir.getPath())); | |||||
| dependencyAnalyzer.addClassPath(config.classpath); | |||||
| classpathLoader = null; | classpathLoader = null; | ||||
| } | } | ||||
| @@ -271,7 +324,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| if (!addedfiles.contains(logicalFilename)) { | if (!addedfiles.contains(logicalFilename)) { | ||||
| iStream = new FileInputStream(inputFile); | iStream = new FileInputStream(inputFile); | ||||
| // Create the zip entry and add it to the jar file | // 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); | jStream.putNextEntry(zipEntry); | ||||
| // Create the file input stream, and buffer everything over | // 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 | //add it to list of files in jar | ||||
| addedfiles.add(logicalFilename); | addedfiles.add(logicalFilename); | ||||
| } | } | ||||
| } | |||||
| catch (IOException ioe) { | |||||
| } catch (IOException ioe) { | |||||
| log("WARNING: IOException while adding entry " + | log("WARNING: IOException while adding entry " + | ||||
| logicalFilename + " to jarfile from " + inputFile.getPath() + " " + | logicalFilename + " to jarfile from " + inputFile.getPath() + " " + | ||||
| ioe.getClass().getName() + "-" + ioe.getMessage(), Project.MSG_WARN); | ioe.getClass().getName() + "-" + ioe.getMessage(), Project.MSG_WARN); | ||||
| } | |||||
| finally { | |||||
| } finally { | |||||
| // Close up the file input stream for the class file | // Close up the file input stream for the class file | ||||
| if (iStream != null) { | if (iStream != null) { | ||||
| try { | try { | ||||
| iStream.close(); | iStream.close(); | ||||
| } | |||||
| catch (IOException closeException) {} | |||||
| } catch (IOException closeException) {} | |||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| @@ -383,23 +433,20 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| String publicId = getPublicId(); | String publicId = getPublicId(); | ||||
| writeJar(baseName, jarFile, ejbFiles, publicId); | writeJar(baseName, jarFile, ejbFiles, publicId); | ||||
| } | |||||
| else { | |||||
| } else { | |||||
| // Log that the file is up to date... | // Log that the file is up to date... | ||||
| log(jarFile.toString() + " is up to date.", | log(jarFile.toString() + " is up to date.", | ||||
| Project.MSG_VERBOSE); | Project.MSG_VERBOSE); | ||||
| } | } | ||||
| } | |||||
| catch (SAXException se) { | |||||
| } catch (SAXException se) { | |||||
| String msg = "SAXException while parsing '" | String msg = "SAXException while parsing '" | ||||
| + descriptorFileName.toString() | + descriptorFileName.toString() | ||||
| + "'. This probably indicates badly-formed XML." | + "'. This probably indicates badly-formed XML." | ||||
| + " Details: " | + " Details: " | ||||
| + se.getMessage(); | + se.getMessage(); | ||||
| throw new BuildException(msg, se); | throw new BuildException(msg, se); | ||||
| } | |||||
| catch (IOException ioe) { | |||||
| } catch (IOException ioe) { | |||||
| String msg = "IOException while parsing'" | String msg = "IOException while parsing'" | ||||
| + descriptorFileName.toString() | + descriptorFileName.toString() | ||||
| + "'. This probably indicates that the descriptor" | + "'. This probably indicates that the descriptor" | ||||
| @@ -466,8 +513,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| if (descriptorStream != null) { | if (descriptorStream != null) { | ||||
| try { | try { | ||||
| descriptorStream.close(); | descriptorStream.close(); | ||||
| } | |||||
| catch (IOException closeException) {} | |||||
| } catch (IOException closeException) {} | |||||
| } | } | ||||
| } | } | ||||
| @@ -566,8 +612,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| int index = canonicalDescriptor.lastIndexOf('/'); | int index = canonicalDescriptor.lastIndexOf('/'); | ||||
| if (index == -1) { | if (index == -1) { | ||||
| ddPrefix = ""; | ddPrefix = ""; | ||||
| } | |||||
| else { | |||||
| } else { | |||||
| ddPrefix = descriptorFileName.substring(0, index + 1); | 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 | // Loop through the files seeing if any has been touched | ||||
| // more recently than the destination jar. | // more recently than the destination jar. | ||||
| while(fileIter.hasNext()) { | |||||
| while (fileIter.hasNext()) { | |||||
| File currentFile = (File) fileIter.next(); | File currentFile = (File) fileIter.next(); | ||||
| if (lastBuild < currentFile.lastModified()) { | if (lastBuild < currentFile.lastModified()) { | ||||
| log("Build needed because " + currentFile.getPath() + " is out of date", | 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 | * every vendor-specific <code>DeploymentTool</code> will need to reference | ||||
| * this value or may want to determine this value in a vendor-specific way. | * 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() { | protected String getPublicId() { | ||||
| return handler.getPublicId(); | return handler.getPublicId(); | ||||
| @@ -677,15 +722,13 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| File manifestFile = new File(getConfig().descriptorDir, baseName + "-manifest.mf"); | File manifestFile = new File(getConfig().descriptorDir, baseName + "-manifest.mf"); | ||||
| if (manifestFile.exists()) { | if (manifestFile.exists()) { | ||||
| in = new FileInputStream(manifestFile); | in = new FileInputStream(manifestFile); | ||||
| } | |||||
| else if (config.manifest != null) { | |||||
| } else if (config.manifest != null) { | |||||
| in = new FileInputStream(config.manifest); | in = new FileInputStream(config.manifest); | ||||
| if ( in == null ) { | if ( in == null ) { | ||||
| throw new BuildException("Could not find manifest file: " + config.manifest, | throw new BuildException("Could not find manifest file: " + config.manifest, | ||||
| getLocation()); | getLocation()); | ||||
| } | } | ||||
| } | |||||
| else { | |||||
| } else { | |||||
| String defaultManifest = "/org/apache/tools/ant/defaultManifest.mf"; | String defaultManifest = "/org/apache/tools/ant/defaultManifest.mf"; | ||||
| in = this.getClass().getResourceAsStream(defaultManifest); | in = this.getClass().getResourceAsStream(defaultManifest); | ||||
| if ( in == null ) { | if ( in == null ) { | ||||
| @@ -695,11 +738,9 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| } | } | ||||
| manifest = new Manifest(in); | manifest = new Manifest(in); | ||||
| } | |||||
| catch (IOException e) { | |||||
| } catch (IOException e) { | |||||
| throw new BuildException ("Unable to read manifest", e, getLocation()); | throw new BuildException ("Unable to read manifest", e, getLocation()); | ||||
| } | |||||
| finally { | |||||
| } finally { | |||||
| if (in != null) { | if (in != null) { | ||||
| in.close(); | in.close(); | ||||
| } | } | ||||
| @@ -728,11 +769,10 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| for (int i = 0, n = innerfiles.length; i < n; i++) { | for (int i = 0, n = innerfiles.length; i < n; i++) { | ||||
| //get and clean up innerclass name | //get and clean up innerclass name | ||||
| int entryIndex = entryName.lastIndexOf(entryFile.getName()) -1; | |||||
| int entryIndex = entryName.lastIndexOf(entryFile.getName()) - 1; | |||||
| if ( entryIndex < 0) { | if ( entryIndex < 0) { | ||||
| entryName = innerfiles[i]; | entryName = innerfiles[i]; | ||||
| } | |||||
| else { | |||||
| } else { | |||||
| entryName = entryName.substring(0, entryIndex) + File.separatorChar + innerfiles[i]; | entryName = entryName.substring(0, entryIndex) + File.separatorChar + innerfiles[i]; | ||||
| } | } | ||||
| // link the file | // 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 '" | String msg = "IOException while processing ejb-jar file '" | ||||
| + jarfile.toString() | + jarfile.toString() | ||||
| + "'. Details: " | + "'. Details: " | ||||
| + ioe.getMessage(); | + ioe.getMessage(); | ||||
| throw new BuildException(msg, ioe); | throw new BuildException(msg, ioe); | ||||
| } | |||||
| finally { | |||||
| } finally { | |||||
| if (jarStream != null) { | if (jarStream != null) { | ||||
| try { | try { | ||||
| jarStream.close(); | jarStream.close(); | ||||
| } | |||||
| catch (IOException closeException) {} | |||||
| } catch (IOException closeException) {} | |||||
| } | } | ||||
| } | } | ||||
| } // end of writeJar | } // end of writeJar | ||||
| @@ -770,55 +807,35 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| * @param checkEntries files, that are extracted from the deployment descriptor | * @param checkEntries files, that are extracted from the deployment descriptor | ||||
| */ | */ | ||||
| protected void checkAndAddDependants(Hashtable checkEntries) | 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(); | Iterator i = checkEntries.keySet().iterator(); | ||||
| while (i.hasNext()) { | while (i.hasNext()) { | ||||
| String entryName = (String)i.next(); | String entryName = (String)i.next(); | ||||
| if (entryName.endsWith(".class")) { | 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. | * being added to the jar. | ||||
| * | * | ||||
| */ | */ | ||||
| protected ClassLoader getClassLoaderForBuild() | |||||
| { | |||||
| protected ClassLoader getClassLoaderForBuild() { | |||||
| if (classpathLoader != null) { | if (classpathLoader != null) { | ||||
| return classpathLoader; | return classpathLoader; | ||||
| } | } | ||||
| @@ -840,8 +856,7 @@ public class GenericDeploymentTool implements EJBDeploymentTool { | |||||
| // only generate a new ClassLoader if we have a classpath | // only generate a new ClassLoader if we have a classpath | ||||
| if (combinedClasspath == null) { | if (combinedClasspath == null) { | ||||
| classpathLoader = getClass().getClassLoader(); | classpathLoader = getClass().getClassLoader(); | ||||
| } | |||||
| else { | |||||
| } else { | |||||
| classpathLoader = new AntClassLoader(getTask().getProject(), combinedClasspath); | 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.DirectoryScanner; | ||||
| import org.apache.tools.ant.Project; | import org.apache.tools.ant.Project; | ||||
| import org.apache.tools.ant.types.FileSet; | 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 | * nested FileSet attribute provides the domain, that is used for searching | ||||
| * for dependent classes | * for dependent classes | ||||
| * | * | ||||
| * @author <a href="mailto:hengels@innovidata.com">Holger Engels</a> | * @author <a href="mailto:hengels@innovidata.com">Holger Engels</a> | ||||
| */ | */ | ||||
| public class ClassfileSet extends FileSet { | 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 { | public static class ClassRoot { | ||||
| /** The name of the root class */ | |||||
| private String rootClass; | private String rootClass; | ||||
| /** | |||||
| * Set the root class name | |||||
| * | |||||
| * @param name the name of the root class | |||||
| */ | |||||
| public void setClassname(String name) { | public void setClassname(String name) { | ||||
| this.rootClass = name; | this.rootClass = name; | ||||
| } | } | ||||
| /** | |||||
| * Get the name of the root class | |||||
| * | |||||
| * @return the name of the root class. | |||||
| */ | |||||
| public String getClassname() { | public String getClassname() { | ||||
| return rootClass; | return rootClass; | ||||
| } | } | ||||
| } | } | ||||
| /** | |||||
| * Default constructor | |||||
| */ | |||||
| public ClassfileSet() { | |||||
| } | |||||
| /** | |||||
| * Create a ClassfileSet from another ClassfileSet | |||||
| * | |||||
| * @param s the other classfileset | |||||
| */ | |||||
| protected ClassfileSet(ClassfileSet s) { | protected ClassfileSet(ClassfileSet s) { | ||||
| super(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. | * 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) { | public DirectoryScanner getDirectoryScanner(Project p) { | ||||
| DependScanner scanner = new DependScanner(); | DependScanner scanner = new DependScanner(); | ||||
| @@ -110,10 +145,20 @@ public class ClassfileSet extends FileSet { | |||||
| return scanner; | return scanner; | ||||
| } | } | ||||
| /** | |||||
| * Add a nested root class definition to this class file set | |||||
| * | |||||
| * @param root the configured class root. | |||||
| */ | |||||
| public void addConfiguredRoot(ClassRoot root) { | public void addConfiguredRoot(ClassRoot root) { | ||||
| rootClasses.add(root.getClassname()); | rootClasses.add(root.getClassname()); | ||||
| } | } | ||||
| /** | |||||
| * Clone this data type. | |||||
| * | |||||
| * @return a clone of the class file set | |||||
| */ | |||||
| public Object clone() { | public Object clone() { | ||||
| if (isReference()) { | if (isReference()) { | ||||
| return new ClassfileSet((ClassfileSet) getRef(getProject())); | return new ClassfileSet((ClassfileSet) getRef(getProject())); | ||||
| @@ -54,43 +54,43 @@ | |||||
| package org.apache.tools.ant.types.optional.depend; | package org.apache.tools.ant.types.optional.depend; | ||||
| import java.io.File; | 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.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 | * An interface used to describe the actions required by any type of | ||||
| * directory scanner. | * 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 { | 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 | * Sets the basedir for scanning. This is the directory that is scanned | ||||
| * recursively. | * recursively. | ||||
| @@ -100,6 +100,7 @@ public class DependScanner extends DirectoryScanner { | |||||
| public void setBasedir(File basedir) { | public void setBasedir(File basedir) { | ||||
| this.basedir = basedir; | this.basedir = basedir; | ||||
| } | } | ||||
| /** | /** | ||||
| * Gets the basedir that is used for scanning. | * Gets the basedir that is used for scanning. | ||||
| * | * | ||||
| @@ -108,11 +109,11 @@ public class DependScanner extends DirectoryScanner { | |||||
| public File getBasedir() { return basedir; } | 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; | this.rootClasses = rootClasses; | ||||
| } | } | ||||
| @@ -125,7 +126,11 @@ public class DependScanner extends DirectoryScanner { | |||||
| int count = included.size(); | int count = included.size(); | ||||
| String[] files = new String[count]; | String[] files = new String[count]; | ||||
| for (int i = 0; i < count; i++) { | 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]); | //System.err.println(" " + files[i]); | ||||
| } | } | ||||
| return files; | return files; | ||||
| @@ -136,68 +141,86 @@ public class DependScanner extends DirectoryScanner { | |||||
| * | * | ||||
| * @exception IllegalStateException when basedir was set incorrecly | * @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 { | 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; | |||||
| } | |||||
| } | |||||