| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| * |
| */ |
| 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. |
| * |
| */ |
| 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. |
| */ |
| public Enumeration getFileDependencies() { |
| if (!supportsFileDependencies()) { |
| throw new RuntimeException("File dependencies are not supported " |
| + "by this analyzer"); |
| } |
| 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.removeAllElements(); |
| 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; |
| } |
| } |
| |