KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > tools > ant > taskdefs > optional > depend > Depend


1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */

18 package org.apache.tools.ant.taskdefs.optional.depend;
19
20 import java.io.BufferedReader JavaDoc;
21 import java.io.File JavaDoc;
22 import java.io.FileReader JavaDoc;
23 import java.io.FileWriter JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.io.PrintWriter JavaDoc;
26 import java.net.URL JavaDoc;
27 import java.util.Enumeration JavaDoc;
28 import java.util.Hashtable JavaDoc;
29 import java.util.Vector JavaDoc;
30 import org.apache.tools.ant.AntClassLoader;
31 import org.apache.tools.ant.BuildException;
32 import org.apache.tools.ant.DirectoryScanner;
33 import org.apache.tools.ant.Project;
34 import org.apache.tools.ant.taskdefs.MatchingTask;
35 import org.apache.tools.ant.taskdefs.rmic.DefaultRmicAdapter;
36 import org.apache.tools.ant.taskdefs.rmic.WLRmic;
37 import org.apache.tools.ant.types.Path;
38 import org.apache.tools.ant.types.Reference;
39 import org.apache.tools.ant.util.FileUtils;
40 import org.apache.tools.ant.util.depend.DependencyAnalyzer;
41
42 /**
43  * Generates a dependency file for a given set of classes.
44  *
45  */

