/******************************************************************************* * Copyright (c) 2000, 2011 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.*; import java.net.URI; import java.text.MessageFormat; import java.util.*; import java.util.Map.Entry; import java.util.zip.ZipException; 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.*; import org.eclipse.core.runtime.*; 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.IPreferencesService; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; import org.eclipse.jdt.core.*; 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.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.core.util.LRUCache.Stats; 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 { private static final String NON_CHAINING_JARS_CACHE = "nonChainingJarsCache"; //$NON-NLS-1$ private static final String INVALID_ARCHIVES_CACHE = "invalidArchivesCache"; //$NON-NLS-1$ /** * 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$ /** * Name of the JVM parameter to specify whether or not referenced JAR should be resolved for container libraries. */ private static final String RESOLVE_REFERENCED_LIBRARIES_FOR_CONTAINERS = "resolveReferencedLibrariesForContainers"; //$NON-NLS-1$ /** * 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; // Non-static, which will give it a chance to retain the default when and if JavaModelManager is restarted. boolean resolveReferencedLibrariesForContainers = false; public final static ICompilationUnit[] NO_WORKING_COPY = new ICompilationUnit[0]; // Options private final static int UNKNOWN_OPTION = 0; private final static int DEPRECATED_OPTION = 1; private final static int VALID_OPTION = 2; HashSet optionNames = new HashSet(20); Map deprecatedOptions = new HashMap(); Hashtable optionsCache; // Preferences 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; boolean isJavaLike = org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(resourcePath.lastSegment()); IClasspathEntry[] entries = isJavaLike ? project.getRawClasspath() // JAVA file can only live inside SRC folder (on the raw path) : ((JavaProject)project).getResolvedClasspath(); int length = entries.length; if (length > 0) { String sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true); String complianceLevel = project.getOption(JavaCore.COMPILER_COMPLIANCE, true); for (int i = 0; i < length; i++) { IClasspathEntry entry = entries[i]; if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) continue; IPath rootPath = entry.getPath(); if (rootPath.equals(resourcePath)) { if (isJavaLike) return null; return project.getPackageFragmentRoot(resource); } else if (rootPath.isPrefixOf(resourcePath)) { // allow creation of package fragment if it contains a .java file that is included if (!Util.isExcluded(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 and invalid jars cache JavaModelManager.getJavaModelManager().resetClasspathListCache(); // null out resolved information return setResolvedClasspath(null, null, null, null, this.rawTimeStamp, true/*add classpath change*/); } private ClasspathChange setClasspath(IClasspathEntry[] newRawClasspath, IClasspathEntry[] referencedEntries, IPath newOutputLocation, IJavaModelStatus newRawClasspathStatus, IClasspathEntry[] newResolvedClasspath, Map newRootPathToRawEntries, Map newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus, boolean addClasspathChange) { ClasspathChange classpathChange = addClasspathChange ? addClasspathChange() : null; if (referencedEntries != null) this.referencedEntries = referencedEntries; if (this.referencedEntries == null) this.referencedEntries = ClasspathEntry.NO_ENTRIES; this.rawClasspath = newRawClasspath; this.outputLocation = newOutputLocation; this.rawClasspathStatus = newRawClasspathStatus; this.resolvedClasspath = newResolvedClasspath; this.rootPathToRawEntries = newRootPathToRawEntries; this.rootPathToResolvedEntries = newRootPathToResolvedEntries; this.unresolvedEntryStatus = newUnresolvedEntryStatus; this.javadocCache = new LRUCache(JAVADOC_CACHE_INITIAL_SIZE); return classpathChange; } protected ClasspathChange addClasspathChange() { // remember old info 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; // temporary debug flag to track failures of bug 302850 public static boolean DEBUG_302850 = 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; /* * List of IPath of jars that are known to be invalid - such as not being a valid/known format */ private Set invalidArchives; /** * 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] = InstanceScope.INSTANCE.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] = DefaultScope.INSTANCE.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 = loadClasspathListCache(NON_CHAINING_JARS_CACHE); this.invalidArchives = loadClasspathListCache(INVALID_ARCHIVES_CACHE); String includeContainerReferencedLib = System.getProperty(RESOLVE_REFERENCED_LIBRARIES_FOR_CONTAINERS); this.resolveReferencedLibrariesForContainers = TRUE.equalsIgnoreCase(includeContainerReferencedLib); } } /** * @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); } public void addInvalidArchive(IPath path) { // unlikely to be null if (this.invalidArchives == null) { this.invalidArchives = Collections.synchronizedSet(new HashSet()); } if(this.invalidArchives != null) { this.invalidArchives.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, i.e. 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; } int optionLevel = getOptionLevel(optionName); if (optionLevel != UNKNOWN_OPTION){ IPreferencesService service = Platform.getPreferencesService(); String value = service.get(optionName, null, this.preferencesLookup); if (value == null && optionLevel == DEPRECATED_OPTION) { // May be a deprecated option, retrieve the new value in compatible options String[] compatibleOptions = (String[]) this.deprecatedOptions.get(optionName); value = service.get(compatibleOptions[0], null, this.preferencesLookup); } return value==null ? null : value.trim(); } return null; } /** * Returns the value of the given option for the given Eclipse preferences. * If no value was already set, then inherits from the global options if specified. * * @param optionName The name of the option * @param inheritJavaCoreOptions Tells whether the value can be inherited from global JavaCore options * @param projectPreferences The eclipse preferences from which to get the value * @return The value of the option. May be <code>null</code> */ public String getOption(String optionName, boolean inheritJavaCoreOptions, IEclipsePreferences projectPreferences) { // Return the option value depending on its level switch (getOptionLevel(optionName)) { case VALID_OPTION: // Valid option, return the preference value String javaCoreDefault = inheritJavaCoreOptions ? JavaCore.getOption(optionName) : null; if (projectPreferences == null) return javaCoreDefault; String value = projectPreferences.get(optionName, javaCoreDefault); return value == null ? null : value.trim(); case DEPRECATED_OPTION: // Return the deprecated option value if it was already set String oldValue = projectPreferences.get(optionName, null); if (oldValue != null) { return oldValue.trim(); } // Get the new compatible value String[] compatibleOptions = (String[]) this.deprecatedOptions.get(optionName); String newDefault = inheritJavaCoreOptions ? JavaCore.getOption(compatibleOptions[0]) : null; String newValue = projectPreferences.get(compatibleOptions[0], newDefault); return newValue == null ? null : newValue.trim(); } return null; } /** * Returns whether an option name is known or not. * * @param optionName The name of the option * @return <code>true</code> when the option name is either * {@link #VALID_OPTION valid} or {@link #DEPRECATED_OPTION deprecated}, * <code>false</code> otherwise. */ public boolean knowsOption(String optionName) { boolean knownOption = this.optionNames.contains(optionName); if (!knownOption) { knownOption = this.deprecatedOptions.get(optionName) != null; } return knownOption; } /** * Returns the level of the given option. * * @param optionName The name of the option * @return The level of the option as an int which may have the following * values: * <ul> * <li>{@link #UNKNOWN_OPTION}: the given option is unknown</li> * <li>{@link #DEPRECATED_OPTION}: the given option is deprecated</li> * <li>{@link #VALID_OPTION}: the given option is valid</li> * </ul> */ public int getOptionLevel(String optionName) { if (this.optionNames.contains(optionName)) { return VALID_OPTION; } if (this.deprecatedOptions.get(optionName) != null) { return DEPRECATED_OPTION; } return UNKNOWN_OPTION; } 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) { if (DEBUG_302850) checkTaskTags("Retrieving options from optionsCache", this.optionsCache); //$NON-NLS-1$ return new Hashtable(cachedOptions); } if (DEBUG_302850) System.out.println("optionsCache was null"); //$NON-NLS-1$ if (!Platform.isRunning()) { this.optionsCache = getDefaultOptionsNoInitialization(); if (DEBUG_302850) checkTaskTags("Platform is not running", this.optionsCache); //$NON-NLS-1$ return new Hashtable(this.optionsCache); } // init Hashtable options = new Hashtable(10); IPreferencesService service = Platform.getPreferencesService(); // set options using preferences service lookup Iterator iterator = this.optionNames.iterator(); while (iterator.hasNext()) { String propertyName = (String) iterator.next(); String propertyValue = service.get(propertyName, null, this.preferencesLookup); if (propertyValue != null) { options.put(propertyName, propertyValue); } } if (DEBUG_302850) checkTaskTags("Options initialized from preferences", options); //$NON-NLS-1$ // set deprecated options using preferences service lookup Iterator deprecatedEntries = this.deprecatedOptions.entrySet().iterator(); while (deprecatedEntries.hasNext()) { Entry entry = (Entry) deprecatedEntries.next(); String propertyName = (String) entry.getKey(); String propertyValue = service.get(propertyName, null, this.preferencesLookup); if (propertyValue != null) { options.put(propertyName, propertyValue); String[] compatibleOptions = (String[]) entry.getValue(); for (int co=0, length=compatibleOptions.length; co < length; co++) { String compatibleOption = compatibleOptions[co]; if (!options.containsKey(compatibleOption)) options.put(compatibleOption, propertyValue); } } } // get encoding through resource plugin options.put(JavaCore.CORE_ENCODING, JavaCore.getEncoding()); // backward compatibility addDeprecatedOptions(options); Util.fixTaskTags(options); if (DEBUG_302850) checkTaskTags("Retrieved options from preferences", options); //$NON-NLS-1$ // store built map in cache this.optionsCache = new Hashtable(options); if (DEBUG_302850) checkTaskTags("Stored optionsCache", this.optionsCache); //$NON-NLS-1$ // return built map return options; } // debugging bug 302850: private void checkTaskTags(String msg, Hashtable someOptions) { System.out.println(msg); Object taskTags = someOptions.get(JavaCore.COMPILER_TASK_TAGS); System.out.println(" + Task tags: " + taskTags); //$NON-NLS-1$ if (taskTags == null || "".equals(taskTags)) { //$NON-NLS-1$ System.out.println(" - option names: "+this.optionNames); //$NON-NLS-1$ System.out.println(" - Call stack:"); //$NON-NLS-1$ StackTraceElement[] elements = new Exception().getStackTrace(); for (int i=0,n=elements.length; i<n; i++) { System.out.println(" + "+elements[i]); //$NON-NLS-1$ } } } // 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; // By passing a null reference path, we keep it relative to workspace root. resolvedPath = ClasspathEntry.resolveDotDot(null, 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; } public void verifyArchiveContent(IPath path) throws CoreException { if (isInvalidArchive(path)) { throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, new ZipException())); } ZipFile file = getZipFile(path); closeZipFile(file); } /** * Returns the 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 { if (isInvalidArchive(path)) throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, new ZipException())); ZipCache zipCache; ZipFile zipFile; if ((zipCache = (ZipCache)this.zipFiles.get()) != null && (zipFile = zipCache.getCache(path)) != null) { return zipFile; } File localFile = null; IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IResource file = root.findMember(path); if (file != null) { // internal resource URI location; if (file.getType() != IResource.FILE || (location = file.getLocationURI()) == null) { throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.bind(Messages.file_notFound, path.toString()), null)); } localFile = Util.toLocalFile(location, null/*no progress availaible*/); if (localFile == null) throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.bind(Messages.file_notFound, path.toString()), null)); } else { // external resource -> it is ok to use toFile() localFile= path.toFile(); } try { if (ZIP_ACCESS_VERBOSE) { System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + localFile ); //$NON-NLS-1$ //$NON-NLS-2$ } zipFile = new ZipFile(localFile); if (zipCache != null) { zipCache.setCache(path, zipFile); } return zipFile; } catch (IOException e) { addInvalidArchive(path); throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e)); } } /* * 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] = InstanceScope.INSTANCE.getNode(JavaCore.PLUGIN_ID); this.preferencesLookup[PREF_DEFAULT] = DefaultScope.INSTANCE.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] = InstanceScope.INSTANCE.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] = DefaultScope.INSTANCE.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 boolean isInvalidArchive(IPath path) { return this.invalidArchives != null && this.invalidArchives.contains(path); } public void setClasspathBeingResolved(IJavaProject project, boolean classpathIsResolved) { if (classpathIsResolved) { getClasspathBeingResolved().add(project); } else { getClasspathBeingResolved().remove(project); } } private Set loadClasspathListCache(String cacheName) { Set pathCache = new HashSet(); File cacheFile = getClasspathListFile(cacheName); DataInputStream in = null; try { in = new DataInputStream(new BufferedInputStream(new FileInputStream(cacheFile))); int size = in.readInt(); while (size-- > 0) { String path = in.readUTF(); pathCache.add(Path.fromPortableString(path)); } } catch (IOException e) { if (cacheFile.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(pathCache); } private File getClasspathListFile(String fileName) { return JavaCore.getPlugin().getStateLocation().append(fileName).toFile(); } 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; } private Set getClasspathListCache(String cacheName) throws CoreException { if (cacheName == NON_CHAINING_JARS_CACHE) return getNonChainingJarsCache(); else if (cacheName == INVALID_ARCHIVES_CACHE) return this.invalidArchives; else return null; } 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, boolean removeExtJarInfo) { synchronized(this.perProjectInfos) { // use the perProjectInfo collection as its own lock IProject project = javaProject.getProject(); PerProjectInfo info= (PerProjectInfo) this.perProjectInfos.get(project); if (info != null) { this.perProjectInfos.remove(project); if (removeExtJarInfo) { info.forgetExternalTimestampsAndIndexes(); } } } resetClasspathListCache(); } /* * 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 resetClasspathListCache() { if (this.nonChainingJars != null) this.nonChainingJars.clear(); if (this.invalidArchives != null) this.invalidArchives.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 saveClasspathListCache(String cacheName) throws CoreException { File file = getClasspathListFile(cacheName); DataOutputStream out = null; try { out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file))); Set pathCache = getClasspathListCache(cacheName); synchronized (pathCache) { out.writeInt(pathCache.size()); Iterator entries = pathCache.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 { saveProjects(getJavaModel().getJavaProjects()); // 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); } 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 and invalid jar caches on full save saveClasspathListCache(NON_CHAINING_JARS_CACHE); saveClasspathListCache(INVALID_ARCHIVES_CACHE); // 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 { names.remove(); } } } // 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 } } } /** * Store the preferences value for the given option name. * * @param optionName The name of the option * @param optionValue The value of the option. If <code>null</code>, then * the option will be removed from the preferences instead. * @param eclipsePreferences The eclipse preferences to be updated * @return <code>true</code> if the preferences have been changed, * <code>false</code> otherwise. */ public boolean storePreference(String optionName, String optionValue, IEclipsePreferences eclipsePreferences) { int optionLevel = this.getOptionLevel(optionName); if (optionLevel == UNKNOWN_OPTION) return false; // unrecognized option // Store option value switch (optionLevel) { case JavaModelManager.VALID_OPTION: if (optionValue == null) { eclipsePreferences.remove(optionName); } else { eclipsePreferences.put(optionName, optionValue); } break; case JavaModelManager.DEPRECATED_OPTION: // Try to migrate deprecated option eclipsePreferences.remove(optionName); // get rid off old preference String[] compatibleOptions = (String[]) this.deprecatedOptions.get(optionName); for (int co=0, length=compatibleOptions.length; co < length; co++) { if (optionValue == null) { eclipsePreferences.remove(compatibleOptions[co]); } else { eclipsePreferences.put(compatibleOptions[co], optionValue); } } break; default: return false; } return true; } public void setOptions(Hashtable newOptions) { if (DEBUG_302850) { System.out.println("Entering in JavaModelManager.setOptions():"); //$NON-NLS-1$ System.out.println(new CompilerOptions(newOptions).toString()); System.out.println(" - Call stack:"); //$NON-NLS-1$ StackTraceElement[] elements = new Exception().getStackTrace(); for (int i=0,n=elements.length; i<n; i++) { System.out.println(" + "+elements[i]); //$NON-NLS-1$ } } Hashtable cachedValue = newOptions == null ? null : new Hashtable(newOptions); IEclipsePreferences defaultPreferences = getDefaultPreferences(); IEclipsePreferences instancePreferences = getInstancePreferences(); if (newOptions == null){ try { instancePreferences.clear(); } catch(BackingStoreException e) { // ignore } } else { Enumeration keys = newOptions.keys(); while (keys.hasMoreElements()){ String key = (String)keys.nextElement(); int optionLevel = getOptionLevel(key); if (optionLevel == UNKNOWN_OPTION) continue; // unrecognized option if (key.equals(JavaCore.CORE_ENCODING)) { if (cachedValue != null) { cachedValue.put(key, JavaCore.getEncoding()); } continue; // skipped, contributed by resource prefs } String value = (String) newOptions.get(key); String defaultValue = defaultPreferences.get(key, null); // Store value in preferences if (defaultValue != null && defaultValue.equals(value)) { value = null; } storePreference(key, value, instancePreferences); } try { // persist options instancePreferences.flush(); } catch(BackingStoreException e) { // ignore } } // update cache Util.fixTaskTags(cachedValue); this.optionsCache = cachedValue; } 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; } }; InstanceScope.INSTANCE.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(); InstanceScope.INSTANCE.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 = InstanceScope.INSTANCE.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(); InstanceScope.INSTANCE.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(); } }