diff --git a/src/etc/testcases/taskdefs/optional/depend/depend.xml b/src/etc/testcases/taskdefs/optional/depend/depend.xml index 955c5f966..11430da9e 100644 --- a/src/etc/testcases/taskdefs/optional/depend/depend.xml +++ b/src/etc/testcases/taskdefs/optional/depend/depend.xml @@ -5,6 +5,7 @@ + @@ -41,6 +42,12 @@ + + + + + + @@ -137,4 +144,12 @@ + + + + + + + + diff --git a/src/etc/testcases/taskdefs/optional/depend/src5/A.java b/src/etc/testcases/taskdefs/optional/depend/src5/A.java new file mode 100644 index 000000000..b8f918025 --- /dev/null +++ b/src/etc/testcases/taskdefs/optional/depend/src5/A.java @@ -0,0 +1,6 @@ +public class A { + APrivate dependency = new APrivate(); +} + +class APrivate extends B { +} diff --git a/src/etc/testcases/taskdefs/optional/depend/src5/B.java b/src/etc/testcases/taskdefs/optional/depend/src5/B.java new file mode 100644 index 000000000..b4e908f59 --- /dev/null +++ b/src/etc/testcases/taskdefs/optional/depend/src5/B.java @@ -0,0 +1,3 @@ +public class B { +} + diff --git a/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java b/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java index a32955d44..2bc894dee 100644 --- a/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java +++ b/src/main/org/apache/tools/ant/taskdefs/optional/depend/Depend.java @@ -95,14 +95,14 @@ public class Depend extends MatchingTask { /** The file where the class file is stored in the file system */ private File absoluteFile; - /** - * The location of the file relative to its base directory - the - * root of the package namespace - */ - private String relativeName; - /** The Java class name of this class */ private String className; + + /** The source File containing this class */ + private File sourceFile; + + /** if user has been warned about this file not having a source file */ + private boolean isUserWarned = false; } /** The path where source files exist */ @@ -114,8 +114,11 @@ public class Depend extends MatchingTask { /** The directory which contains the dependency cache. */ private File cache; + /** The list of source paths derived from the srcPath field. */ + private String[] srcPathList; + /** - * A map which gives for every class a list of te class which it + * A map which gives for every class a list of the class which it * affects. */ private Hashtable affectedClassMap; @@ -204,34 +207,29 @@ public class Depend extends MatchingTask { * @return a collection of class dependencies * @exception IOException if the dependnecy file cannot be read */ - private Hashtable readCachedDependencies() throws IOException { + private Hashtable readCachedDependencies(File depFile) throws IOException { Hashtable dependencyMap = new Hashtable(); - if (cache != null) { - File depFile = new File(cache, CACHE_FILE_NAME); - BufferedReader in = null; - if (depFile.exists()) { - try { - in = new BufferedReader(new FileReader(depFile)); - String line = null; - Vector dependencyList = null; - String className = null; - int prependLength = CLASSNAME_PREPEND.length(); - while ((line = in.readLine()) != null) { - if (line.startsWith(CLASSNAME_PREPEND)) { - dependencyList = new Vector(); - className = line.substring(prependLength); - dependencyMap.put(className, dependencyList); - } else { - dependencyList.addElement(line); - } - } - } finally { - if (in != null) { - in.close(); - } + BufferedReader in = null; + try { + in = new BufferedReader(new FileReader(depFile)); + String line = null; + Vector dependencyList = null; + String className = null; + int prependLength = CLASSNAME_PREPEND.length(); + while ((line = in.readLine()) != null) { + if (line.startsWith(CLASSNAME_PREPEND)) { + dependencyList = new Vector(); + className = line.substring(prependLength); + dependencyMap.put(className, dependencyList); + } else { + dependencyList.addElement(line); } } + } finally { + if (in != null) { + in.close(); + } } return dependencyMap; @@ -277,9 +275,19 @@ public class Depend extends MatchingTask { /** * Determine the dependencies between classes. Class dependencies are * determined by examining the class references in a class file to other - * classes + * classes. + * + * This method sets up the following fields + *
    + *
  • affectedClassMap - the list of classes each class affects
  • + *
  • classFileInfoMap - information about each class
  • + *
  • classpathDependencies - the list of jars and classes from the + * classpath that each class depends upon.
  • + *
+ * + * If required, the dependencies are written to the cache. * - * @exception IOException if either the dependnecies cache or the class + * @exception IOException if either the dependencies cache or the class * files cannot be read or written */ private void determineDependencies() throws IOException { @@ -288,16 +296,18 @@ public class Depend extends MatchingTask { boolean cacheDirty = false; Hashtable dependencyMap = new Hashtable(); - File depCacheFile = null; - boolean depCacheFileExists = true; - long depCacheFileLastModified = Long.MAX_VALUE; + File cacheFile = null; + boolean cacheFileExists = true; + long cacheLastModified = Long.MAX_VALUE; // read the dependency cache from the disk if (cache != null) { - dependencyMap = readCachedDependencies(); - depCacheFile = new File(cache, CACHE_FILE_NAME); - depCacheFileExists = depCacheFile.exists(); - depCacheFileLastModified = depCacheFile.lastModified(); + cacheFile = new File(cache, CACHE_FILE_NAME); + cacheFileExists = cacheFile.exists(); + cacheLastModified = cacheFile.lastModified(); + if (cacheFileExists) { + dependencyMap = readCachedDependencies(cacheFile); + } } Enumeration classfileEnum = getClassFiles(destPath).elements(); while (classfileEnum.hasMoreElements()) { @@ -310,8 +320,8 @@ public class Depend extends MatchingTask { if (cache != null) { // try to read the dependency info from the map if it is // not out of date - if (depCacheFileExists - && depCacheFileLastModified > info.absoluteFile.lastModified()) { + if (cacheFileExists + && cacheLastModified > info.absoluteFile.lastModified()) { // depFile exists and is newer than the class file // need to get dependency list from the map. dependencyList = (Vector)dependencyMap.get(info.className); @@ -446,39 +456,61 @@ public class Depend extends MatchingTask { int count = 0; Hashtable affectedClasses = (Hashtable)affectedClassMap.get(className); - if (affectedClasses != null) { - for (Enumeration e = affectedClasses.keys(); e.hasMoreElements();) { - String affectedClassName = (String)e.nextElement(); - ClassFileInfo affectedClassInfo = (ClassFileInfo)affectedClasses.get(affectedClassName); - if (affectedClassInfo.absoluteFile.exists()) { - log("Deleting file " + affectedClassInfo.absoluteFile.getPath() + " since " + - className + " out of date", Project.MSG_VERBOSE); - affectedClassInfo.absoluteFile.delete(); + if (affectedClasses == null) { + return count; + } + for (Enumeration e = affectedClasses.keys(); e.hasMoreElements();) { + String affectedClass = (String)e.nextElement(); + ClassFileInfo affectedClassInfo + = (ClassFileInfo)affectedClasses.get(affectedClass); + + if (!affectedClassInfo.absoluteFile.exists()) { + continue; + } + + if (affectedClassInfo.sourceFile == null) { + if (!affectedClassInfo.isUserWarned) { + log("The class " + affectedClass + " in file " + + affectedClassInfo.absoluteFile.getPath() + + " is out of date due to " + className + + " but has not been deleted because its source file" + + " could not be determined", Project.MSG_WARN); + affectedClassInfo.isUserWarned = true; + } + continue; + } + + log("Deleting file " + affectedClassInfo.absoluteFile.getPath() + + " since " + className + " out of date", Project.MSG_VERBOSE); + + affectedClassInfo.absoluteFile.delete(); + count++; + if (closure) { + count += deleteAffectedFiles(affectedClass); + } else { + // without closure we may delete an inner class but not the + // top level class which would not trigger a recompile. + + if (affectedClass.indexOf("$") == -1) { + continue; + } + // need to delete the main class + String topLevelClassName + = affectedClass.substring(0, affectedClass.indexOf("$")); + log("Top level class = " + topLevelClassName, + Project.MSG_VERBOSE); + ClassFileInfo topLevelClassInfo + = (ClassFileInfo)classFileInfoMap.get(topLevelClassName); + if (topLevelClassInfo != null && + topLevelClassInfo.absoluteFile.exists()) { + log("Deleting file " + + topLevelClassInfo.absoluteFile.getPath() + + " since one of its inner classes was removed", + Project.MSG_VERBOSE); + topLevelClassInfo.absoluteFile.delete(); count++; if (closure) { - count += deleteAffectedFiles(affectedClassName); - } else { - // without closure we may delete an inner class but not the - // top level class which would not trigger a recompile. - - if (affectedClassName.indexOf("$") != -1) { - // need to delete the main class - String topLevelClassName - = affectedClassName.substring(0, affectedClassName.indexOf("$")); - log("Top level class = " + topLevelClassName, Project.MSG_VERBOSE); - ClassFileInfo topLevelClassInfo - = (ClassFileInfo)classFileInfoMap.get(topLevelClassName); - if (topLevelClassInfo != null && - topLevelClassInfo.absoluteFile.exists()) { - log("Deleting file " + topLevelClassInfo.absoluteFile.getPath() + " since " + - "one of its inner classes was removed", Project.MSG_VERBOSE); - topLevelClassInfo.absoluteFile.delete(); - count++; - if (closure) { - count += deleteAffectedFiles(topLevelClassName); - } - } - } + count += deleteAffectedFiles(topLevelClassName); } } } @@ -528,7 +560,7 @@ public class Depend extends MatchingTask { } } - private void determineOutOfDateClasses(String[] srcPathList) { + private void determineOutOfDateClasses() { outOfDateClasses = new Hashtable(); for (int i = 0; i < srcPathList.length; i++) { File srcDir = (File)project.resolveFile(srcPathList[i]); @@ -583,7 +615,7 @@ public class Depend extends MatchingTask { location); } - String[] srcPathList = srcPath.list(); + srcPathList = srcPath.list(); if (srcPathList.length == 0) { throw new BuildException("srcdir attribute must be non-empty", location); @@ -603,12 +635,10 @@ public class Depend extends MatchingTask { } determineDependencies(); - if (dump) { dumpDependencies(); } - - determineOutOfDateClasses(srcPathList); + determineOutOfDateClasses(); int count = deleteAllAffectedFiles(); long duration = (System.currentTimeMillis() - start) / 1000; @@ -677,6 +707,28 @@ public class Depend extends MatchingTask { return classFileList; } + /** + * Find the source file for a given class + * + * @param classname the classname in slash format. + */ + private File findSourceFile(String classname) { + String sourceFilename = classname + ".java"; + int innerIndex = classname.indexOf("$"); + if (innerIndex != -1) { + sourceFilename = classname.substring(0, innerIndex) + ".java"; + } + + // search the various source path entries + for (int i = 0; i < srcPathList.length; ++i) { + File sourceFile = new File(srcPathList[i], sourceFilename); + if (sourceFile.exists()) { + return sourceFile; + } + } + return null; + } + /** * Add the list of class files from the given directory to the class * file vector, including any subdirectories. @@ -692,21 +744,25 @@ public class Depend extends MatchingTask { private void addClassFiles(Vector classFileList, File dir, File root) { String[] filesInDir = dir.list(); - if (filesInDir != null) { - int length = filesInDir.length; - - for (int i = 0; i < length; ++i) { - File file = new File(dir, filesInDir[i]); - if (file.isDirectory()) { - addClassFiles(classFileList, file, root); - } else if (file.getName().endsWith(".class")) { - ClassFileInfo info = new ClassFileInfo(); - info.absoluteFile = file; - info.relativeName = file.getPath().substring(root.getPath().length() + 1, - file.getPath().length() - 6); - info.className = ClassFileUtils.convertSlashName(info.relativeName); - classFileList.addElement(info); - } + if (filesInDir == null) { + return; + } + int length = filesInDir.length; + + int rootLength = root.getPath().length(); + for (int i = 0; i < length; ++i) { + File file = new File(dir, filesInDir[i]); + if (file.isDirectory()) { + addClassFiles(classFileList, file, root); + } else if (file.getName().endsWith(".class")) { + ClassFileInfo info = new ClassFileInfo(); + info.absoluteFile = file; + String relativeName = file.getPath().substring(rootLength + 1, + file.getPath().length() - 6); + info.className + = ClassFileUtils.convertSlashName(relativeName); + info.sourceFile = findSourceFile(relativeName); + classFileList.addElement(info); } } } @@ -742,7 +798,7 @@ public class Depend extends MatchingTask { /** * Set the closure flag. When not set, the depend task will only follow * direct dependencies between classes. When set, transitive - * dependenecies are followed until the closure of the dependency set if + * dependencies are followed until the closure of the dependency set if * reached. * * @param closure indicate if dependency closure is required. diff --git a/src/testcases/org/apache/tools/ant/taskdefs/optional/depend/DependTest.java b/src/testcases/org/apache/tools/ant/taskdefs/optional/depend/DependTest.java index c9cc1ed9f..9c48b076b 100644 --- a/src/testcases/org/apache/tools/ant/taskdefs/optional/depend/DependTest.java +++ b/src/testcases/org/apache/tools/ant/taskdefs/optional/depend/DependTest.java @@ -187,4 +187,17 @@ public class DependTest extends BuildFileTest { executeTarget("testcache"); } + /** + * Test the detection and warning of non public classes + */ + public void testNonPublic() { + executeTarget("testnonpublic"); + String log = getLog(); + assertTrue("Expected warning about APrivate", + log.indexOf("The class APrivate in file") != -1); + assertTrue("but has not been deleted because its source file " + + "could not be determined", + log.indexOf("The class APrivate in file") != -1); + } + }