46 public class Depend extends MatchingTask {
47     /**
48      * A class (struct) user to manage information about a class
49      *
50      */

51     private static class ClassFileInfo {
52         /** The file where the class file is stored in the file system */
53         private File JavaDoc absoluteFile;
54
55         /** The Java class name of this class */
56         private String JavaDoc className;
57
58         /** The source File containing this class */
59         private File JavaDoc sourceFile;
60
61         /** if user has been warned about this file not having a source file */
62         private boolean isUserWarned = false;
63     }
64
65     /** The path where source files exist */
66     private Path srcPath;
67
68     /** The path where compiled class files exist. */
69     private Path destPath;
70
71     /** The directory which contains the dependency cache. */
72     private File JavaDoc cache;
73
74     /** The list of source paths derived from the srcPath field. */
75     private String JavaDoc[] srcPathList;
76
77     /**
78      * A map which gives for every class a list of the class which it
79      * affects.
80      */

81     private Hashtable JavaDoc affectedClassMap;
82
83     /** A map which gives information about a class */
84     private Hashtable JavaDoc classFileInfoMap;
85
86     /**
87      * A map which gives the list of jars and classes from the classpath
88      * that a class depends upon
89      */

90     private Hashtable JavaDoc classpathDependencies;
91
92     /** The list of classes which are out of date. */
93     private Hashtable JavaDoc outOfDateClasses;
94
95     /**
96      * indicates that the dependency relationships should be extended beyond
97      * direct dependencies to include all classes. So if A directly affects
98      * B and B directly affects C, then A indirectly affects C.
99      */

100     private boolean closure = false;
101
102     /**
103      * flag to enable warning if we encounter RMI stubs
104      */

105     private boolean warnOnRmiStubs = true;
106
107     /**
108      * Flag which controls whether the reversed dependencies should be
109      * dumped to the log
110      */

111     private boolean dump = false;
112
113     /** The classpath to look for additional dependencies */
114     private Path dependClasspath;
115
116     /** constants used with the cache file */
117     private static final String JavaDoc CACHE_FILE_NAME = "dependencies.txt";
118     /** String Used to separate classnames in the dependency file */
119     private static final String JavaDoc CLASSNAME_PREPEND = "||:";
120
121     /**
122      * Set the classpath to be used for this dependency check.
123      *
124      * @param classpath the classpath to be used when checking for
125      * dependencies on elements in the classpath
126      */

127     public void setClasspath(Path classpath) {
128         if (dependClasspath == null) {
129             dependClasspath = classpath;
130         } else {
131             dependClasspath.append(classpath);
132         }
133     }
134
135     /**
136      * Gets the classpath to be used for this dependency check.
137      *
138      * @return the current dependency classpath
139      */

140     public Path getClasspath() {
141         return dependClasspath;
142     }
143
144     /**
145      * Adds a classpath to be used for this dependency check.
146      *
147      * @return A path object to be configured by Ant
148      */

149     public Path createClasspath() {
150         if (dependClasspath == null) {
151             dependClasspath = new Path(getProject());
152         }
153         return dependClasspath.createPath();
154     }
155
156     /**
157      * Adds a reference to a classpath defined elsewhere.
158      *
159      * @param r a reference to a path object to be used as the depend
160      * classpath
161      */

162     public void setClasspathRef(Reference r) {
163         createClasspath().setRefid(r);
164     }
165
166     /**
167      * Flag to set to true if you want dependency issues with RMI
168      * stubs to appear at warning level.
169      * @param warnOnRmiStubs if true set dependency issues to appear at warning level.
170      * @since Ant1.7
171      */

172     public void setWarnOnRmiStubs(boolean warnOnRmiStubs) {
173         this.warnOnRmiStubs = warnOnRmiStubs;
174     }
175
176     /**
177      * Read the dependencies from cache file
178      *
179      * @return a collection of class dependencies
180      * @exception IOException if the dependency file cannot be read
181      */

182     private Hashtable JavaDoc readCachedDependencies(File JavaDoc depFile) throws IOException JavaDoc {
183         Hashtable JavaDoc dependencyMap = new Hashtable JavaDoc();
184
185         BufferedReader JavaDoc in = null;
186         try {
187             in = new BufferedReader JavaDoc(new FileReader JavaDoc(depFile));
188             String JavaDoc line = null;
189             Vector JavaDoc dependencyList = null;
190             String JavaDoc className = null;
191             int prependLength = CLASSNAME_PREPEND.length();
192             while ((line = in.readLine()) != null) {
193                 if (line.startsWith(CLASSNAME_PREPEND)) {
194                     dependencyList = new Vector JavaDoc();
195                     className = line.substring(prependLength);
196                     dependencyMap.put(className, dependencyList);
197                 } else {
198                     dependencyList.addElement(line);
199                 }
200             }
201         } finally {
202             if (in != null) {
203                 in.close();
204             }
205         }
206
207         return dependencyMap;
208     }
209
210     /**
211      * Write the dependencies to cache file
212      *
213      * @param dependencyMap the map of dependencies to be written out.
214      * @exception IOException if the dependency file cannot be written out.
215      */

216     private void writeCachedDependencies(Hashtable JavaDoc dependencyMap)
217          throws IOException JavaDoc {
218         if (cache != null) {
219             PrintWriter JavaDoc pw = null;
220             try {
221                 cache.mkdirs();
222                 File JavaDoc depFile = new File JavaDoc(cache, CACHE_FILE_NAME);
223
224                 pw = new PrintWriter JavaDoc(new FileWriter JavaDoc(depFile));
225                 Enumeration JavaDoc e = dependencyMap.keys();
226                 while (e.hasMoreElements()) {
227                     String JavaDoc className = (String JavaDoc) e.nextElement();
228
229                     pw.println(CLASSNAME_PREPEND + className);
230
231                     Vector JavaDoc dependencyList
232                          = (Vector JavaDoc) dependencyMap.get(className);
233                     int size = dependencyList.size();
234                     for (int x = 0; x < size; x++) {
235                         pw.println(dependencyList.elementAt(x));
236                     }
237                 }
238             } finally {
239                 if (pw != null) {
240                     pw.close();
241                 }
242             }
243         }
244     }
245
246     /**
247      * Get the classpath for dependency checking.
248      *
249      * This method removes the dest dirs if it is given from the dependency classpath
250      */

251     private Path getCheckClassPath() {
252         if (dependClasspath == null) {
253             return null;
254         }
255
256         String JavaDoc[] destPathElements = destPath.list();
257         String JavaDoc[] classpathElements = dependClasspath.list();
258         String JavaDoc checkPath = "";
259         for (int i = 0; i < classpathElements.length; ++i) {
260             String JavaDoc element = classpathElements[i];
261             boolean inDestPath = false;
262             for (int j = 0; j < destPathElements.length && !inDestPath; ++j) {
263                 inDestPath = destPathElements[j].equals(element);
264             }
265             if (!inDestPath) {
266                 if (checkPath.length() == 0) {
267                     checkPath = element;
268                 } else {
269                     checkPath += ":" + element;
270                 }
271             }
272         }
273
274         if (checkPath.length() == 0) {
275             return null;
276         }
277
278         return new Path(getProject(), checkPath);
279     }
280
281     /**
282      * Determine the dependencies between classes. Class dependencies are
283      * determined by examining the class references in a class file to other
284      * classes.
285      *
286      * This method sets up the following fields
287      * <ul>
288      * <li>affectedClassMap - the list of classes each class affects</li>
289      * <li>classFileInfoMap - information about each class</li>
290      * <li>classpathDependencies - the list of jars and classes from the
291      * classpath that each class depends upon.</li>
292      * </ul>
293      *
294      * If required, the dependencies are written to the cache.
295      *
296      * @exception IOException if either the dependencies cache or the class
297      * files cannot be read or written
298      */

299     private void determineDependencies() throws IOException JavaDoc {
300         affectedClassMap = new Hashtable JavaDoc();
301         classFileInfoMap = new Hashtable JavaDoc();
302         boolean cacheDirty = false;
303
304         Hashtable JavaDoc dependencyMap = new Hashtable JavaDoc();
305         File JavaDoc cacheFile = null;
306         boolean cacheFileExists = true;
307         long cacheLastModified = Long.MAX_VALUE;
308
309         // read the dependency cache from the disk
310
if (cache != null) {
311             cacheFile = new File JavaDoc(cache, CACHE_FILE_NAME);
312             cacheFileExists = cacheFile.exists();
313             cacheLastModified = cacheFile.lastModified();
314             if (cacheFileExists) {
315                 dependencyMap = readCachedDependencies(cacheFile);
316             }
317         }
318         Enumeration JavaDoc classfileEnum = getClassFiles(destPath).elements();
319         while (classfileEnum.hasMoreElements()) {
320             ClassFileInfo info = (ClassFileInfo) classfileEnum.nextElement();
321             log("Adding class info for " + info.className, Project.MSG_DEBUG);
322             classFileInfoMap.put(info.className, info);
323
324             Vector JavaDoc dependencyList = null;
325
326             if (cache != null) {
327                 // try to read the dependency info from the map if it is
328
// not out of date
329
if (cacheFileExists
330                     && cacheLastModified > info.absoluteFile.lastModified()) {
331                     // depFile exists and is newer than the class file
332
// need to get dependency list from the map.
333
dependencyList = (Vector JavaDoc) dependencyMap.get(info.className);
334                 }
335             }
336
337             if (dependencyList == null) {
338                 // not cached - so need to read directly from the class file
339
DependencyAnalyzer analyzer = new AntAnalyzer();
340                 analyzer.addRootClass(info.className);
341                 analyzer.addClassPath(destPath);
342                 analyzer.setClosure(false);
343                 dependencyList = new Vector JavaDoc();
344                 Enumeration JavaDoc depEnum = analyzer.getClassDependencies();
345                 while (depEnum.hasMoreElements()) {
346                     dependencyList.addElement(depEnum.nextElement());
347                 }
348                 cacheDirty = true;
349                 dependencyMap.put(info.className, dependencyList);
350             }
351
352             // This class depends on each class in the dependency list. For each
353
// one of those, add this class into their affected classes list
354
Enumeration JavaDoc depEnum = dependencyList.elements();
355             while (depEnum.hasMoreElements()) {
356                 String JavaDoc dependentClass = (String JavaDoc) depEnum.nextElement();
357
358                 Hashtable JavaDoc affectedClasses
359                     = (Hashtable JavaDoc) affectedClassMap.get(dependentClass);
360                 if (affectedClasses == null) {
361                     affectedClasses = new Hashtable JavaDoc();
362                     affectedClassMap.put(dependentClass, affectedClasses);
363                 }
364
365                 affectedClasses.put(info.className, info);
366             }
367         }
368
369         classpathDependencies = null;
370         Path checkPath = getCheckClassPath();
371         if (checkPath != null) {
372             // now determine which jars each class depends upon
373
classpathDependencies = new Hashtable JavaDoc();
374             AntClassLoader loader = getProject().createClassLoader(checkPath);
375
376             Hashtable JavaDoc classpathFileCache = new Hashtable JavaDoc();
377             Object JavaDoc nullFileMarker = new Object JavaDoc();
378             for (Enumeration JavaDoc e = dependencyMap.keys(); e.hasMoreElements();) {
379                 String JavaDoc className = (String JavaDoc) e.nextElement();
380                 Vector JavaDoc dependencyList = (Vector JavaDoc) dependencyMap.get(className);
381                 Hashtable JavaDoc dependencies = new Hashtable JavaDoc();
382                 classpathDependencies.put(className, dependencies);
383                 Enumeration JavaDoc e2 = dependencyList.elements();
384                 while (e2.hasMoreElements()) {
385                     String JavaDoc dependency = (String JavaDoc) e2.nextElement();
386                     Object JavaDoc classpathFileObject
387                         = classpathFileCache.get(dependency);
388                     if (classpathFileObject == null) {
389                         classpathFileObject = nullFileMarker;
390
391                         if (!dependency.startsWith("java.")
392                             && !dependency.startsWith("javax.")) {
393                             URL JavaDoc classURL
394                                 = loader.getResource(dependency.replace('.', '/') + ".class");
395                             if (classURL != null) {
396                                 if (classURL.getProtocol().equals("jar")) {
397                                     String JavaDoc jarFilePath = classURL.getFile();
398                                     int classMarker = jarFilePath.indexOf('!');
399                                     jarFilePath = jarFilePath.substring(0, classMarker);
400                                     if (jarFilePath.startsWith("file:")) {
401                                         classpathFileObject = new File JavaDoc(
402                                             FileUtils.getFileUtils().fromURI(jarFilePath));
403                                     } else {
404                                         throw new IOException JavaDoc(
405                                             "Bizarre nested path in jar: protocol: "
406                                             + jarFilePath);
407                                     }
408                                 } else if (classURL.getProtocol().equals("file")) {
409                                     classpathFileObject = new File JavaDoc(
410                                         FileUtils.getFileUtils()
411                                         .fromURI(classURL.toExternalForm()));
412                                 }
413                                 log("Class " + className
414                                     + " depends on " + classpathFileObject
415                                     + " due to " + dependency, Project.MSG_DEBUG);
416                             }
417                         }
418                         classpathFileCache.put(dependency, classpathFileObject);
419                     }
420                     if (classpathFileObject != null && classpathFileObject != nullFileMarker) {
421                         // we need to add this jar to the list for this class.
422
File JavaDoc jarFile = (File JavaDoc) classpathFileObject;
423                         dependencies.put(jarFile, jarFile);
424                     }
425                 }
426             }
427         }
428
429         // write the dependency cache to the disk
430
if (cache != null && cacheDirty) {
431             writeCachedDependencies(dependencyMap);
432         }
433     }
434
435     /**
436      * Delete all the class files which are out of date, by way of their
437      * dependency on a class which is out of date
438      *
439      * @return the number of files deleted.
440      */

441     private int deleteAllAffectedFiles() {
442         int count = 0;
443         for (Enumeration JavaDoc e = outOfDateClasses.elements(); e.hasMoreElements();) {
444             String JavaDoc className = (String JavaDoc) e.nextElement();
445             count += deleteAffectedFiles(className);
446             ClassFileInfo classInfo
447                 = (ClassFileInfo) classFileInfoMap.get(className);
448             if (classInfo != null && classInfo.absoluteFile.exists()) {
449                 classInfo.absoluteFile.delete();
450                 count++;
451             }
452         }
453         return count;
454     }
455
456     /**
457      * Delete all the class files of classes which depend on the given class
458      *
459      * @param className the name of the class whose dependent classes will be
460      * deleted
461      * @return the number of class files removed
462      */

463     private int deleteAffectedFiles(String JavaDoc className) {
464         int count = 0;
465
466         Hashtable JavaDoc affectedClasses = (Hashtable JavaDoc) affectedClassMap.get(className);
467         if (affectedClasses == null) {
468             return count;
469         }
470         for (Enumeration JavaDoc e = affectedClasses.keys(); e.hasMoreElements();) {
471             String JavaDoc affectedClass = (String JavaDoc) e.nextElement();
472             ClassFileInfo affectedClassInfo
473                 = (ClassFileInfo) affectedClasses.get(affectedClass);
474
475             if (!affectedClassInfo.absoluteFile.exists()) {
476                 continue;
477             }
478
479             if (affectedClassInfo.sourceFile == null) {
480                 warnOutOfDateButNotDeleted(affectedClassInfo, affectedClass, className);
481                 continue;
482             }
483
484             log("Deleting file " + affectedClassInfo.absoluteFile.getPath()
485                 + " since " + className + " out of date", Project.MSG_VERBOSE);
486
487             affectedClassInfo.absoluteFile.delete();
488             count++;
489             if (closure) {
490                 count += deleteAffectedFiles(affectedClass);
491             } else {
492                 // without closure we may delete an inner class but not the
493
// top level class which would not trigger a recompile.
494

495                 if (affectedClass.indexOf("$") == -1) {
496                     continue;
497                 }
498                 // need to delete the main class
499
String JavaDoc topLevelClassName
500                      = affectedClass.substring(0, affectedClass.indexOf("$"));
501                 log("Top level class = " + topLevelClassName,
502                     Project.MSG_VERBOSE);
503                 ClassFileInfo topLevelClassInfo
504                      = (ClassFileInfo) classFileInfoMap.get(topLevelClassName);
505                 if (topLevelClassInfo != null
506                     && topLevelClassInfo.absoluteFile.exists()) {
507                     log("Deleting file "
508                         + topLevelClassInfo.absoluteFile.getPath()
509                         + " since one of its inner classes was removed",
510                         Project.MSG_VERBOSE);
511                     topLevelClassInfo.absoluteFile.delete();
512                     count++;
513                     if (closure) {
514                         count += deleteAffectedFiles(topLevelClassName);
515                     }
516                 }
517             }
518         }
519         return count;
520     }
521
522     /**
523      * warn when a class is out of date, but not deleted as its source is unknown.
524      * MSG_WARN is the normal level, but we downgrade to MSG_VERBOSE for RMI files
525      * if {@link #warnOnRmiStubs is false}
526      * @param affectedClassInfo info about the affectd class
527      * @param affectedClass the name of the affected .class file
528      * @param className the file that is triggering the out of dateness
529      */

530     private void warnOutOfDateButNotDeleted(
531             ClassFileInfo affectedClassInfo, String JavaDoc affectedClass,
532             String JavaDoc className) {
533         if (affectedClassInfo.isUserWarned) {
534             return;
535         }
536         int level = Project.MSG_WARN;
537         if (!warnOnRmiStubs) {
538             //downgrade warnings on RMI stublike classes, as they are generated
539
//by rmic, so there is no need to tell the user that their source is
540
//missing.
541
if (isRmiStub(affectedClass, className)) {
542                 level = Project.MSG_VERBOSE;
543             }
544         }
545         log("The class " + affectedClass + " in file "
546             + affectedClassInfo.absoluteFile.getPath()
547             + " is out of date due to " + className
548             + " but has not been deleted because its source file"
549             + " could not be determined", level);
550         affectedClassInfo.isUserWarned = true;
551     }
552
553     /**
554      * test for being an RMI stub
555      * @param affectedClass class being tested
556      * @param className possible origin of the RMI stub
557      * @return whether the class affectedClass is a RMI stub
558      */

559     private boolean isRmiStub(String JavaDoc affectedClass, String JavaDoc className) {
560         return isStub(affectedClass, className, DefaultRmicAdapter.RMI_STUB_SUFFIX)
561                 || isStub(affectedClass, className, DefaultRmicAdapter.RMI_SKEL_SUFFIX)
562                 || isStub(affectedClass, className, WLRmic.RMI_STUB_SUFFIX)
563                 || isStub(affectedClass, className, WLRmic.RMI_SKEL_SUFFIX);
564     }
565
566     private boolean isStub(String JavaDoc affectedClass, String JavaDoc baseClass, String JavaDoc suffix) {
567         return (baseClass + suffix).equals(affectedClass);
568     }
569
570     /**
571      * Dump the dependency information loaded from the classes to the Ant log
572      */

573     private void dumpDependencies() {
574         log("Reverse Dependency Dump for " + affectedClassMap.size()
575             + " classes:", Project.MSG_DEBUG);
576
577         Enumeration JavaDoc classEnum = affectedClassMap.keys();
578         while (classEnum.hasMoreElements()) {
579             String JavaDoc className = (String JavaDoc) classEnum.nextElement();
580             log(" Class " + className + " affects:", Project.MSG_DEBUG);
581             Hashtable JavaDoc affectedClasses
582                 = (Hashtable JavaDoc) affectedClassMap.get(className);
583             Enumeration JavaDoc affectedClassEnum = affectedClasses.keys();
584             while (affectedClassEnum.hasMoreElements()) {
585                 String JavaDoc affectedClass = (String JavaDoc) affectedClassEnum.nextElement();
586                 ClassFileInfo info
587                     = (ClassFileInfo) affectedClasses.get(affectedClass);
588                 log(" " + affectedClass + " in "
589                     + info.absoluteFile.getPath(), Project.MSG_DEBUG);
590             }
591         }
592
593         if (classpathDependencies != null) {
594             log("Classpath file dependencies (Forward):", Project.MSG_DEBUG);
595
596             Enumeration JavaDoc classpathEnum = classpathDependencies.keys();
597             while (classpathEnum.hasMoreElements()) {
598                 String JavaDoc className = (String JavaDoc) classpathEnum.nextElement();
599                 log(" Class " + className + " depends on:", Project.MSG_DEBUG);
600                 Hashtable JavaDoc dependencies
601                     = (Hashtable JavaDoc) classpathDependencies.get(className);
602
603                 Enumeration JavaDoc classpathFileEnum = dependencies.elements();
604                 while (classpathFileEnum.hasMoreElements()) {
605                     File JavaDoc classpathFile = (File JavaDoc) classpathFileEnum.nextElement();
606                     log(" " + classpathFile.getPath(), Project.MSG_DEBUG);
607                 }
608             }
609         }
610     }
611
612     private void determineOutOfDateClasses() {
613         outOfDateClasses = new Hashtable JavaDoc();
614         for (int i = 0; i < srcPathList.length; i++) {
615             File JavaDoc srcDir = getProject().resolveFile(srcPathList[i]);
616             if (srcDir.exists()) {
617                 DirectoryScanner ds = this.getDirectoryScanner(srcDir);
618                 String JavaDoc[] files = ds.getIncludedFiles();
619                 scanDir(srcDir, files);
620             }
621         }
622
623         // now check classpath file dependencies
624
if (classpathDependencies == null) {
625             return;
626         }
627
628         Enumeration JavaDoc classpathDepsEnum = classpathDependencies.keys();
629         while (classpathDepsEnum.hasMoreElements()) {
630             String JavaDoc className = (String JavaDoc) classpathDepsEnum.nextElement();
631             if (outOfDateClasses.containsKey(className)) {
632                 continue;
633             }
634             ClassFileInfo info
635                 = (ClassFileInfo) classFileInfoMap.get(className);
636
637             // if we have no info about the class - it may have been deleted already and we
638
// are using cached info.
639
if (info != null) {
640                 Hashtable JavaDoc dependencies
641                     = (Hashtable JavaDoc) classpathDependencies.get(className);
642                 for (Enumeration JavaDoc e2 = dependencies.elements(); e2.hasMoreElements();) {
643                     File JavaDoc classpathFile = (File JavaDoc) e2.nextElement();
644                     if (classpathFile.lastModified()
645                         > info.absoluteFile.lastModified()) {
646                         log("Class " + className
647                             + " is out of date with respect to "
648                             + classpathFile, Project.MSG_DEBUG);
649                         outOfDateClasses.put(className, className);
650                         break;
651                     }
652                 }
653             }
654         }
655     }
656
657     /**
658      * Does the work.
659      *
660      * @exception BuildException Thrown in case of an unrecoverable error.
661      */

662     public void execute() throws BuildException {
663         try {
664             long start = System.currentTimeMillis();
665             if (srcPath == null) {
666                 throw new BuildException("srcdir attribute must be set",
667                                          getLocation());
668             }
669
670             srcPathList = srcPath.list();
671             if (srcPathList.length == 0) {
672                 throw new BuildException("srcdir attribute must be non-empty",
673                                          getLocation());
674             }
675
676             if (destPath == null) {
677                 destPath = srcPath;
678             }
679
680             if (cache != null && cache.exists() && !cache.isDirectory()) {
681                 throw new BuildException("The cache, if specified, must "
682                     + "point to a directory");
683             }
684
685             if (cache != null && !cache.exists()) {
686                 cache.mkdirs();
687             }
688
689             determineDependencies();
690             if (dump) {
691                 dumpDependencies();
692             }
693             determineOutOfDateClasses();
694             int count = deleteAllAffectedFiles();
695
696             long duration = (System.currentTimeMillis() - start) / 1000;
697
698             final int summaryLogLevel;
699             if (count > 0) {
700                 summaryLogLevel = Project.MSG_INFO;
701             } else {
702                 summaryLogLevel = Project.MSG_DEBUG;
703             }
704
705             log("Deleted " + count + " out of date files in "
706                 + duration + " seconds", summaryLogLevel);
707         } catch (Exception JavaDoc e) {
708             throw new BuildException(e);
709         }
710     }
711
712     /**
713      * Scans the directory looking for source files that are newer than
714      * their class files. The results are returned in the class variable
715      * compileList
716      *
717      * @param srcDir the source directory
718      * @param files the names of the files in the source dir which are to be
719      * checked.
720      */

721     protected void scanDir(File JavaDoc srcDir, String JavaDoc[] files) {
722
723         for (int i = 0; i < files.length; i++) {
724             File JavaDoc srcFile = new File JavaDoc(srcDir, files[i]);
725             if (files[i].endsWith(".java")) {
726                 String JavaDoc filePath = srcFile.getPath();
727                 String JavaDoc className
728                     = filePath.substring(srcDir.getPath().length() + 1,
729                         filePath.length() - ".java".length());
730                 className = ClassFileUtils.convertSlashName(className);
731                 ClassFileInfo info
732                     = (ClassFileInfo) classFileInfoMap.get(className);
733                 if (info == null) {
734                     // there was no class file. add this class to the list
735
outOfDateClasses.put(className, className);
736                 } else {
737                     if (srcFile.lastModified()
738                         > info.absoluteFile.lastModified()) {
739                         outOfDateClasses.put(className, className);
740                     }
741                 }
742             }
743         }
744     }
745
746
747     /**
748      * Get the list of class files we are going to analyse.
749      *
750      * @param classLocations a path structure containing all the directories
751      * where classes can be found.
752      * @return a vector containing the classes to analyse.
753      */

754     private Vector JavaDoc getClassFiles(Path classLocations) {
755         // break the classLocations into its components.
756
String JavaDoc[] classLocationsList = classLocations.list();
757
758         Vector JavaDoc classFileList = new Vector JavaDoc();
759
760         for (int i = 0; i < classLocationsList.length; ++i) {
761             File JavaDoc dir = new File JavaDoc(classLocationsList[i]);
762             if (dir.isDirectory()) {
763                 addClassFiles(classFileList, dir, dir);
764             }
765         }
766
767         return classFileList;
768     }
769
770     /**
771      * Find the source file for a given class
772      *
773      * @param classname the classname in slash format.
774      */

775     private File JavaDoc findSourceFile(String JavaDoc classname) {
776         String JavaDoc sourceFilename = classname + ".java";
777         int innerIndex = classname.indexOf("$");
778         if (innerIndex != -1) {
779             sourceFilename = classname.substring(0, innerIndex) + ".java";
780         }
781
782         // search the various source path entries
783
for (int i = 0; i < srcPathList.length; ++i) {
784             File JavaDoc sourceFile = new File JavaDoc(srcPathList[i], sourceFilename);
785             if (sourceFile.exists()) {
786                 return sourceFile;
787             }
788         }
789         return null;
790     }
791
792     /**
793      * Add the list of class files from the given directory to the class
794      * file vector, including any subdirectories.
795      *
796      * @param classFileList a list of ClassFileInfo objects for all the
797      * files in the directory tree
798      * @param dir the directory tree to be searched, recursively, for class
799      * files
800      * @param root the root of the source tree. This is used to determine
801      * the absolute class name from the relative position in the
802      * source tree
803      */

804     private void addClassFiles(Vector JavaDoc classFileList, File JavaDoc dir, File JavaDoc root) {
805         String JavaDoc[] filesInDir = dir.list();
806
807         if (filesInDir == null) {
808             return;
809         }
810         int length = filesInDir.length;
811
812         int rootLength = root.getPath().length();
813         for (int i = 0; i < length; ++i) {
814             File JavaDoc file = new File JavaDoc(dir, filesInDir[i]);
815             if (file.isDirectory()) {
816                 addClassFiles(classFileList, file, root);
817             } else if (file.getName().endsWith(".class")) {
818                 ClassFileInfo info = new ClassFileInfo();
819                 info.absoluteFile = file;
820                 String JavaDoc relativeName = file.getPath().substring(rootLength + 1,
821                     file.getPath().length() - 6);
822                 info.className
823                     = ClassFileUtils.convertSlashName(relativeName);
824                 info.sourceFile = findSourceFile(relativeName);
825                 classFileList.addElement(info);
826             }
827         }
828     }
829
830
831     /**
832      * Set the directories path to find the Java source files.
833      *
834      * @param srcPath the source path
835      */

836     public void setSrcdir(Path srcPath) {
837         this.srcPath = srcPath;
838     }
839
840     /**
841      * Set the destination directory where the compiled Java files exist.
842      *
843      * @param destPath the destination areas where build files are written
844      */

845     public void setDestDir(Path destPath) {
846         this.destPath = destPath;
847     }
848
849     /**
850      * Sets the dependency cache file.
851      *
852      * @param cache the dependency cache file
853      */

854     public void setCache(File JavaDoc cache) {
855         this.cache = cache;
856     }
857
858     /**
859      * If true, transitive dependencies are followed until the
860      * closure of the dependency set if reached.
861      * When not set, the depend task will only follow
862      * direct dependencies between classes.
863      *
864      * @param closure indicate if dependency closure is required.
865      */

866     public void setClosure(boolean closure) {
867         this.closure = closure;
868     }
869
870     /**
871      * If true, the dependency information will be written
872      * to the debug level log.
873      *
874      * @param dump set to true to dump dependency information to the log
875      */

876     public void setDump(boolean dump) {
877         this.dump = dump;
878     }
879 }
880
881
Popular Tags