/******************************************************************************* * Copyright (c) 2004, 2012 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.che.jdt.internal.core; import org.eclipse.che.jdt.core.JavaConventions; import org.eclipse.che.jdt.internal.core.search.BasicSearchEngine; import org.eclipse.che.jdt.internal.core.search.IRestrictedAccessTypeRequestor; import org.eclipse.che.jdt.internal.core.search.JavaWorkspaceScope; import org.eclipse.che.jdt.internal.core.search.Util; import org.eclipse.che.jdt.internal.core.search.indexing.IndexManager; 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.jdt.core.IClassFile; 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.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.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.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.core.DefaultWorkingCopyOwner; import org.eclipse.jdt.internal.core.JavaModelStatus; 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.WeakHashSet; import org.eclipse.jdt.internal.core.util.WeakHashSetOfCharArray; import java.io.File; import java.io.IOException; 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 final static String INDEXED_SECONDARY_TYPES = "#@*_indexing secondary cache_*@#"; //$NON-NLS-1$ public static boolean ZIP_ACCESS_VERBOSE = false; public static boolean VERBOSE = false; public final static ICompilationUnit[] NO_WORKING_COPY = new ICompilationUnit[0]; /** * 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(); /** * A set of external files ({@link #existingExternalFiles}) which have * been confirmed as file (i.e. which returns true to {@link java.io.File#isFile()}. * Note this cache is kept for the whole session. */ public static HashSet<File> existingExternalConfirmedFiles = new HashSet<>(); /* whether an AbortCompilationUnit should be thrown when the source of a compilation unit cannot be retrieved */ public ThreadLocal abortOnMissingSource = new ThreadLocal(); /** * 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); /** * List of IPath of jars that are known to be invalid - such as not being a valid/known format */ private Set<IPath> invalidArchives; /** * 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(); /* * 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); /** * Infos cache. */ private JavaModelCache cache; public IndexManager indexManager; private PerProjectInfo info; private BufferManager DEFAULT_BUFFER_MANAGER; /** * Holds the state used for delta processing. */ public DeltaProcessingState deltaState; /** * Unique handle onto the JavaModel */ final JavaModel javaModel; /** * A weak set of the known search scopes. */ protected WeakHashMap searchScopes = new WeakHashMap(); /* * The unique workspace scope */ public JavaWorkspaceScope workspaceScope; private JavaProject javaProject; // public static JavaModelManager getJavaModelManager() { // return MANAGER; // } public JavaModelManager() { // initialize Java model cache this.cache = new JavaModelCache(); javaModel = new JavaModel(this); deltaState = new DeltaProcessingState(this); } /** * 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) { File externalFile = new File(path.toOSString()); if (!checkResourceExistence) { return externalFile; } else if (existingExternalFilesContains(externalFile)) { return externalFile; } else { if (JavaModelManager.ZIP_ACCESS_VERBOSE) { System.out.println("(" + Thread.currentThread() + ") [JavaModel.getTarget...)] Checking existence of " + path.toString()); //$NON-NLS-1$ //$NON-NLS-2$ } if (externalFile.isFile()) { // isFile() checks for existence (it returns false if a directory) // cache external file existingExternalFilesAdd(externalFile); return externalFile; } else { if (externalFile.exists()) { existingExternalFilesAdd(externalFile); return externalFile; } } } return null; } public IndexManager getIndexManager() { return indexManager; } public JavaModel getJavaModel() { return javaModel; } private synchronized static void existingExternalFilesAdd(File externalFile) { existingExternalFiles.add(externalFile); } private synchronized static boolean existingExternalFilesContains(File externalFile) { return existingExternalFiles.contains(externalFile); } /** * 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 java.io.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 java.io.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; } /** * 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(File 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) { // 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.getAbsolutePath()); //$NON-NLS-1$//$NON-NLS-2$ } } return pkg.getCompilationUnit(file.getName()); } /** * 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(File resource, JavaProject project) { IPath resourcePath = new Path(resource.getAbsolutePath()); boolean isExternal = false; //ExternalFoldersManager.isInternalPathForExternalFolder(resourcePath); // if (isExternal) // resourcePath = resource.getLocation(); try { JavaProjectElementInfo projectInfo = (JavaProjectElementInfo)((JavaProject)project).manager.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.isFile()) { // 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; } 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 org.eclipse.core.runtime.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 = 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(); // } if (!localFile.exists()) { throw new CoreException( new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.bind(Messages.file_notFound, path.toString()), null)); } 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 ICompilationUnit[] getWorkingCopies(DefaultWorkingCopyOwner primary, boolean b) { return null; } public ICompilationUnit[] getWorkingCopies(WorkingCopyOwner workingCopyOwner, boolean b) { return null; } /** * 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) { //todo // 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(boolean create) { // synchronized(this.perProjectInfos) { // use the perProjectInfo collection as its own lock // PerProjectInfo info= (PerProjectInfo) this.perProjectInfos.get(project); if (info == null && create) { info = new PerProjectInfo(); // 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() throws JavaModelException { JavaModelManager.PerProjectInfo info = getPerProjectInfo(false /* don't create info */); if (info == null) { // if (!JavaProject.hasJavaNature(project)) { // throw ((JavaProject)JavaCore.create(project)).newNotPresentException(); // } info = getPerProjectInfo(true /* create info */); } return info; } /** * 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(); 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(); File file = (File)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; } /** * 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(File 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(false); if (projectInfo != null && projectInfo.secondaryTypes != null) { if (VERBOSE) { Util.verbose("-> remove file from cache of project: " + file.getAbsolutePath()); //$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, File 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.getAbsolutePath()); 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(indexManager, (JavaProject)project) .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)) { File file = new File(path);// ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path)); ICompilationUnit unit = 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; } /* * 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 (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 void setIndexManager(IndexManager indexManager) { this.indexManager = indexManager; } public synchronized BufferManager getDefaultBufferManager() { if (DEFAULT_BUFFER_MANAGER == null) { DEFAULT_BUFFER_MANAGER = new BufferManager(); } return DEFAULT_BUFFER_MANAGER; } /** * 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(File resource, IJavaProject project) { if (resource == null) { return null; } if(resource.isFile()) { return createFromFile(resource, project); } else { return createFromDirectory(resource, project); } // 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 createFromFile(File 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, (JavaProject)project); // } // return null; } /** * 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(File 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(File file, JavaProject 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 = new Path(file.getPath()); try { IClasspathEntry entry = project.getClasspathEntryFor(resourcePath); if (entry != null) { return project.getPackageFragmentRoot(file); } } catch (JavaModelException e) { // project doesn't exist: return null } 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 createFromDirectory(File 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 = 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, (JavaProject)project); // } return element; } public boolean forceBatchInitializations(boolean initAfterLoad) { return false; } public void setJavaProject(JavaProject javaProject) { this.javaProject = javaProject; } public JavaProject getJavaProject() { return javaProject; } /** * 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 (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 // JavaModelManager.getJavaModelManager().resetClasspathListCache(); // // // null out resolved information // return setResolvedClasspath(null, null, null, null, this.rawTimeStamp, true/*add classpath change*/); // throw new UnsupportedOperationException(); // } // 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 = org.eclipse.jdt.internal.core.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 //// JavaModelManager manager = JavaModelManager.getJavaModelManager(); //// ClasspathChange classpathChange = //// manager.deltaState.addClasspathChange(this.project, this.rawClasspath, this.outputLocation, this.resolvedClasspath); //// return classpathChange; // throw new UnsupportedOperationException(); // } // // 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 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; throw new UnsupportedOperationException(); } 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(); } } }