/******************************************************************************* * Copyright (c) 2004, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.che.jdt.internal.core.search.matching; import org.eclipse.che.jdt.internal.core.builder.CodenvyClasspathLocation; import org.eclipse.che.jdt.internal.core.util.ResourceCompilationUnit; import org.eclipse.che.jdt.internal.core.util.Util; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.internal.codeassist.ISearchRequestor; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.IOException; import java.nio.file.DirectoryStream; import java.nio.file.Files; import java.nio.file.Path; import java.util.HashSet; import java.util.Set; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; public class ClasspathSourceDirectory extends CodenvyClasspathLocation { private static final Logger LOG = LoggerFactory.getLogger(ClasspathSourceDirectory.class); //uses as marker for packages that isn't from this source directory private final Future<SimpleLookupTable> missingPackageHolder = new FutureTask<>(new Callable<SimpleLookupTable>() { @Override public SimpleLookupTable call() throws Exception { return null; } }); File sourceFolder; // SimpleLookupTable directoryCache; volatile ConcurrentHashMap<String, Future<SimpleLookupTable>> directoryCache; volatile Set<String> packagesCache; char[][] fullExclusionPatternChars; char[][] fulInclusionPatternChars; ClasspathSourceDirectory(File sourceFolder, char[][] fullExclusionPatternChars, char[][] fulInclusionPatternChars) { this.sourceFolder = sourceFolder; this.directoryCache = new ConcurrentHashMap<>(5); this.fullExclusionPatternChars = fullExclusionPatternChars; this.fulInclusionPatternChars = fulInclusionPatternChars; } public void cleanup() { this.directoryCache = new ConcurrentHashMap<>(5); packagesCache = null; } SimpleLookupTable directoryTable(final String qualifiedPackageName) { final ConcurrentHashMap<String, Future<SimpleLookupTable>> directoryCache = this.directoryCache; Future<SimpleLookupTable> future = directoryCache.get(qualifiedPackageName); if (future == missingPackageHolder) { // package exists in another classpath directory or jar return null; } if (future == null) { FutureTask<SimpleLookupTable> newFuture = new FutureTask<>(new Callable<SimpleLookupTable>() { @Override public SimpleLookupTable call() throws Exception { File container = new File(sourceFolder, qualifiedPackageName); SimpleLookupTable dirTable = new SimpleLookupTable(); if (container.isDirectory()) { try (DirectoryStream<Path> members = Files.newDirectoryStream(container.toPath())) { for (Path member : members) { String name; if (!member.toFile().isDirectory()) { int index = Util.indexOfJavaLikeExtension(name = member.getFileName().toString()); if (index >= 0) { String fullPath = member.toAbsolutePath().toString(); if (!org.eclipse.jdt.internal.compiler.util.Util .isExcluded(fullPath.toCharArray(), fulInclusionPatternChars, fullExclusionPatternChars, false/*not a folder path*/)) { dirTable.put(name.substring(0, index), member.toString()); } } } } return dirTable; } } directoryCache.put(qualifiedPackageName, missingPackageHolder); return null; } }); future = directoryCache.putIfAbsent(qualifiedPackageName, newFuture); if (future == null) { future = newFuture; newFuture.run(); } } try { return future.get(); } catch (InterruptedException | ExecutionException e) { LOG.error("Error while reading source directory", e); } directoryCache.put(qualifiedPackageName, missingPackageHolder); return null; } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof ClasspathSourceDirectory)) return false; return this.sourceFolder.equals(((ClasspathSourceDirectory)o).sourceFolder); } public NameEnvironmentAnswer findClass(String sourceFileWithoutExtension, String qualifiedPackageName, String qualifiedSourceFileWithoutExtension) { SimpleLookupTable dirTable = directoryTable(qualifiedPackageName); if (dirTable != null && dirTable.elementSize > 0) { String file = (String)dirTable.get(sourceFileWithoutExtension); if (file != null) { return new NameEnvironmentAnswer(new ResourceCompilationUnit(new File(file)), null /* no access restriction */); } } return null; } public IPath getProjectRelativePath() { // return this.sourceFolder.getProjectRelativePath(); throw new UnsupportedOperationException(); } public int hashCode() { return this.sourceFolder == null ? super.hashCode() : this.sourceFolder.hashCode(); } public boolean isPackage(String qualifiedPackageName) { return directoryTable(qualifiedPackageName) != null; } public void reset() { cleanup(); } public String toString() { return "Source classpath directory " + this.sourceFolder.getPath(); //$NON-NLS-1$ } public String debugPathString() { return this.sourceFolder.getPath(); } @Override public void findPackages(String[] pkgName, ISearchRequestor requestor) { Set<String> packages = packagesCache; if (packages == null) { synchronized (this) { packages = packagesCache; if (packages == null) { packages = new HashSet<>(); packages.add(""); fillPackagesCache(sourceFolder, "", packages); packagesCache = packages; } } } String pkg = org.eclipse.jdt.internal.core.util.Util.concatWith(pkgName, '.'); for (String s : packages) { if (s.startsWith(pkg)) { requestor.acceptPackage(s.toCharArray()); } } } private void fillPackagesCache(File parentFolder, String parentPackage, Set<String> cache) { try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(parentFolder.toPath())) { for (Path path : directoryStream) { if (path.toFile().isDirectory()) { if (org.eclipse.jdt.internal.core.util.Util.isValidFolderNameForPackage(path.getFileName().toString(), "1.7", "1.7")) { String pack = parentPackage + "." + path.getFileName(); cache.add(pack); fillPackagesCache(path.toFile(), pack, cache); } } } } catch (IOException e) { LOG.error("Can't read packages", e); } } }