1. /*
  2. * Copyright 2000-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.taskdefs.optional.depend;
  18. import java.io.BufferedReader;
  19. import java.io.File;
  20. import java.io.FileReader;
  21. import java.io.FileWriter;
  22. import java.io.IOException;
  23. import java.io.PrintWriter;
  24. import java.net.URL;
  25. import java.util.Enumeration;
  26. import java.util.Hashtable;
  27. import java.util.Vector;
  28. import org.apache.tools.ant.AntClassLoader;
  29. import org.apache.tools.ant.BuildException;
  30. import org.apache.tools.ant.DirectoryScanner;
  31. import org.apache.tools.ant.Project;
  32. import org.apache.tools.ant.taskdefs.MatchingTask;
  33. import org.apache.tools.ant.types.Path;
  34. import org.apache.tools.ant.types.Reference;
  35. import org.apache.tools.ant.util.depend.DependencyAnalyzer;
  36. /**
  37. * Generates a dependency file for a given set of classes.
  38. *
  39. */
  40. public class Depend extends MatchingTask {
  41. /**
  42. * A class (struct) user to manage information about a class
  43. *
  44. */
  45. private static class ClassFileInfo {
  46. /** The file where the class file is stored in the file system */
  47. private File absoluteFile;
  48. /** The Java class name of this class */
  49. private String className;
  50. /** The source File containing this class */
  51. private File sourceFile;
  52. /** if user has been warned about this file not having a source file */
  53. private boolean isUserWarned = false;
  54. }
  55. /** The path where source files exist */
  56. private Path srcPath;
  57. /** The path where compiled class files exist. */
  58. private Path destPath;
  59. /** The directory which contains the dependency cache. */
  60. private File cache;
  61. /** The list of source paths derived from the srcPath field. */
  62. private String[] srcPathList;
  63. /**
  64. * A map which gives for every class a list of the class which it
  65. * affects.
  66. */
  67. private Hashtable affectedClassMap;
  68. /** A map which gives information about a class */
  69. private Hashtable classFileInfoMap;
  70. /**
  71. * A map which gives the list of jars and classes from the classpath
  72. * that a class depends upon
  73. */
  74. private Hashtable classpathDependencies;
  75. /** The list of classes which are out of date. */
  76. private Hashtable outOfDateClasses;
  77. /**
  78. * indicates that the dependency relationships should be extended beyond
  79. * direct dependencies to include all classes. So if A directly affects
  80. * B and B directly affects C, then A indirectly affects C.
  81. */
  82. private boolean closure = false;
  83. /**
  84. * Flag which controls whether the reversed dependencies should be
  85. * dumped to the log
  86. */
  87. private boolean dump = false;
  88. /** The classpath to look for additional dependencies */
  89. private Path dependClasspath;
  90. /** constants used with the cache file */
  91. private static final String CACHE_FILE_NAME = "dependencies.txt";
  92. /** String Used to separate classnames in the dependency file */
  93. private static final String CLASSNAME_PREPEND = "||:";
  94. /**
  95. * Set the classpath to be used for this dependency check.
  96. *
  97. * @param classpath the classpath to be used when checking for
  98. * dependencies on elements in the classpath
  99. */
  100. public void setClasspath(Path classpath) {
  101. if (dependClasspath == null) {
  102. dependClasspath = classpath;
  103. } else {
  104. dependClasspath.append(classpath);
  105. }
  106. }
  107. /**
  108. * Gets the classpath to be used for this dependency check.
  109. *
  110. * @return the current dependency classpath
  111. */
  112. public Path getClasspath() {
  113. return dependClasspath;
  114. }
  115. /**
  116. * Adds a classpath to be used for this dependency check.
  117. *
  118. * @return A path object to be configured by Ant
  119. */
  120. public Path createClasspath() {
  121. if (dependClasspath == null) {
  122. dependClasspath = new Path(getProject());
  123. }
  124. return dependClasspath.createPath();
  125. }
  126. /**
  127. * Adds a reference to a classpath defined elsewhere.
  128. *
  129. * @param r a reference to a path object to be used as the depend
  130. * classpath
  131. */
  132. public void setClasspathRef(Reference r) {
  133. createClasspath().setRefid(r);
  134. }
  135. /**
  136. * Read the dependencies from cache file
  137. *
  138. * @return a collection of class dependencies
  139. * @exception IOException if the dependency file cannot be read
  140. */
  141. private Hashtable readCachedDependencies(File depFile) throws IOException {
  142. Hashtable dependencyMap = new Hashtable();
  143. BufferedReader in = null;
  144. try {
  145. in = new BufferedReader(new FileReader(depFile));
  146. String line = null;
  147. Vector dependencyList = null;
  148. String className = null;
  149. int prependLength = CLASSNAME_PREPEND.length();
  150. while ((line = in.readLine()) != null) {
  151. if (line.startsWith(CLASSNAME_PREPEND)) {
  152. dependencyList = new Vector();
  153. className = line.substring(prependLength);
  154. dependencyMap.put(className, dependencyList);
  155. } else {
  156. dependencyList.addElement(line);
  157. }
  158. }
  159. } finally {
  160. if (in != null) {
  161. in.close();
  162. }
  163. }
  164. return dependencyMap;
  165. }
  166. /**
  167. * Write the dependencies to cache file
  168. *
  169. * @param dependencyMap the map of dependencies to be written out.
  170. * @exception IOException if the dependency file cannot be written out.
  171. */
  172. private void writeCachedDependencies(Hashtable dependencyMap)
  173. throws IOException {
  174. if (cache != null) {
  175. PrintWriter pw = null;
  176. try {
  177. cache.mkdirs();
  178. File depFile = new File(cache, CACHE_FILE_NAME);
  179. pw = new PrintWriter(new FileWriter(depFile));
  180. Enumeration e = dependencyMap.keys();
  181. while (e.hasMoreElements()) {
  182. String className = (String) e.nextElement();
  183. pw.println(CLASSNAME_PREPEND + className);
  184. Vector dependencyList
  185. = (Vector) dependencyMap.get(className);
  186. int size = dependencyList.size();
  187. for (int x = 0; x < size; x++) {
  188. pw.println(dependencyList.elementAt(x));
  189. }
  190. }
  191. } finally {
  192. if (pw != null) {
  193. pw.close();
  194. }
  195. }
  196. }
  197. }
  198. /**
  199. * Get the classpath for dependency checking.
  200. *
  201. * This method removes the dest dirs if it is given from the dependency classpath
  202. */
  203. private Path getCheckClassPath() {
  204. if (dependClasspath == null) {
  205. return null;
  206. }
  207. String[] destPathElements = destPath.list();
  208. String[] classpathElements = dependClasspath.list();
  209. String checkPath = "";
  210. for (int i = 0; i < classpathElements.length; ++i) {
  211. String element = classpathElements[i];
  212. boolean inDestPath = false;
  213. for (int j = 0; j < destPathElements.length && !inDestPath; ++j) {
  214. inDestPath = destPathElements[j].equals(element);
  215. }
  216. if (!inDestPath) {
  217. if (checkPath.length() == 0) {
  218. checkPath = element;
  219. } else {
  220. checkPath += ":" + element;
  221. }
  222. }
  223. }
  224. if (checkPath.length() == 0) {
  225. return null;
  226. }
  227. return new Path(getProject(), checkPath);
  228. }
  229. /**
  230. * Determine the dependencies between classes. Class dependencies are
  231. * determined by examining the class references in a class file to other
  232. * classes.
  233. *
  234. * This method sets up the following fields
  235. * <ul>
  236. * <li>affectedClassMap - the list of classes each class affects</li>
  237. * <li>classFileInfoMap - information about each class</li>
  238. * <li>classpathDependencies - the list of jars and classes from the
  239. * classpath that each class depends upon.</li>
  240. * </ul>
  241. *
  242. * If required, the dependencies are written to the cache.
  243. *
  244. * @exception IOException if either the dependencies cache or the class
  245. * files cannot be read or written
  246. */
  247. private void determineDependencies() throws IOException {
  248. affectedClassMap = new Hashtable();
  249. classFileInfoMap = new Hashtable();
  250. boolean cacheDirty = false;
  251. Hashtable dependencyMap = new Hashtable();
  252. File cacheFile = null;
  253. boolean cacheFileExists = true;
  254. long cacheLastModified = Long.MAX_VALUE;
  255. // read the dependency cache from the disk
  256. if (cache != null) {
  257. cacheFile = new File(cache, CACHE_FILE_NAME);
  258. cacheFileExists = cacheFile.exists();
  259. cacheLastModified = cacheFile.lastModified();
  260. if (cacheFileExists) {
  261. dependencyMap = readCachedDependencies(cacheFile);
  262. }
  263. }
  264. Enumeration classfileEnum = getClassFiles(destPath).elements();
  265. while (classfileEnum.hasMoreElements()) {
  266. ClassFileInfo info = (ClassFileInfo) classfileEnum.nextElement();
  267. log("Adding class info for " + info.className, Project.MSG_DEBUG);
  268. classFileInfoMap.put(info.className, info);
  269. Vector dependencyList = null;
  270. if (cache != null) {
  271. // try to read the dependency info from the map if it is
  272. // not out of date
  273. if (cacheFileExists
  274. && cacheLastModified > info.absoluteFile.lastModified()) {
  275. // depFile exists and is newer than the class file
  276. // need to get dependency list from the map.
  277. dependencyList = (Vector) dependencyMap.get(info.className);
  278. }
  279. }
  280. if (dependencyList == null) {
  281. // not cached - so need to read directly from the class file
  282. DependencyAnalyzer analyzer = new AntAnalyzer();
  283. analyzer.addRootClass(info.className);
  284. analyzer.addClassPath(destPath);
  285. analyzer.setClosure(false);
  286. dependencyList = new Vector();
  287. Enumeration depEnum = analyzer.getClassDependencies();
  288. while (depEnum.hasMoreElements()) {
  289. dependencyList.addElement(depEnum.nextElement());
  290. }
  291. if (dependencyList != null) {
  292. cacheDirty = true;
  293. dependencyMap.put(info.className, dependencyList);
  294. }
  295. }
  296. // This class depends on each class in the dependency list. For each
  297. // one of those, add this class into their affected classes list
  298. Enumeration depEnum = dependencyList.elements();
  299. while (depEnum.hasMoreElements()) {
  300. String dependentClass = (String) depEnum.nextElement();
  301. Hashtable affectedClasses
  302. = (Hashtable) affectedClassMap.get(dependentClass);
  303. if (affectedClasses == null) {
  304. affectedClasses = new Hashtable();
  305. affectedClassMap.put(dependentClass, affectedClasses);
  306. }
  307. affectedClasses.put(info.className, info);
  308. }
  309. }
  310. classpathDependencies = null;
  311. Path checkPath = getCheckClassPath();
  312. if (checkPath != null) {
  313. // now determine which jars each class depends upon
  314. classpathDependencies = new Hashtable();
  315. AntClassLoader loader = getProject().createClassLoader(checkPath);
  316. Hashtable classpathFileCache = new Hashtable();
  317. Object nullFileMarker = new Object();
  318. for (Enumeration e = dependencyMap.keys(); e.hasMoreElements();) {
  319. String className = (String) e.nextElement();
  320. Vector dependencyList = (Vector) dependencyMap.get(className);
  321. Hashtable dependencies = new Hashtable();
  322. classpathDependencies.put(className, dependencies);
  323. Enumeration e2 = dependencyList.elements();
  324. while (e2.hasMoreElements()) {
  325. String dependency = (String) e2.nextElement();
  326. Object classpathFileObject
  327. = classpathFileCache.get(dependency);
  328. if (classpathFileObject == null) {
  329. classpathFileObject = nullFileMarker;
  330. if (!dependency.startsWith("java.")
  331. && !dependency.startsWith("javax.")) {
  332. URL classURL
  333. = loader.getResource(dependency.replace('.', '/') + ".class");
  334. if (classURL != null) {
  335. if (classURL.getProtocol().equals("jar")) {
  336. String jarFilePath = classURL.getFile();
  337. if (jarFilePath.startsWith("file:")) {
  338. int classMarker = jarFilePath.indexOf('!');
  339. jarFilePath = jarFilePath.substring(5, classMarker);
  340. }
  341. classpathFileObject = new File(jarFilePath);
  342. } else if (classURL.getProtocol().equals("file")) {
  343. String classFilePath = classURL.getFile();
  344. classpathFileObject = new File(classFilePath);
  345. }
  346. log("Class " + className
  347. + " depends on " + classpathFileObject
  348. + " due to " + dependency, Project.MSG_DEBUG);
  349. }
  350. }
  351. classpathFileCache.put(dependency, classpathFileObject);
  352. }
  353. if (classpathFileObject != null && classpathFileObject != nullFileMarker) {
  354. // we need to add this jar to the list for this class.
  355. File jarFile = (File) classpathFileObject;
  356. dependencies.put(jarFile, jarFile);
  357. }
  358. }
  359. }
  360. }
  361. // write the dependency cache to the disk
  362. if (cache != null && cacheDirty) {
  363. writeCachedDependencies(dependencyMap);
  364. }
  365. }
  366. /**
  367. * Delete all the class files which are out of date, by way of their
  368. * dependency on a class which is out of date
  369. *
  370. * @return the number of files deleted.
  371. */
  372. private int deleteAllAffectedFiles() {
  373. int count = 0;
  374. for (Enumeration e = outOfDateClasses.elements(); e.hasMoreElements();) {
  375. String className = (String) e.nextElement();
  376. count += deleteAffectedFiles(className);
  377. ClassFileInfo classInfo
  378. = (ClassFileInfo) classFileInfoMap.get(className);
  379. if (classInfo != null && classInfo.absoluteFile.exists()) {
  380. classInfo.absoluteFile.delete();
  381. count++;
  382. }
  383. }
  384. return count;
  385. }
  386. /**
  387. * Delete all the class files of classes which depend on the given class
  388. *
  389. * @param className the name of the class whose dependent classes will be
  390. * deleted
  391. * @return the number of class files removed
  392. */
  393. private int deleteAffectedFiles(String className) {
  394. int count = 0;
  395. Hashtable affectedClasses = (Hashtable) affectedClassMap.get(className);
  396. if (affectedClasses == null) {
  397. return count;
  398. }
  399. for (Enumeration e = affectedClasses.keys(); e.hasMoreElements();) {
  400. String affectedClass = (String) e.nextElement();
  401. ClassFileInfo affectedClassInfo
  402. = (ClassFileInfo) affectedClasses.get(affectedClass);
  403. if (!affectedClassInfo.absoluteFile.exists()) {
  404. continue;
  405. }
  406. if (affectedClassInfo.sourceFile == null) {
  407. if (!affectedClassInfo.isUserWarned) {
  408. log("The class " + affectedClass + " in file "
  409. + affectedClassInfo.absoluteFile.getPath()
  410. + " is out of date due to " + className
  411. + " but has not been deleted because its source file"
  412. + " could not be determined", Project.MSG_WARN);
  413. affectedClassInfo.isUserWarned = true;
  414. }
  415. continue;
  416. }
  417. log("Deleting file " + affectedClassInfo.absoluteFile.getPath()
  418. + " since " + className + " out of date", Project.MSG_VERBOSE);
  419. affectedClassInfo.absoluteFile.delete();
  420. count++;
  421. if (closure) {
  422. count += deleteAffectedFiles(affectedClass);
  423. } else {
  424. // without closure we may delete an inner class but not the
  425. // top level class which would not trigger a recompile.
  426. if (affectedClass.indexOf("$") == -1) {
  427. continue;
  428. }
  429. // need to delete the main class
  430. String topLevelClassName
  431. = affectedClass.substring(0, affectedClass.indexOf("$"));
  432. log("Top level class = " + topLevelClassName,
  433. Project.MSG_VERBOSE);
  434. ClassFileInfo topLevelClassInfo
  435. = (ClassFileInfo) classFileInfoMap.get(topLevelClassName);
  436. if (topLevelClassInfo != null
  437. && topLevelClassInfo.absoluteFile.exists()) {
  438. log("Deleting file "
  439. + topLevelClassInfo.absoluteFile.getPath()
  440. + " since one of its inner classes was removed",
  441. Project.MSG_VERBOSE);
  442. topLevelClassInfo.absoluteFile.delete();
  443. count++;
  444. if (closure) {
  445. count += deleteAffectedFiles(topLevelClassName);
  446. }
  447. }
  448. }
  449. }
  450. return count;
  451. }
  452. /**
  453. * Dump the dependency information loaded from the classes to the Ant log
  454. */
  455. private void dumpDependencies() {
  456. log("Reverse Dependency Dump for " + affectedClassMap.size()
  457. + " classes:", Project.MSG_DEBUG);
  458. Enumeration classEnum = affectedClassMap.keys();
  459. while (classEnum.hasMoreElements()) {
  460. String className = (String) classEnum.nextElement();
  461. log(" Class " + className + " affects:", Project.MSG_DEBUG);
  462. Hashtable affectedClasses
  463. = (Hashtable) affectedClassMap.get(className);
  464. Enumeration affectedClassEnum = affectedClasses.keys();
  465. while (affectedClassEnum.hasMoreElements()) {
  466. String affectedClass = (String) affectedClassEnum.nextElement();
  467. ClassFileInfo info
  468. = (ClassFileInfo) affectedClasses.get(affectedClass);
  469. log(" " + affectedClass + " in "
  470. + info.absoluteFile.getPath(), Project.MSG_DEBUG);
  471. }
  472. }
  473. if (classpathDependencies != null) {
  474. log("Classpath file dependencies (Forward):", Project.MSG_DEBUG);
  475. Enumeration classpathEnum = classpathDependencies.keys();
  476. while (classpathEnum.hasMoreElements()) {
  477. String className = (String) classpathEnum.nextElement();
  478. log(" Class " + className + " depends on:", Project.MSG_DEBUG);
  479. Hashtable dependencies
  480. = (Hashtable) classpathDependencies.get(className);
  481. Enumeration classpathFileEnum = dependencies.elements();
  482. while (classpathFileEnum.hasMoreElements()) {
  483. File classpathFile = (File) classpathFileEnum.nextElement();
  484. log(" " + classpathFile.getPath(), Project.MSG_DEBUG);
  485. }
  486. }
  487. }
  488. }
  489. private void determineOutOfDateClasses() {
  490. outOfDateClasses = new Hashtable();
  491. for (int i = 0; i < srcPathList.length; i++) {
  492. File srcDir = (File) getProject().resolveFile(srcPathList[i]);
  493. if (srcDir.exists()) {
  494. DirectoryScanner ds = this.getDirectoryScanner(srcDir);
  495. String[] files = ds.getIncludedFiles();
  496. scanDir(srcDir, files);
  497. }
  498. }
  499. // now check classpath file dependencies
  500. if (classpathDependencies == null) {
  501. return;
  502. }
  503. Enumeration classpathDepsEnum = classpathDependencies.keys();
  504. while (classpathDepsEnum.hasMoreElements()) {
  505. String className = (String) classpathDepsEnum.nextElement();
  506. if (outOfDateClasses.containsKey(className)) {
  507. continue;
  508. }
  509. ClassFileInfo info
  510. = (ClassFileInfo) classFileInfoMap.get(className);
  511. // if we have no info about the class - it may have been deleted already and we
  512. // are using cached info.
  513. if (info != null) {
  514. Hashtable dependencies
  515. = (Hashtable) classpathDependencies.get(className);
  516. for (Enumeration e2 = dependencies.elements(); e2.hasMoreElements();) {
  517. File classpathFile = (File) e2.nextElement();
  518. if (classpathFile.lastModified()
  519. > info.absoluteFile.lastModified()) {
  520. log("Class " + className
  521. + " is out of date with respect to "
  522. + classpathFile, Project.MSG_DEBUG);
  523. outOfDateClasses.put(className, className);
  524. break;
  525. }
  526. }
  527. }
  528. }
  529. }
  530. /**
  531. * Does the work.
  532. *
  533. * @exception BuildException Thrown in case of an unrecoverable error.
  534. */
  535. public void execute() throws BuildException {
  536. try {
  537. long start = System.currentTimeMillis();
  538. if (srcPath == null) {
  539. throw new BuildException("srcdir attribute must be set",
  540. getLocation());
  541. }
  542. srcPathList = srcPath.list();
  543. if (srcPathList.length == 0) {
  544. throw new BuildException("srcdir attribute must be non-empty",
  545. getLocation());
  546. }
  547. if (destPath == null) {
  548. destPath = srcPath;
  549. }
  550. if (cache != null && cache.exists() && !cache.isDirectory()) {
  551. throw new BuildException("The cache, if specified, must "
  552. + "point to a directory");
  553. }
  554. if (cache != null && !cache.exists()) {
  555. cache.mkdirs();
  556. }
  557. determineDependencies();
  558. if (dump) {
  559. dumpDependencies();
  560. }
  561. determineOutOfDateClasses();
  562. int count = deleteAllAffectedFiles();
  563. long duration = (System.currentTimeMillis() - start) / 1000;
  564. final int summaryLogLevel;
  565. if(count>0) {
  566. summaryLogLevel = Project.MSG_INFO;
  567. } else {
  568. summaryLogLevel = Project.MSG_DEBUG;
  569. }
  570. log("Deleted " + count + " out of date files in "
  571. + duration + " seconds", summaryLogLevel);
  572. } catch (Exception e) {
  573. throw new BuildException(e);
  574. }
  575. }
  576. /**
  577. * Scans the directory looking for source files that are newer than
  578. * their class files. The results are returned in the class variable
  579. * compileList
  580. *
  581. * @param srcDir the source directory
  582. * @param files the names of the files in the source dir which are to be
  583. * checked.
  584. */
  585. protected void scanDir(File srcDir, String files[]) {
  586. for (int i = 0; i < files.length; i++) {
  587. File srcFile = new File(srcDir, files[i]);
  588. if (files[i].endsWith(".java")) {
  589. String filePath = srcFile.getPath();
  590. String className
  591. = filePath.substring(srcDir.getPath().length() + 1,
  592. filePath.length() - ".java".length());
  593. className = ClassFileUtils.convertSlashName(className);
  594. ClassFileInfo info
  595. = (ClassFileInfo) classFileInfoMap.get(className);
  596. if (info == null) {
  597. // there was no class file. add this class to the list
  598. outOfDateClasses.put(className, className);
  599. } else {
  600. if (srcFile.lastModified()
  601. > info.absoluteFile.lastModified()) {
  602. outOfDateClasses.put(className, className);
  603. }
  604. }
  605. }
  606. }
  607. }
  608. /**
  609. * Get the list of class files we are going to analyse.
  610. *
  611. * @param classLocations a path structure containing all the directories
  612. * where classes can be found.
  613. * @return a vector containing the classes to analyse.
  614. */
  615. private Vector getClassFiles(Path classLocations) {
  616. // break the classLocations into its components.
  617. String[] classLocationsList = classLocations.list();
  618. Vector classFileList = new Vector();
  619. for (int i = 0; i < classLocationsList.length; ++i) {
  620. File dir = new File(classLocationsList[i]);
  621. if (dir.isDirectory()) {
  622. addClassFiles(classFileList, dir, dir);
  623. }
  624. }
  625. return classFileList;
  626. }
  627. /**
  628. * Find the source file for a given class
  629. *
  630. * @param classname the classname in slash format.
  631. */
  632. private File findSourceFile(String classname) {
  633. String sourceFilename = classname + ".java";
  634. int innerIndex = classname.indexOf("$");
  635. if (innerIndex != -1) {
  636. sourceFilename = classname.substring(0, innerIndex) + ".java";
  637. }
  638. // search the various source path entries
  639. for (int i = 0; i < srcPathList.length; ++i) {
  640. File sourceFile = new File(srcPathList[i], sourceFilename);
  641. if (sourceFile.exists()) {
  642. return sourceFile;
  643. }
  644. }
  645. return null;
  646. }
  647. /**
  648. * Add the list of class files from the given directory to the class
  649. * file vector, including any subdirectories.
  650. *
  651. * @param classFileList a list of ClassFileInfo objects for all the
  652. * files in the directory tree
  653. * @param dir the directory tree to be searched, recursively, for class
  654. * files
  655. * @param root the root of the source tree. This is used to determine
  656. * the absolute class name from the relative position in the
  657. * source tree
  658. */
  659. private void addClassFiles(Vector classFileList, File dir, File root) {
  660. String[] filesInDir = dir.list();
  661. if (filesInDir == null) {
  662. return;
  663. }
  664. int length = filesInDir.length;
  665. int rootLength = root.getPath().length();
  666. for (int i = 0; i < length; ++i) {
  667. File file = new File(dir, filesInDir[i]);
  668. if (file.isDirectory()) {
  669. addClassFiles(classFileList, file, root);
  670. } else if (file.getName().endsWith(".class")) {
  671. ClassFileInfo info = new ClassFileInfo();
  672. info.absoluteFile = file;
  673. String relativeName = file.getPath().substring(rootLength + 1,
  674. file.getPath().length() - 6);
  675. info.className
  676. = ClassFileUtils.convertSlashName(relativeName);
  677. info.sourceFile = findSourceFile(relativeName);
  678. classFileList.addElement(info);
  679. }
  680. }
  681. }
  682. /**
  683. * Set the directories path to find the Java source files.
  684. *
  685. * @param srcPath the source path
  686. */
  687. public void setSrcdir(Path srcPath) {
  688. this.srcPath = srcPath;
  689. }
  690. /**
  691. * Set the destination directory where the compiled Java files exist.
  692. *
  693. * @param destPath the destination areas where build files are written
  694. */
  695. public void setDestDir(Path destPath) {
  696. this.destPath = destPath;
  697. }
  698. /**
  699. * Sets the dependency cache file.
  700. *
  701. * @param cache the dependency cache file
  702. */
  703. public void setCache(File cache) {
  704. this.cache = cache;
  705. }
  706. /**
  707. * If true, transitive dependencies are followed until the
  708. * closure of the dependency set if reached.
  709. * When not set, the depend task will only follow
  710. * direct dependencies between classes.
  711. *
  712. * @param closure indicate if dependency closure is required.
  713. */
  714. public void setClosure(boolean closure) {
  715. this.closure = closure;
  716. }
  717. /**
  718. * If true, the dependency information will be written
  719. * to the debug level log.
  720. *
  721. * @param dump set to true to dump dependency information to the log
  722. */
  723. public void setDump(boolean dump) {
  724. this.dump = dump;
  725. }
  726. }