blob: 312a3ea6d67f748abcb3e46f208b02991b547fa1 [file] [log] [blame]
/*
* 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;
}
}