/*******************************************************************************
* Copyright (c) 2012-2015 Codenvy, S.A.
* 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:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core;
import org.eclipse.che.jdt.core.launching.JREContainerInitializer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.jdt.core.ClasspathContainerInitializer;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelStatus;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.IProblemRequestor;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
import org.eclipse.jdt.internal.codeassist.impl.AssistOptions;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.core.builder.JavaBuilder;
import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
import org.eclipse.jdt.internal.core.search.IRestrictedAccessTypeRequestor;
import org.eclipse.jdt.internal.core.search.JavaWorkspaceScope;
import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject;
import org.eclipse.jdt.internal.core.util.LRUCache;
import org.eclipse.jdt.internal.core.util.Messages;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.jdt.internal.core.util.WeakHashSet;
import org.eclipse.jdt.internal.core.util.WeakHashSetOfCharArray;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
/**
* @author Evgen Vidolob
*/
public class JavaModelManager {
private static Map<String, String> defaultOptions= new HashMap<>();
static {
defaultOptions.put(JavaCore.COMPILER_COMPLIANCE, org.eclipse.jdt.core.JavaCore.VERSION_1_8);
defaultOptions.put(CompilerOptions.OPTION_TargetPlatform, org.eclipse.jdt.core.JavaCore.VERSION_1_8);
defaultOptions.put(JavaCore.COMPILER_SOURCE, org.eclipse.jdt.core.JavaCore.VERSION_1_8);
defaultOptions.put(AssistOptions.OPTION_PerformVisibilityCheck, AssistOptions.ENABLED);
defaultOptions.put(CompilerOptions.OPTION_ReportUnusedLocal, CompilerOptions.WARNING);
defaultOptions.put(CompilerOptions.OPTION_TaskTags, CompilerOptions.WARNING);
defaultOptions.put(CompilerOptions.OPTION_ReportUnusedPrivateMember, CompilerOptions.WARNING);
defaultOptions.put(CompilerOptions.OPTION_SuppressWarnings, CompilerOptions.ENABLED);
defaultOptions.put(JavaCore.COMPILER_TASK_TAGS, "TODO,FIXME,XXX");
defaultOptions.put(org.eclipse.jdt.core.JavaCore.COMPILER_TASK_PRIORITIES, org.eclipse.jdt.core.JavaCore.DEFAULT_TASK_PRIORITIES);
defaultOptions.put(org.eclipse.jdt.core.JavaCore.COMPILER_PB_UNUSED_PARAMETER_INCLUDE_DOC_COMMENT_REFERENCE, org.eclipse.jdt.core.JavaCore.ENABLED);
defaultOptions.put(org.eclipse.jdt.core.JavaCore.COMPILER_DOC_COMMENT_SUPPORT, org.eclipse.jdt.core.JavaCore.ENABLED);
defaultOptions.put(org.eclipse.jdt.core.JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, org.eclipse.jdt.core.JavaCore.ENABLED);
defaultOptions.put(CompilerOptions.OPTION_Process_Annotations, org.eclipse.jdt.core.JavaCore.ENABLED);
defaultOptions.put(CompilerOptions.OPTION_GenerateClassFiles, org.eclipse.jdt.core.JavaCore.ENABLED);
defaultOptions.put(JavaCore.CODEASSIST_FIELD_PREFIXES, "");
defaultOptions.put(JavaCore.CODEASSIST_FIELD_SUFFIXES, "");
defaultOptions.put(JavaCore.CODEASSIST_STATIC_FIELD_PREFIXES, "");
defaultOptions.put(JavaCore.CODEASSIST_STATIC_FIELD_SUFFIXES, "");
defaultOptions.put(JavaCore.CODEASSIST_ARGUMENT_PREFIXES, "");
defaultOptions.put(JavaCore.CODEASSIST_ARGUMENT_SUFFIXES, "");
defaultOptions.put(JavaCore.CODEASSIST_LOCAL_PREFIXES, "");
defaultOptions.put(JavaCore.CODEASSIST_LOCAL_SUFFIXES, "");
defaultOptions.put(JavaCore.CODEASSIST_STATIC_FINAL_FIELD_PREFIXES, "");
defaultOptions.put(JavaCore.CODEASSIST_STATIC_FINAL_FIELD_SUFFIXES, "");
defaultOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE);
defaultOptions.put(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, "4");
}
public final static String TRUE = "true"; //$NON-NLS-1$
public final static ICompilationUnit[] NO_WORKING_COPY = new ICompilationUnit[0];
private final static String INDEXED_SECONDARY_TYPES = "#@*_indexing secondary cache_*@#"; //$NON-NLS-1$
public static boolean VERBOSE = false;
public static boolean CP_RESOLVE_VERBOSE = false;
public static boolean CP_RESOLVE_VERBOSE_ADVANCED = false;
public static boolean CP_RESOLVE_VERBOSE_FAILURE = false;
public static boolean ZIP_ACCESS_VERBOSE = false;
// Options
private final static int UNKNOWN_OPTION = 0;
private final static int DEPRECATED_OPTION = 1;
private final static int VALID_OPTION = 2;
HashSet optionNames = new HashSet(20);
Map deprecatedOptions = new HashMap();
/**
* Name of the JVM parameter to specify whether or not referenced JAR should be resolved for container libraries.
*/
private static final String RESOLVE_REFERENCED_LIBRARIES_FOR_CONTAINERS = "resolveReferencedLibrariesForContainers";
//$NON-NLS-1$
public final static IClasspathContainer CONTAINER_INITIALIZATION_IN_PROGRESS = new IClasspathContainer() {
public IClasspathEntry[] getClasspathEntries() {
return null;
}
public String getDescription() {
return "Container Initialization In Progress";
} //$NON-NLS-1$
public int getKind() {
return 0;
}
public IPath getPath() {
return null;
}
public String toString() {
return getDescription();
}
};
/**
* A set of java.io.Files used as a cache of external jars that
* are known to be existing.
* Note this cache is kept for the whole session.
*/
public static HashSet<File> existingExternalFiles = new HashSet<>();
// /**
// * The singleton manager
// */
private static JavaModelManager MANAGER = new JavaModelManager();
// Non-static, which will give it a chance to retain the default when and if JavaModelManager is restarted.
boolean resolveReferencedLibrariesForContainers = false;
/*
* A HashSet that contains the IJavaProject whose classpath is being resolved.
*/
private ThreadLocal<HashSet<IJavaProject>> classpathsBeingResolved = new ThreadLocal<>();
/**
* A set of external files ({@link #existingExternalFiles}) which have
* been confirmed as file (i.e. which returns true to {@link File#isFile()}.
* Note this cache is kept for the whole session.
*/
public static HashSet<File> existingExternalConfirmedFiles = new HashSet<>();
/**
* Unique handle onto the JavaModel
*/
final org.eclipse.jdt.internal.core.JavaModel javaModel;
/* whether an AbortCompilationUnit should be thrown when the source of a compilation unit cannot be retrieved */
public ThreadLocal abortOnMissingSource = new ThreadLocal();
public IndexManager indexManager;
/**
* Holds the state used for delta processing.
*/
public DeltaProcessingState deltaState;
/*
* The unique workspace scope
*/
public JavaWorkspaceScope workspaceScope;
/**
* Set of elements which are out of sync with their buffers.
*/
protected HashSet elementsOutOfSynchWithBuffers = new HashSet(11);
/**
* Table from WorkingCopyOwner to a table of ICompilationUnit (working copy handle) to PerWorkingCopyInfo.
* NOTE: this object itself is used as a lock to synchronize creation/removal of per working copy infos
*/
protected Map perWorkingCopyInfos = new HashMap(5);
/**
* A weak set of the known search scopes.
*/
protected WeakHashMap searchScopes = new WeakHashMap();
/*
* A set of IPaths for jars that are known to not contain a chaining (through MANIFEST.MF) to another library
*/
private Set nonChainingJars;
/*
* A set of IPaths for jars that are known to be invalid - such as not being a valid/known format
*/
private Set invalidArchives;
/*
* A set of IPaths for files that are known to be external to the workspace.
* Need not be referenced by the classpath.
*/
private Set externalFiles;
/*
* A set of IPaths for files that do not exist on the file system but are assumed to be
* external archives (rather than external folders).
*/
private Set assumedExternalFiles;
/**
* A cache of opened zip files per thread.
* (for a given thread, the object value is a HashMap from IPath to java.io.ZipFile)
*/
private ThreadLocal<ZipCache> zipFiles = new ThreadLocal<>();
/*
* Temporary cache of newly opened elements
*/
private ThreadLocal temporaryCache = new ThreadLocal();
private ThreadLocal containerInitializationInProgress = new ThreadLocal();
ThreadLocal containersBeingInitialized = new ThreadLocal();
public Hashtable<String, ClasspathContainerInitializer> containerInitializersCache = new Hashtable<>(5);
/**
* Table from IProject to PerProjectInfo.
* NOTE: this object itself is used as a lock to synchronize creation/removal of per project infos
*/
protected Map<IProject, PerProjectInfo> perProjectInfos = new HashMap<>(5);
/*
* Pools of symbols used in the Java model.
* Used as a replacement for String#intern() that could prevent garbage collection of strings on some VMs.
*/
private WeakHashSet stringSymbols = new WeakHashSet(5);
private WeakHashSetOfCharArray charArraySymbols = new WeakHashSetOfCharArray(5);
public HashMap containers = new HashMap(5);
public HashMap previousSessionContainers = new HashMap(5);
public HashMap previousSessionVariables = new HashMap(5);
/**
* Infos cache.
*/
private JavaModelCache cache;
private BufferManager DEFAULT_BUFFER_MANAGER;
Hashtable<String, String> optionsCache;
public static JavaModelManager getJavaModelManager() {
return MANAGER;
}
public JavaModelManager() {
// initialize Java model cache
this.cache = new JavaModelCache();
optionsCache = new Hashtable<>(defaultOptions);
javaModel = new org.eclipse.jdt.internal.core.JavaModel();
this.indexManager = new IndexManager(ResourcesPlugin.getIndexPath());
deltaState = new DeltaProcessingState(this);
this.nonChainingJars = new HashSet();//loadClasspathListCache(NON_CHAINING_JARS_CACHE);
this.invalidArchives = new HashSet(); //loadClasspathListCache(INVALID_ARCHIVES_CACHE);
this.externalFiles = new HashSet(); //loadClasspathListCache(EXTERNAL_FILES_CACHE);
this.assumedExternalFiles = new HashSet(); //loadClasspathListCache(ASSUMED_EXTERNAL_FILES_CACHE);
String includeContainerReferencedLib = System.getProperty(RESOLVE_REFERENCED_LIBRARIES_FOR_CONTAINERS);
containerInitializersCache.put(JREContainerInitializer.JRE_CONTAINER, new JREContainerInitializer());
startIndexing();
}
/**
* Initiate the background indexing process.
* This should be deferred after the plug-in activation.
*/
private void startIndexing() {
if (this.indexManager != null) this.indexManager.reset();
}
/**
* Helper method - returns the {@link IResource} corresponding to the provided {@link IPath},
* or <code>null</code> if no such resource exists.
*/
public static IResource getWorkspaceTarget(IPath path) {
if (path == null || path.getDevice() != null)
return null;
IWorkspace workspace = ResourcesPlugin.getWorkspace();
if (workspace == null)
return null;
return workspace.getRoot().findMember(path);
}
/**
* Helper method - returns the targeted item (IResource if internal or java.io.File if external),
* or null if unbound
* Internal items must be referred to using container relative paths.
*/
public static Object getTarget(IPath path, boolean checkResourceExistence) {
Object target = getWorkspaceTarget(path); // Implicitly checks resource existence
if (target != null)
return target;
return getExternalTarget(path, checkResourceExistence);
}
/**
* Helper method - returns either the linked {@link IFolder} or the {@link File} corresponding
* to the provided {@link IPath}. If <code>checkResourceExistence</code> is <code>false</code>,
* then the IFolder or File object is always returned, otherwise <code>null</code> is returned
* if it does not exist on the file system.
*/
public static Object getExternalTarget(IPath path, boolean checkResourceExistence) {
if (path == null)
return null;
// ExternalFoldersManager externalFoldersManager = JavaModelManager.getExternalManager();
// Object linkedFolder = externalFoldersManager.getFolder(path);
// if (linkedFolder != null) {
// if (checkResourceExistence) {
// check if external folder is present
// File externalFile = new File(path.toOSString());
// if (!externalFile.isDirectory()) {
// return null;
// }
// }
// return linkedFolder;
// }
File externalFile = new File(path.toOSString());
if (!checkResourceExistence) {
return externalFile;
} else if (externalFile.exists()) {
return externalFile;
}
return null;
}
private synchronized static void existingExternalFilesAdd(File externalFile) {
existingExternalFiles.add(externalFile);
}
private synchronized static boolean existingExternalFilesContains(File externalFile) {
return existingExternalFiles.contains(externalFile);
}
public boolean isNonChainingJar(IPath path) {
return this.nonChainingJars != null && this.nonChainingJars.contains(path);
}
/**
* Flushes the cache of external files known to be existing.
*/
public static void flushExternalFileCache() {
existingExternalFiles = new HashSet<>();
existingExternalConfirmedFiles = new HashSet<>();
}
/**
* Helper method - returns whether an object is afile (i.e. which returns true to {@link File#isFile()}.
*/
public static boolean isFile(Object target) {
return getFile(target) != null;
}
/**
* Helper method - returns the file item (i.e. which returns true to {@link File#isFile()},
* or null if unbound
*/
public static synchronized File getFile(Object target) {
if (existingExternalConfirmedFiles.contains(target))
return (File)target;
if (target instanceof File) {
File f = (File)target;
if (f.isFile()) {
existingExternalConfirmedFiles.add(f);
return f;
}
}
return null;
}
public static DeltaProcessingState getDeltaState() {
return MANAGER.deltaState;
}
/**
* Returns a persisted container from previous session if any. Note that it is not the original container from previous
* session (i.e. it did not get serialized) but rather a summary of its entries recreated for CP initialization purpose.
* As such it should not be stored into container caches.
*/
public IClasspathContainer getPreviousSessionContainer(IPath containerPath, IJavaProject project) {
Map previousContainerValues = (Map)this.previousSessionContainers.get(project);
if (previousContainerValues != null) {
IClasspathContainer previousContainer = (IClasspathContainer)previousContainerValues.get(containerPath);
if (previousContainer != null) {
if (JavaModelManager.CP_RESOLVE_VERBOSE_ADVANCED)
// verbose_reentering_project_container_access(containerPath, project, previousContainer);
return previousContainer;
}
}
return null; // break cycle if none found
}
/**
* Creates and returns a compilation unit element for the given <code>.java</code>
* file, its project being the given project. Returns <code>null</code> if unable
* to recognize the compilation unit.
*/
public static ICompilationUnit createCompilationUnitFrom(IFile file, IJavaProject project) {
if (file == null) return null;
if (project == null) {
project = JavaCore.create(file.getProject());
}
IPackageFragment pkg = (IPackageFragment)determineIfOnClasspath(file, project);
if (pkg == null) {
// not on classpath - make the root its folder, and a default package
PackageFragmentRoot root = (PackageFragmentRoot)project.getPackageFragmentRoot(file.getParent());
pkg = root.getPackageFragment(CharOperation.NO_STRINGS);
if (VERBOSE) {
System.out.println("WARNING : creating unit element outside classpath (" + Thread.currentThread() + "): " +
file.getFullPath()); //$NON-NLS-1$//$NON-NLS-2$
}
}
return pkg.getCompilationUnit(file.getName());
}
public void verifyArchiveContent(IPath path) throws CoreException {
if (isInvalidArchive(path)) {
throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, new ZipException()));
}
ZipFile file = getZipFile(path);
closeZipFile(file);
}
/**
* Returns the cached value for whether the file referred to by <code>path</code> exists
* and is a file, as determined by the return value of {@link File#isFile()}.
*/
public boolean isExternalFile(IPath path) {
return this.externalFiles != null && this.externalFiles.contains(path);
}
/**
* Returns whether the provided {@link IPath} appears to be an external file,
* which is true if the path does not represent an internal resource, does not
* exist on the file system, and does have a file extension (this is the definition
* provided by {@link ExternalFoldersManager#isExternalFolderPath}).
*/
public boolean isAssumedExternalFile(IPath path) {
if (this.assumedExternalFiles == null) {
return false;
}
return this.assumedExternalFiles.contains(path);
}
/**
* Returns the package fragment root represented by the resource, or
* the package fragment the given resource is located in, or <code>null</code>
* if the given resource is not on the classpath of the given project.
*/
public static IJavaElement determineIfOnClasspath(IResource resource, IJavaProject project) {
IPath resourcePath = resource.getFullPath();
boolean isExternal = ExternalFoldersManager.isExternalFolderPath(resourcePath);
if (isExternal)
resourcePath = resource.getLocation();
try {
JavaProjectElementInfo projectInfo =
(JavaProjectElementInfo)org.eclipse.jdt.internal.core.JavaModelManager.getJavaModelManager().getInfo(project);
JavaProjectElementInfo.ProjectCache projectCache = projectInfo == null ? null : projectInfo.projectCache;
HashtableOfArrayToObject allPkgFragmentsCache = projectCache == null ? null : projectCache.allPkgFragmentsCache;
boolean isJavaLike = Util.isJavaLikeFileName(resourcePath.lastSegment());
IClasspathEntry[] entries =
isJavaLike ? project.getRawClasspath() // JAVA file can only live inside SRC folder (on the raw path)
: ((JavaProject)project).getResolvedClasspath();
int length = entries.length;
if (length > 0) {
String sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true);
String complianceLevel = project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
for (int i = 0; i < length; i++) {
IClasspathEntry entry = entries[i];
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) continue;
IPath rootPath = entry.getPath();
if (rootPath.equals(resourcePath)) {
if (isJavaLike)
return null;
return project.getPackageFragmentRoot(resource);
} else if (rootPath.isPrefixOf(resourcePath)) {
// allow creation of package fragment if it contains a .java file that is included
if (!Util
.isExcluded(resourcePath, ((ClasspathEntry)entry).fullInclusionPatternChars(),
((ClasspathEntry)entry).fullExclusionPatternChars(), true)) {
// given we have a resource child of the root, it cannot be a JAR pkg root
PackageFragmentRoot root =
isExternal ?
new ExternalPackageFragmentRoot(rootPath, (JavaProject) project) :
(PackageFragmentRoot)((JavaProject)project).getFolderPackageFragmentRoot(rootPath);
if (root == null) return null;
IPath pkgPath = resourcePath.removeFirstSegments(rootPath.segmentCount());
if (resource.getType() == IResource.FILE) {
// if the resource is a file, then remove the last segment which
// is the file name in the package
pkgPath = pkgPath.removeLastSegments(1);
}
String[] pkgName = pkgPath.segments();
// if package name is in the cache, then it has already been validated
// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=133141)
if (allPkgFragmentsCache != null && allPkgFragmentsCache.containsKey(pkgName))
return root.getPackageFragment(pkgName);
if (pkgName.length != 0 && JavaConventions.validatePackageName(
Util.packageName(pkgPath, sourceLevel, complianceLevel), sourceLevel,
complianceLevel).getSeverity() == IStatus.ERROR) {
return null;
}
return root.getPackageFragment(pkgName);
}
}
}
}
} catch (JavaModelException npe) {
return null;
}
return null;
}
/**
* Returns the Java element corresponding to the given resource, or
* <code>null</code> if unable to associate the given resource
* with a Java element.
* <p>
* The resource must be one of:<ul>
* <li>a project - the element returned is the corresponding <code>IJavaProject</code></li>
* <li>a <code>.java</code> file - the element returned is the corresponding <code>ICompilationUnit</code></li>
* <li>a <code>.class</code> file - the element returned is the corresponding <code>IClassFile</code></li>
* <li>a ZIP archive (e.g. a <code>.jar</code>, a <code>.zip</code> file, etc.) - the element returned is the corresponding
* <code>IPackageFragmentRoot</code></li>
* <li>a folder - the element returned is the corresponding <code>IPackageFragmentRoot</code>
* or <code>IPackageFragment</code></li>
* <li>the workspace root resource - the element returned is the <code>IJavaModel</code></li>
* </ul>
* <p>
* Creating a Java element has the side effect of creating and opening all of the
* element's parents if they are not yet open.
*/
public static IJavaElement create(IResource resource, IJavaProject project) {
if (resource == null) {
return null;
}
int type = resource.getType();
switch (type) {
case IResource.PROJECT :
return JavaCore.create((IProject) resource);
case IResource.FILE :
return create((IFile) resource, project);
case IResource.FOLDER :
return create((IFolder) resource, project);
case IResource.ROOT :
return JavaCore.create((IWorkspaceRoot) resource);
default :
return null;
}
}
/**
* Returns the Java element corresponding to the given file, its project being the given
* project.
* Returns <code>null</code> if unable to associate the given file
* with a Java element.
*
* <p>The file must be one of:<ul>
* <li>a <code>.java</code> file - the element returned is the corresponding <code>ICompilationUnit</code></li>
* <li>a <code>.class</code> file - the element returned is the corresponding <code>IClassFile</code></li>
* <li>a ZIP archive (e.g. a <code>.jar</code>, a <code>.zip</code> file, etc.) - the element returned is the corresponding <code>IPackageFragmentRoot</code></li>
* </ul>
* <p>
* Creating a Java element has the side effect of creating and opening all of the
* element's parents if they are not yet open.
*/
public static IJavaElement create(IFile file, IJavaProject project) {
if (file == null) {
return null;
}
if (project == null) {
project = JavaCore.create(file.getProject());
}
if (file.getFileExtension() != null) {
String name = file.getName();
if (Util.isJavaLikeFileName(name))
return createCompilationUnitFrom(file, project);
if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(name))
return createClassFileFrom(file, project);
return createJarPackageFragmentRootFrom(file, project);
}
return null;
}
/**
* Returns the package fragment or package fragment root corresponding to the given folder,
* its parent or great parent being the given project.
* or <code>null</code> if unable to associate the given folder with a Java element.
* <p>
* Note that a package fragment root is returned rather than a default package.
* <p>
* Creating a Java element has the side effect of creating and opening all of the
* element's parents if they are not yet open.
*/
public static IJavaElement create(IFolder folder, IJavaProject project) {
if (folder == null) {
return null;
}
IJavaElement element;
if (project == null) {
project = JavaCore.create(folder.getProject());
element = determineIfOnClasspath(folder, project);
if (element == null) {
// walk all projects and find one that have the given folder on its classpath
IJavaProject[] projects;
try {
projects = org.eclipse.jdt.internal.core.JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects();
} catch (JavaModelException e) {
return null;
}
for (int i = 0, length = projects.length; i < length; i++) {
project = projects[i];
element = determineIfOnClasspath(folder, project);
if (element != null)
break;
}
}
} else {
element = determineIfOnClasspath(folder, project);
}
return element;
}
/**
* Creates and returns a class file element for the given <code>.class</code> file,
* its project being the given project. Returns <code>null</code> if unable
* to recognize the class file.
*/
public static IClassFile createClassFileFrom(IFile file, IJavaProject project) {
if (file == null) {
return null;
}
if (project == null) {
project = JavaCore.create(file.getProject());
}
IPackageFragment pkg = (IPackageFragment)determineIfOnClasspath(file, (JavaProject)project);
if (pkg == null) {
// fix for 1FVS7WE
// not on classpath - make the root its folder, and a default package
PackageFragmentRoot root = (PackageFragmentRoot)project.getPackageFragmentRoot(file.getParent());
pkg = root.getPackageFragment(CharOperation.NO_STRINGS);
}
return pkg.getClassFile(file.getName());
}
/**
* Creates and returns a handle for the given JAR file, its project being the given project.
* The Java model associated with the JAR's project may be
* created as a side effect.
* Returns <code>null</code> if unable to create a JAR package fragment root.
* (for example, if the JAR file represents a non-Java resource)
*/
public static IPackageFragmentRoot createJarPackageFragmentRootFrom(IFile file, IJavaProject project) {
if (file == null) {
return null;
}
if (project == null) {
project = JavaCore.create(file.getProject());
}
// Create a jar package fragment root only if on the classpath
IPath resourcePath = file.getFullPath();
try {
IClasspathEntry entry = ((JavaProject)project).getClasspathEntryFor(resourcePath);
if (entry != null) {
return project.getPackageFragmentRoot(file);
}
} catch (JavaModelException e) {
// project doesn't exist: return null
}
return null;
}
public static IndexManager getIndexManager() {
return MANAGER.indexManager;
}
public org.eclipse.jdt.internal.core.JavaModel getJavaModel() {
return javaModel;
}
public DeltaProcessor getDeltaProcessor() {
return this.deltaState.getDeltaProcessor();
}
/**
* Flushes ZipFiles cache if there are no more clients.
*/
public void flushZipFiles(Object owner) {
ZipCache zipCache = this.zipFiles.get();
if (zipCache == null) {
return;
}
// the owner will be responsible for flushing the cache
// we want to check object identity to make sure this is the owner that created the cache
if (zipCache.owner == owner) {
this.zipFiles.set(null);
zipCache.flush();
}
}
/**
* Starts caching ZipFiles.
* Ignores if there are already clients.
*/
public void cacheZipFiles(Object owner) {
ZipCache zipCache = this.zipFiles.get();
if (zipCache != null) {
return;
}
// the owner will be responsible for flushing the cache
this.zipFiles.set(new ZipCache(owner));
}
public synchronized char[] intern(char[] array) {
return this.charArraySymbols.add(array);
}
public synchronized String intern(String s) {
// make sure to copy the string (so that it doesn't hold on the underlying char[] that might be much bigger than necessary)
return (String)this.stringSymbols.add(new String(s));
// Note1: String#intern() cannot be used as on some VMs this prevents the string from being garbage collected
// Note 2: Instead of using a WeakHashset, one could use a WeakHashMap with the following implementation
// This would costs more per entry (one Entry object and one WeakReference more))
/*
WeakReference reference = (WeakReference) this.symbols.get(s);
String existing;
if (reference != null && (existing = (String) reference.get()) != null)
return existing;
this.symbols.put(s, new WeakReference(s));
return s;
*/
}
/**
* Returns the set of elements which are out of synch with their buffers.
*/
protected HashSet getElementsOutOfSynchWithBuffers() {
return this.elementsOutOfSynchWithBuffers;
}
/**
* Returns the open ZipFile at the given path. If the ZipFile
* does not yet exist, it is created, opened, and added to the cache
* of open ZipFiles.
* <p/>
* The path must be a file system path if representing an external
* zip/jar, or it must be an absolute workspace relative path if
* representing a zip/jar inside the workspace.
*
* @throws CoreException
* If unable to create/open the ZipFile
*/
public ZipFile getZipFile(IPath path) throws CoreException {
if (isInvalidArchive(path))
throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, new ZipException()));
ZipCache zipCache;
ZipFile zipFile;
if ((zipCache = (ZipCache)this.zipFiles.get()) != null
&& (zipFile = zipCache.getCache(path)) != null) {
return zipFile;
}
File localFile = null;
IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
IResource file = root.findMember(path);
if (file != null) {
// internal resource
URI location;
if (file.getType() != IResource.FILE || (location = file.getLocationURI()) == null) {
throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.bind(Messages.file_notFound, path.toString()), null));
}
localFile = Util.toLocalFile(location, null/*no progress availaible*/);
if (localFile == null)
throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.bind(Messages.file_notFound, path.toString()), null));
} else {
// external resource -> it is ok to use toFile()
localFile= path.toFile();
}
try {
if (ZIP_ACCESS_VERBOSE) {
System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + localFile ); //$NON-NLS-1$ //$NON-NLS-2$
}
zipFile = new ZipFile(localFile);
if (zipCache != null) {
zipCache.setCache(path, zipFile);
}
return zipFile;
} catch (IOException e) {
addInvalidArchive(path);
throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e));
}
}
public boolean isInvalidArchive(IPath path) {
return this.invalidArchives != null && this.invalidArchives.contains(path);
}
public void removeFromInvalidArchiveCache(IPath path) {
if (this.invalidArchives != null) {
this.invalidArchives.remove(path);
}
}
public void addInvalidArchive(IPath path) {
// unlikely to be null
if (this.invalidArchives == null) {
this.invalidArchives = Collections.synchronizedSet(new HashSet<IPath>());
}
if (this.invalidArchives != null) {
this.invalidArchives.add(path);
}
}
public void addNonChainingJar(IPath path) {
if (this.nonChainingJars != null)
this.nonChainingJars.add(path);
}
public ICompilationUnit[] getWorkingCopies(WorkingCopyOwner owner, boolean addPrimary) {
synchronized(this.perWorkingCopyInfos) {
ICompilationUnit[] primaryWCs = addPrimary && owner != DefaultWorkingCopyOwner.PRIMARY
? getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, false)
: null;
Map workingCopyToInfos = (Map)this.perWorkingCopyInfos.get(owner);
if (workingCopyToInfos == null) return primaryWCs;
int primaryLength = primaryWCs == null ? 0 : primaryWCs.length;
int size = workingCopyToInfos.size(); // note size is > 0 otherwise pathToPerWorkingCopyInfos would be null
ICompilationUnit[] result = new ICompilationUnit[primaryLength + size];
int index = 0;
if (primaryWCs != null) {
for (int i = 0; i < primaryLength; i++) {
ICompilationUnit primaryWorkingCopy = primaryWCs[i];
ICompilationUnit workingCopy = new CompilationUnit((PackageFragment) primaryWorkingCopy.getParent(), primaryWorkingCopy.getElementName(), owner);
if (!workingCopyToInfos.containsKey(workingCopy))
result[index++] = primaryWorkingCopy;
}
if (index != primaryLength)
System.arraycopy(result, 0, result = new ICompilationUnit[index+size], 0, index);
}
Iterator iterator = workingCopyToInfos.values().iterator();
while(iterator.hasNext()) {
result[index++] = ((JavaModelManager.PerWorkingCopyInfo)iterator.next()).getWorkingCopy();
}
return result;
}
}
/**
* Returns the info for the element.
*/
public synchronized Object getInfo(IJavaElement element) {
HashMap tempCache = (HashMap)this.temporaryCache.get();
if (tempCache != null) {
Object result = tempCache.get(element);
if (result != null) {
return result;
}
}
return this.cache.getInfo(element);
}
/**
* Returns the info for this element without
* disturbing the cache ordering.
*/
protected synchronized Object peekAtInfo(IJavaElement element) {
HashMap tempCache = (HashMap)this.temporaryCache.get();
if (tempCache != null) {
Object result = tempCache.get(element);
if (result != null) {
return result;
}
}
return this.cache.peekAtInfo(element);
}
/*
* Removes all cached info for the given element (including all children)
* from the cache.
* Returns the info for the given element, or null if it was closed.
*/
public synchronized Object removeInfoAndChildren(JavaElement element) throws JavaModelException {
Object info = this.cache.peekAtInfo(element);
if (info != null) {
boolean wasVerbose = false;
try {
if (JavaModelCache.VERBOSE) {
String elementType;
switch (element.getElementType()) {
case IJavaElement.JAVA_PROJECT:
elementType = "project"; //$NON-NLS-1$
break;
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
elementType = "root"; //$NON-NLS-1$
break;
case IJavaElement.PACKAGE_FRAGMENT:
elementType = "package"; //$NON-NLS-1$
break;
case IJavaElement.CLASS_FILE:
elementType = "class file"; //$NON-NLS-1$
break;
case IJavaElement.COMPILATION_UNIT:
elementType = "compilation unit"; //$NON-NLS-1$
break;
default:
elementType = "element"; //$NON-NLS-1$
}
System.out.println(Thread.currentThread() + " CLOSING " + elementType + " " +
element.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$
wasVerbose = true;
JavaModelCache.VERBOSE = false;
}
element.closing(info);
if (element instanceof IParent) {
closeChildren(info);
}
this.cache.removeInfo(element);
if (wasVerbose) {
System.out.println(this.cache.toStringFillingRation("-> ")); //$NON-NLS-1$
}
} finally {
JavaModelCache.VERBOSE = wasVerbose;
}
return info;
}
return null;
}
public void removePerProjectInfo(JavaProject javaProject, boolean removeExtJarInfo) {
synchronized(this.perProjectInfos) { // use the perProjectInfo collection as its own lock
IProject project = javaProject.getProject();
PerProjectInfo info= (PerProjectInfo) this.perProjectInfos.get(project);
if (info != null) {
this.perProjectInfos.remove(project);
// if (removeExtJarInfo) {
// info.forgetExternalTimestampsAndIndexes();
// }
}
}
resetClasspathListCache();
}
/*
* The given project is being removed. Remove all containers for this project from the cache.
*/
public synchronized void containerRemove(IJavaProject project) {
//TODO
// Map initializations = (Map) this.containerInitializationInProgress.get();
// if (initializations != null) {
// initializations.remove(project);
// }
// this.containers.remove(project);
}
/*
* Returns whether there is a temporary cache for the current thread.
*/
public boolean hasTemporaryCache() {
return this.temporaryCache.get() != null;
}
/**
* Returns the temporary cache for newly opened elements for the current thread.
* Creates it if not already created.
*/
public HashMap getTemporaryCache() {
HashMap result = (HashMap)this.temporaryCache.get();
if (result == null) {
result = new HashMap();
this.temporaryCache.set(result);
}
return result;
}
/*
* Puts the infos in the given map (keys are IJavaElements and values are JavaElementInfos)
* in the Java model cache in an atomic way if the info is not already present in the cache.
* If the info is already present in the cache, it depends upon the forceAdd parameter.
* If forceAdd is false it just returns the existing info and if true, this element and it's children are closed and then
* this particular info is added to the cache.
*/
protected synchronized Object putInfos(IJavaElement openedElement, Object newInfo, boolean forceAdd, Map newElements) {
// remove existing children as the are replaced with the new children contained in newElements
Object existingInfo = this.cache.peekAtInfo(openedElement);
if (existingInfo != null && !forceAdd) {
// If forceAdd is false, then it could mean that the particular element
// wasn't in cache at that point of time, but would have got added through
// another thread. In that case, removing the children could remove it's own
// children. So, we should not remove the children but return the already existing
// info.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=372687
return existingInfo;
}
if (openedElement instanceof IParent) {
closeChildren(existingInfo);
}
// Need to put any JarPackageFragmentRoot in first.
// This is due to the way the LRU cache flushes entries.
// When a JarPackageFragment is flushed from the LRU cache, the entire
// jar is flushed by removing the JarPackageFragmentRoot and all of its
// children (see ElementCache.close()). If we flush the JarPackageFragment
// when its JarPackageFragmentRoot is not in the cache and the root is about to be
// added (during the 'while' loop), we will end up in an inconsistent state.
// Subsequent resolution against package in the jar would fail as a result.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102422
// (theodora)
for (Iterator it = newElements.entrySet().iterator(); it.hasNext(); ) {
Map.Entry entry = (Map.Entry)it.next();
IJavaElement element = (IJavaElement)entry.getKey();
if (element instanceof JarPackageFragmentRoot) {
Object info = entry.getValue();
it.remove();
this.cache.putInfo(element, info);
}
}
Iterator iterator = newElements.entrySet().iterator();
while (iterator.hasNext()) {
Map.Entry entry = (Map.Entry)iterator.next();
this.cache.putInfo((IJavaElement)entry.getKey(), entry.getValue());
}
return newInfo;
}
private void closeChildren(Object info) {
if (info instanceof JavaElementInfo) {
IJavaElement[] children = ((JavaElementInfo)info).getChildren();
for (int i = 0, size = children.length; i < size; ++i) {
JavaElement child = (JavaElement)children[i];
try {
child.close();
} catch (JavaModelException e) {
// ignore
}
}
}
}
/*
* Returns the per-working copy info for the given working copy at the given path.
* If it doesn't exist and if create, add a new per-working copy info with the given problem requestor.
* If recordUsage, increment the per-working copy info's use count.
* Returns null if it doesn't exist and not create.
*/
public PerWorkingCopyInfo getPerWorkingCopyInfo(CompilationUnit workingCopy, boolean create, boolean recordUsage,
IProblemRequestor problemRequestor) {
synchronized (this.perWorkingCopyInfos) { // use the perWorkingCopyInfo collection as its own lock
WorkingCopyOwner owner = workingCopy.owner;
Map workingCopyToInfos = (Map)this.perWorkingCopyInfos.get(owner);
if (workingCopyToInfos == null && create) {
workingCopyToInfos = new HashMap();
this.perWorkingCopyInfos.put(owner, workingCopyToInfos);
}
PerWorkingCopyInfo info = workingCopyToInfos == null ? null : (PerWorkingCopyInfo)workingCopyToInfos.get(workingCopy);
if (info == null && create) {
info = new PerWorkingCopyInfo(workingCopy, problemRequestor);
workingCopyToInfos.put(workingCopy, info);
}
if (info != null && recordUsage) info.useCount++;
return info;
}
}
/*
* Returns the per-project info for the given project. If specified, create the info if the info doesn't exist.
*/
public PerProjectInfo getPerProjectInfo(IProject project, boolean create) {
synchronized(this.perProjectInfos) { // use the perProjectInfo collection as its own lock
PerProjectInfo info= this.perProjectInfos.get(project);
if (info == null && create) {
info = new PerProjectInfo(project);
this.perProjectInfos.put(project, info);
}
return info;
}
}
/*
* Returns the per-project info for the given project.
* If the info doesn't exist, check for the project existence and create the info.
* @throws JavaModelException if the project doesn't exist.
*/
public PerProjectInfo getPerProjectInfoCheckExistence(IProject project) throws JavaModelException {
org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo info = getPerProjectInfo(project, false /* don't create info */);
if (info == null) {
// if (!JavaProject.hasJavaNature(project)) {
// throw ((JavaProject)JavaCore.create(project)).newNotPresentException();
// }
info = getPerProjectInfo(project, true /* create info */);
}
return info;
}
public boolean isClasspathBeingResolved(IJavaProject project) {
return getClasspathBeingResolved().contains(project);
}
private HashSet getClasspathBeingResolved() {
HashSet<IJavaProject> result = this.classpathsBeingResolved.get();
if (result == null) {
result = new HashSet<>();
this.classpathsBeingResolved.set(result);
}
return result;
}
public void setClasspathBeingResolved(IJavaProject project, boolean classpathIsResolved) {
if (classpathIsResolved) {
getClasspathBeingResolved().add(project);
} else {
getClasspathBeingResolved().remove(project);
}
}
/**
* Get all secondary types for a project and store result in per project info cache.
* <p>
* This cache is an <code>Hashtable<String, HashMap<String, IType>></code>:
* <ul>
* <li>key: package name
* <li>value:
* <ul>
* <li>key: type name
* <li>value: java model handle for the secondary type
* </ul>
* </ul>
* Hashtable was used to protect callers from possible concurrent access.
* </p>
* Note that this map may have a specific entry which key is {@link #INDEXED_SECONDARY_TYPES }
* and value is a map containing all secondary types created during indexing.
* When this key is in cache and indexing is finished, returned map is merged
* with the value of this special key. If indexing is not finished and caller does
* not wait for the end of indexing, returned map is the current secondary
* types cache content which may be invalid...
*
* @param project
* Project we want get secondary types from
* @return HashMap Table of secondary type names->path for given project
*/
public Map secondaryTypes(IJavaProject project, boolean waitForIndexes, IProgressMonitor monitor) throws JavaModelException {
if (VERBOSE) {
StringBuffer buffer = new StringBuffer("JavaModelManager.secondaryTypes("); //$NON-NLS-1$
buffer.append(project.getElementName());
buffer.append(',');
buffer.append(waitForIndexes);
buffer.append(')');
Util.verbose(buffer.toString());
}
// Return cache if not empty and there's no new secondary types created during indexing
final PerProjectInfo projectInfo = getPerProjectInfoCheckExistence(project.getProject());
Map indexingSecondaryCache =
projectInfo.secondaryTypes == null ? null : (Map)projectInfo.secondaryTypes.get(INDEXED_SECONDARY_TYPES);
if (projectInfo.secondaryTypes != null && indexingSecondaryCache == null) {
return projectInfo.secondaryTypes;
}
// Perform search request only if secondary types cache is not initialized yet (this will happen only once!)
if (projectInfo.secondaryTypes == null) {
return secondaryTypesSearching(project, waitForIndexes, monitor, projectInfo);
}
// New secondary types have been created while indexing secondary types cache
// => need to know whether the indexing is finished or not
boolean indexing = this.indexManager.awaitingJobsCount() > 0;
if (indexing) {
if (!waitForIndexes) {
// Indexing is running but caller cannot wait => return current cache
return projectInfo.secondaryTypes;
}
// Wait for the end of indexing or a cancel
while (this.indexManager.awaitingJobsCount() > 0) {
if (monitor != null && monitor.isCanceled()) {
return projectInfo.secondaryTypes;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
return projectInfo.secondaryTypes;
}
}
}
// Indexing is finished => merge caches and return result
return secondaryTypesMerging(projectInfo.secondaryTypes);
}
/*
* Return secondary types cache merged with new secondary types created while indexing
* Note that merge result is directly stored in given parameter map.
*/
private Hashtable secondaryTypesMerging(Hashtable secondaryTypes) {
if (VERBOSE) {
Util.verbose("JavaModelManager.getSecondaryTypesMerged()"); //$NON-NLS-1$
Util.verbose(" - current cache to merge:"); //$NON-NLS-1$
Iterator entries = secondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry)entries.next();
String packName = (String)entry.getKey();
Util.verbose(" + " + packName + ':' + entry.getValue()); //$NON-NLS-1$
}
}
// Return current cache if there's no indexing cache (double check, this should not happen)
HashMap indexedSecondaryTypes = (HashMap)secondaryTypes.remove(INDEXED_SECONDARY_TYPES);
if (indexedSecondaryTypes == null) {
return secondaryTypes;
}
// Merge indexing cache in secondary types one
Iterator entries = indexedSecondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry)entries.next();
IFile file = (IFile) entry.getKey();
// Remove all secondary types of indexed file from cache
secondaryTypesRemoving(secondaryTypes, file);
// Add all indexing file secondary types in given secondary types cache
HashMap fileSecondaryTypes = (HashMap)entry.getValue();
Iterator entries2 = fileSecondaryTypes.entrySet().iterator();
while (entries2.hasNext()) {
Map.Entry entry2 = (Map.Entry)entries2.next();
String packageName = (String)entry2.getKey();
HashMap cachedTypes = (HashMap)secondaryTypes.get(packageName);
if (cachedTypes == null) {
secondaryTypes.put(packageName, entry2.getValue());
} else {
HashMap types = (HashMap)entry2.getValue();
Iterator entries3 = types.entrySet().iterator();
while (entries3.hasNext()) {
Map.Entry entry3 = (Map.Entry)entries3.next();
String typeName = (String)entry3.getKey();
cachedTypes.put(typeName, entry3.getValue());
}
}
}
}
if (VERBOSE) {
Util.verbose(" - secondary types cache merged:"); //$NON-NLS-1$
entries = secondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry)entries.next();
String packName = (String)entry.getKey();
Util.verbose(" + " + packName + ':' + entry.getValue()); //$NON-NLS-1$
}
}
return secondaryTypes;
}
/**
* Returns the last built state for the given project, or null if there is none.
* Deserializes the state if necessary.
*
* For use by image builder and evaluation support only
*/
public Object getLastBuiltState(IProject project, IProgressMonitor monitor) {
if (!JavaProject.hasJavaNature(project)) {
if (JavaBuilder.DEBUG)
System.out.println(project + " is not a Java project"); //$NON-NLS-1$
return null; // should never be requested on non-Java projects
}
PerProjectInfo info = getPerProjectInfo(project, true/*create if missing*/);
if (!info.triedRead) {
info.triedRead = true;
// try {
if (monitor != null)
monitor.subTask(Messages.bind(Messages.build_readStateProgress, project.getName()));
info.savedState = null;//readState(project);
// } catch (CoreException e) {
// e.printStackTrace();
// }
}
return info.savedState;
}
/**
* Remove from secondary types cache all types belonging to a given file.
* Clean secondary types cache built while indexing if requested.
* <p/>
* Project's secondary types cache is found using file location.
*
* @param file
* File to remove
*/
public void secondaryTypesRemoving(IFile file, boolean cleanIndexCache) {
if (VERBOSE) {
StringBuffer buffer = new StringBuffer("JavaModelManager.removeFromSecondaryTypesCache("); //$NON-NLS-1$
buffer.append(file.getName());
buffer.append(')');
Util.verbose(buffer.toString());
}
if (file != null) {
PerProjectInfo projectInfo = getPerProjectInfo(file.getProject(), false);
if (projectInfo != null && projectInfo.secondaryTypes != null) {
if (VERBOSE) {
Util.verbose("-> remove file from cache of project: " + file.getProject().getName()); //$NON-NLS-1$
}
// Clean current cache
secondaryTypesRemoving(projectInfo.secondaryTypes, file);
// Clean indexing cache if necessary
HashMap indexingCache = (HashMap)projectInfo.secondaryTypes.get(INDEXED_SECONDARY_TYPES);
if (!cleanIndexCache) {
if (indexingCache == null) {
// Need to signify that secondary types indexing will happen before any request happens
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=152841
projectInfo.secondaryTypes.put(INDEXED_SECONDARY_TYPES, new HashMap());
}
return;
}
if (indexingCache != null) {
Set keys = indexingCache.keySet();
int filesSize = keys.size(), filesCount = 0;
File[] removed = null;
Iterator cachedFiles = keys.iterator();
while (cachedFiles.hasNext()) {
File cachedFile = (File)cachedFiles.next();
if (file.equals(cachedFile)) {
if (removed == null) removed = new File[filesSize];
filesSize--;
removed[filesCount++] = cachedFile;
}
}
if (removed != null) {
for (int i = 0; i < filesCount; i++) {
indexingCache.remove(removed[i]);
}
}
}
}
}
}
/*
* Remove from a given cache map all secondary types belonging to a given file.
* Note that there can have several secondary types per file...
*/
private void secondaryTypesRemoving(Hashtable secondaryTypesMap, IFile file) {
if (VERBOSE) {
StringBuffer buffer = new StringBuffer("JavaModelManager.removeSecondaryTypesFromMap("); //$NON-NLS-1$
Iterator entries = secondaryTypesMap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry)entries.next();
String qualifiedName = (String)entry.getKey();
buffer.append(qualifiedName + ':' + entry.getValue());
}
buffer.append(',');
buffer.append(file.getFullPath());
buffer.append(')');
Util.verbose(buffer.toString());
}
Set packageEntries = secondaryTypesMap.entrySet();
int packagesSize = packageEntries.size(), removedPackagesCount = 0;
String[] removedPackages = null;
Iterator packages = packageEntries.iterator();
while (packages.hasNext()) {
Map.Entry entry = (Map.Entry)packages.next();
String packName = (String)entry.getKey();
if (packName != INDEXED_SECONDARY_TYPES) { // skip indexing cache entry if present (!= is intentional)
HashMap types = (HashMap)entry.getValue();
Set nameEntries = types.entrySet();
int namesSize = nameEntries.size(), removedNamesCount = 0;
String[] removedNames = null;
Iterator names = nameEntries.iterator();
while (names.hasNext()) {
Map.Entry entry2 = (Map.Entry)names.next();
String typeName = (String)entry2.getKey();
JavaElement type = (JavaElement)entry2.getValue();
if (file.equals(type.resource())) {
if (removedNames == null) removedNames = new String[namesSize];
namesSize--;
removedNames[removedNamesCount++] = typeName;
}
}
if (removedNames != null) {
for (int i = 0; i < removedNamesCount; i++) {
types.remove(removedNames[i]);
}
}
if (types.size() == 0) {
if (removedPackages == null) removedPackages = new String[packagesSize];
packagesSize--;
removedPackages[removedPackagesCount++] = packName;
}
}
}
if (removedPackages != null) {
for (int i = 0; i < removedPackagesCount; i++) {
secondaryTypesMap.remove(removedPackages[i]);
}
}
if (VERBOSE) {
Util.verbose(" - new secondary types map:"); //$NON-NLS-1$
Iterator entries = secondaryTypesMap.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry)entries.next();
String qualifiedName = (String)entry.getKey();
Util.verbose(" + " + qualifiedName + ':' + entry.getValue()); //$NON-NLS-1$
}
}
}
/**
* Returns the existing element in the cache that is equal to the given element.
*/
public synchronized IJavaElement getExistingElement(IJavaElement element) {
return this.cache.getExistingElement(element);
}
/**
* Remember the info for the jar binary type
*/
protected synchronized void putJarTypeInfo(IJavaElement type, Object info) {
this.cache.jarTypeCache.put(type, info);
}
/*
* Perform search request to get all secondary types of a given project.
* If not waiting for indexes and indexing is running, will return types found in current built indexes...
*/
private Map secondaryTypesSearching(IJavaProject project, boolean waitForIndexes, IProgressMonitor monitor,
final PerProjectInfo projectInfo) throws JavaModelException {
if (VERBOSE || BasicSearchEngine.VERBOSE) {
StringBuffer buffer = new StringBuffer("JavaModelManager.secondaryTypesSearch("); //$NON-NLS-1$
buffer.append(project.getElementName());
buffer.append(',');
buffer.append(waitForIndexes);
buffer.append(')');
Util.verbose(buffer.toString());
}
final Hashtable secondaryTypes = new Hashtable(3);
IRestrictedAccessTypeRequestor nameRequestor = new IRestrictedAccessTypeRequestor() {
public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path,
AccessRestriction access) {
String key = packageName == null ? "" : new String(packageName); //$NON-NLS-1$
HashMap types = (HashMap)secondaryTypes.get(key);
if (types == null) types = new HashMap(3);
types.put(new String(simpleTypeName), path);
secondaryTypes.put(key, types);
}
};
// Build scope using prereq projects but only source folders
IPackageFragmentRoot[] allRoots = project.getAllPackageFragmentRoots();
int length = allRoots.length, size = 0;
IPackageFragmentRoot[] allSourceFolders = new IPackageFragmentRoot[length];
for (int i = 0; i < length; i++) {
if (allRoots[i].getKind() == IPackageFragmentRoot.K_SOURCE) {
allSourceFolders[size++] = allRoots[i];
}
}
if (size < length) {
System.arraycopy(allSourceFolders, 0, allSourceFolders = new IPackageFragmentRoot[size], 0, size);
}
// Search all secondary types on scope
new BasicSearchEngine()
.searchAllSecondaryTypeNames(allSourceFolders, nameRequestor, waitForIndexes, monitor);
// Build types from paths
Iterator packages = secondaryTypes.values().iterator();
while (packages.hasNext()) {
HashMap types = (HashMap)packages.next();
HashMap tempTypes = new HashMap(types.size());
Iterator names = types.entrySet().iterator();
while (names.hasNext()) {
Map.Entry entry = (Map.Entry)names.next();
String typeName = (String)entry.getKey();
String path = (String)entry.getValue();
names.remove();
if (Util.isJavaLikeFileName(path)) {
IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path));
ICompilationUnit unit = org.eclipse.jdt.internal.core.JavaModelManager
.createCompilationUnitFrom(file, null);
IType type = unit.getType(typeName);
tempTypes.put(typeName, type);
}
}
types.putAll(tempTypes);
}
// Store result in per project info cache if still null or there's still an indexing cache (may have been set by another thread...)
if (projectInfo.secondaryTypes == null || projectInfo.secondaryTypes.get(INDEXED_SECONDARY_TYPES) != null) {
projectInfo.secondaryTypes = secondaryTypes;
if (VERBOSE || BasicSearchEngine.VERBOSE) {
System.out.print(Thread.currentThread() + " -> secondary paths stored in cache: "); //$NON-NLS-1$
System.out.println();
Iterator entries = secondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry)entries.next();
String qualifiedName = (String)entry.getKey();
Util.verbose(" - " + qualifiedName + '-' + entry.getValue()); //$NON-NLS-1$
}
}
}
return projectInfo.secondaryTypes;
}
public String getOption(String optionName) {
if (org.eclipse.jdt.core.JavaCore.CORE_ENCODING.equals(optionName)){
return "UTF-8";
}
// backward compatibility
// if (isDeprecatedOption(optionName)) {
// return JavaCore.ERROR;
// }
int optionLevel = getOptionLevel(optionName);
if (optionLevel != UNKNOWN_OPTION) {
String value = optionsCache.get(optionName);
// if (value == null && optionLevel == DEPRECATED_OPTION) {
// May be a deprecated option, retrieve the new value in compatible options
// String[] compatibleOptions = (String[]) this.deprecatedOptions.get(optionName);
// value = service.get(compatibleOptions[0], null, this.preferencesLookup);
// }
return value == null ? null : value.trim();
}
return null;
}
public void setOptions(Hashtable newOptions) {
Hashtable cachedValue = newOptions == null ? null : new Hashtable(newOptions);
// IEclipsePreferences defaultPreferences = getDefaultPreferences();
// IEclipsePreferences instancePreferences = getInstancePreferences();
if (newOptions == null){
// try {
optionsCache.clear();
// } catch(BackingStoreException e) {
// ignore
// }
}
// else {
// Enumeration keys = newOptions.keys();
// while (keys.hasMoreElements()){
// String key = (String)keys.nextElement();
// int optionLevel = getOptionLevel(key);
// if (optionLevel == UNKNOWN_OPTION) continue; // unrecognized option
// if (key.equals(JavaCore.CORE_ENCODING)) {
// if (cachedValue != null) {
// cachedValue.put(key, JavaCore.getEncoding());
// }
// continue; // skipped, contributed by resource prefs
// }
// String value = (String) newOptions.get(key);
// String defaultValue = defaultPreferences.get(key, null);
// // Store value in preferences
// if (defaultValue != null && defaultValue.equals(value)) {
// value = null;
// }
// storePreference(key, value, instancePreferences, newOptions);
// }
// try {
// // persist options
// instancePreferences.flush();
// } catch(BackingStoreException e) {
// // ignore
// }
// }
// update cache
Util.fixTaskTags(cachedValue);
this.optionsCache.putAll(cachedValue);
}
public Hashtable getOptions() {
// return cached options if already computed
Hashtable cachedOptions; // use a local variable to avoid race condition (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=256329 )
if ((cachedOptions = this.optionsCache) != null) {
return new Hashtable(cachedOptions);
}
// if (!Platform.isRunning()) {
this.optionsCache = getDefaultOptionsNoInitialization();
return new Hashtable(this.optionsCache);
// }
// // init
// Hashtable options = new Hashtable(10);
// IPreferencesService service = Platform.getPreferencesService();
//
// // set options using preferences service lookup
// Iterator iterator = this.optionNames.iterator();
// while (iterator.hasNext()) {
// String propertyName = (String) iterator.next();
// String propertyValue = service.get(propertyName, null, this.preferencesLookup);
// if (propertyValue != null) {
// options.put(propertyName, propertyValue);
// }
// }
//
// // set deprecated options using preferences service lookup
// Iterator deprecatedEntries = this.deprecatedOptions.entrySet().iterator();
// while (deprecatedEntries.hasNext()) {
// Entry entry = (Entry) deprecatedEntries.next();
// String propertyName = (String) entry.getKey();
// String propertyValue = service.get(propertyName, null, this.preferencesLookup);
// if (propertyValue != null) {
// options.put(propertyName, propertyValue);
// String[] compatibleOptions = (String[]) entry.getValue();
// for (int co=0, length=compatibleOptions.length; co < length; co++) {
// String compatibleOption = compatibleOptions[co];
// if (!options.containsKey(compatibleOption))
// options.put(compatibleOption, propertyValue);
// }
// }
// }
//
// // get encoding through resource plugin
// options.put(JavaCore.CORE_ENCODING, JavaCore.getEncoding());
//
// // backward compatibility
// addDeprecatedOptions(options);
//
// Util.fixTaskTags(options);
// // store built map in cache
// this.optionsCache = new Hashtable(options);
// // return built map
// return options;
}
// If modified, also modify the method getDefaultOptionsNoInitialization()
public Hashtable getDefaultOptions(){
Hashtable defaultOptions = new Hashtable(10);
// see JavaCorePreferenceInitializer#initializeDefaultPluginPreferences() for changing default settings
// If modified, also modify the method getDefaultOptionsNoInitialization()
// IEclipsePreferences defaultPreferences = getDefaultPreferences();
// initialize preferences to their default
// Iterator iterator = this.optionNames.iterator();
// while (iterator.hasNext()) {
// String propertyName = (String) iterator.next();
// String value = defaultPreferences.get(propertyName, null);
// if (value != null) defaultOptions.put(propertyName, value);
// }
// get encoding through resource plugin
defaultOptions.put(org.eclipse.jdt.core.JavaCore.CORE_ENCODING, "UTF-8");
defaultOptions.putAll(getDefaultOptionsNoInitialization());
// backward compatibility
// addDeprecatedOptions(defaultOptions);
return defaultOptions;
}
// Do not modify without modifying getDefaultOptions()
private Hashtable getDefaultOptionsNoInitialization() {
Map defaultOptionsMap = new CompilerOptions().getMap(); // compiler defaults
// Override some compiler defaults
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.COMPILER_LOCAL_VARIABLE_ATTR, org.eclipse.jdt.core.JavaCore.GENERATE);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.COMPILER_CODEGEN_UNUSED_LOCAL, org.eclipse.jdt.core.JavaCore.PRESERVE);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.COMPILER_TASK_TAGS, org.eclipse.jdt.core.JavaCore.DEFAULT_TASK_TAGS);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.COMPILER_TASK_PRIORITIES, org.eclipse.jdt.core.JavaCore.DEFAULT_TASK_PRIORITIES);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.COMPILER_TASK_CASE_SENSITIVE, org.eclipse.jdt.core.JavaCore.ENABLED);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.COMPILER_DOC_COMMENT_SUPPORT, org.eclipse.jdt.core.JavaCore.ENABLED);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, org.eclipse.jdt.core.JavaCore.ERROR);
// Builder settings
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, org.eclipse.jdt.core.JavaCore.ABORT);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE, org.eclipse.jdt.core.JavaCore.WARNING);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER, org.eclipse.jdt.core.JavaCore.CLEAN);
// JavaCore settings
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_JAVA_BUILD_ORDER, org.eclipse.jdt.core.JavaCore.IGNORE);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_INCOMPLETE_CLASSPATH, org.eclipse.jdt.core.JavaCore.ERROR);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_CIRCULAR_CLASSPATH, org.eclipse.jdt.core.JavaCore.ERROR);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, org.eclipse.jdt.core.JavaCore.IGNORE);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE, org.eclipse.jdt.core.JavaCore.ERROR);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS, org.eclipse.jdt.core.JavaCore.ENABLED);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS, org.eclipse.jdt.core.JavaCore.ENABLED);
// Formatter settings
defaultOptionsMap.putAll(DefaultCodeFormatterConstants.getEclipseDefaultSettings());
// CodeAssist settings
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_VISIBILITY_CHECK, org.eclipse.jdt.core.JavaCore.DISABLED);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_DEPRECATION_CHECK, org.eclipse.jdt.core.JavaCore.DISABLED);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_IMPLICIT_QUALIFICATION, org.eclipse.jdt.core.JavaCore.DISABLED);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_FIELD_PREFIXES, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_STATIC_FIELD_PREFIXES, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_STATIC_FINAL_FIELD_PREFIXES, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_LOCAL_PREFIXES, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_ARGUMENT_PREFIXES, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_FIELD_SUFFIXES, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_STATIC_FIELD_SUFFIXES, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_STATIC_FINAL_FIELD_SUFFIXES, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_LOCAL_SUFFIXES, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_ARGUMENT_SUFFIXES, ""); //$NON-NLS-1$
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_FORBIDDEN_REFERENCE_CHECK, org.eclipse.jdt.core.JavaCore.ENABLED);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_DISCOURAGED_REFERENCE_CHECK, org.eclipse.jdt.core.JavaCore.DISABLED);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_CAMEL_CASE_MATCH, org.eclipse.jdt.core.JavaCore.ENABLED);
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.CODEASSIST_SUGGEST_STATIC_IMPORTS, org.eclipse.jdt.core.JavaCore.ENABLED);
// Time out for parameter names
defaultOptionsMap.put(org.eclipse.jdt.core.JavaCore.TIMEOUT_FOR_PARAMETER_NAME_FROM_ATTACHED_JAVADOC, "50"); //$NON-NLS-1$
return new Hashtable(defaultOptionsMap);
}
/**
* Returns the level of the given option.
*
* @param optionName The name of the option
* @return The level of the option as an int which may have the following
* values:
* <ul>
* <li>{@link #UNKNOWN_OPTION}: the given option is unknown</li>
* <li>{@link #DEPRECATED_OPTION}: the given option is deprecated</li>
* <li>{@link #VALID_OPTION}: the given option is valid</li>
* </ul>
*/
public int getOptionLevel(String optionName) {
if (this.optionNames.contains(optionName)) {
return VALID_OPTION;
}
if (this.deprecatedOptions.get(optionName) != null) {
return DEPRECATED_OPTION;
}
return UNKNOWN_OPTION;
}
/**
* Returns the value of the given option for the given Eclipse preferences.
* If no value was already set, then inherits from the global options if specified.
*
* @param optionName The name of the option
* @param inheritJavaCoreOptions Tells whether the value can be inherited from global JavaCore options
* @param projectPreferences The eclipse preferences from which to get the value
* @return The value of the option. May be <code>null</code>
*/
public String getOption(String optionName, boolean inheritJavaCoreOptions, IEclipsePreferences projectPreferences) {
// Return the option value depending on its level
switch (getOptionLevel(optionName)) {
case VALID_OPTION:
// Valid option, return the preference value
String javaCoreDefault = inheritJavaCoreOptions ? JavaCore.getOption(optionName) : null;
if (projectPreferences == null) return javaCoreDefault;
String value = projectPreferences.get(optionName, javaCoreDefault);
return value == null ? null : value.trim();
case DEPRECATED_OPTION:
// Return the deprecated option value if it was already set
String oldValue = projectPreferences.get(optionName, null);
if (oldValue != null) {
return oldValue.trim();
}
// Get the new compatible value
String[] compatibleOptions = (String[]) this.deprecatedOptions.get(optionName);
String newDefault = inheritJavaCoreOptions ? JavaCore.getOption(compatibleOptions[0]) : null;
String newValue = projectPreferences.get(compatibleOptions[0], newDefault);
return newValue == null ? null : newValue.trim();
}
return null;
}
/**
* Returns whether an option name is known or not.
*
* @param optionName The name of the option
* @return <code>true</code> when the option name is either
* {@link #VALID_OPTION valid} or {@link #DEPRECATED_OPTION deprecated},
* <code>false</code> otherwise.
*/
public boolean knowsOption(String optionName) {
boolean knownOption = this.optionNames.contains(optionName);
if (!knownOption) {
knownOption = this.deprecatedOptions.get(optionName) != null;
}
return knownOption;
}
/**
* Store the preferences value for the given option name.
*
* @param optionName The name of the option
* @param optionValue The value of the option. If <code>null</code>, then
* the option will be removed from the preferences instead.
* @param eclipsePreferences The eclipse preferences to be updated
* @param otherOptions more options being stored, used to avoid conflict between deprecated option and its compatible
* @return <code>true</code> if the preferences have been changed,
* <code>false</code> otherwise.
*/
public boolean storePreference(String optionName, String optionValue, IEclipsePreferences eclipsePreferences, Map otherOptions) {
int optionLevel = this.getOptionLevel(optionName);
if (optionLevel == UNKNOWN_OPTION) return false; // unrecognized option
// Store option value
switch (optionLevel) {
case org.eclipse.jdt.internal.core.JavaModelManager.VALID_OPTION:
if (optionValue == null) {
eclipsePreferences.remove(optionName);
} else {
eclipsePreferences.put(optionName, optionValue);
}
break;
case org.eclipse.jdt.internal.core.JavaModelManager.DEPRECATED_OPTION:
// Try to migrate deprecated option
eclipsePreferences.remove(optionName); // get rid off old preference
String[] compatibleOptions = (String[]) this.deprecatedOptions.get(optionName);
for (int co=0, length=compatibleOptions.length; co < length; co++) {
if (otherOptions != null && otherOptions.containsKey(compatibleOptions[co]))
continue; // don't overwrite explicit value of otherOptions at compatibleOptions[co]
if (optionValue == null) {
eclipsePreferences.remove(compatibleOptions[co]);
} else {
eclipsePreferences.put(compatibleOptions[co], optionValue);
}
}
break;
default:
return false;
}
return true;
}
/*
* Discards the per working copy info for the given working copy (making it a compilation unit)
* if its use count was 1. Otherwise, just decrement the use count.
* If the working copy is primary, computes the delta between its state and the original compilation unit
* and register it.
* Close the working copy, its buffer and remove it from the shared working copy table.
* Ignore if no per-working copy info existed.
* NOTE: it must NOT be synchronized as it may interact with the element info cache (if useCount is decremented to 0), see bug 50667.
* Returns the new use count (or -1 if it didn't exist).
*/
public int discardPerWorkingCopyInfo(CompilationUnit workingCopy) throws JavaModelException {
// create the delta builder (this remembers the current content of the working copy)
// outside the perWorkingCopyInfos lock (see bug 50667)
JavaElementDeltaBuilder deltaBuilder = null;
if (workingCopy.isPrimary() && workingCopy.hasUnsavedChanges()) {
deltaBuilder = new JavaElementDeltaBuilder(workingCopy);
}
PerWorkingCopyInfo info = null;
synchronized(this.perWorkingCopyInfos) {
WorkingCopyOwner owner = workingCopy.owner;
Map workingCopyToInfos = (Map)this.perWorkingCopyInfos.get(owner);
if (workingCopyToInfos == null) return -1;
info = (PerWorkingCopyInfo)workingCopyToInfos.get(workingCopy);
if (info == null) return -1;
if (--info.useCount == 0) {
// remove per working copy info
workingCopyToInfos.remove(workingCopy);
if (workingCopyToInfos.isEmpty()) {
this.perWorkingCopyInfos.remove(owner);
}
}
}
if (info.useCount == 0) { // info cannot be null here (check was done above)
// remove infos + close buffer (since no longer working copy)
// outside the perWorkingCopyInfos lock (see bug 50667)
removeInfoAndChildren(workingCopy);
workingCopy.closeBuffer();
// compute the delta if needed and register it if there are changes
if (deltaBuilder != null) {
deltaBuilder.buildDeltas();
if (deltaBuilder.delta != null) {
getDeltaProcessor().registerJavaModelDelta(deltaBuilder.delta);
}
}
}
return info.useCount;
}
public IClasspathContainer getClasspathContainer(final IPath containerPath, final IJavaProject project) throws JavaModelException {
IClasspathContainer container = containerGet(project, containerPath);
if (container == null) {
// if (batchContainerInitializations()) {
// // avoid deep recursion while initializing container on workspace restart
// // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=60437)
// try {
// container = initializeAllContainers(project, containerPath);
// } finally {
// batchInitializationFinished();
// }
// } else {
container = initializeContainer(project, containerPath);
// containerBeingInitializedRemove(project, containerPath);
SetContainerOperation operation = new SetContainerOperation(containerPath, new IJavaProject[] {project}, new IClasspathContainer[] {container});
operation.runOperation(null);
// }
}
return container;
}
private IClasspathContainer initializeContainer(IJavaProject project, IPath containerPath) throws JavaModelException {
ClasspathContainerInitializer initializer = containerInitializersCache.get(containerPath.segment(0));
IClasspathContainer container = null;
if(initializer != null){
containerPut(project, containerPath, CONTAINER_INITIALIZATION_IN_PROGRESS); // avoid initialization cycles
try {
initializer.initialize(containerPath, project);
// if (monitor != null)
// monitor.subTask(""); //$NON-NLS-1$
// retrieve value (if initialization was successful)
container = containerBeingInitializedGet(project, containerPath);
if (container == null && containerGet(project, containerPath) == null) {
// initializer failed to do its job: redirect to the failure container
container = initializer.getFailureContainer(containerPath, project);
// if (container == null) {
// if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
// verbose_container_null_failure_container(project, containerPath, initializer);
// return null; // break cycle
// }
// if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
// verbose_container_using_failure_container(project, containerPath, initializer);
containerPut(project, containerPath, container);
}
} catch (CoreException e) {
if (e instanceof JavaModelException) {
throw (JavaModelException) e;
} else {
throw new JavaModelException(e);
}
}
} else {
// create a dummy initializer and get the default failure container
container = (new ClasspathContainerInitializer() {
public void initialize(IPath path, IJavaProject javaProject) throws CoreException {
// not used
}
}).getFailureContainer(containerPath, project);
}
return container;
}
/**
* Add a secondary type in temporary indexing cache for a project got from given path.
*
* Current secondary types cache is not modified as we want to wait that indexing
* was finished before taking new secondary types into account.
*
* Indexing cache is a specific entry in secondary types cache which key is
* {@link #INDEXED_SECONDARY_TYPES } and value a map with same structure than
* secondary types cache itself.
*
* @see #secondaryTypes(IJavaProject, boolean, IProgressMonitor)
*/
public void secondaryTypeAdding(String path, char[] typeName, char[] packageName) {
if (VERBOSE) {
StringBuffer buffer = new StringBuffer("JavaModelManager.addSecondaryType("); //$NON-NLS-1$
buffer.append(path);
buffer.append(',');
buffer.append('[');
buffer.append(new String(packageName));
buffer.append('.');
buffer.append(new String(typeName));
buffer.append(']');
buffer.append(')');
Util.verbose(buffer.toString());
}
IWorkspaceRoot wRoot = ResourcesPlugin.getWorkspace().getRoot();
IResource resource = wRoot.findMember(path);
if (resource != null) {
if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(path) && resource.getType() == IResource.FILE) {
IProject project = resource.getProject();
try {
PerProjectInfo projectInfo = getPerProjectInfoCheckExistence(project);
// Get or create map to cache secondary types while indexing (can be not synchronized as indexing insure a non-concurrent usage)
HashMap indexedSecondaryTypes = null;
if (projectInfo.secondaryTypes == null) {
projectInfo.secondaryTypes = new Hashtable(3);
indexedSecondaryTypes = new HashMap(3);
projectInfo.secondaryTypes.put(INDEXED_SECONDARY_TYPES, indexedSecondaryTypes);
} else {
indexedSecondaryTypes = (HashMap) projectInfo.secondaryTypes.get(INDEXED_SECONDARY_TYPES);
if (indexedSecondaryTypes == null) {
indexedSecondaryTypes = new HashMap(3);
projectInfo.secondaryTypes.put(INDEXED_SECONDARY_TYPES, indexedSecondaryTypes);
}
}
// Store the secondary type in temporary cache (these are just handles => no problem to create it now...)
HashMap allTypes = (HashMap) indexedSecondaryTypes.get(resource);
if (allTypes == null) {
allTypes = new HashMap(3);
indexedSecondaryTypes.put(resource, allTypes);
}
ICompilationUnit unit = JavaModelManager.createCompilationUnitFrom((IFile)resource, null);
if (unit != null) {
String typeString = new String(typeName);
IType type = unit.getType(typeString);
// String packageString = new String(packageName);
// use package fragment name instead of parameter as it may be invalid...
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=186781
String packageString = type.getPackageFragment().getElementName();
HashMap packageTypes = (HashMap) allTypes.get(packageString);
if (packageTypes == null) {
packageTypes = new HashMap(3);
allTypes.put(packageString, packageTypes);
}
packageTypes.put(typeString, type);
}
if (VERBOSE) {
Util.verbose(" - indexing cache:"); //$NON-NLS-1$
Iterator entries = indexedSecondaryTypes.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry = (Map.Entry) entries.next();
IFile file = (IFile) entry.getKey();
Util.verbose(" + "+file.getFullPath()+':'+ entry.getValue()); //$NON-NLS-1$
}
}
}
catch (JavaModelException jme) {
// do nothing
}
}
}
}
public synchronized IClasspathContainer containerGet(IJavaProject project, IPath containerPath) {
// // check initialization in progress first
if (containerIsInitializationInProgress(project, containerPath)) {
return CONTAINER_INITIALIZATION_IN_PROGRESS;
}
Map projectContainers = (Map)this.containers.get(project);
if (projectContainers == null){
return null;
}
IClasspathContainer container = (IClasspathContainer)projectContainers.get(containerPath);
return container;
}
public boolean containerPutIfInitializingWithSameEntries(IPath containerPath, IJavaProject[] projects, IClasspathContainer[] respectiveContainers) {
int projectLength = projects.length;
if (projectLength != 1)
return false;
final IClasspathContainer container = respectiveContainers[0];
IJavaProject project = projects[0];
// // optimize only if initializing, otherwise we are in a regular setContainer(...) call
if (!containerIsInitializationInProgress(project, containerPath))
return false;
IClasspathContainer previousContainer = containerGetDefaultToPreviousSession(project, containerPath);
if (container == null) {
if (previousContainer == null) {
containerPut(project, containerPath, null);
return true;
}
return false;
}
final IClasspathEntry[] newEntries = container.getClasspathEntries();
if (previousContainer == null)
if (newEntries.length == 0) {
containerPut(project, containerPath, container);
return true;
} else {
if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
verbose_missbehaving_container(containerPath, projects, respectiveContainers, container, newEntries, null/*no old entries*/);
return false;
}
final IClasspathEntry[] oldEntries = previousContainer.getClasspathEntries();
if (oldEntries.length != newEntries.length) {
if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
verbose_missbehaving_container(containerPath, projects, respectiveContainers, container, newEntries, oldEntries);
return false;
}
for (int i = 0, length = newEntries.length; i < length; i++) {
if (newEntries[i] == null) {
if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
verbose_missbehaving_container(project, containerPath, newEntries);
return false;
}
if (!newEntries[i].equals(oldEntries[i])) {
if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
verbose_missbehaving_container(containerPath, projects, respectiveContainers, container, newEntries, oldEntries);
return false;
}
}
containerPut(project, containerPath, container);
return true;
}
private boolean containerIsInitializationInProgress(IJavaProject project, IPath containerPath) {
Map initializations = (Map)this.containerInitializationInProgress.get();
if (initializations == null)
return false;
HashSet projectInitializations = (HashSet) initializations.get(project);
if (projectInitializations == null)
return false;
return projectInitializations.contains(containerPath);
}
private void containerAddInitializationInProgress(IJavaProject project, IPath containerPath) {
Map initializations = (Map)this.containerInitializationInProgress.get();
if (initializations == null)
this.containerInitializationInProgress.set(initializations = new HashMap());
HashSet projectInitializations = (HashSet) initializations.get(project);
if (projectInitializations == null)
initializations.put(project, projectInitializations = new HashSet());
projectInitializations.add(containerPath);
}
private void containerRemoveInitializationInProgress(IJavaProject project, IPath containerPath) {
Map initializations = (Map)this.containerInitializationInProgress.get();
if (initializations == null)
return;
HashSet projectInitializations = (HashSet) initializations.get(project);
if (projectInitializations == null)
return;
projectInitializations.remove(containerPath);
if (projectInitializations.size() == 0)
initializations.remove(project);
if (initializations.size() == 0)
this.containerInitializationInProgress.set(null);
}
public void containerBeingInitializedPut(IJavaProject project, IPath containerPath, IClasspathContainer container) {
Map perProjectContainers = (Map)this.containersBeingInitialized.get();
if (perProjectContainers == null)
this.containersBeingInitialized.set(perProjectContainers = new HashMap());
HashMap perPathContainers = (HashMap) perProjectContainers.get(project);
if (perPathContainers == null)
perProjectContainers.put(project, perPathContainers = new HashMap());
perPathContainers.put(containerPath, container);
}
public IClasspathContainer containerBeingInitializedGet(IJavaProject project, IPath containerPath) {
Map perProjectContainers = (Map)this.containersBeingInitialized.get();
if (perProjectContainers == null)
return null;
HashMap perPathContainers = (HashMap) perProjectContainers.get(project);
if (perPathContainers == null)
return null;
return (IClasspathContainer) perPathContainers.get(containerPath);
}
private void verbose_missbehaving_container(
IPath containerPath,
IJavaProject[] projects,
IClasspathContainer[] respectiveContainers,
final IClasspathContainer container,
final IClasspathEntry[] newEntries,
final IClasspathEntry[] oldEntries) {
Util.verbose(
"CPContainer SET - missbehaving container\n" + //$NON-NLS-1$
" container path: " + containerPath + '\n' + //$NON-NLS-1$
" projects: {" +//$NON-NLS-1$
org.eclipse.jdt.internal.compiler.util.Util.toString(
projects,
new org.eclipse.jdt.internal.compiler.util.Util.Displayable(){
public String displayString(Object o) { return ((IJavaProject) o).getElementName(); }
}) +
"}\n values on previous session: {\n" +//$NON-NLS-1$
org.eclipse.jdt.internal.compiler.util.Util.toString(
respectiveContainers,
new org.eclipse.jdt.internal.compiler.util.Util.Displayable(){
public String displayString(Object o) {
StringBuffer buffer = new StringBuffer(" "); //$NON-NLS-1$
if (o == null) {
buffer.append("<null>"); //$NON-NLS-1$
return buffer.toString();
}
buffer.append(container.getDescription());
buffer.append(" {\n"); //$NON-NLS-1$
if (oldEntries == null) {
buffer.append(" "); //$NON-NLS-1$
buffer.append("<null>\n"); //$NON-NLS-1$
} else {
for (int j = 0; j < oldEntries.length; j++){
buffer.append(" "); //$NON-NLS-1$
buffer.append(oldEntries[j]);
buffer.append('\n');
}
}
buffer.append(" }"); //$NON-NLS-1$
return buffer.toString();
}
}) +
"}\n new values: {\n" +//$NON-NLS-1$
org.eclipse.jdt.internal.compiler.util.Util.toString(
respectiveContainers,
new org.eclipse.jdt.internal.compiler.util.Util.Displayable(){
public String displayString(Object o) {
StringBuffer buffer = new StringBuffer(" "); //$NON-NLS-1$
if (o == null) {
buffer.append("<null>"); //$NON-NLS-1$
return buffer.toString();
}
buffer.append(container.getDescription());
buffer.append(" {\n"); //$NON-NLS-1$
for (int j = 0; j < newEntries.length; j++){
buffer.append(" "); //$NON-NLS-1$
buffer.append(newEntries[j]);
buffer.append('\n');
}
buffer.append(" }"); //$NON-NLS-1$
return buffer.toString();
}
}) +
"\n }"); //$NON-NLS-1$
}
void verbose_missbehaving_container(IJavaProject project, IPath containerPath, IClasspathEntry[] classpathEntries) {
Util.verbose(
"CPContainer GET - missbehaving container (returning null classpath entry)\n" + //$NON-NLS-1$
" project: " + project.getElementName() + '\n' + //$NON-NLS-1$
" container path: " + containerPath + '\n' + //$NON-NLS-1$
" classpath entries: {\n" + //$NON-NLS-1$
org.eclipse.jdt.internal.compiler.util.Util.toString(
classpathEntries,
new org.eclipse.jdt.internal.compiler.util.Util.Displayable(){
public String displayString(Object o) {
StringBuffer buffer = new StringBuffer(" "); //$NON-NLS-1$
if (o == null) {
buffer.append("<null>"); //$NON-NLS-1$
return buffer.toString();
}
buffer.append(o);
return buffer.toString();
}
}) +
"\n }" //$NON-NLS-1$
);
}
public synchronized IClasspathContainer containerGetDefaultToPreviousSession(IJavaProject project, IPath containerPath) {
Map projectContainers = (Map)this.containers.get(project);
if (projectContainers == null)
return getPreviousSessionContainer(containerPath, project);
IClasspathContainer container = (IClasspathContainer)projectContainers.get(containerPath);
if (container == null)
return getPreviousSessionContainer(containerPath, project);
return container;
}
public IClasspathEntry resolveVariableEntry(IClasspathEntry entry, boolean usePreviousSession) {
if (entry.getEntryKind() != IClasspathEntry.CPE_VARIABLE)
return entry;
IPath resolvedPath = getResolvedVariablePath(entry.getPath(), usePreviousSession);
if (resolvedPath == null)
return null;
// By passing a null reference path, we keep it relative to workspace root.
resolvedPath = ClasspathEntry.resolveDotDot(null, resolvedPath);
Object target = org.eclipse.jdt.internal.core.JavaModel.getTarget(resolvedPath, false);
if (target == null)
return null;
// inside the workspace
if (target instanceof IResource) {
IResource resolvedResource = (IResource) target;
switch (resolvedResource.getType()) {
case IResource.PROJECT :
// internal project
return JavaCore.newProjectEntry(
resolvedPath,
entry.getAccessRules(),
entry.combineAccessRules(),
entry.getExtraAttributes(),
entry.isExported());
case IResource.FILE :
// internal binary archive
return JavaCore.newLibraryEntry(
resolvedPath,
getResolvedVariablePath(entry.getSourceAttachmentPath(), usePreviousSession),
getResolvedVariablePath(entry.getSourceAttachmentRootPath(), usePreviousSession),
entry.getAccessRules(),
entry.getExtraAttributes(),
entry.isExported());
case IResource.FOLDER :
// internal binary folder
return JavaCore.newLibraryEntry(
resolvedPath,
getResolvedVariablePath(entry.getSourceAttachmentPath(), usePreviousSession),
getResolvedVariablePath(entry.getSourceAttachmentRootPath(), usePreviousSession),
entry.getAccessRules(),
entry.getExtraAttributes(),
entry.isExported());
}
}
if (target instanceof File) {
File externalFile = JavaModel.getFile(target);
if (externalFile != null) {
// external binary archive
return JavaCore.newLibraryEntry(
resolvedPath,
getResolvedVariablePath(entry.getSourceAttachmentPath(), usePreviousSession),
getResolvedVariablePath(entry.getSourceAttachmentRootPath(), usePreviousSession),
entry.getAccessRules(),
entry.getExtraAttributes(),
entry.isExported());
} else {
// non-existing file
if (resolvedPath.isAbsolute()){
return JavaCore.newLibraryEntry(
resolvedPath,
getResolvedVariablePath(entry.getSourceAttachmentPath(), usePreviousSession),
getResolvedVariablePath(entry.getSourceAttachmentRootPath(), usePreviousSession),
entry.getAccessRules(),
entry.getExtraAttributes(),
entry.isExported());
}
}
}
return null;
}
public IPath getResolvedVariablePath(IPath variablePath, boolean usePreviousSession) {
if (variablePath == null)
return null;
int count = variablePath.segmentCount();
if (count == 0)
return null;
// lookup variable
String variableName = variablePath.segment(0);
IPath resolvedPath = usePreviousSession ? getPreviousSessionVariable(variableName) : /*JavaCore.getClasspathVariable(variableName)*/ null;
if (resolvedPath == null)
return null;
// append path suffix
if (count > 1) {
resolvedPath = resolvedPath.append(variablePath.removeFirstSegments(1));
}
return resolvedPath;
}
/**
* Returns a persisted container from previous session if any
*/
public IPath getPreviousSessionVariable(String variableName) {
IPath previousPath = (IPath)this.previousSessionVariables.get(variableName);
if (previousPath != null){
if (CP_RESOLVE_VERBOSE_ADVANCED)
// verbose_reentering_variable_access(variableName, previousPath);
return previousPath;
}
return null; // break cycle
}
/*
* Resets the temporary cache for newly created elements to null.
*/
public void resetTemporaryCache() {
this.temporaryCache.set(null);
}
public synchronized String cacheToString(String prefix) {
return this.cache.toStringFillingRation(prefix);
}
public void closeZipFile(ZipFile zipFile) {
if (zipFile == null) return;
if (this.zipFiles.get() != null) {
return; // zip file will be closed by call to flushZipFiles
}
try {
if (org.eclipse.jdt.internal.core.JavaModelManager.ZIP_ACCESS_VERBOSE) {
System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.closeZipFile(ZipFile)] Closing ZipFile on " +
zipFile.getName()); //$NON-NLS-1$ //$NON-NLS-2$
}
zipFile.close();
} catch (IOException e) {
// problem occured closing zip file: cannot do much more
}
}
public synchronized BufferManager getDefaultBufferManager() {
if (DEFAULT_BUFFER_MANAGER == null) {
DEFAULT_BUFFER_MANAGER = new BufferManager();
}
return DEFAULT_BUFFER_MANAGER;
}
public boolean forceBatchInitializations(boolean initAfterLoad) {
return false;
}
public synchronized void containerPut(IJavaProject project, IPath containerPath, IClasspathContainer container){
// set/unset the initialization in progress
if (container == CONTAINER_INITIALIZATION_IN_PROGRESS) {
containerAddInitializationInProgress(project, containerPath);
// do not write out intermediate initialization value
return;
} else {
containerRemoveInitializationInProgress(project, containerPath);
Map projectContainers = (Map)this.containers.get(project);
if (projectContainers == null){
projectContainers = new HashMap(1);
this.containers.put(project, projectContainers);
}
if (container == null) {
projectContainers.remove(containerPath);
} else {
projectContainers.put(containerPath, container);
}
// discard obsoleted information about previous session
Map previousContainers = (Map)this.previousSessionContainers.get(project);
if (previousContainers != null){
previousContainers.remove(containerPath);
}
}
// container values are persisted in preferences during save operations, see #saving(ISaveContext)
}
public int getOpenableCacheSize() {
return this.cache.openableCache.getSpaceLimit();
}
/*
* Resets the cache that holds on binary type in jar files
*/
protected synchronized void resetJarTypeCache() {
this.cache.resetJarTypeCache();
}
public void resetClasspathListCache() {
if (this.nonChainingJars != null)
this.nonChainingJars.clear();
if (this.invalidArchives != null)
this.invalidArchives.clear();
if (this.externalFiles != null)
this.externalFiles.clear();
if (this.assumedExternalFiles != null)
this.assumedExternalFiles.clear();
}
/*
* Reset project options stored in info cache.
*/
public void resetProjectOptions(JavaProject javaProject) {
synchronized(this.perProjectInfos) { // use the perProjectInfo collection as its own lock
IProject project = javaProject.getProject();
PerProjectInfo info= (PerProjectInfo) this.perProjectInfos.get(project);
if (info != null) {
info.options = null;
}
}
}
/**
* Define a zip cache object.
*/
static class ZipCache {
Object owner;
private Map<IPath, ZipFile> map;
ZipCache(Object owner) {
this.map = new HashMap<>();
this.owner = owner;
}
public void flush() {
Thread currentThread = Thread.currentThread();
for (ZipFile zipFile : this.map.values()) {
try {
if (org.eclipse.jdt.internal.core.JavaModelManager.ZIP_ACCESS_VERBOSE) {
System.out.println("(" + currentThread + ") [JavaModelManager.flushZipFiles()] Closing ZipFile on " +
zipFile.getName()); //$NON-NLS-1$//$NON-NLS-2$
}
zipFile.close();
} catch (IOException e) {
// problem occured closing zip file: cannot do much more
}
}
}
public ZipFile getCache(IPath path) {
return this.map.get(path);
}
public void setCache(IPath path, ZipFile zipFile) {
this.map.put(path, zipFile);
}
}
public static class PerProjectInfo {
static final IJavaModelStatus NEED_RESOLUTION = new JavaModelStatus();
private static final int JAVADOC_CACHE_INITIAL_SIZE = 10;
public IProject project;
public Object savedState;
public boolean triedRead;
public IClasspathEntry[] rawClasspath;
public IClasspathEntry[] referencedEntries;
public IJavaModelStatus rawClasspathStatus;
public int rawTimeStamp = 0;
public boolean writtingRawClasspath = false;
public IClasspathEntry[] resolvedClasspath;
public IJavaModelStatus unresolvedEntryStatus;
public Map rootPathToRawEntries; // reverse map from a package fragment root's path to the raw entry
public Map rootPathToResolvedEntries; // map from a package fragment root's path to the resolved entry
public IPath outputLocation;
public IEclipsePreferences preferences;
public Hashtable options;
public Hashtable secondaryTypes;
public LRUCache javadocCache;
public PerProjectInfo(IProject project) {
this.triedRead = false;
this.savedState = null;
this.project = project;
this.javadocCache = new LRUCache(JAVADOC_CACHE_INITIAL_SIZE);
}
public synchronized IClasspathEntry[] getResolvedClasspath() {
if (this.unresolvedEntryStatus == NEED_RESOLUTION)
return null;
return this.resolvedClasspath;
}
public void forgetExternalTimestampsAndIndexes() {
// IClasspathEntry[] classpath = this.resolvedClasspath;
// if (classpath == null) return;
// JavaModelManager manager = JavaModelManager.getJavaModelManager();
// IndexManager indexManager = manager.indexManager;
// Map externalTimeStamps = manager.deltaState.getExternalLibTimeStamps();
// HashMap rootInfos = JavaModelManager.getDeltaState().otherRoots;
// for (int i = 0, length = classpath.length; i < length; i++) {
// IClasspathEntry entry = classpath[i];
// if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
// IPath path = entry.getPath();
// if (rootInfos.get(path) == null) {
// externalTimeStamps.remove(path);
// indexManager.removeIndex(
// path); // force reindexing on next reference (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=250083 )
// }
// }
// }
throw new UnsupportedOperationException();
}
public void rememberExternalLibTimestamps() {
// IClasspathEntry[] classpath = this.resolvedClasspath;
// if (classpath == null) return;
// Map externalTimeStamps = JavaModelManager.getJavaModelManager().deltaState.getExternalLibTimeStamps();
// for (int i = 0, length = classpath.length; i < length; i++) {
// IClasspathEntry entry = classpath[i];
// if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
// IPath path = entry.getPath();
// if (externalTimeStamps.get(path) == null) {
// Object target = JavaModel.getExternalTarget(path, true);
// if (target instanceof File) {
// long timestamp = DeltaProcessor.getTimeStamp((java.io.File)target);
// externalTimeStamps.put(path, new Long(timestamp));
// }
// }
// }
// }
// throw new UnsupportedOperationException();
}
public synchronized ClasspathChange resetResolvedClasspath() {
// clear non-chaining jars cache and invalid jars cache
org.eclipse.jdt.internal.core.JavaModelManager.getJavaModelManager().resetClasspathListCache();
// null out resolved information
return setResolvedClasspath(null, null, null, null, this.rawTimeStamp, true/*add classpath change*/);
}
private ClasspathChange setClasspath(IClasspathEntry[] newRawClasspath, IClasspathEntry[] referencedEntries,
IPath newOutputLocation, IJavaModelStatus newRawClasspathStatus,
IClasspathEntry[] newResolvedClasspath, Map newRootPathToRawEntries,
Map newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus,
boolean addClasspathChange) {
ClasspathChange classpathChange = addClasspathChange ? addClasspathChange() : null;
if (referencedEntries != null) this.referencedEntries = referencedEntries;
if (this.referencedEntries == null) this.referencedEntries = ClasspathEntry.NO_ENTRIES;
this.rawClasspath = newRawClasspath;
this.outputLocation = newOutputLocation;
this.rawClasspathStatus = newRawClasspathStatus;
this.resolvedClasspath = newResolvedClasspath;
this.rootPathToRawEntries = newRootPathToRawEntries;
this.rootPathToResolvedEntries = newRootPathToResolvedEntries;
this.unresolvedEntryStatus = newUnresolvedEntryStatus;
this.javadocCache = new LRUCache(JAVADOC_CACHE_INITIAL_SIZE);
return classpathChange;
}
protected ClasspathChange addClasspathChange() {
// remember old info
org.eclipse.jdt.internal.core.JavaModelManager
manager = org.eclipse.jdt.internal.core.JavaModelManager.getJavaModelManager();
ClasspathChange classpathChange =
manager.deltaState.addClasspathChange(this.project, this.rawClasspath, this.outputLocation, this.resolvedClasspath);
return classpathChange;
}
public ClasspathChange setRawClasspath(IClasspathEntry[] newRawClasspath, IPath newOutputLocation,
IJavaModelStatus newRawClasspathStatus) {
return setRawClasspath(newRawClasspath, null, newOutputLocation, newRawClasspathStatus);
}
public synchronized ClasspathChange setRawClasspath(IClasspathEntry[] newRawClasspath, IClasspathEntry[] referencedEntries,
IPath newOutputLocation, IJavaModelStatus newRawClasspathStatus) {
this.rawTimeStamp++;
return setClasspath(newRawClasspath, referencedEntries, newOutputLocation, newRawClasspathStatus, null/*resolved classpath*/,
null/*root to raw map*/, null/*root to resolved map*/, null/*unresolved status*/, true/*add classpath
change*/);
}
public ClasspathChange setResolvedClasspath(IClasspathEntry[] newResolvedClasspath, Map newRootPathToRawEntries,
Map newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus,
int timeStamp, boolean addClasspathChange) {
return setResolvedClasspath(newResolvedClasspath, null, newRootPathToRawEntries, newRootPathToResolvedEntries,
newUnresolvedEntryStatus, timeStamp, addClasspathChange);
}
public synchronized ClasspathChange setResolvedClasspath(IClasspathEntry[] newResolvedClasspath,
IClasspathEntry[] referencedEntries, Map newRootPathToRawEntries,
Map newRootPathToResolvedEntries,
IJavaModelStatus newUnresolvedEntryStatus, int timeStamp,
boolean addClasspathChange) {
if (this.rawTimeStamp != timeStamp)
return null;
return setClasspath(this.rawClasspath, referencedEntries, this.outputLocation, this.rawClasspathStatus, newResolvedClasspath,
newRootPathToRawEntries, newRootPathToResolvedEntries, newUnresolvedEntryStatus, addClasspathChange);
}
/**
* Reads the classpath and caches the entries. Returns a two-dimensional array, where the number of elements in the row is fixed
* to 2.
* The first element is an array of raw classpath entries and the second element is an array of referenced entries that may have
* been stored
* by the client earlier. See {@link org.eclipse.jdt.core.IJavaProject#getReferencedClasspathEntries()} for more details.
*/
public synchronized IClasspathEntry[][] readAndCacheClasspath(JavaProject javaProject) {
// read file entries and update status
IClasspathEntry[][] classpath;
IJavaModelStatus status;
try {
classpath = javaProject.readFileEntriesWithException(null/*not interested in unknown elements*/);
status = JavaModelStatus.VERIFIED_OK;
} catch (CoreException e) {
classpath = new IClasspathEntry[][]{JavaProject.INVALID_CLASSPATH,
ClasspathEntry.NO_ENTRIES};
status =
new JavaModelStatus(
IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
Messages.bind(Messages.classpath_cannotReadClasspathFile, javaProject.getElementName()));
} catch (IOException e) {
classpath = new IClasspathEntry[][]{JavaProject.INVALID_CLASSPATH,
ClasspathEntry.NO_ENTRIES};
if (Messages.file_badFormat.equals(e.getMessage()))
status =
new JavaModelStatus(
IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
Messages.bind(Messages.classpath_xmlFormatError, javaProject.getElementName(),
Messages.file_badFormat));
else
status =
new JavaModelStatus(
IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
Messages.bind(Messages.classpath_cannotReadClasspathFile, javaProject.getElementName()));
} catch (ClasspathEntry.AssertionFailedException e) {
classpath = new IClasspathEntry[][]{JavaProject.INVALID_CLASSPATH,
ClasspathEntry.NO_ENTRIES};
status =
new JavaModelStatus(
IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
Messages.bind(Messages.classpath_illegalEntryInClasspathFile,
new String[]{javaProject.getElementName(), e.getMessage()}));
}
// extract out the output location
int rawClasspathLength = classpath[0].length;
IPath output = null;
if (rawClasspathLength > 0) {
IClasspathEntry entry = classpath[0][rawClasspathLength - 1];
if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
output = entry.getPath();
IClasspathEntry[] copy = new IClasspathEntry[rawClasspathLength - 1];
System.arraycopy(classpath[0], 0, copy, 0, copy.length);
classpath[0] = copy;
}
}
// store new raw classpath, new output and new status, and null out resolved info
setRawClasspath(classpath[0], classpath[1], output, status);
return classpath;
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("Info for "); //$NON-NLS-1$
// buffer.append(this.project.getFullPath());
buffer.append("\nRaw classpath:\n"); //$NON-NLS-1$
if (this.rawClasspath == null) {
buffer.append(" <null>\n"); //$NON-NLS-1$
} else {
for (int i = 0, length = this.rawClasspath.length; i < length; i++) {
buffer.append(" "); //$NON-NLS-1$
buffer.append(this.rawClasspath[i]);
buffer.append('\n');
}
}
buffer.append("Resolved classpath:\n"); //$NON-NLS-1$
IClasspathEntry[] resolvedCP = this.resolvedClasspath;
if (resolvedCP == null) {
buffer.append(" <null>\n"); //$NON-NLS-1$
} else {
for (int i = 0, length = resolvedCP.length; i < length; i++) {
buffer.append(" "); //$NON-NLS-1$
buffer.append(resolvedCP[i]);
buffer.append('\n');
}
}
buffer.append("Resolved classpath status: "); //$NON-NLS-1$
if (this.unresolvedEntryStatus == NEED_RESOLUTION)
buffer.append("NEED RESOLUTION"); //$NON-NLS-1$
else
buffer.append(this.unresolvedEntryStatus == null ? "<null>\n" : this.unresolvedEntryStatus.toString()); //$NON-NLS-1$
buffer.append("Output location:\n "); //$NON-NLS-1$
if (this.outputLocation == null) {
buffer.append("<null>"); //$NON-NLS-1$
} else {
buffer.append(this.outputLocation);
}
return buffer.toString();
}
public boolean writeAndCacheClasspath(
JavaProject javaProject,
final IClasspathEntry[] newRawClasspath,
IClasspathEntry[] newReferencedEntries,
final IPath newOutputLocation) throws JavaModelException {
try {
this.writtingRawClasspath = true;
if (newReferencedEntries == null) newReferencedEntries = this.referencedEntries;
// // write .classpath
// if (!javaProject.writeFileEntries(newRawClasspath, newReferencedEntries, newOutputLocation)) {
// return false;
// }
// // store new raw classpath, new output and new status, and null out resolved info
// setRawClasspath(newRawClasspath, newReferencedEntries, newOutputLocation, JavaModelStatus.VERIFIED_OK);
} finally {
this.writtingRawClasspath = false;
}
return true;
}
public boolean writeAndCacheClasspath(JavaProject javaProject, final IClasspathEntry[] newRawClasspath,
final IPath newOutputLocation) throws JavaModelException {
return writeAndCacheClasspath(javaProject, newRawClasspath, null, newOutputLocation);
}
}
public static class PerWorkingCopyInfo implements IProblemRequestor {
int useCount = 0;
IProblemRequestor problemRequestor;
CompilationUnit workingCopy;
public PerWorkingCopyInfo(CompilationUnit workingCopy, IProblemRequestor problemRequestor) {
this.workingCopy = workingCopy;
this.problemRequestor = problemRequestor;
}
public void acceptProblem(IProblem problem) {
IProblemRequestor requestor = getProblemRequestor();
if (requestor == null) return;
requestor.acceptProblem(problem);
}
public void beginReporting() {
IProblemRequestor requestor = getProblemRequestor();
if (requestor == null) return;
requestor.beginReporting();
}
public void endReporting() {
IProblemRequestor requestor = getProblemRequestor();
if (requestor == null) return;
requestor.endReporting();
}
public IProblemRequestor getProblemRequestor() {
if (this.problemRequestor == null && this.workingCopy.owner != null) {
return this.workingCopy.owner.getProblemRequestor(this.workingCopy);
}
return this.problemRequestor;
}
public ICompilationUnit getWorkingCopy() {
return this.workingCopy;
}
public boolean isActive() {
IProblemRequestor requestor = getProblemRequestor();
return requestor != null && requestor.isActive();
}
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("Info for "); //$NON-NLS-1$
buffer.append(((JavaElement)this.workingCopy).toStringWithAncestors());
buffer.append("\nUse count = "); //$NON-NLS-1$
buffer.append(this.useCount);
buffer.append("\nProblem requestor:\n "); //$NON-NLS-1$
buffer.append(this.problemRequestor);
if (this.problemRequestor == null) {
IProblemRequestor requestor = getProblemRequestor();
buffer.append("\nOwner problem requestor:\n "); //$NON-NLS-1$
buffer.append(requestor);
}
return buffer.toString();
}
}
public JavaWorkspaceScope getWorkspaceScope() {
// if (this.workspaceScope == null) {
this.workspaceScope = new JavaWorkspaceScope();
// }
return this.workspaceScope;
}
}