1. /*
  2. * Copyright 2002,2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.util.depend;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.util.Enumeration;
  21. import java.util.Vector;
  22. import java.util.zip.ZipFile;
  23. import org.apache.tools.ant.types.Path;
  24. /**
  25. * An abstract implementation of the analyzer interface providing support
  26. * for the bulk of interface methods.
  27. *
  28. */
  29. public abstract class AbstractAnalyzer implements DependencyAnalyzer {
  30. /** Maximum number of loops for looking for indirect dependencies. */
  31. public static final int MAX_LOOPS = 1000;
  32. /** The source path for the source files */
  33. private Path sourcePath = new Path(null);
  34. /** The classpath containg dirs and jars of class files */
  35. private Path classPath = new Path(null);
  36. /** The list of root classes */
  37. private Vector rootClasses = new Vector();
  38. /** true if dependencies have been determined */
  39. private boolean determined = false;
  40. /** the list of File objects that the root classes depend upon */
  41. private Vector fileDependencies;
  42. /** the list of java classes the root classes depend upon */
  43. private Vector classDependencies;
  44. /** true if indirect dependencies should be gathered */
  45. private boolean closure = true;
  46. /** Setup the analyzer */
  47. protected AbstractAnalyzer() {
  48. reset();
  49. }
  50. /**
  51. * Set the closure flag. If this flag is true the analyzer will traverse
  52. * all class relationships until it has collected the entire set of
  53. * direct and indirect dependencies
  54. *
  55. * @param closure true if dependencies should be traversed to determine
  56. * indirect dependencies.
  57. */
  58. public void setClosure(boolean closure) {
  59. this.closure = closure;
  60. }
  61. /**
  62. * Get the list of files in the file system upon which the root classes
  63. * depend. The files will be either the classfiles or jar files upon
  64. * which the root classes depend.
  65. *
  66. * @return an enumeration of File instances.
  67. */
  68. public Enumeration getFileDependencies() {
  69. if (!supportsFileDependencies()) {
  70. throw new RuntimeException("File dependencies are not supported "
  71. + "by this analyzer");
  72. }
  73. if (!determined) {
  74. determineDependencies(fileDependencies, classDependencies);
  75. }
  76. return fileDependencies.elements();
  77. }
  78. /**
  79. * Get the list of classes upon which root classes depend. This is a
  80. * list of Java classnames in dot notation.
  81. *
  82. * @return an enumeration of Strings, each being the name of a Java
  83. * class in dot notation.
  84. */
  85. public Enumeration getClassDependencies() {
  86. if (!determined) {
  87. determineDependencies(fileDependencies, classDependencies);
  88. }
  89. return classDependencies.elements();
  90. }
  91. /**
  92. * Get the file that contains the class definition
  93. *
  94. * @param classname the name of the required class
  95. * @return the file instance, zip or class, containing the
  96. * class or null if the class could not be found.
  97. * @exception IOException if the files in the classpath cannot be read.
  98. */
  99. public File getClassContainer(String classname) throws IOException {
  100. String classLocation = classname.replace('.', '/') + ".class";
  101. // we look through the classpath elements. If the element is a dir
  102. // we look for the file. IF it is a zip, we look for the zip entry
  103. return getResourceContainer(classLocation, classPath.list());
  104. }
  105. /**
  106. * Get the file that contains the class source.
  107. *
  108. * @param classname the name of the required class
  109. * @return the file instance, zip or java, containing the
  110. * source or null if the source for the class could not be found.
  111. * @exception IOException if the files in the sourcepath cannot be read.
  112. */
  113. public File getSourceContainer(String classname) throws IOException {
  114. String sourceLocation = classname.replace('.', '/') + ".java";
  115. // we look through the source path elements. If the element is a dir
  116. // we look for the file. If it is a zip, we look for the zip entry.
  117. // This isn't normal for source paths but we get it for free
  118. return getResourceContainer(sourceLocation, sourcePath.list());
  119. }
  120. /**
  121. * Add a source path to the source path used by this analyzer. The
  122. * elements in the given path contain the source files for the classes
  123. * being analyzed. Not all analyzers will use this information.
  124. *
  125. * @param sourcePath The Path instance specifying the source path
  126. * elements.
  127. */
  128. public void addSourcePath(Path sourcePath) {
  129. if (sourcePath == null) {
  130. return;
  131. }
  132. this.sourcePath.append(sourcePath);
  133. this.sourcePath.setProject(sourcePath.getProject());
  134. }
  135. /**
  136. * Add a classpath to the classpath being used by the analyzer. The
  137. * classpath contains the binary classfiles for the classes being
  138. * analyzed The elements may either be the directories or jar files.Not
  139. * all analyzers will use this information.
  140. *
  141. * @param classPath the Path instance specifying the classpath elements
  142. */
  143. public void addClassPath(Path classPath) {
  144. if (classPath == null) {
  145. return;
  146. }
  147. this.classPath.append(classPath);
  148. this.classPath.setProject(classPath.getProject());
  149. }
  150. /**
  151. * Add a root class. The root classes are used to drive the
  152. * determination of dependency information. The analyzer will start at
  153. * the root classes and add dependencies from there.
  154. *
  155. * @param className the name of the class in Java dot notation.
  156. */
  157. public void addRootClass(String className) {
  158. if (className == null) {
  159. return;
  160. }
  161. if (!rootClasses.contains(className)) {
  162. rootClasses.addElement(className);
  163. }
  164. }
  165. /**
  166. * Configure an aspect of the analyzer. The set of aspects that are
  167. * supported is specific to each analyzer instance.
  168. *
  169. * @param name the name of the aspect being configured
  170. * @param info the configuration info.
  171. */
  172. public void config(String name, Object info) {
  173. // do nothing by default
  174. }
  175. /**
  176. * Reset the dependency list. This will reset the determined
  177. * dependencies and the also list of root classes.
  178. */
  179. public void reset() {
  180. rootClasses.removeAllElements();
  181. determined = false;
  182. fileDependencies = new Vector();
  183. classDependencies = new Vector();
  184. }
  185. /**
  186. * Get an enumeration of the root classes
  187. *
  188. * @return an enumeration of Strings, each of which is a class name
  189. * for a root class.
  190. */
  191. protected Enumeration getRootClasses() {
  192. return rootClasses.elements();
  193. }
  194. /**
  195. * Indicate if the analyzer is required to follow
  196. * indirect class relationships.
  197. *
  198. * @return true if indirect relationships should be followed.
  199. */
  200. protected boolean isClosureRequired() {
  201. return closure;
  202. }
  203. /**
  204. * Determine the dependencies of the current set of root classes
  205. *
  206. * @param files a vector into which Files upon which the root classes
  207. * depend should be placed.
  208. * @param classes a vector of Strings into which the names of classes
  209. * upon which the root classes depend should be placed.
  210. */
  211. protected abstract void determineDependencies(Vector files, Vector classes);
  212. /**
  213. * Indicate if the particular subclass supports file dependency
  214. * information.
  215. *
  216. * @return true if file dependencies are supported.
  217. */
  218. protected abstract boolean supportsFileDependencies();
  219. /**
  220. * Get the file that contains the resource
  221. *
  222. * @param resourceLocation the name of the required resource.
  223. * @param paths the paths which will be searched for the resource.
  224. * @return the file instance, zip or class, containing the
  225. * class or null if the class could not be found.
  226. * @exception IOException if the files in the given paths cannot be read.
  227. */
  228. private File getResourceContainer(String resourceLocation, String[] paths)
  229. throws IOException {
  230. for (int i = 0; i < paths.length; ++i) {
  231. File element = new File(paths[i]);
  232. if (!element.exists()) {
  233. continue;
  234. }
  235. if (element.isDirectory()) {
  236. File resource = new File(element, resourceLocation);
  237. if (resource.exists()) {
  238. return resource;
  239. }
  240. } else {
  241. // must be a zip of some sort
  242. ZipFile zipFile = null;
  243. try {
  244. zipFile = new ZipFile(element);
  245. if (zipFile.getEntry(resourceLocation) != null) {
  246. return element;
  247. }
  248. } finally {
  249. if (zipFile != null) {
  250. zipFile.close();
  251. }
  252. }
  253. }
  254. }
  255. return null;
  256. }
  257. }