/******************************************************************************* * Copyright (c) 2000, 2010 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 * Theodora Yeung (tyeung@bea.com) - ensure that JarPackageFragmentRoot make it into cache * before its contents * (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=102422) *******************************************************************************/ package org.eclipse.jdt.internal.core; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.StringReader; import java.net.URI; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.WeakHashMap; import java.util.zip.ZipFile; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; 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.IResourceChangeEvent; import org.eclipse.core.resources.ISaveContext; import org.eclipse.core.resources.ISaveParticipant; import org.eclipse.core.resources.ISavedState; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceDescription; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.ISafeRunnable; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.PerformanceStats; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Plugin; import org.eclipse.core.runtime.QualifiedName; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent; import org.eclipse.core.runtime.content.IContentTypeManager.IContentTypeChangeListener; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.DefaultScope; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.core.runtime.preferences.IPreferencesService; import org.eclipse.core.runtime.preferences.IScopeContext; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.jdt.core.ClasspathContainerInitializer; import org.eclipse.jdt.core.IAccessRule; import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IClasspathAttribute; 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.IJavaModel; 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.CompilationParticipant; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; import org.eclipse.jdt.internal.codeassist.CompletionEngine; import org.eclipse.jdt.internal.codeassist.SelectionEngine; import org.eclipse.jdt.internal.compiler.AbstractAnnotationProcessorManager; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt; import org.eclipse.jdt.internal.core.JavaProjectElementInfo.ProjectCache; import org.eclipse.jdt.internal.core.builder.JavaBuilder; import org.eclipse.jdt.internal.core.hierarchy.TypeHierarchy; import org.eclipse.jdt.internal.core.search.AbstractSearchScope; 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.search.processing.JobManager; import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject; import org.eclipse.jdt.internal.core.util.LRUCache; import org.eclipse.jdt.internal.core.util.LRUCache.Stats; 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 org.eclipse.jdt.internal.formatter.DefaultCodeFormatter; import org.osgi.service.prefs.BackingStoreException; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.InputSource; import org.xml.sax.SAXException; /** * The <code>JavaModelManager</code> manages instances of <code>IJavaModel</code>. * <code>IElementChangedListener</code>s register with the <code>JavaModelManager</code>, and * receive <code>ElementChangedEvent</code>s for all <code>IJavaModel</code>s. * <p> * The single instance of <code>JavaModelManager</code> is available from the static method * <code>JavaModelManager.getJavaModelManager()</code>. */ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeListener { /** * Define a zip cache object. */ static class ZipCache { private Map map; Object owner; ZipCache(Object owner) { this.map= new HashMap(); this.owner= owner; } public void flush() { Thread currentThread= Thread.currentThread(); Iterator iterator= this.map.values().iterator(); while (iterator.hasNext()) { try { ZipFile zipFile= (ZipFile)iterator.next(); 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 (ZipFile)this.map.get(path); } public void setCache(IPath path, ZipFile zipFile) { this.map.put(path, zipFile); } } /** * Unique handle onto the JavaModel */ final JavaModel javaModel= new JavaModel(); /** * Classpath variables pool */ public HashMap variables= new HashMap(5); public HashSet variablesWithInitializer= new HashSet(5); public HashMap deprecatedVariables= new HashMap(5); public HashSet readOnlyVariables= new HashSet(5); public HashMap previousSessionVariables= new HashMap(5); private ThreadLocal variableInitializationInProgress= new ThreadLocal(); /** * Classpath containers pool */ public HashMap containers= new HashMap(5); public HashMap previousSessionContainers= new HashMap(5); private ThreadLocal containerInitializationInProgress= new ThreadLocal(); ThreadLocal containersBeingInitialized= new ThreadLocal(); public static final int NO_BATCH_INITIALIZATION= 0; public static final int NEED_BATCH_INITIALIZATION= 1; public static final int BATCH_INITIALIZATION_IN_PROGRESS= 2; public static final int BATCH_INITIALIZATION_FINISHED= 3; public int batchContainerInitializations= NO_BATCH_INITIALIZATION; public BatchInitializationMonitor batchContainerInitializationsProgress= new BatchInitializationMonitor(); public Hashtable containerInitializersCache= new Hashtable(5); /* * A HashSet that contains the IJavaProject whose classpath is being resolved. */ private ThreadLocal classpathsBeingResolved= new ThreadLocal(); /* * The unique workspace scope */ public JavaWorkspaceScope workspaceScope; /* * 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); /* * Extension used to construct Java 6 annotation processor managers */ private IConfigurationElement annotationProcessorManagerFactory= null; /* * Map from a package fragment root's path to a source attachment property (source path + ATTACHMENT_PROPERTY_DELIMITER + source root path) */ public Map rootPathToAttachments= new Hashtable(); public final static String CP_VARIABLE_PREFERENCES_PREFIX= JavaCore.PLUGIN_ID + ".classpathVariable."; //$NON-NLS-1$ public final static String CP_CONTAINER_PREFERENCES_PREFIX= JavaCore.PLUGIN_ID + ".classpathContainer."; //$NON-NLS-1$ public final static String CP_USERLIBRARY_PREFERENCES_PREFIX= JavaCore.PLUGIN_ID + ".userLibrary."; //$NON-NLS-1$ public final static String CP_ENTRY_IGNORE= "##<cp entry ignore>##"; //$NON-NLS-1$ public final static IPath CP_ENTRY_IGNORE_PATH= new Path(CP_ENTRY_IGNORE); public final static String TRUE= "true"; //$NON-NLS-1$ private final static int VARIABLES_AND_CONTAINERS_FILE_VERSION= 2; /** * Name of the extension point for contributing classpath variable initializers */ public static final String CPVARIABLE_INITIALIZER_EXTPOINT_ID= "classpathVariableInitializer"; //$NON-NLS-1$ /** * Name of the extension point for contributing classpath container initializers */ public static final String CPCONTAINER_INITIALIZER_EXTPOINT_ID= "classpathContainerInitializer"; //$NON-NLS-1$ /** * Name of the extension point for contributing a source code formatter */ public static final String FORMATTER_EXTPOINT_ID= "codeFormatter"; //$NON-NLS-1$ /** * Name of the extension point for contributing a compilation participant */ public static final String COMPILATION_PARTICIPANT_EXTPOINT_ID= "compilationParticipant"; //$NON-NLS-1$ /** * Name of the extension point for contributing the Java 6 annotation processor manager */ public static final String ANNOTATION_PROCESSOR_MANAGER_EXTPOINT_ID= "annotationProcessorManager"; //$NON-NLS-1$ /** * Special value used for recognizing ongoing initialization and breaking initialization cycles */ public final static IPath VARIABLE_INITIALIZATION_IN_PROGRESS= new Path("Variable Initialization In Progress"); //$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(); } }; private static final String BUFFER_MANAGER_DEBUG= JavaCore.PLUGIN_ID + "/debug/buffermanager"; //$NON-NLS-1$ private static final String INDEX_MANAGER_DEBUG= JavaCore.PLUGIN_ID + "/debug/indexmanager"; //$NON-NLS-1$ private static final String INDEX_MANAGER_ADVANCED_DEBUG= JavaCore.PLUGIN_ID + "/debug/indexmanager/advanced"; //$NON-NLS-1$ private static final String COMPILER_DEBUG= JavaCore.PLUGIN_ID + "/debug/compiler"; //$NON-NLS-1$ private static final String JAVAMODEL_DEBUG= JavaCore.PLUGIN_ID + "/debug/javamodel"; //$NON-NLS-1$ private static final String JAVAMODELCACHE_DEBUG= JavaCore.PLUGIN_ID + "/debug/javamodel/cache"; //$NON-NLS-1$ private static final String CP_RESOLVE_DEBUG= JavaCore.PLUGIN_ID + "/debug/cpresolution"; //$NON-NLS-1$ private static final String CP_RESOLVE_ADVANCED_DEBUG= JavaCore.PLUGIN_ID + "/debug/cpresolution/advanced"; //$NON-NLS-1$ private static final String CP_RESOLVE_FAILURE_DEBUG= JavaCore.PLUGIN_ID + "/debug/cpresolution/failure"; //$NON-NLS-1$ private static final String ZIP_ACCESS_DEBUG= JavaCore.PLUGIN_ID + "/debug/zipaccess"; //$NON-NLS-1$ private static final String DELTA_DEBUG= JavaCore.PLUGIN_ID + "/debug/javadelta"; //$NON-NLS-1$ private static final String DELTA_DEBUG_VERBOSE= JavaCore.PLUGIN_ID + "/debug/javadelta/verbose"; //$NON-NLS-1$ private static final String HIERARCHY_DEBUG= JavaCore.PLUGIN_ID + "/debug/hierarchy"; //$NON-NLS-1$ private static final String POST_ACTION_DEBUG= JavaCore.PLUGIN_ID + "/debug/postaction"; //$NON-NLS-1$ private static final String BUILDER_DEBUG= JavaCore.PLUGIN_ID + "/debug/builder"; //$NON-NLS-1$ private static final String BUILDER_STATS_DEBUG= JavaCore.PLUGIN_ID + "/debug/builder/stats"; //$NON-NLS-1$ private static final String COMPLETION_DEBUG= JavaCore.PLUGIN_ID + "/debug/completion"; //$NON-NLS-1$ private static final String RESOLUTION_DEBUG= JavaCore.PLUGIN_ID + "/debug/resolution"; //$NON-NLS-1$ private static final String SELECTION_DEBUG= JavaCore.PLUGIN_ID + "/debug/selection"; //$NON-NLS-1$ private static final String SEARCH_DEBUG= JavaCore.PLUGIN_ID + "/debug/search"; //$NON-NLS-1$ private static final String SOURCE_MAPPER_DEBUG_VERBOSE= JavaCore.PLUGIN_ID + "/debug/sourcemapper"; //$NON-NLS-1$ private static final String FORMATTER_DEBUG= JavaCore.PLUGIN_ID + "/debug/formatter"; //$NON-NLS-1$ public static final String COMPLETION_PERF= JavaCore.PLUGIN_ID + "/perf/completion"; //$NON-NLS-1$ public static final String SELECTION_PERF= JavaCore.PLUGIN_ID + "/perf/selection"; //$NON-NLS-1$ public static final String DELTA_LISTENER_PERF= JavaCore.PLUGIN_ID + "/perf/javadeltalistener"; //$NON-NLS-1$ public static final String VARIABLE_INITIALIZER_PERF= JavaCore.PLUGIN_ID + "/perf/variableinitializer"; //$NON-NLS-1$ public static final String CONTAINER_INITIALIZER_PERF= JavaCore.PLUGIN_ID + "/perf/containerinitializer"; //$NON-NLS-1$ public static final String RECONCILE_PERF= JavaCore.PLUGIN_ID + "/perf/reconcile"; //$NON-NLS-1$ private final static String INDEXED_SECONDARY_TYPES= "#@*_indexing secondary cache_*@#"; //$NON-NLS-1$ public static boolean PERF_VARIABLE_INITIALIZER= false; public static boolean PERF_CONTAINER_INITIALIZER= false; public final static ICompilationUnit[] NO_WORKING_COPY= new ICompilationUnit[0]; // Preferences HashSet optionNames= new HashSet(20); Hashtable optionsCache; public final IEclipsePreferences[] preferencesLookup= new IEclipsePreferences[2]; static final int PREF_INSTANCE= 0; static final int PREF_DEFAULT= 1; static final Object[][] NO_PARTICIPANTS= new Object[0][]; public static class CompilationParticipants { private final static int MAX_SOURCE_LEVEL= 7; // 1.1 to 1.7 /* * The registered compilation participants (a table from int (source level) to Object[]) * The Object array contains first IConfigurationElements when not resolved yet, then * it contains CompilationParticipants. */ private Object[][] registeredParticipants= null; private HashSet managedMarkerTypes; public CompilationParticipant[] getCompilationParticipants(IJavaProject project) { final Object[][] participantsPerSource= getRegisteredParticipants(); if (participantsPerSource == NO_PARTICIPANTS) return null; String sourceLevel= project.getOption(JavaCore.COMPILER_SOURCE, true/*inherit options*/); final int sourceLevelIndex= indexForSourceLevel(sourceLevel); final Object[] participants= participantsPerSource[sourceLevelIndex]; int length= participants.length; CompilationParticipant[] result= new CompilationParticipant[length]; int index= 0; for (int i= 0; i < length; i++) { if (participants[i] instanceof IConfigurationElement) { final IConfigurationElement configElement= (IConfigurationElement)participants[i]; final int participantIndex= i; SafeRunner.run(new ISafeRunnable() { public void handleException(Throwable exception) { Util.log(exception, "Exception occurred while creating compilation participant"); //$NON-NLS-1$ } public void run() throws Exception { Object executableExtension= configElement.createExecutableExtension("class"); //$NON-NLS-1$ for (int j= sourceLevelIndex; j < MAX_SOURCE_LEVEL; j++) participantsPerSource[j][participantIndex]= executableExtension; } }); } CompilationParticipant participant; if ((participants[i] instanceof CompilationParticipant) && (participant= (CompilationParticipant)participants[i]).isActive(project)) result[index++]= participant; } if (index == 0) return null; if (index < length) System.arraycopy(result, 0, result= new CompilationParticipant[index], 0, index); return result; } public HashSet managedMarkerTypes() { if (this.managedMarkerTypes == null) { // force extension points to be read getRegisteredParticipants(); } return this.managedMarkerTypes; } private synchronized Object[][] getRegisteredParticipants() { if (this.registeredParticipants != null) { return this.registeredParticipants; } this.managedMarkerTypes= new HashSet(); IExtensionPoint extension= Platform.getExtensionRegistry().getExtensionPoint(JavaCore.PLUGIN_ID, COMPILATION_PARTICIPANT_EXTPOINT_ID); if (extension == null) return this.registeredParticipants= NO_PARTICIPANTS; final ArrayList modifyingEnv= new ArrayList(); final ArrayList creatingProblems= new ArrayList(); final ArrayList others= new ArrayList(); IExtension[] extensions= extension.getExtensions(); // for all extensions of this point... for (int i= 0; i < extensions.length; i++) { IConfigurationElement[] configElements= extensions[i].getConfigurationElements(); // for all config elements named "compilationParticipant" for (int j= 0; j < configElements.length; j++) { final IConfigurationElement configElement= configElements[j]; String elementName= configElement.getName(); if (!("compilationParticipant".equals(elementName))) { //$NON-NLS-1$ continue; } // add config element in the group it belongs to if (TRUE.equals(configElement.getAttribute("modifiesEnvironment"))) //$NON-NLS-1$ modifyingEnv.add(configElement); else if (TRUE.equals(configElement.getAttribute("createsProblems"))) //$NON-NLS-1$ creatingProblems.add(configElement); else others.add(configElement); // add managed marker types IConfigurationElement[] managedMarkers= configElement.getChildren("managedMarker"); //$NON-NLS-1$ for (int k= 0, length= managedMarkers.length; k < length; k++) { IConfigurationElement element= managedMarkers[k]; String markerType= element.getAttribute("markerType"); //$NON-NLS-1$ if (markerType != null) this.managedMarkerTypes.add(markerType); } } } int size= modifyingEnv.size() + creatingProblems.size() + others.size(); if (size == 0) return this.registeredParticipants= NO_PARTICIPANTS; // sort config elements in each group IConfigurationElement[] configElements= new IConfigurationElement[size]; int index= 0; index= sortParticipants(modifyingEnv, configElements, index); index= sortParticipants(creatingProblems, configElements, index); index= sortParticipants(others, configElements, index); // create result table Object[][] result= new Object[MAX_SOURCE_LEVEL][]; int length= configElements.length; for (int i= 0; i < MAX_SOURCE_LEVEL; i++) { result[i]= new Object[length]; } for (int i= 0; i < length; i++) { String sourceLevel= configElements[i].getAttribute("requiredSourceLevel"); //$NON-NLS-1$ int sourceLevelIndex= indexForSourceLevel(sourceLevel); for (int j= sourceLevelIndex; j < MAX_SOURCE_LEVEL; j++) { result[j][i]= configElements[i]; } } return this.registeredParticipants= result; } /* * 1.1 -> 0 * 1.2 -> 1 * ... * 1.6 -> 5 * 1.7 -> 6 * null -> 0 */ private int indexForSourceLevel(String sourceLevel) { if (sourceLevel == null) return 0; int majVersion= (int)(CompilerOptions.versionToJdkLevel(sourceLevel) >>> 16); switch (majVersion) { case ClassFileConstants.MAJOR_VERSION_1_2: return 1; case ClassFileConstants.MAJOR_VERSION_1_3: return 2; case ClassFileConstants.MAJOR_VERSION_1_4: return 3; case ClassFileConstants.MAJOR_VERSION_1_5: return 4; case ClassFileConstants.MAJOR_VERSION_1_6: return 5; case ClassFileConstants.MAJOR_VERSION_1_7: return 6; default: // all other cases including ClassFileConstants.MAJOR_VERSION_1_1 return 0; } } private int sortParticipants(ArrayList group, IConfigurationElement[] configElements, int index) { int size= group.size(); if (size == 0) return index; Object[] elements= group.toArray(); Util.sort(elements, new Util.Comparer() { public int compare(Object a, Object b) { if (a == b) return 0; String id= ((IConfigurationElement)a).getAttribute("id"); //$NON-NLS-1$ if (id == null) return -1; IConfigurationElement[] requiredElements= ((IConfigurationElement)b).getChildren("requires"); //$NON-NLS-1$ for (int i= 0, length= requiredElements.length; i < length; i++) { IConfigurationElement required= requiredElements[i]; if (id.equals(required.getAttribute("id"))) //$NON-NLS-1$ return 1; } return -1; } }); for (int i= 0; i < size; i++) configElements[index + i]= (IConfigurationElement)elements[i]; return index + size; } } public final CompilationParticipants compilationParticipants= new CompilationParticipants(); /* whether an AbortCompilationUnit should be thrown when the source of a compilation unit cannot be retrieved */ public ThreadLocal abortOnMissingSource= new ThreadLocal(); private ExternalFoldersManager externalFoldersManager= ExternalFoldersManager.getExternalFoldersManager(); /** * Returns whether the given full path (for a package) conflicts with the output location of the * given project. */ public static boolean conflictsWithOutputLocation(IPath folderPath, JavaProject project) { try { IPath outputLocation= project.getOutputLocation(); if (outputLocation == null) { // in doubt, there is a conflict return true; } if (outputLocation.isPrefixOf(folderPath)) { // only allow nesting in project's output if there is a corresponding source folder // or if the project's output is not used (in other words, if all source folders have their custom output) IClasspathEntry[] classpath= project.getResolvedClasspath(); boolean isOutputUsed= false; for (int i= 0, length= classpath.length; i < length; i++) { IClasspathEntry entry= classpath[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { if (entry.getPath().equals(outputLocation)) { return false; } if (entry.getOutputLocation() == null) { isOutputUsed= true; } } } return isOutputUsed; } return false; } catch (JavaModelException e) { // in doubt, there is a conflict return true; } } 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 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; } 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); } 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); } public IClasspathContainer containerBeingInitializedRemove(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; IClasspathContainer container= (IClasspathContainer)perPathContainers.remove(containerPath); if (perPathContainers.size() == 0) perPathContainers.remove(project); if (perProjectContainers.size() == 0) this.containersBeingInitialized.set(null); return container; } 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) } /* * The given project is being removed. Remove all containers for this project from the cache. */ public synchronized void containerRemove(IJavaProject project) { Map initializations= (Map)this.containerInitializationInProgress.get(); if (initializations != null) { initializations.remove(project); } this.containers.remove(project); } 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 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$ ); } void verbose_missbehaving_container_null_entries(IJavaProject project, IPath containerPath) { Util.verbose( "CPContainer GET - missbehaving container (returning null as classpath entries)\n" + //$NON-NLS-1$ " project: " + project.getElementName() + '\n' + //$NON-NLS-1$ " container path: " + containerPath + '\n' + //$NON-NLS-1$ " classpath entries: <null>" //$NON-NLS-1$ ); } 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); } private synchronized void containersReset(String[] containerIDs) { for (int i= 0; i < containerIDs.length; i++) { String containerID= containerIDs[i]; Iterator projectIterator= this.containers.values().iterator(); while (projectIterator.hasNext()) { Map projectContainers= (Map)projectIterator.next(); if (projectContainers != null) { Iterator containerIterator= projectContainers.keySet().iterator(); while (containerIterator.hasNext()) { IPath containerPath= (IPath)containerIterator.next(); if (containerID.equals(containerPath.segment(0))) { // registered container projectContainers.put(containerPath, null); // reset container value, but leave entry in Map } } } } } } /** * 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 (org.eclipse.jdt.internal.core.util.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= 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, 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 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()); } /** * 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; } /** * 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.isInternalPathForExternalFolder(resourcePath); if (isExternal) resourcePath= resource.getLocation(); try { JavaProjectElementInfo projectInfo= (JavaProjectElementInfo)getJavaModelManager().getInfo(project); ProjectCache projectCache= projectInfo == null ? null : projectInfo.projectCache; HashtableOfArrayToObject allPkgFragmentsCache= projectCache == null ? null : projectCache.allPkgFragmentsCache; IClasspathEntry[] entries= org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(resourcePath.lastSegment()) ? 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)) { 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(resource, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars())) { // 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; } /** * The singleton manager */ private static JavaModelManager MANAGER= new JavaModelManager(); /** * Infos cache. */ private JavaModelCache cache; /* * Temporary cache of newly opened elements */ private ThreadLocal temporaryCache= new ThreadLocal(); /** * Set of elements which are out of sync with their buffers. */ protected HashSet elementsOutOfSynchWithBuffers= new HashSet(11); /** * Holds the state used for delta processing. */ public DeltaProcessingState deltaState= new DeltaProcessingState(); public IndexManager indexManager= null; /** * Table from IProject to PerProjectInfo. NOTE: this object itself is used as a lock to * synchronize creation/removal of per project infos */ protected Map perProjectInfos= new HashMap(5); /** * 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(); public static class PerProjectInfo { private static final int JAVADOC_CACHE_INITIAL_SIZE= 10; static final IJavaModelStatus NEED_RESOLUTION= new JavaModelStatus(); 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 ) } } } } 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)); } } } } } public synchronized ClasspathChange resetResolvedClasspath() { // clear non-chaining jars cache JavaModelManager.getJavaModelManager().resetNonChainingJarsCache(); // 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 JavaModelManager manager= 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 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 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; /** * 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 zipFiles= new ThreadLocal(); private UserLibraryManager userLibraryManager; /* * List of IPath of jars that are known to not contain a chaining (through MANIFEST.MF) to another library */ private Set nonChainingJars; /** * Update the classpath variable cache */ public static class EclipsePreferencesListener implements IEclipsePreferences.IPreferenceChangeListener { /** * @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent) */ public void preferenceChange(IEclipsePreferences.PreferenceChangeEvent event) { String propertyName= event.getKey(); if (propertyName.startsWith(JavaCore.PLUGIN_ID)) { if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)) { String varName= propertyName.substring(CP_VARIABLE_PREFERENCES_PREFIX.length()); JavaModelManager manager= getJavaModelManager(); if (manager.variablesWithInitializer.contains(varName)) { // revert preference value as we will not apply it to JavaCore classpath variable String oldValue= (String)event.getOldValue(); if (oldValue == null) { // unexpected old value => remove variable from set manager.variablesWithInitializer.remove(varName); } else { manager.getInstancePreferences().put(varName, oldValue); } } else { String newValue= (String)event.getNewValue(); IPath newPath; if (newValue != null && !(newValue= newValue.trim()).equals(CP_ENTRY_IGNORE)) { newPath= new Path(newValue); } else { newPath= null; } try { SetVariablesOperation operation= new SetVariablesOperation(new String[] { varName }, new IPath[] { newPath }, false/*don't update preferences*/); operation.runOperation(null/*no progress available*/); } catch (JavaModelException e) { Util.log(e, "Could not set classpath variable " + varName + " to " + newPath); //$NON-NLS-1$ //$NON-NLS-2$ } } } else if (propertyName.startsWith(CP_CONTAINER_PREFERENCES_PREFIX)) { recreatePersistedContainer(propertyName, (String)event.getNewValue(), false); } else if (propertyName.equals(JavaCore.CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER) || propertyName.equals(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER) || propertyName.equals(JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE) || propertyName.equals(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER) || propertyName.equals(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH) || propertyName.equals(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS) || propertyName.equals(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS) || propertyName.equals(JavaCore.CORE_INCOMPLETE_CLASSPATH) || propertyName.equals(JavaCore.CORE_CIRCULAR_CLASSPATH) || propertyName.equals(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL)) { JavaModelManager manager= JavaModelManager.getJavaModelManager(); IJavaModel model= manager.getJavaModel(); IJavaProject[] projects; try { projects= model.getJavaProjects(); for (int i= 0, pl= projects.length; i < pl; i++) { JavaProject javaProject= (JavaProject)projects[i]; manager.deltaState.addClasspathValidation(javaProject); try { // need to touch the project to force validation by DeltaProcessor javaProject.getProject().touch(null); } catch (CoreException e) { // skip } } } catch (JavaModelException e) { // skip } } else if (propertyName.startsWith(CP_USERLIBRARY_PREFERENCES_PREFIX)) { String libName= propertyName.substring(CP_USERLIBRARY_PREFERENCES_PREFIX.length()); UserLibraryManager manager= JavaModelManager.getUserLibraryManager(); manager.updateUserLibrary(libName, (String)event.getNewValue()); } } // Reset all project caches (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=233568 ) try { IJavaProject[] projects= JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects(); for (int i= 0, length= projects.length; i < length; i++) { ((JavaProject)projects[i]).resetCaches(); } } catch (JavaModelException e) { // cannot retrieve Java projects } } } /** * Listener on eclipse preferences changes. */ EclipsePreferencesListener instancePreferencesListener= new EclipsePreferencesListener(); /** * Listener on eclipse preferences default/instance node changes. */ IEclipsePreferences.INodeChangeListener instanceNodeListener= new IEclipsePreferences.INodeChangeListener() { public void added(IEclipsePreferences.NodeChangeEvent event) { // do nothing } public void removed(IEclipsePreferences.NodeChangeEvent event) { if (event.getChild() == JavaModelManager.this.preferencesLookup[PREF_INSTANCE]) { JavaModelManager.this.preferencesLookup[PREF_INSTANCE]= ((IScopeContext)new InstanceScope()).getNode(JavaCore.PLUGIN_ID); JavaModelManager.this.preferencesLookup[PREF_INSTANCE].addPreferenceChangeListener(new EclipsePreferencesListener()); } } }; IEclipsePreferences.INodeChangeListener defaultNodeListener= new IEclipsePreferences.INodeChangeListener() { public void added(IEclipsePreferences.NodeChangeEvent event) { // do nothing } public void removed(IEclipsePreferences.NodeChangeEvent event) { if (event.getChild() == JavaModelManager.this.preferencesLookup[PREF_DEFAULT]) { JavaModelManager.this.preferencesLookup[PREF_DEFAULT]= ((IScopeContext)new DefaultScope()).getNode(JavaCore.PLUGIN_ID); } } }; /** * Listener on properties changes. */ IEclipsePreferences.IPreferenceChangeListener propertyListener; IEclipsePreferences.IPreferenceChangeListener resourcesPropertyListener; /** * Constructs a new JavaModelManager */ private JavaModelManager() { // singleton: prevent others from creating a new instance /* * It is required to initialize all fields that depends on a headless environment * only if the platform is running. Otherwise this breaks the ability to use * ASTParser in a non-headless environment. */ if (Platform.isRunning()) { this.indexManager= new IndexManager(); this.nonChainingJars= loadNonChainingJarsCache(); } } /** * @deprecated */ private void addDeprecatedOptions(Hashtable options) { options.put(JavaCore.COMPILER_PB_INVALID_IMPORT, JavaCore.ERROR); options.put(JavaCore.COMPILER_PB_UNREACHABLE_CODE, JavaCore.ERROR); } public void addNonChainingJar(IPath path) { if (this.nonChainingJars != null) this.nonChainingJars.add(path); } /** * Starts caching ZipFiles. Ignores if there are already clients. */ public void cacheZipFiles(Object owner) { ZipCache 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 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 } } /** * Configure the plugin with respect to option settings defined in ".options" file */ public void configurePluginDebugOptions() { if (JavaCore.getPlugin().isDebugging()) { String option= Platform.getDebugOption(BUFFER_MANAGER_DEBUG); if (option != null) BufferManager.VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(BUILDER_DEBUG); if (option != null) JavaBuilder.DEBUG= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(COMPILER_DEBUG); if (option != null) Compiler.DEBUG= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(BUILDER_STATS_DEBUG); if (option != null) JavaBuilder.SHOW_STATS= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(COMPLETION_DEBUG); if (option != null) CompletionEngine.DEBUG= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(CP_RESOLVE_DEBUG); if (option != null) JavaModelManager.CP_RESOLVE_VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(CP_RESOLVE_ADVANCED_DEBUG); if (option != null) JavaModelManager.CP_RESOLVE_VERBOSE_ADVANCED= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(CP_RESOLVE_FAILURE_DEBUG); if (option != null) JavaModelManager.CP_RESOLVE_VERBOSE_FAILURE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(DELTA_DEBUG); if (option != null) DeltaProcessor.DEBUG= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(DELTA_DEBUG_VERBOSE); if (option != null) DeltaProcessor.VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(HIERARCHY_DEBUG); if (option != null) TypeHierarchy.DEBUG= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(INDEX_MANAGER_DEBUG); if (option != null) JobManager.VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(INDEX_MANAGER_ADVANCED_DEBUG); if (option != null) IndexManager.DEBUG= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(JAVAMODEL_DEBUG); if (option != null) JavaModelManager.VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(JAVAMODELCACHE_DEBUG); if (option != null) JavaModelCache.VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(POST_ACTION_DEBUG); if (option != null) JavaModelOperation.POST_ACTION_VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(RESOLUTION_DEBUG); if (option != null) NameLookup.VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(SEARCH_DEBUG); if (option != null) BasicSearchEngine.VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(SELECTION_DEBUG); if (option != null) SelectionEngine.DEBUG= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(ZIP_ACCESS_DEBUG); if (option != null) JavaModelManager.ZIP_ACCESS_VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(SOURCE_MAPPER_DEBUG_VERBOSE); if (option != null) SourceMapper.VERBOSE= option.equalsIgnoreCase(TRUE); option= Platform.getDebugOption(FORMATTER_DEBUG); if (option != null) DefaultCodeFormatter.DEBUG= option.equalsIgnoreCase(TRUE); } // configure performance options if (PerformanceStats.ENABLED) { CompletionEngine.PERF= PerformanceStats.isEnabled(COMPLETION_PERF); SelectionEngine.PERF= PerformanceStats.isEnabled(SELECTION_PERF); DeltaProcessor.PERF= PerformanceStats.isEnabled(DELTA_LISTENER_PERF); JavaModelManager.PERF_VARIABLE_INITIALIZER= PerformanceStats.isEnabled(VARIABLE_INITIALIZER_PERF); JavaModelManager.PERF_CONTAINER_INITIALIZER= PerformanceStats.isEnabled(CONTAINER_INITIALIZER_PERF); ReconcileWorkingCopyOperation.PERF= PerformanceStats.isEnabled(RECONCILE_PERF); } } /* * Return a new Java 6 annotation processor manager. The manager will need to * be configured before it can be used. Returns null if a manager cannot be * created, ie if the current VM does not support Java 6 annotation processing. */ public AbstractAnnotationProcessorManager createAnnotationProcessorManager() { synchronized (this) { if (this.annotationProcessorManagerFactory == null) { IExtensionPoint extension= Platform.getExtensionRegistry().getExtensionPoint(JavaCore.PLUGIN_ID, ANNOTATION_PROCESSOR_MANAGER_EXTPOINT_ID); if (extension == null) return null; IExtension[] extensions= extension.getExtensions(); for (int i= 0; i < extensions.length; i++) { if (i > 0) { Util.log(null, "An annotation processor manager is already registered: ignoring " + extensions[i].getUniqueIdentifier()); //$NON-NLS-1$ break; } IConfigurationElement[] configElements= extensions[i].getConfigurationElements(); for (int j= 0; j < configElements.length; j++) { final IConfigurationElement configElement= configElements[j]; if ("annotationProcessorManager".equals(configElement.getName())) { //$NON-NLS-1$ this.annotationProcessorManagerFactory= configElement; break; } } } } } if (this.annotationProcessorManagerFactory == null) { return null; } final AbstractAnnotationProcessorManager[] apm= new AbstractAnnotationProcessorManager[1]; apm[0]= null; final IConfigurationElement factory= this.annotationProcessorManagerFactory; SafeRunner.run(new ISafeRunnable() { public void handleException(Throwable exception) { Util.log(exception, "Exception occurred while loading annotation processor manager"); //$NON-NLS-1$ } public void run() throws Exception { Object executableExtension= factory.createExecutableExtension("class"); //$NON-NLS-1$ if (executableExtension instanceof AbstractAnnotationProcessorManager) { apm[0]= (AbstractAnnotationProcessorManager)executableExtension; } } }); return apm[0]; } /* * 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; } /** * @see ISaveParticipant */ public void doneSaving(ISaveContext context) { // nothing to do for jdt.core } /** * Flushes ZipFiles cache if there are no more clients. */ public void flushZipFiles(Object owner) { ZipCache 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(); } } /* * Returns true if forcing batch initialization was successful. * Returns false if batch initialization is already running. */ public synchronized boolean forceBatchInitializations(boolean initAfterLoad) { switch (this.batchContainerInitializations) { case NO_BATCH_INITIALIZATION: this.batchContainerInitializations= NEED_BATCH_INITIALIZATION; return true; case BATCH_INITIALIZATION_FINISHED: if (initAfterLoad) return false; // no need to initialize again this.batchContainerInitializations= NEED_BATCH_INITIALIZATION; return true; } return false; } private synchronized boolean batchContainerInitializations() { switch (this.batchContainerInitializations) { case NEED_BATCH_INITIALIZATION: this.batchContainerInitializations= BATCH_INITIALIZATION_IN_PROGRESS; return true; case BATCH_INITIALIZATION_IN_PROGRESS: return true; } return false; } private synchronized void batchInitializationFinished() { this.batchContainerInitializations= BATCH_INITIALIZATION_FINISHED; } 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; } public IClasspathEntry[] getReferencedClasspathEntries(IClasspathEntry libraryEntry, IJavaProject project) { IClasspathEntry[] referencedEntries= ((ClasspathEntry)libraryEntry).resolvedChainedLibraries(); if (project == null) return referencedEntries; PerProjectInfo perProjectInfo= getPerProjectInfo(project.getProject(), false); if (perProjectInfo == null) return referencedEntries; List pathToReferencedEntries= new ArrayList(referencedEntries.length); for (int index= 0; index < referencedEntries.length; index++) { if (pathToReferencedEntries.contains(referencedEntries[index].getPath())) continue; IClasspathEntry persistedEntry= null; if ((persistedEntry= (IClasspathEntry)perProjectInfo.rootPathToResolvedEntries.get(referencedEntries[index].getPath())) != null) { // TODO: reconsider this - may want to copy the values instead of reference assignment? referencedEntries[index]= persistedEntry; } pathToReferencedEntries.add(referencedEntries[index].getPath()); } return referencedEntries; } public DeltaProcessor getDeltaProcessor() { return this.deltaState.getDeltaProcessor(); } public static DeltaProcessingState getDeltaState() { return MANAGER.deltaState; } /** * Returns the set of elements which are out of synch with their buffers. */ protected HashSet getElementsOutOfSynchWithBuffers() { return this.elementsOutOfSynchWithBuffers; } public static ExternalFoldersManager getExternalManager() { return MANAGER.externalFoldersManager; } public static IndexManager getIndexManager() { return MANAGER.indexManager; } /** * 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 existing element in the cache that is equal to the given element. */ public synchronized IJavaElement getExistingElement(IJavaElement element) { return this.cache.getExistingElement(element); } public HashSet getExternalWorkingCopyProjects() { synchronized (this.perWorkingCopyInfos) { HashSet result= null; Iterator values= this.perWorkingCopyInfos.values().iterator(); while (values.hasNext()) { Map ownerCopies= (Map)values.next(); Iterator workingCopies= ownerCopies.keySet().iterator(); while (workingCopies.hasNext()) { ICompilationUnit workingCopy= (ICompilationUnit)workingCopies.next(); IJavaProject project= workingCopy.getJavaProject(); if (project.getElementName().equals(ExternalJavaProject.EXTERNAL_PROJECT_NAME)) { if (result == null) result= new HashSet(); result.add(project); } } } return result; } } /** * Get workspace eclipse preference for JavaCore plug-in. */ public IEclipsePreferences getInstancePreferences() { return this.preferencesLookup[PREF_INSTANCE]; } // 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(JavaCore.CORE_ENCODING, JavaCore.getEncoding()); // backward compatibility addDeprecatedOptions(defaultOptions); return defaultOptions; } /** * Get default eclipse preference for JavaCore plugin. */ public IEclipsePreferences getDefaultPreferences() { return this.preferencesLookup[PREF_DEFAULT]; } /** * Returns the handle to the active Java Model. */ public final JavaModel getJavaModel() { return this.javaModel; } /** * Returns the singleton JavaModelManager */ public final static JavaModelManager getJavaModelManager() { return MANAGER; } /** * 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= readState(project); } catch (CoreException e) { e.printStackTrace(); } } return info.savedState; } public String getOption(String optionName) { if (JavaCore.CORE_ENCODING.equals(optionName)) { return JavaCore.getEncoding(); } // backward compatibility if (isDeprecatedOption(optionName)) { return JavaCore.ERROR; } String propertyName= optionName; if (this.optionNames.contains(propertyName)) { IPreferencesService service= Platform.getPreferencesService(); String value= service.get(optionName, null, this.preferencesLookup); return value == null ? null : value.trim(); } return null; } 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()) { return this.optionsCache= getDefaultOptionsNoInitialization(); } // 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); } } // 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; } // Do not modify without modifying getDefaultOptions() private Hashtable getDefaultOptionsNoInitialization() { Map defaultOptionsMap= new CompilerOptions().getMap(); // compiler defaults // Override some compiler defaults defaultOptionsMap.put(JavaCore.COMPILER_LOCAL_VARIABLE_ATTR, JavaCore.GENERATE); defaultOptionsMap.put(JavaCore.COMPILER_CODEGEN_UNUSED_LOCAL, JavaCore.PRESERVE); defaultOptionsMap.put(JavaCore.COMPILER_TASK_TAGS, JavaCore.DEFAULT_TASK_TAGS); defaultOptionsMap.put(JavaCore.COMPILER_TASK_PRIORITIES, JavaCore.DEFAULT_TASK_PRIORITIES); defaultOptionsMap.put(JavaCore.COMPILER_TASK_CASE_SENSITIVE, JavaCore.ENABLED); defaultOptionsMap.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED); defaultOptionsMap.put(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, JavaCore.ERROR); // Builder settings defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, JavaCore.ABORT); defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE, JavaCore.WARNING); defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER, JavaCore.CLEAN); // JavaCore settings defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_ORDER, JavaCore.IGNORE); defaultOptionsMap.put(JavaCore.CORE_INCOMPLETE_CLASSPATH, JavaCore.ERROR); defaultOptionsMap.put(JavaCore.CORE_CIRCULAR_CLASSPATH, JavaCore.ERROR); defaultOptionsMap.put(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, JavaCore.IGNORE); defaultOptionsMap.put(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS, JavaCore.ENABLED); defaultOptionsMap.put(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS, JavaCore.ENABLED); // Formatter settings defaultOptionsMap.putAll(DefaultCodeFormatterConstants.getEclipseDefaultSettings()); // CodeAssist settings defaultOptionsMap.put(JavaCore.CODEASSIST_VISIBILITY_CHECK, JavaCore.DISABLED); defaultOptionsMap.put(JavaCore.CODEASSIST_DEPRECATION_CHECK, JavaCore.DISABLED); defaultOptionsMap.put(JavaCore.CODEASSIST_IMPLICIT_QUALIFICATION, JavaCore.DISABLED); defaultOptionsMap.put(JavaCore.CODEASSIST_FIELD_PREFIXES, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CODEASSIST_STATIC_FIELD_PREFIXES, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CODEASSIST_STATIC_FINAL_FIELD_PREFIXES, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CODEASSIST_LOCAL_PREFIXES, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CODEASSIST_ARGUMENT_PREFIXES, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CODEASSIST_FIELD_SUFFIXES, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CODEASSIST_STATIC_FIELD_SUFFIXES, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CODEASSIST_STATIC_FINAL_FIELD_SUFFIXES, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CODEASSIST_LOCAL_SUFFIXES, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CODEASSIST_ARGUMENT_SUFFIXES, ""); //$NON-NLS-1$ defaultOptionsMap.put(JavaCore.CODEASSIST_FORBIDDEN_REFERENCE_CHECK, JavaCore.ENABLED); defaultOptionsMap.put(JavaCore.CODEASSIST_DISCOURAGED_REFERENCE_CHECK, JavaCore.DISABLED); defaultOptionsMap.put(JavaCore.CODEASSIST_CAMEL_CASE_MATCH, JavaCore.ENABLED); defaultOptionsMap.put(JavaCore.CODEASSIST_SUGGEST_STATIC_IMPORTS, JavaCore.ENABLED); // Time out for parameter names defaultOptionsMap.put(JavaCore.TIMEOUT_FOR_PARAMETER_NAME_FROM_ATTACHED_JAVADOC, "50"); //$NON-NLS-1$ return new Hashtable(defaultOptionsMap); } /* * 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= (PerProjectInfo)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 { 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; } /* * 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 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 } private void verbose_reentering_project_container_access(IPath containerPath, IJavaProject project, IClasspathContainer previousContainer) { StringBuffer buffer= new StringBuffer(); buffer.append("CPContainer INIT - reentering access to project container during its initialization, will see previous value\n"); //$NON-NLS-1$ buffer.append(" project: " + project.getElementName() + '\n'); //$NON-NLS-1$ buffer.append(" container path: " + containerPath + '\n'); //$NON-NLS-1$ buffer.append(" previous value: "); //$NON-NLS-1$ buffer.append(previousContainer.getDescription()); buffer.append(" {\n"); //$NON-NLS-1$ IClasspathEntry[] entries= previousContainer.getClasspathEntries(); if (entries != null) { for (int j= 0; j < entries.length; j++) { buffer.append(" "); //$NON-NLS-1$ buffer.append(entries[j]); buffer.append('\n'); } } buffer.append(" }"); //$NON-NLS-1$ Util.verbose(buffer.toString()); new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$ } /** * 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 } private void verbose_reentering_variable_access(String variableName, IPath previousPath) { Util.verbose( "CPVariable INIT - reentering access to variable during its initialization, will see previous value\n" + //$NON-NLS-1$ " variable: " + variableName + '\n' + //$NON-NLS-1$ " previous value: " + previousPath); //$NON-NLS-1$ new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$ } /** * 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; } private File getVariableAndContainersFile() { return JavaCore.getPlugin().getStateLocation().append("variablesAndContainers.dat").toFile(); //$NON-NLS-1$ } /** * Returns the name of the variables for which an CP variable initializer is registered through * an extension point */ public static String[] getRegisteredVariableNames() { Plugin jdtCorePlugin= JavaCore.getPlugin(); if (jdtCorePlugin == null) return null; ArrayList variableList= new ArrayList(5); IExtensionPoint extension= Platform.getExtensionRegistry().getExtensionPoint(JavaCore.PLUGIN_ID, JavaModelManager.CPVARIABLE_INITIALIZER_EXTPOINT_ID); if (extension != null) { IExtension[] extensions= extension.getExtensions(); for (int i= 0; i < extensions.length; i++) { IConfigurationElement[] configElements= extensions[i].getConfigurationElements(); for (int j= 0; j < configElements.length; j++) { String varAttribute= configElements[j].getAttribute("variable"); //$NON-NLS-1$ if (varAttribute != null) variableList.add(varAttribute); } } } String[] variableNames= new String[variableList.size()]; variableList.toArray(variableNames); return variableNames; } /** * Returns the name of the container IDs for which an CP container initializer is registered * through an extension point */ public static String[] getRegisteredContainerIDs() { Plugin jdtCorePlugin= JavaCore.getPlugin(); if (jdtCorePlugin == null) return null; ArrayList containerIDList= new ArrayList(5); IExtensionPoint extension= Platform.getExtensionRegistry().getExtensionPoint(JavaCore.PLUGIN_ID, JavaModelManager.CPCONTAINER_INITIALIZER_EXTPOINT_ID); if (extension != null) { IExtension[] extensions= extension.getExtensions(); for (int i= 0; i < extensions.length; i++) { IConfigurationElement[] configElements= extensions[i].getConfigurationElements(); for (int j= 0; j < configElements.length; j++) { String idAttribute= configElements[j].getAttribute("id"); //$NON-NLS-1$ if (idAttribute != null) containerIDList.add(idAttribute); } } } String[] containerIDs= new String[containerIDList.size()]; containerIDList.toArray(containerIDs); return containerIDs; } 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; resolvedPath= ClasspathEntry.resolveDotDot(resolvedPath); Object target= 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); if (resolvedPath == null) return null; // append path suffix if (count > 1) { resolvedPath= resolvedPath.append(variablePath.removeFirstSegments(1)); } return resolvedPath; } /** * Returns the File to use for saving and restoring the last built state for the given project. */ private File getSerializationFile(IProject project) { if (!project.exists()) return null; IPath workingLocation= project.getWorkingLocation(JavaCore.PLUGIN_ID); return workingLocation.append("state.dat").toFile(); //$NON-NLS-1$ } public static UserLibraryManager getUserLibraryManager() { if (MANAGER.userLibraryManager == null) { UserLibraryManager libraryManager= new UserLibraryManager(); synchronized (MANAGER) { if (MANAGER.userLibraryManager == null) { // ensure another library manager was not set while creating the instance above MANAGER.userLibraryManager= libraryManager; } } } return MANAGER.userLibraryManager; } /* * Returns all the working copies which have the given owner. * Adds the working copies of the primary owner if specified. * Returns null if it has none. */ 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; } } public JavaWorkspaceScope getWorkspaceScope() { if (this.workspaceScope == null) { this.workspaceScope= new JavaWorkspaceScope(); } return this.workspaceScope; } /** * 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. * * 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. * * @exception CoreException If unable to create/open the ZipFile */ public ZipFile getZipFile(IPath path) throws CoreException { 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) { throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e)); } } /* * Returns whether there is a temporary cache for the current thread. */ public boolean hasTemporaryCache() { return this.temporaryCache.get() != null; } /* * Initialize all container at the same time as the given container. * Return the container for the given path and project. */ private IClasspathContainer initializeAllContainers(IJavaProject javaProjectToInit, IPath containerToInit) throws JavaModelException { if (CP_RESOLVE_VERBOSE_ADVANCED) verbose_batching_containers_initialization(javaProjectToInit, containerToInit); // collect all container paths final HashMap allContainerPaths= new HashMap(); IProject[] projects= ResourcesPlugin.getWorkspace().getRoot().getProjects(); for (int i= 0, length= projects.length; i < length; i++) { IProject project= projects[i]; if (!JavaProject.hasJavaNature(project)) continue; IJavaProject javaProject= new JavaProject(project, getJavaModel()); HashSet paths= (HashSet)allContainerPaths.get(javaProject); IClasspathEntry[] rawClasspath= javaProject.getRawClasspath(); for (int j= 0, length2= rawClasspath.length; j < length2; j++) { IClasspathEntry entry= rawClasspath[j]; IPath path= entry.getPath(); if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER && containerGet(javaProject, path) == null) { if (paths == null) { paths= new HashSet(); allContainerPaths.put(javaProject, paths); } paths.add(path); } } /* TODO (frederic) put back when JDT/UI dummy project will be thrown away... * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=97524 * if (javaProject.equals(javaProjectToInit)) { if (paths == null) { paths = new HashSet(); allContainerPaths.put(javaProject, paths); } paths.add(containerToInit); } */ } // TODO (frederic) remove following block when JDT/UI dummy project will be thrown away... if (javaProjectToInit != null) { HashSet containerPaths= (HashSet)allContainerPaths.get(javaProjectToInit); if (containerPaths == null) { containerPaths= new HashSet(); allContainerPaths.put(javaProjectToInit, containerPaths); } containerPaths.add(containerToInit); } // end block // initialize all containers boolean ok= false; try { // if possible run inside an IWokspaceRunnable with AVOID_UPATE to avoid unwanted builds // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=118507) IWorkspaceRunnable runnable= new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { try { // Collect all containers Set entrySet= allContainerPaths.entrySet(); int length= entrySet.size(); if (monitor != null) monitor.beginTask("", length); //$NON-NLS-1$ Map.Entry[] entries= new Map.Entry[length]; // clone as the following will have a side effect entrySet.toArray(entries); for (int i= 0; i < length; i++) { Map.Entry entry= entries[i]; IJavaProject javaProject= (IJavaProject)entry.getKey(); HashSet pathSet= (HashSet)entry.getValue(); if (pathSet == null) continue; int length2= pathSet.size(); IPath[] paths= new IPath[length2]; pathSet.toArray(paths); // clone as the following will have a side effect for (int j= 0; j < length2; j++) { IPath path= paths[j]; initializeContainer(javaProject, path); IClasspathContainer container= containerBeingInitializedGet(javaProject, path); if (container != null) { containerPut(javaProject, path, container); } } if (monitor != null) monitor.worked(1); } // Set all containers Map perProjectContainers= (Map)JavaModelManager.this.containersBeingInitialized.get(); if (perProjectContainers != null) { Iterator entriesIterator= perProjectContainers.entrySet().iterator(); while (entriesIterator.hasNext()) { Map.Entry entry= (Map.Entry)entriesIterator.next(); IJavaProject project= (IJavaProject)entry.getKey(); HashMap perPathContainers= (HashMap)entry.getValue(); Iterator containersIterator= perPathContainers.entrySet().iterator(); while (containersIterator.hasNext()) { Map.Entry containerEntry= (Map.Entry)containersIterator.next(); IPath containerPath= (IPath)containerEntry.getKey(); IClasspathContainer container= (IClasspathContainer)containerEntry.getValue(); SetContainerOperation operation= new SetContainerOperation(containerPath, new IJavaProject[] { project }, new IClasspathContainer[] { container }); operation.runOperation(monitor); } } JavaModelManager.this.containersBeingInitialized.set(null); } } finally { if (monitor != null) monitor.done(); } } }; IProgressMonitor monitor= this.batchContainerInitializationsProgress; IWorkspace workspace= ResourcesPlugin.getWorkspace(); if (workspace.isTreeLocked()) runnable.run(monitor); else workspace.run( runnable, null/*don't take any lock*/, IWorkspace.AVOID_UPDATE, monitor); ok= true; } catch (CoreException e) { // ignore Util.log(e, "Exception while initializing all containers"); //$NON-NLS-1$ } finally { if (!ok) { // if we're being traversed by an exception, ensure that that containers are // no longer marked as initialization in progress // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=66437) this.containerInitializationInProgress.set(null); } } return containerGet(javaProjectToInit, containerToInit); } private void verbose_batching_containers_initialization(IJavaProject javaProjectToInit, IPath containerToInit) { Util.verbose( "CPContainer INIT - batching containers initialization\n" + //$NON-NLS-1$ " project to init: " + (javaProjectToInit == null ? "null" : javaProjectToInit.getElementName()) + '\n' + //$NON-NLS-1$ //$NON-NLS-2$ " container path to init: " + containerToInit); //$NON-NLS-1$ } IClasspathContainer initializeContainer(IJavaProject project, IPath containerPath) throws JavaModelException { IProgressMonitor monitor= this.batchContainerInitializationsProgress; if (monitor != null && monitor.isCanceled()) throw new OperationCanceledException(); IClasspathContainer container= null; final ClasspathContainerInitializer initializer= JavaCore.getClasspathContainerInitializer(containerPath.segment(0)); if (initializer != null) { if (CP_RESOLVE_VERBOSE) verbose_triggering_container_initialization(project, containerPath, initializer); if (CP_RESOLVE_VERBOSE_ADVANCED) verbose_triggering_container_initialization_invocation_trace(); PerformanceStats stats= null; if (JavaModelManager.PERF_CONTAINER_INITIALIZER) { stats= PerformanceStats.getStats(JavaModelManager.CONTAINER_INITIALIZER_PERF, this); stats.startRun(containerPath + " of " + project.getPath()); //$NON-NLS-1$ } containerPut(project, containerPath, CONTAINER_INITIALIZATION_IN_PROGRESS); // avoid initialization cycles boolean ok= false; try { if (monitor != null) monitor.subTask(Messages.bind(Messages.javamodel_configuring, initializer.getDescription(containerPath, project))); // let OperationCanceledException go through // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59363) 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) == CONTAINER_INITIALIZATION_IN_PROGRESS) { // 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); } ok= true; } catch (CoreException e) { if (e instanceof JavaModelException) { throw (JavaModelException)e; } else { throw new JavaModelException(e); } } catch (RuntimeException e) { if (JavaModelManager.CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE) e.printStackTrace(); throw e; } catch (Error e) { if (JavaModelManager.CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE) e.printStackTrace(); throw e; } finally { if (JavaModelManager.PERF_CONTAINER_INITIALIZER) { stats.endRun(); } if (!ok) { // just remove initialization in progress and keep previous session container so as to avoid a full build // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=92588 containerRemoveInitializationInProgress(project, containerPath); if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE) verbose_container_initialization_failed(project, containerPath, container, initializer); } } if (CP_RESOLVE_VERBOSE_ADVANCED) verbose_container_value_after_initialization(project, containerPath, container); } 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); if (CP_RESOLVE_VERBOSE_ADVANCED || CP_RESOLVE_VERBOSE_FAILURE) verbose_no_container_initializer_found(project, containerPath); } return container; } private void verbose_no_container_initializer_found(IJavaProject project, IPath containerPath) { Util.verbose( "CPContainer INIT - no initializer found\n" + //$NON-NLS-1$ " project: " + project.getElementName() + '\n' + //$NON-NLS-1$ " container path: " + containerPath); //$NON-NLS-1$ } private void verbose_container_value_after_initialization(IJavaProject project, IPath containerPath, IClasspathContainer container) { StringBuffer buffer= new StringBuffer(); buffer.append("CPContainer INIT - after resolution\n"); //$NON-NLS-1$ buffer.append(" project: " + project.getElementName() + '\n'); //$NON-NLS-1$ buffer.append(" container path: " + containerPath + '\n'); //$NON-NLS-1$ if (container != null) { buffer.append(" container: " + container.getDescription() + " {\n"); //$NON-NLS-2$//$NON-NLS-1$ IClasspathEntry[] entries= container.getClasspathEntries(); if (entries != null) { for (int i= 0; i < entries.length; i++) { buffer.append(" " + entries[i] + '\n'); //$NON-NLS-1$ } } buffer.append(" }");//$NON-NLS-1$ } else { buffer.append(" container: {unbound}");//$NON-NLS-1$ } Util.verbose(buffer.toString()); } private void verbose_container_initialization_failed(IJavaProject project, IPath containerPath, IClasspathContainer container, ClasspathContainerInitializer initializer) { if (container == CONTAINER_INITIALIZATION_IN_PROGRESS) { Util.verbose( "CPContainer INIT - FAILED (initializer did not initialize container)\n" + //$NON-NLS-1$ " project: " + project.getElementName() + '\n' + //$NON-NLS-1$ " container path: " + containerPath + '\n' + //$NON-NLS-1$ " initializer: " + initializer); //$NON-NLS-1$ } else { Util.verbose( "CPContainer INIT - FAILED (see exception above)\n" + //$NON-NLS-1$ " project: " + project.getElementName() + '\n' + //$NON-NLS-1$ " container path: " + containerPath + '\n' + //$NON-NLS-1$ " initializer: " + initializer); //$NON-NLS-1$ } } private void verbose_container_null_failure_container(IJavaProject project, IPath containerPath, ClasspathContainerInitializer initializer) { Util.verbose( "CPContainer INIT - FAILED (and failure container is null)\n" + //$NON-NLS-1$ " project: " + project.getElementName() + '\n' + //$NON-NLS-1$ " container path: " + containerPath + '\n' + //$NON-NLS-1$ " initializer: " + initializer); //$NON-NLS-1$ } private void verbose_container_using_failure_container(IJavaProject project, IPath containerPath, ClasspathContainerInitializer initializer) { Util.verbose( "CPContainer INIT - FAILED (using failure container)\n" + //$NON-NLS-1$ " project: " + project.getElementName() + '\n' + //$NON-NLS-1$ " container path: " + containerPath + '\n' + //$NON-NLS-1$ " initializer: " + initializer); //$NON-NLS-1$ } private void verbose_triggering_container_initialization(IJavaProject project, IPath containerPath, ClasspathContainerInitializer initializer) { Util.verbose( "CPContainer INIT - triggering initialization\n" + //$NON-NLS-1$ " project: " + project.getElementName() + '\n' + //$NON-NLS-1$ " container path: " + containerPath + '\n' + //$NON-NLS-1$ " initializer: " + initializer); //$NON-NLS-1$ } private void verbose_triggering_container_initialization_invocation_trace() { Util.verbose( "CPContainer INIT - triggering initialization\n" + //$NON-NLS-1$ " invocation trace:"); //$NON-NLS-1$ new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$ } /** * Initialize preferences lookups for JavaCore plug-in. */ public void initializePreferences() { // Create lookups this.preferencesLookup[PREF_INSTANCE]= ((IScopeContext)new InstanceScope()).getNode(JavaCore.PLUGIN_ID); this.preferencesLookup[PREF_DEFAULT]= ((IScopeContext)new DefaultScope()).getNode(JavaCore.PLUGIN_ID); // Listen to instance preferences node removal from parent in order to refresh stored one this.instanceNodeListener= new IEclipsePreferences.INodeChangeListener() { public void added(IEclipsePreferences.NodeChangeEvent event) { // do nothing } public void removed(IEclipsePreferences.NodeChangeEvent event) { if (event.getChild() == JavaModelManager.this.preferencesLookup[PREF_INSTANCE]) { JavaModelManager.this.preferencesLookup[PREF_INSTANCE]= ((IScopeContext)new InstanceScope()).getNode(JavaCore.PLUGIN_ID); JavaModelManager.this.preferencesLookup[PREF_INSTANCE].addPreferenceChangeListener(new EclipsePreferencesListener()); } } }; ((IEclipsePreferences)this.preferencesLookup[PREF_INSTANCE].parent()).addNodeChangeListener(this.instanceNodeListener); this.preferencesLookup[PREF_INSTANCE].addPreferenceChangeListener(this.instancePreferencesListener= new EclipsePreferencesListener()); // Listen to default preferences node removal from parent in order to refresh stored one this.defaultNodeListener= new IEclipsePreferences.INodeChangeListener() { public void added(IEclipsePreferences.NodeChangeEvent event) { // do nothing } public void removed(IEclipsePreferences.NodeChangeEvent event) { if (event.getChild() == JavaModelManager.this.preferencesLookup[PREF_DEFAULT]) { JavaModelManager.this.preferencesLookup[PREF_DEFAULT]= ((IScopeContext)new DefaultScope()).getNode(JavaCore.PLUGIN_ID); } } }; ((IEclipsePreferences)this.preferencesLookup[PREF_DEFAULT].parent()).addNodeChangeListener(this.defaultNodeListener); } 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; */ } private HashSet getClasspathBeingResolved() { HashSet result= (HashSet)this.classpathsBeingResolved.get(); if (result == null) { result= new HashSet(); this.classpathsBeingResolved.set(result); } return result; } public boolean isClasspathBeingResolved(IJavaProject project) { return getClasspathBeingResolved().contains(project); } /** * @deprecated */ private boolean isDeprecatedOption(String optionName) { return JavaCore.COMPILER_PB_INVALID_IMPORT.equals(optionName) || JavaCore.COMPILER_PB_UNREACHABLE_CODE.equals(optionName); } public boolean isNonChainingJar(IPath path) { return this.nonChainingJars != null && this.nonChainingJars.contains(path); } public void setClasspathBeingResolved(IJavaProject project, boolean classpathIsResolved) { if (classpathIsResolved) { getClasspathBeingResolved().add(project); } else { getClasspathBeingResolved().remove(project); } } private Set loadNonChainingJarsCache() { Set nonChainingJarsCache= new HashSet(); File nonChainingJarsFile= getNonChainingJarsFile(); DataInputStream in= null; try { in= new DataInputStream(new BufferedInputStream(new FileInputStream(nonChainingJarsFile))); int size= in.readInt(); while (size-- > 0) { String path= in.readUTF(); nonChainingJarsCache.add(Path.fromPortableString(path)); } } catch (IOException e) { if (nonChainingJarsFile.exists()) Util.log(e, "Unable to read non-chaining jar cache file"); //$NON-NLS-1$ } finally { if (in != null) { try { in.close(); } catch (IOException e) { // nothing we can do: ignore } } } return Collections.synchronizedSet(nonChainingJarsCache); } private File getNonChainingJarsFile() { return JavaCore.getPlugin().getStateLocation().append("nonChainingJarsCache").toFile(); //$NON-NLS-1$ } private Set getNonChainingJarsCache() throws CoreException { // Even if there is one entry in the cache, just return it. It may not be // the complete cache, but avoid going through all the projects to populate the cache. if (this.nonChainingJars != null && this.nonChainingJars.size() > 0) { return this.nonChainingJars; } Set result= new HashSet(); IJavaProject[] projects= getJavaModel().getJavaProjects(); for (int i= 0, length= projects.length; i < length; i++) { IJavaProject javaProject= projects[i]; IClasspathEntry[] classpath= ((JavaProject)javaProject).getResolvedClasspath(); for (int j= 0, length2= classpath.length; j < length2; j++) { IClasspathEntry entry= classpath[j]; IPath path; if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY && !result.contains(path= entry.getPath()) && ClasspathEntry.resolvedChainedLibraries(path).length == 0) { result.add(path); } } } this.nonChainingJars= Collections.synchronizedSet(result); return this.nonChainingJars; } public void loadVariablesAndContainers() throws CoreException { // backward compatibility, consider persistent property QualifiedName qName= new QualifiedName(JavaCore.PLUGIN_ID, "variables"); //$NON-NLS-1$ String xmlString= ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(qName); try { if (xmlString != null) { StringReader reader= new StringReader(xmlString); Element cpElement; try { DocumentBuilder parser= DocumentBuilderFactory.newInstance().newDocumentBuilder(); cpElement= parser.parse(new InputSource(reader)).getDocumentElement(); } catch (SAXException e) { return; } catch (ParserConfigurationException e) { return; } finally { reader.close(); } if (cpElement == null) return; if (!cpElement.getNodeName().equalsIgnoreCase("variables")) { //$NON-NLS-1$ return; } NodeList list= cpElement.getChildNodes(); int length= list.getLength(); for (int i= 0; i < length; ++i) { Node node= list.item(i); short type= node.getNodeType(); if (type == Node.ELEMENT_NODE) { Element element= (Element)node; if (element.getNodeName().equalsIgnoreCase("variable")) { //$NON-NLS-1$ variablePut( element.getAttribute("name"), //$NON-NLS-1$ new Path(element.getAttribute("path"))); //$NON-NLS-1$ } } } } } catch (IOException e) { // problem loading xml file: nothing we can do } finally { if (xmlString != null) { ResourcesPlugin.getWorkspace().getRoot().setPersistentProperty(qName, null); // flush old one } } // backward compatibility, load variables and containers from preferences into cache loadVariablesAndContainers(getDefaultPreferences()); loadVariablesAndContainers(getInstancePreferences()); // load variables and containers from saved file into cache File file= getVariableAndContainersFile(); DataInputStream in= null; try { in= new DataInputStream(new BufferedInputStream(new FileInputStream(file))); switch (in.readInt()) { case 2: new VariablesAndContainersLoadHelper(in).load(); break; case 1: // backward compatibility, load old format // variables int size= in.readInt(); while (size-- > 0) { String varName= in.readUTF(); String pathString= in.readUTF(); if (CP_ENTRY_IGNORE.equals(pathString)) continue; IPath varPath= Path.fromPortableString(pathString); this.variables.put(varName, varPath); this.previousSessionVariables.put(varName, varPath); } // containers IJavaModel model= getJavaModel(); int projectSize= in.readInt(); while (projectSize-- > 0) { String projectName= in.readUTF(); IJavaProject project= model.getJavaProject(projectName); int containerSize= in.readInt(); while (containerSize-- > 0) { IPath containerPath= Path.fromPortableString(in.readUTF()); int length= in.readInt(); byte[] containerString= new byte[length]; in.readFully(containerString); recreatePersistedContainer(project, containerPath, new String(containerString), true/*add to container values*/); } } break; } } catch (IOException e) { if (file.exists()) Util.log(e, "Unable to read variable and containers file"); //$NON-NLS-1$ } catch (RuntimeException e) { if (file.exists()) Util.log(e, "Unable to read variable and containers file (file is corrupt)"); //$NON-NLS-1$ } finally { if (in != null) { try { in.close(); } catch (IOException e) { // nothing we can do: ignore } } } // override persisted values for variables which have a registered initializer String[] registeredVariables= getRegisteredVariableNames(); for (int i= 0; i < registeredVariables.length; i++) { String varName= registeredVariables[i]; this.variables.put(varName, null); // reset variable, but leave its entry in the Map, so it will be part of variable names. } // override persisted values for containers which have a registered initializer containersReset(getRegisteredContainerIDs()); } private void loadVariablesAndContainers(IEclipsePreferences preferences) { try { // only get variable from preferences not set to their default String[] propertyNames= preferences.keys(); int variablePrefixLength= CP_VARIABLE_PREFERENCES_PREFIX.length(); for (int i= 0; i < propertyNames.length; i++) { String propertyName= propertyNames[i]; if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)) { String varName= propertyName.substring(variablePrefixLength); String propertyValue= preferences.get(propertyName, null); if (propertyValue != null) { String pathString= propertyValue.trim(); if (CP_ENTRY_IGNORE.equals(pathString)) { // cleanup old preferences preferences.remove(propertyName); continue; } // add variable to table IPath varPath= new Path(pathString); this.variables.put(varName, varPath); this.previousSessionVariables.put(varName, varPath); } } else if (propertyName.startsWith(CP_CONTAINER_PREFERENCES_PREFIX)) { String propertyValue= preferences.get(propertyName, null); if (propertyValue != null) { // cleanup old preferences preferences.remove(propertyName); // recreate container recreatePersistedContainer(propertyName, propertyValue, true/*add to container values*/); } } } } catch (BackingStoreException e1) { // TODO (frederic) see if it's necessary to report this failure... } } private static final class PersistedClasspathContainer implements IClasspathContainer { private final IPath containerPath; private final IClasspathEntry[] entries; private final IJavaProject project; PersistedClasspathContainer(IJavaProject project, IPath containerPath, IClasspathEntry[] entries) { super(); this.containerPath= containerPath; this.entries= entries; this.project= project; } public IClasspathEntry[] getClasspathEntries() { return this.entries; } public String getDescription() { return "Persisted container [" + this.containerPath //$NON-NLS-1$ + " for project [" + this.project.getElementName() //$NON-NLS-1$ + "]]"; //$NON-NLS-1$ } public int getKind() { return 0; } public IPath getPath() { return this.containerPath; } public String toString() { return getDescription(); } } private final class VariablesAndContainersLoadHelper { private static final int ARRAY_INCREMENT= 200; private IClasspathEntry[] allClasspathEntries; private int allClasspathEntryCount; private final Map allPaths; // String -> IPath private String[] allStrings; private int allStringsCount; private final DataInputStream in; VariablesAndContainersLoadHelper(DataInputStream in) { super(); this.allClasspathEntries= null; this.allClasspathEntryCount= 0; this.allPaths= new HashMap(); this.allStrings= null; this.allStringsCount= 0; this.in= in; } void load() throws IOException { loadProjects(getJavaModel()); loadVariables(); } private IAccessRule loadAccessRule() throws IOException { int problemId= loadInt(); IPath pattern= loadPath(); return new ClasspathAccessRule(pattern.toString().toCharArray(), problemId); } private IAccessRule[] loadAccessRules() throws IOException { int count= loadInt(); if (count == 0) return ClasspathEntry.NO_ACCESS_RULES; IAccessRule[] rules= new IAccessRule[count]; for (int i= 0; i < count; ++i) rules[i]= loadAccessRule(); return rules; } private IClasspathAttribute loadAttribute() throws IOException { String name= loadString(); String value= loadString(); return new ClasspathAttribute(name, value); } private IClasspathAttribute[] loadAttributes() throws IOException { int count= loadInt(); if (count == 0) return ClasspathEntry.NO_EXTRA_ATTRIBUTES; IClasspathAttribute[] attributes= new IClasspathAttribute[count]; for (int i= 0; i < count; ++i) attributes[i]= loadAttribute(); return attributes; } private boolean loadBoolean() throws IOException { return this.in.readBoolean(); } private IClasspathEntry[] loadClasspathEntries() throws IOException { int count= loadInt(); IClasspathEntry[] entries= new IClasspathEntry[count]; for (int i= 0; i < count; ++i) entries[i]= loadClasspathEntry(); return entries; } private IClasspathEntry loadClasspathEntry() throws IOException { int id= loadInt(); if (id < 0 || id > this.allClasspathEntryCount) throw new IOException("Unexpected classpathentry id"); //$NON-NLS-1$ if (id < this.allClasspathEntryCount) return this.allClasspathEntries[id]; int contentKind= loadInt(); int entryKind= loadInt(); IPath path= loadPath(); IPath[] inclusionPatterns= loadPaths(); IPath[] exclusionPatterns= loadPaths(); IPath sourceAttachmentPath= loadPath(); IPath sourceAttachmentRootPath= loadPath(); IPath specificOutputLocation= loadPath(); boolean isExported= loadBoolean(); IAccessRule[] accessRules= loadAccessRules(); boolean combineAccessRules= loadBoolean(); IClasspathAttribute[] extraAttributes= loadAttributes(); IClasspathEntry entry= new ClasspathEntry(contentKind, entryKind, path, inclusionPatterns, exclusionPatterns, sourceAttachmentPath, sourceAttachmentRootPath, specificOutputLocation, isExported, accessRules, combineAccessRules, extraAttributes); IClasspathEntry[] array= this.allClasspathEntries; if (array == null || id == array.length) { array= new IClasspathEntry[id + ARRAY_INCREMENT]; if (id != 0) System.arraycopy(this.allClasspathEntries, 0, array, 0, id); this.allClasspathEntries= array; } array[id]= entry; this.allClasspathEntryCount= id + 1; return entry; } private void loadContainers(IJavaProject project) throws IOException { boolean projectIsAccessible= project.getProject().isAccessible(); int count= loadInt(); for (int i= 0; i < count; ++i) { IPath path= loadPath(); IClasspathEntry[] entries= loadClasspathEntries(); if (!projectIsAccessible) // avoid leaking deleted project's persisted container, // but still read the container as it is is part of the file format continue; IClasspathContainer container= new PersistedClasspathContainer(project, path, entries); containerPut(project, path, container); Map oldContainers= (Map)JavaModelManager.this.previousSessionContainers.get(project); if (oldContainers == null) { oldContainers= new HashMap(); JavaModelManager.this.previousSessionContainers.put(project, oldContainers); } oldContainers.put(path, container); } } private int loadInt() throws IOException { return this.in.readInt(); } private IPath loadPath() throws IOException { if (loadBoolean()) return null; String portableString= loadString(); IPath path= (IPath)this.allPaths.get(portableString); if (path == null) { path= Path.fromPortableString(portableString); this.allPaths.put(portableString, path); } return path; } private IPath[] loadPaths() throws IOException { int count= loadInt(); IPath[] pathArray= new IPath[count]; for (int i= 0; i < count; ++i) pathArray[i]= loadPath(); return pathArray; } private void loadProjects(IJavaModel model) throws IOException { int count= loadInt(); for (int i= 0; i < count; ++i) { String projectName= loadString(); loadContainers(model.getJavaProject(projectName)); } } private String loadString() throws IOException { int id= loadInt(); if (id < 0 || id > this.allStringsCount) throw new IOException("Unexpected string id"); //$NON-NLS-1$ if (id < this.allStringsCount) return this.allStrings[id]; String string= this.in.readUTF(); String[] array= this.allStrings; if (array == null || id == array.length) { array= new String[id + ARRAY_INCREMENT]; if (id != 0) System.arraycopy(this.allStrings, 0, array, 0, id); this.allStrings= array; } array[id]= string; this.allStringsCount= id + 1; return string; } private void loadVariables() throws IOException { int size= loadInt(); Map loadedVars= new HashMap(size); for (int i= 0; i < size; ++i) { String varName= loadString(); IPath varPath= loadPath(); if (varPath != null) loadedVars.put(varName, varPath); } JavaModelManager.this.previousSessionVariables.putAll(loadedVars); JavaModelManager.this.variables.putAll(loadedVars); } } /** * 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); } /** * @see ISaveParticipant */ public void prepareToSave(ISaveContext context) /*throws CoreException*/{ // nothing to do } /* * Puts the infos in the given map (keys are IJavaElements and values are JavaElementInfos) * in the Java model cache in an atomic way. */ protected synchronized void putInfos(IJavaElement openedElement, Map newElements) { // remove existing children as the are replaced with the new children contained in newElements Object existingInfo= this.cache.peekAtInfo(openedElement); 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()); } } 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 } } } } /* * Remember the info for the jar binary type */ protected synchronized void putJarTypeInfo(IJavaElement type, Object info) { this.cache.jarTypeCache.put(type, info); } /** * Reads the build state for the relevant project. */ protected Object readState(IProject project) throws CoreException { File file= getSerializationFile(project); if (file != null && file.exists()) { try { DataInputStream in= new DataInputStream(new BufferedInputStream(new FileInputStream(file))); try { String pluginID= in.readUTF(); if (!pluginID.equals(JavaCore.PLUGIN_ID)) throw new IOException(Messages.build_wrongFileFormat); String kind= in.readUTF(); if (!kind.equals("STATE")) //$NON-NLS-1$ throw new IOException(Messages.build_wrongFileFormat); if (in.readBoolean()) return JavaBuilder.readState(project, in); if (JavaBuilder.DEBUG) System.out.println("Saved state thinks last build failed for " + project.getName()); //$NON-NLS-1$ } finally { in.close(); } } catch (Exception e) { e.printStackTrace(); throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR, "Error reading last build state for project " + project.getName(), e)); //$NON-NLS-1$ } } else if (JavaBuilder.DEBUG) { if (file == null) System.out.println("Project does not exist: " + project); //$NON-NLS-1$ else System.out.println("Build state file " + file.getPath() + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$ } return null; } public static void recreatePersistedContainer(String propertyName, String containerString, boolean addToContainerValues) { int containerPrefixLength= CP_CONTAINER_PREFERENCES_PREFIX.length(); int index= propertyName.indexOf('|', containerPrefixLength); if (containerString != null) containerString= containerString.trim(); if (index > 0) { String projectName= propertyName.substring(containerPrefixLength, index).trim(); IJavaProject project= getJavaModelManager().getJavaModel().getJavaProject(projectName); IPath containerPath= new Path(propertyName.substring(index + 1).trim()); recreatePersistedContainer(project, containerPath, containerString, addToContainerValues); } } private static void recreatePersistedContainer(final IJavaProject project, final IPath containerPath, String containerString, boolean addToContainerValues) { if (!project.getProject().isAccessible()) return; // avoid leaking deleted project's persisted container if (containerString == null) { getJavaModelManager().containerPut(project, containerPath, null); } else { IClasspathEntry[] entries; try { entries= ((JavaProject)project).decodeClasspath(containerString, null/*not interested in unknown elements*/)[0]; } catch (IOException e) { Util.log(e, "Could not recreate persisted container: \n" + containerString); //$NON-NLS-1$ entries= JavaProject.INVALID_CLASSPATH; } if (entries != JavaProject.INVALID_CLASSPATH) { final IClasspathEntry[] containerEntries= entries; IClasspathContainer container= new IClasspathContainer() { public IClasspathEntry[] getClasspathEntries() { return containerEntries; } public String getDescription() { return "Persisted container [" + containerPath + " for project [" + project.getElementName() + "]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ } public int getKind() { return 0; } public IPath getPath() { return containerPath; } public String toString() { return getDescription(); } }; if (addToContainerValues) { getJavaModelManager().containerPut(project, containerPath, container); } Map projectContainers= (Map)getJavaModelManager().previousSessionContainers.get(project); if (projectContainers == null) { projectContainers= new HashMap(1); getJavaModelManager().previousSessionContainers.put(project, projectContainers); } projectContainers.put(containerPath, container); } } } /** * Remembers the given scope in a weak set (so no need to remove it: it will be removed by the * garbage collector) */ public void rememberScope(AbstractSearchScope scope) { // NB: The value has to be null so as to not create a strong reference on the scope this.searchScopes.put(scope, null); } /* * 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) { 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); info.forgetExternalTimestampsAndIndexes(); } } resetNonChainingJarsCache(); } /* * 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; } } } /* * Reset project preferences stored in info cache. */ public void resetProjectPreferences(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.preferences= null; } } } public static final void doNotUse() { // used by tests to simulate a startup MANAGER.deltaState.doNotUse(); MANAGER= new JavaModelManager(); } /* * Resets the cache that holds on binary type in jar files */ protected synchronized void resetJarTypeCache() { this.cache.resetJarTypeCache(); } public void resetNonChainingJarsCache() { if (this.nonChainingJars != null) this.nonChainingJars.clear(); } /* * Resets the temporary cache for newly created elements to null. */ public void resetTemporaryCache() { this.temporaryCache.set(null); } /** * @see ISaveParticipant */ public void rollback(ISaveContext context) { // nothing to do } private void saveState(PerProjectInfo info, ISaveContext context) throws CoreException { // passed this point, save actions are non trivial if (context.getKind() == ISaveContext.SNAPSHOT) return; // save built state if (info.triedRead) saveBuiltState(info); } /** * Saves the built state for the project. */ private void saveBuiltState(PerProjectInfo info) throws CoreException { if (JavaBuilder.DEBUG) System.out.println(Messages.bind(Messages.build_saveStateProgress, info.project.getName())); File file= getSerializationFile(info.project); if (file == null) return; long t= System.currentTimeMillis(); try { DataOutputStream out= new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); try { out.writeUTF(JavaCore.PLUGIN_ID); out.writeUTF("STATE"); //$NON-NLS-1$ if (info.savedState == null) { out.writeBoolean(false); } else { out.writeBoolean(true); JavaBuilder.writeState(info.savedState, out); } } finally { out.close(); } } catch (RuntimeException e) { try { file.delete(); } catch (SecurityException se) { // could not delete file: cannot do much more } throw new CoreException( new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR, Messages.bind(Messages.build_cannotSaveState, info.project.getName()), e)); } catch (IOException e) { try { file.delete(); } catch (SecurityException se) { // could not delete file: cannot do much more } throw new CoreException( new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR, Messages.bind(Messages.build_cannotSaveState, info.project.getName()), e)); } if (JavaBuilder.DEBUG) { t= System.currentTimeMillis() - t; System.out.println(Messages.bind(Messages.build_saveStateComplete, String.valueOf(t))); } } private void saveNonChainingJarsCache() throws CoreException { File file= getNonChainingJarsFile(); DataOutputStream out= null; try { out= new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); Set nonChainingJarsCache= getNonChainingJarsCache(); synchronized (nonChainingJarsCache) { out.writeInt(nonChainingJarsCache.size()); Iterator entries= nonChainingJarsCache.iterator(); while (entries.hasNext()) { IPath path= (IPath)entries.next(); out.writeUTF(path.toPortableString()); } } } catch (IOException e) { IStatus status= new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.ERROR, "Problems while saving non-chaining jar cache", e); //$NON-NLS-1$ throw new CoreException(status); } finally { if (out != null) { try { out.close(); } catch (IOException e) { // nothing we can do: ignore } } } } private void saveVariablesAndContainers(ISaveContext context) throws CoreException { File file= getVariableAndContainersFile(); DataOutputStream out= null; try { out= new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); out.writeInt(VARIABLES_AND_CONTAINERS_FILE_VERSION); new VariablesAndContainersSaveHelper(out).save(context); } catch (IOException e) { IStatus status= new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.ERROR, "Problems while saving variables and containers", e); //$NON-NLS-1$ throw new CoreException(status); } finally { if (out != null) { try { out.close(); } catch (IOException e) { // nothing we can do: ignore } } } } private final class VariablesAndContainersSaveHelper { private final HashtableOfObjectToInt classpathEntryIds; // IClasspathEntry -> int private final DataOutputStream out; private final HashtableOfObjectToInt stringIds; // Strings -> int VariablesAndContainersSaveHelper(DataOutputStream out) { super(); this.classpathEntryIds= new HashtableOfObjectToInt(); this.out= out; this.stringIds= new HashtableOfObjectToInt(); } void save(ISaveContext context) throws IOException, JavaModelException { IProject project= context.getProject(); if (project == null) { // save all projects if none specified (snapshot or full save) saveProjects(getJavaModel().getJavaProjects()); } else { saveProjects(new IJavaProject[] { JavaCore.create(project) }); } switch (context.getKind()) { case ISaveContext.FULL_SAVE: // TODO (eric) - investigate after 3.3 if variables should be saved for a SNAPSHOT case ISaveContext.SNAPSHOT: // remove variables that should not be saved HashMap varsToSave= null; Iterator iterator= JavaModelManager.this.variables.entrySet().iterator(); IEclipsePreferences defaultPreferences= getDefaultPreferences(); while (iterator.hasNext()) { Map.Entry entry= (Map.Entry)iterator.next(); String varName= (String)entry.getKey(); if (defaultPreferences.get(CP_VARIABLE_PREFERENCES_PREFIX + varName, null) != null // don't save classpath variables from the default preferences as there is no delta if they are removed || CP_ENTRY_IGNORE_PATH.equals(entry.getValue())) { if (varsToSave == null) varsToSave= new HashMap(JavaModelManager.this.variables); varsToSave.remove(varName); } } saveVariables(varsToSave != null ? varsToSave : JavaModelManager.this.variables); break; default: // do nothing } } private void saveAccessRule(ClasspathAccessRule rule) throws IOException { saveInt(rule.problemId); savePath(rule.getPattern()); } private void saveAccessRules(IAccessRule[] rules) throws IOException { int count= rules == null ? 0 : rules.length; saveInt(count); for (int i= 0; i < count; ++i) saveAccessRule((ClasspathAccessRule)rules[i]); } private void saveAttribute(IClasspathAttribute attribute) throws IOException { saveString(attribute.getName()); saveString(attribute.getValue()); } private void saveAttributes(IClasspathAttribute[] attributes) throws IOException { int count= attributes == null ? 0 : attributes.length; saveInt(count); for (int i= 0; i < count; ++i) saveAttribute(attributes[i]); } private void saveClasspathEntries(IClasspathEntry[] entries) throws IOException { int count= entries == null ? 0 : entries.length; saveInt(count); for (int i= 0; i < count; ++i) saveClasspathEntry(entries[i]); } private void saveClasspathEntry(IClasspathEntry entry) throws IOException { if (saveNewId(entry, this.classpathEntryIds)) { saveInt(entry.getContentKind()); saveInt(entry.getEntryKind()); savePath(entry.getPath()); savePaths(entry.getInclusionPatterns()); savePaths(entry.getExclusionPatterns()); savePath(entry.getSourceAttachmentPath()); savePath(entry.getSourceAttachmentRootPath()); savePath(entry.getOutputLocation()); this.out.writeBoolean(entry.isExported()); saveAccessRules(entry.getAccessRules()); this.out.writeBoolean(entry.combineAccessRules()); saveAttributes(entry.getExtraAttributes()); } } private void saveContainers(IJavaProject project, Map containerMap) throws IOException { saveInt(containerMap.size()); for (Iterator i= containerMap.entrySet().iterator(); i.hasNext();) { Entry entry= (Entry)i.next(); IPath path= (IPath)entry.getKey(); IClasspathContainer container= (IClasspathContainer)entry.getValue(); IClasspathEntry[] cpEntries= null; if (container == null) { // container has not been initialized yet, use previous // session value // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=73969) container= getPreviousSessionContainer(path, project); } if (container != null) cpEntries= container.getClasspathEntries(); savePath(path); saveClasspathEntries(cpEntries); } } private void saveInt(int value) throws IOException { this.out.writeInt(value); } private boolean saveNewId(Object key, HashtableOfObjectToInt map) throws IOException { int id= map.get(key); if (id == -1) { int newId= map.size(); map.put(key, newId); saveInt(newId); return true; } else { saveInt(id); return false; } } private void savePath(IPath path) throws IOException { if (path == null) { this.out.writeBoolean(true); } else { this.out.writeBoolean(false); saveString(path.toPortableString()); } } private void savePaths(IPath[] paths) throws IOException { int count= paths == null ? 0 : paths.length; saveInt(count); for (int i= 0; i < count; ++i) savePath(paths[i]); } private void saveProjects(IJavaProject[] projects) throws IOException, JavaModelException { int count= projects.length; saveInt(count); for (int i= 0; i < count; ++i) { IJavaProject project= projects[i]; saveString(project.getElementName()); Map containerMap= (Map)JavaModelManager.this.containers.get(project); if (containerMap == null) { containerMap= Collections.EMPTY_MAP; } else { // clone while iterating // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59638) containerMap= new HashMap(containerMap); } saveContainers(project, containerMap); } } private void saveString(String string) throws IOException { if (saveNewId(string, this.stringIds)) this.out.writeUTF(string); } private void saveVariables(Map map) throws IOException { saveInt(map.size()); for (Iterator i= map.entrySet().iterator(); i.hasNext();) { Entry entry= (Entry)i.next(); String varName= (String)entry.getKey(); IPath varPath= (IPath)entry.getValue(); saveString(varName); savePath(varPath); } } } private void traceVariableAndContainers(String action, long start) { Long delta= new Long(System.currentTimeMillis() - start); Long length= new Long(getVariableAndContainersFile().length()); String pattern= "{0} {1} bytes in variablesAndContainers.dat in {2}ms"; //$NON-NLS-1$ String message= MessageFormat.format(pattern, new Object[] { action, length, delta }); System.out.println(message); } /** * @see ISaveParticipant */ public void saving(ISaveContext context) throws CoreException { long start= -1; if (VERBOSE) start= System.currentTimeMillis(); // save variable and container values on snapshot/full save saveVariablesAndContainers(context); if (VERBOSE) traceVariableAndContainers("Saved", start); //$NON-NLS-1$ switch (context.getKind()) { case ISaveContext.FULL_SAVE: { // save non-chaining jar cache on snapshot/full save saveNonChainingJarsCache(); // will need delta since this save (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38658) context.needDelta(); // clean up indexes on workspace full save // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52347) IndexManager manager= this.indexManager; if (manager != null // don't force initialization of workspace scope as we could be shutting down // (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=93941) && this.workspaceScope != null) { manager.cleanUpIndexes(); } } //$FALL-THROUGH$ case ISaveContext.SNAPSHOT: { // clean up external folders on full save or snapshot this.externalFoldersManager.cleanUp(null); } } IProject savedProject= context.getProject(); if (savedProject != null) { if (!JavaProject.hasJavaNature(savedProject)) return; // ignore PerProjectInfo info= getPerProjectInfo(savedProject, true /* create info */); saveState(info, context); return; } ArrayList vStats= null; // lazy initialized ArrayList values= null; synchronized (this.perProjectInfos) { values= new ArrayList(this.perProjectInfos.values()); } Iterator iterator= values.iterator(); while (iterator.hasNext()) { try { PerProjectInfo info= (PerProjectInfo)iterator.next(); saveState(info, context); } catch (CoreException e) { if (vStats == null) vStats= new ArrayList(); vStats.add(e.getStatus()); } } if (vStats != null) { IStatus[] stats= new IStatus[vStats.size()]; vStats.toArray(stats); throw new CoreException(new MultiStatus(JavaCore.PLUGIN_ID, IStatus.ERROR, stats, Messages.build_cannotSaveStates, null)); } // save external libs timestamps this.deltaState.saveExternalLibTimeStamps(); } /** * 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 } } } } /** * Get all secondary types for a project and store result in per project info cache. * * This cache is an Hashtable<String, HashMap<String, IType>>: - key: package name - value: + * key: type name + value: java model handle for the secondary type Hashtable was used to * protect callers from possible concurrent access. * * 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; } /* * 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(); 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(); if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(path)) { IFile file= ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path)); ICompilationUnit unit= JavaModelManager.createCompilationUnitFrom(file, null); IType type= unit.getType(typeName); types.put(typeName, type); // replace stored path with type itself } else { types.remove(typeName); } } } // 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; } /** * Remove from secondary types cache all types belonging to a given file. Clean secondary types * cache built while indexing if requested. * * 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; IFile[] removed= null; Iterator cachedFiles= keys.iterator(); while (cachedFiles.hasNext()) { IFile cachedFile= (IFile)cachedFiles.next(); if (file.equals(cachedFile)) { if (removed == null) removed= new IFile[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$ } } } /** * Record the order in which to build the java projects (batch build). This order is based on * the projects classpath settings. */ protected void setBuildOrder(String[] javaBuildOrder) throws JavaModelException { // optional behaviour // possible value of index 0 is Compute if (!JavaCore.COMPUTE.equals(JavaCore.getOption(JavaCore.CORE_JAVA_BUILD_ORDER))) return; // cannot be customized at project level if (javaBuildOrder == null || javaBuildOrder.length <= 1) return; IWorkspace workspace= ResourcesPlugin.getWorkspace(); IWorkspaceDescription description= workspace.getDescription(); String[] wksBuildOrder= description.getBuildOrder(); String[] newOrder; if (wksBuildOrder == null) { newOrder= javaBuildOrder; } else { // remove projects which are already mentionned in java builder order int javaCount= javaBuildOrder.length; HashMap newSet= new HashMap(javaCount); // create a set for fast check for (int i= 0; i < javaCount; i++) { newSet.put(javaBuildOrder[i], javaBuildOrder[i]); } int removed= 0; int oldCount= wksBuildOrder.length; for (int i= 0; i < oldCount; i++) { if (newSet.containsKey(wksBuildOrder[i])) { wksBuildOrder[i]= null; removed++; } } // add Java ones first newOrder= new String[oldCount - removed + javaCount]; System.arraycopy(javaBuildOrder, 0, newOrder, 0, javaCount); // java projects are built first // copy previous items in their respective order int index= javaCount; for (int i= 0; i < oldCount; i++) { if (wksBuildOrder[i] != null) { newOrder[index++]= wksBuildOrder[i]; } } } // commit the new build order out description.setBuildOrder(newOrder); try { workspace.setDescription(description); } catch (CoreException e) { throw new JavaModelException(e); } } /** * Sets the last built state for the given project, or null to reset it. */ public void setLastBuiltState(IProject project, Object state) { if (JavaProject.hasJavaNature(project)) { // should never be requested on non-Java projects PerProjectInfo info= getPerProjectInfo(project, true /*create if missing*/); info.triedRead= true; // no point trying to re-read once using setter info.savedState= state; } if (state == null) { // delete state file to ensure a full build happens if the workspace crashes try { File file= getSerializationFile(project); if (file != null && file.exists()) file.delete(); } catch (SecurityException se) { // could not delete file: cannot do much more } } } public void setOptions(Hashtable newOptions) { try { Hashtable cachedValue= newOptions == null ? null : new Hashtable(newOptions); IEclipsePreferences defaultPreferences= getDefaultPreferences(); IEclipsePreferences instancePreferences= getInstancePreferences(); if (newOptions == null) { instancePreferences.clear(); } else { Enumeration keys= newOptions.keys(); while (keys.hasMoreElements()) { String key= (String)keys.nextElement(); if (!this.optionNames.contains(key)) 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); if (defaultValue != null && defaultValue.equals(value)) { instancePreferences.remove(key); } else { instancePreferences.put(key, value); } } } // persist options instancePreferences.flush(); // update cache Util.fixTaskTags(cachedValue); this.optionsCache= cachedValue; } catch (BackingStoreException e) { // ignore } } public void startup() throws CoreException { try { configurePluginDebugOptions(); // initialize Java model cache this.cache= new JavaModelCache(); // request state folder creation (workaround 19885) JavaCore.getPlugin().getStateLocation(); // Initialize eclipse preferences initializePreferences(); // Listen to preference changes this.propertyListener= new IEclipsePreferences.IPreferenceChangeListener() { public void preferenceChange(PreferenceChangeEvent event) { JavaModelManager.this.optionsCache= null; } }; new InstanceScope().getNode(JavaCore.PLUGIN_ID).addPreferenceChangeListener(this.propertyListener); // listen for encoding changes (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=255501 ) this.resourcesPropertyListener= new IEclipsePreferences.IPreferenceChangeListener() { public void preferenceChange(PreferenceChangeEvent event) { if (ResourcesPlugin.PREF_ENCODING.equals(event.getKey())) { JavaModelManager.this.optionsCache= null; } } }; String resourcesPluginId= ResourcesPlugin.getPlugin().getBundle().getSymbolicName(); new InstanceScope().getNode(resourcesPluginId).addPreferenceChangeListener(this.resourcesPropertyListener); // Listen to content-type changes Platform.getContentTypeManager().addContentTypeChangeListener(this); // retrieve variable values long start= -1; if (VERBOSE) start= System.currentTimeMillis(); loadVariablesAndContainers(); if (VERBOSE) traceVariableAndContainers("Loaded", start); //$NON-NLS-1$ // listen for resource changes this.deltaState.initializeRootsWithPreviousSession(); final IWorkspace workspace= ResourcesPlugin.getWorkspace(); workspace.addResourceChangeListener( this.deltaState, /* update spec in JavaCore#addPreProcessingResourceChangedListener(...) if adding more event types */ IResourceChangeEvent.PRE_BUILD | IResourceChangeEvent.POST_BUILD | IResourceChangeEvent.POST_CHANGE | IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_REFRESH); startIndexing(); // process deltas since last activated in indexer thread so that indexes are up-to-date. // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38658 Job processSavedState= new Job(Messages.savedState_jobName) { protected IStatus run(IProgressMonitor monitor) { try { // add save participant and process delta atomically // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59937 workspace.run( new IWorkspaceRunnable() { public void run(IProgressMonitor progress) throws CoreException { ISavedState savedState= workspace.addSaveParticipant(JavaCore.PLUGIN_ID, JavaModelManager.this); if (savedState != null) { // the event type coming from the saved state is always POST_AUTO_BUILD // force it to be POST_CHANGE so that the delta processor can handle it JavaModelManager.this.deltaState.getDeltaProcessor().overridenEventType= IResourceChangeEvent.POST_CHANGE; savedState.processResourceChangeEvents(JavaModelManager.this.deltaState); } } }, monitor); } catch (CoreException e) { return e.getStatus(); } return Status.OK_STATUS; } }; processSavedState.setSystem(true); processSavedState.setPriority(Job.SHORT); // process asap processSavedState.schedule(); } catch (RuntimeException e) { shutdown(); throw e; } } /** * Initiate the background indexing process. This should be deferred after the plug-in * activation. */ private void startIndexing() { if (this.indexManager != null) this.indexManager.reset(); } public void shutdown() { IEclipsePreferences preferences= new InstanceScope().getNode(JavaCore.PLUGIN_ID); try { preferences.flush(); } catch (BackingStoreException e) { Util.log(e, "Could not save JavaCore preferences"); //$NON-NLS-1$ } IWorkspace workspace= ResourcesPlugin.getWorkspace(); workspace.removeResourceChangeListener(this.deltaState); workspace.removeSaveParticipant(JavaCore.PLUGIN_ID); // Stop listening to content-type changes Platform.getContentTypeManager().removeContentTypeChangeListener(this); // Stop indexing if (this.indexManager != null) { this.indexManager.shutdown(); } // Stop listening to preferences changes preferences.removePreferenceChangeListener(this.propertyListener); ((IEclipsePreferences)this.preferencesLookup[PREF_DEFAULT].parent()).removeNodeChangeListener(this.defaultNodeListener); this.preferencesLookup[PREF_DEFAULT]= null; ((IEclipsePreferences)this.preferencesLookup[PREF_INSTANCE].parent()).removeNodeChangeListener(this.instanceNodeListener); this.preferencesLookup[PREF_INSTANCE].removePreferenceChangeListener(this.instancePreferencesListener); this.preferencesLookup[PREF_INSTANCE]= null; String resourcesPluginId= ResourcesPlugin.getPlugin().getBundle().getSymbolicName(); new InstanceScope().getNode(resourcesPluginId).removePreferenceChangeListener(this.resourcesPropertyListener); // wait for the initialization job to finish try { Job.getJobManager().join(JavaCore.PLUGIN_ID, null); } catch (InterruptedException e) { // ignore } // Note: no need to close the Java model as this just removes Java element infos from the Java model cache } public synchronized IPath variableGet(String variableName) { // check initialization in progress first HashSet initializations= variableInitializationInProgress(); if (initializations.contains(variableName)) { return VARIABLE_INITIALIZATION_IN_PROGRESS; } return (IPath)this.variables.get(variableName); } private synchronized IPath variableGetDefaultToPreviousSession(String variableName) { IPath variablePath= (IPath)this.variables.get(variableName); if (variablePath == null) return getPreviousSessionVariable(variableName); return variablePath; } /* * Returns the set of variable names that are being initialized in the current thread. */ private HashSet variableInitializationInProgress() { HashSet initializations= (HashSet)this.variableInitializationInProgress.get(); if (initializations == null) { initializations= new HashSet(); this.variableInitializationInProgress.set(initializations); } return initializations; } public synchronized String[] variableNames() { int length= this.variables.size(); String[] result= new String[length]; Iterator vars= this.variables.keySet().iterator(); int index= 0; while (vars.hasNext()) { result[index++]= (String)vars.next(); } return result; } public synchronized void variablePut(String variableName, IPath variablePath) { // set/unset the initialization in progress HashSet initializations= variableInitializationInProgress(); if (variablePath == VARIABLE_INITIALIZATION_IN_PROGRESS) { initializations.add(variableName); // do not write out intermediate initialization value return; } else { initializations.remove(variableName); // update cache - do not only rely on listener refresh if (variablePath == null) { // if path is null, record that the variable was removed to avoid asking the initializer to initialize it again // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=112609 this.variables.put(variableName, CP_ENTRY_IGNORE_PATH); // clean other variables caches this.variablesWithInitializer.remove(variableName); this.deprecatedVariables.remove(variableName); } else { this.variables.put(variableName, variablePath); } // discard obsoleted information about previous session this.previousSessionVariables.remove(variableName); } } public void variablePreferencesPut(String variableName, IPath variablePath) { String variableKey= CP_VARIABLE_PREFERENCES_PREFIX + variableName; if (variablePath == null) { getInstancePreferences().remove(variableKey); } else { getInstancePreferences().put(variableKey, variablePath.toString()); } try { getInstancePreferences().flush(); } catch (BackingStoreException e) { // ignore exception } } /* * Optimize startup case where 1 variable is initialized at a time with the same value as on shutdown. */ public boolean variablePutIfInitializingWithSameValue(String[] variableNames, IPath[] variablePaths) { if (variableNames.length != 1) return false; String variableName= variableNames[0]; IPath oldPath= variableGetDefaultToPreviousSession(variableName); if (oldPath == null) return false; IPath newPath= variablePaths[0]; if (!oldPath.equals(newPath)) return false; variablePut(variableName, newPath); return true; } public void contentTypeChanged(ContentTypeChangeEvent event) { Util.resetJavaLikeExtensions(); // Walk through projects to reset their secondary types cache IJavaProject[] projects; try { projects= JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects(); } catch (JavaModelException e) { return; } for (int i= 0, length= projects.length; i < length; i++) { IJavaProject project= projects[i]; final PerProjectInfo projectInfo= getPerProjectInfo(project.getProject(), false /* don't create info */); if (projectInfo != null) { projectInfo.secondaryTypes= null; } } } public synchronized String cacheToString(String prefix) { return this.cache.toStringFillingRation(prefix); } public Stats debugNewOpenableCacheStats() { return this.cache.openableCache.new Stats(); } public int getOpenableCacheSize() { return this.cache.openableCache.getSpaceLimit(); } }