001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018: package org.apache.tools.ant.util.depend;
019:
020: import java.io.File;
021: import java.io.IOException;
022: import java.util.Enumeration;
023: import java.util.Vector;
024: import java.util.zip.ZipFile;
025: import org.apache.tools.ant.types.Path;
026:
027: /**
028: * An abstract implementation of the analyzer interface providing support
029: * for the bulk of interface methods.
030: *
031: */
032: public abstract class AbstractAnalyzer implements DependencyAnalyzer {
033: /** Maximum number of loops for looking for indirect dependencies. */
034: public static final int MAX_LOOPS = 1000;
035:
036: /** The source path for the source files */
037: private Path sourcePath = new Path(null);
038:
039: /** The classpath containg dirs and jars of class files */
040: private Path classPath = new Path(null);
041:
042: /** The list of root classes */
043: private Vector rootClasses = new Vector();
044:
045: /** true if dependencies have been determined */
046: private boolean determined = false;
047:
048: /** the list of File objects that the root classes depend upon */
049: private Vector fileDependencies;
050: /** the list of java classes the root classes depend upon */
051: private Vector classDependencies;
052:
053: /** true if indirect dependencies should be gathered */
054: private boolean closure = true;
055:
056: /** Setup the analyzer */
057: protected AbstractAnalyzer() {
058: reset();
059: }
060:
061: /**
062: * Set the closure flag. If this flag is true the analyzer will traverse
063: * all class relationships until it has collected the entire set of
064: * direct and indirect dependencies
065: *
066: * @param closure true if dependencies should be traversed to determine
067: * indirect dependencies.
068: */
069: public void setClosure(boolean closure) {
070: this .closure = closure;
071: }
072:
073: /**
074: * Get the list of files in the file system upon which the root classes
075: * depend. The files will be either the classfiles or jar files upon
076: * which the root classes depend.
077: *
078: * @return an enumeration of File instances.
079: */
080: public Enumeration getFileDependencies() {
081: if (!supportsFileDependencies()) {
082: throw new RuntimeException(
083: "File dependencies are not supported "
084: + "by this analyzer");
085: }
086: if (!determined) {
087: determineDependencies(fileDependencies, classDependencies);
088: }
089: return fileDependencies.elements();
090: }
091:
092: /**
093: * Get the list of classes upon which root classes depend. This is a
094: * list of Java classnames in dot notation.
095: *
096: * @return an enumeration of Strings, each being the name of a Java
097: * class in dot notation.
098: */
099: public Enumeration getClassDependencies() {
100: if (!determined) {
101: determineDependencies(fileDependencies, classDependencies);
102: }
103: return classDependencies.elements();
104: }
105:
106: /**
107: * Get the file that contains the class definition
108: *
109: * @param classname the name of the required class
110: * @return the file instance, zip or class, containing the
111: * class or null if the class could not be found.
112: * @exception IOException if the files in the classpath cannot be read.
113: */
114: public File getClassContainer(String classname) throws IOException {
115: String classLocation = classname.replace('.', '/') + ".class";
116: // we look through the classpath elements. If the element is a dir
117: // we look for the file. IF it is a zip, we look for the zip entry
118: return getResourceContainer(classLocation, classPath.list());
119: }
120:
121: /**
122: * Get the file that contains the class source.
123: *
124: * @param classname the name of the required class
125: * @return the file instance, zip or java, containing the
126: * source or null if the source for the class could not be found.
127: * @exception IOException if the files in the sourcepath cannot be read.
128: */
129: public File getSourceContainer(String classname) throws IOException {
130: String sourceLocation = classname.replace('.', '/') + ".java";
131:
132: // we look through the source path elements. If the element is a dir
133: // we look for the file. If it is a zip, we look for the zip entry.
134: // This isn't normal for source paths but we get it for free
135: return getResourceContainer(sourceLocation, sourcePath.list());
136: }
137:
138: /**
139: * Add a source path to the source path used by this analyzer. The
140: * elements in the given path contain the source files for the classes
141: * being analyzed. Not all analyzers will use this information.
142: *
143: * @param sourcePath The Path instance specifying the source path
144: * elements.
145: */
146: public void addSourcePath(Path sourcePath) {
147: if (sourcePath == null) {
148: return;
149: }
150: this .sourcePath.append(sourcePath);
151: this .sourcePath.setProject(sourcePath.getProject());
152: }
153:
154: /**
155: * Add a classpath to the classpath being used by the analyzer. The
156: * classpath contains the binary classfiles for the classes being
157: * analyzed The elements may either be the directories or jar files.Not
158: * all analyzers will use this information.
159: *
160: * @param classPath the Path instance specifying the classpath elements
161: */
162: public void addClassPath(Path classPath) {
163: if (classPath == null) {
164: return;
165: }
166:
167: this .classPath.append(classPath);
168: this .classPath.setProject(classPath.getProject());
169: }
170:
171: /**
172: * Add a root class. The root classes are used to drive the
173: * determination of dependency information. The analyzer will start at
174: * the root classes and add dependencies from there.
175: *
176: * @param className the name of the class in Java dot notation.
177: */
178: public void addRootClass(String className) {
179: if (className == null) {
180: return;
181: }
182: if (!rootClasses.contains(className)) {
183: rootClasses.addElement(className);
184: }
185: }
186:
187: /**
188: * Configure an aspect of the analyzer. The set of aspects that are
189: * supported is specific to each analyzer instance.
190: *
191: * @param name the name of the aspect being configured
192: * @param info the configuration info.
193: */
194: public void config(String name, Object info) {
195: // do nothing by default
196: }
197:
198: /**
199: * Reset the dependency list. This will reset the determined
200: * dependencies and the also list of root classes.
201: */
202: public void reset() {
203: rootClasses.removeAllElements();
204: determined = false;
205: fileDependencies = new Vector();
206: classDependencies = new Vector();
207: }
208:
209: /**
210: * Get an enumeration of the root classes
211: *
212: * @return an enumeration of Strings, each of which is a class name
213: * for a root class.
214: */
215: protected Enumeration getRootClasses() {
216: return rootClasses.elements();
217: }
218:
219: /**
220: * Indicate if the analyzer is required to follow
221: * indirect class relationships.
222: *
223: * @return true if indirect relationships should be followed.
224: */
225: protected boolean isClosureRequired() {
226: return closure;
227: }
228:
229: /**
230: * Determine the dependencies of the current set of root classes
231: *
232: * @param files a vector into which Files upon which the root classes
233: * depend should be placed.
234: * @param classes a vector of Strings into which the names of classes
235: * upon which the root classes depend should be placed.
236: */
237: protected abstract void determineDependencies(Vector files,
238: Vector classes);
239:
240: /**
241: * Indicate if the particular subclass supports file dependency
242: * information.
243: *
244: * @return true if file dependencies are supported.
245: */
246: protected abstract boolean supportsFileDependencies();
247:
248: /**
249: * Get the file that contains the resource
250: *
251: * @param resourceLocation the name of the required resource.
252: * @param paths the paths which will be searched for the resource.
253: * @return the file instance, zip or class, containing the
254: * class or null if the class could not be found.
255: * @exception IOException if the files in the given paths cannot be read.
256: */
257: private File getResourceContainer(String resourceLocation,
258: String[] paths) throws IOException {
259: for (int i = 0; i < paths.length; ++i) {
260: File element = new File(paths[i]);
261: if (!element.exists()) {
262: continue;
263: }
264: if (element.isDirectory()) {
265: File resource = new File(element, resourceLocation);
266: if (resource.exists()) {
267: return resource;
268: }
269: } else {
270: // must be a zip of some sort
271: ZipFile zipFile = null;
272: try {
273: zipFile = new ZipFile(element);
274: if (zipFile.getEntry(resourceLocation) != null) {
275: return element;
276: }
277: } finally {
278: if (zipFile != null) {
279: zipFile.close();
280: }
281: }
282: }
283: }
284: return null;
285: }
286: }
|