diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ClasspathDirectory.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ClasspathDirectory.java index 18ae5d338a1..702cde0f9d0 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ClasspathDirectory.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/batch/ClasspathDirectory.java @@ -26,10 +26,10 @@ import java.nio.file.SimpleFileVisitor; import java.nio.file.attribute.BasicFileAttributes; import java.util.HashSet; -import java.util.Hashtable; import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.stream.Stream; import org.eclipse.jdt.core.compiler.CharOperation; @@ -37,6 +37,7 @@ import org.eclipse.jdt.internal.compiler.DefaultErrorHandlingPolicies; import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.batch.FileSystem.Classpath; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider; @@ -51,18 +52,17 @@ import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.compiler.util.Util; -@SuppressWarnings({"rawtypes", "unchecked"}) public class ClasspathDirectory extends ClasspathLocation { -private Hashtable directoryCache; -private final String[] missingPackageHolder = new String[1]; -private final int mode; // ability to only consider one kind of files (source vs. binaries), by default use both -private final String encoding; // only useful if referenced in the source path -private Hashtable> packageSecondaryTypes = null; -Map options; + private final Map directoryCache = new ConcurrentHashMap<>(); + private final String[] missingPackageHolder = new String[1]; + private final int mode; // ability to only consider one kind of files (source vs. binaries), by default use both + private final String encoding; // only useful if referenced in the source path + private final Map> packageSecondaryTypes = new ConcurrentHashMap<>(); + private final Map options; ClasspathDirectory(File directory, String encoding, int mode, - AccessRuleSet accessRuleSet, String destinationPath, Map options) { + AccessRuleSet accessRuleSet, String destinationPath, Map options) { super(accessRuleSet, destinationPath); this.mode = mode; this.options = options; @@ -74,43 +74,47 @@ public class ClasspathDirectory extends ClasspathLocation { } if (!this.path.endsWith(File.separator)) this.path += File.separator; - this.directoryCache = new Hashtable(11); this.encoding = encoding; } -String[] directoryList(String qualifiedPackageName) { - if (File.separatorChar != '/' && qualifiedPackageName.indexOf('/') != -1) { - qualifiedPackageName = qualifiedPackageName.replace('/', File.separatorChar); + +private String[] directoryList(String qualifiedPackageName) { + String[] cached = this.directoryCache.computeIfAbsent(qualifiedPackageName, this::computeDirectoryList); + if (cached == this.missingPackageHolder) { + return null; // package exists in another classpath directory or jar } - String[] dirList = (String[]) this.directoryCache.get(qualifiedPackageName); - if (dirList == this.missingPackageHolder) return null; // package exists in another classpath directory or jar - if (dirList != null) return dirList; + return cached; +} - File dir = new File(this.path + qualifiedPackageName); - notFound : if (dir.isDirectory()) { +private String[] computeDirectoryList(String qualifiedPackageName) { + String qualifiedPackagePath = qualifiedPackageName.replace('/', File.separatorChar); + File dir = new File(this.path + qualifiedPackagePath); + String[] dirList = dir.list(); + notFound: if (dirList != null) { // if isDirectory // must protect against a case insensitive File call // walk the qualifiedPackageName backwards looking for an uppercase character before the '/' - int index = qualifiedPackageName.length(); - int last = qualifiedPackageName.lastIndexOf(File.separatorChar); - while (--index > last && !ScannerHelper.isUpperCase(qualifiedPackageName.charAt(index))){/*empty*/} + int index = qualifiedPackagePath.length(); + int last = qualifiedPackagePath.lastIndexOf(File.separatorChar); + while (--index > last && !ScannerHelper.isUpperCase(qualifiedPackagePath.charAt(index))) { + /* empty */} if (index > last) { if (last == -1) { - if (!doesFileExist(qualifiedPackageName, Util.EMPTY_STRING)) + if (!doesFileExist(qualifiedPackagePath, Util.EMPTY_STRING)) break notFound; } else { - String packageName = qualifiedPackageName.substring(last + 1); - String parentPackage = qualifiedPackageName.substring(0, last); + String packageName = qualifiedPackagePath.substring(last + 1); + String parentPackage = qualifiedPackagePath.substring(0, last); if (!doesFileExist(packageName, parentPackage)) break notFound; } } - if ((dirList = dir.list()) == null) + if (dirList.length == 0) { dirList = CharOperation.NO_STRINGS; - this.directoryCache.put(qualifiedPackageName, dirList); + } return dirList; } - this.directoryCache.put(qualifiedPackageName, this.missingPackageHolder); - return null; + return this.missingPackageHolder; } + boolean doesFileExist(String fileName, String qualifiedPackageName) { String[] dirList = directoryList(qualifiedPackageName); if (dirList == null) return false; // most common case @@ -121,7 +125,7 @@ boolean doesFileExist(String fileName, String qualifiedPackageName) { return false; } @Override -public List fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) { +public List fetchLinkedJars(FileSystem.ClasspathSectionProblemReporter problemReporter) { return null; } private NameEnvironmentAnswer findClassInternal(char[] typeName, String qualifiedPackageName, String qualifiedBinaryFileName, boolean asBinaryOnly) { @@ -202,10 +206,10 @@ public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageN /** * Add all the secondary types in the package */ -private Hashtable getSecondaryTypes(String qualifiedPackageName) { - Hashtable packageEntry = new Hashtable<>(); +private Map getSecondaryTypes(String qualifiedPackageName) { + Map packageEntry = new ConcurrentHashMap<>(); - String[] dirList = (String[]) this.directoryCache.get(qualifiedPackageName); + String[] dirList = this.directoryCache.get(qualifiedPackageName); if (dirList == this.missingPackageHolder // package exists in another classpath directory or jar || dirList == null) return packageEntry; @@ -241,13 +245,7 @@ private Hashtable getSecondaryTypes(String qualifiedPackageName) return packageEntry; } private NameEnvironmentAnswer findSourceSecondaryType(String typeName, String qualifiedPackageName, String qualifiedBinaryFileName) { - - if (this.packageSecondaryTypes == null) this.packageSecondaryTypes = new Hashtable<>(); - Hashtable packageEntry = this.packageSecondaryTypes.get(qualifiedPackageName); - if (packageEntry == null) { - packageEntry = getSecondaryTypes(qualifiedPackageName); - this.packageSecondaryTypes.put(qualifiedPackageName, packageEntry); - } + Map packageEntry = this.packageSecondaryTypes.computeIfAbsent(qualifiedPackageName, this::getSecondaryTypes); String fileName = packageEntry.get(typeName); return fileName != null ? new NameEnvironmentAnswer(new CompilationUnit(null, fileName, this.encoding, this.destinationPath), @@ -360,7 +358,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { @Override public void reset() { super.reset(); - this.directoryCache = new Hashtable(11); + this.directoryCache.clear(); } @Override public String toString() {