/******************************************************************************* * 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; } }