| /******************************************************************************* |
| * Copyright (c) 2000, 2018 IBM Corporation and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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) |
| * Stephan Herrmann - Contributions for |
| * Bug 346010 - [model] strange initialization dependency in OptionTests |
| * Bug 440477 - [null] Infrastructure for feeding external annotations into compilation |
| * Terry Parker <tparker@google.com> - DeltaProcessor misses state changes in archive files, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=357425 |
| * Thirumala Reddy Mutchukota <thirumala@google.com> - Contribution to bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=411423 |
| * Terry Parker <tparker@google.com> - [performance] Low hit rates in JavaModel caches - https://bugs.eclipse.org/421165 |
| * Terry Parker <tparker@google.com> - Enable the Java model caches to recover from IO errors - https://bugs.eclipse.org/455042 |
| * Gábor Kövesdán - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions |
| * Karsten Thoms - Bug 532505 - Reduce memory footprint of ClasspathAccessRule |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.core; |
| |
| import java.io.BufferedInputStream; |
| import java.io.BufferedOutputStream; |
| import java.io.DataInputStream; |
| import java.io.DataOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.StringReader; |
| import java.net.URI; |
| import java.text.MessageFormat; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Enumeration; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Hashtable; |
| import java.util.Iterator; |
| import java.util.LinkedHashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Map.Entry; |
| import java.util.Set; |
| import java.util.WeakHashMap; |
| import java.util.zip.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.IFile; |
| import org.eclipse.core.resources.IFolder; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceChangeEvent; |
| import org.eclipse.core.resources.ISaveContext; |
| import org.eclipse.core.resources.ISaveParticipant; |
| import org.eclipse.core.resources.ISavedState; |
| import org.eclipse.core.resources.IWorkspace; |
| import org.eclipse.core.resources.IWorkspaceDescription; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.IWorkspaceRunnable; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.resources.WorkspaceJob; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IConfigurationElement; |
| import org.eclipse.core.runtime.IExtension; |
| import org.eclipse.core.runtime.IExtensionPoint; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.ISafeRunnable; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.MultiStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.PerformanceStats; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.core.runtime.Plugin; |
| import org.eclipse.core.runtime.QualifiedName; |
| import org.eclipse.core.runtime.SafeRunner; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.core.runtime.content.IContentTypeManager; |
| import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent; |
| import org.eclipse.core.runtime.content.IContentTypeManager.IContentTypeChangeListener; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.core.runtime.preferences.DefaultScope; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent; |
| import org.eclipse.core.runtime.preferences.IPreferencesService; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.jdt.core.ClasspathContainerInitializer; |
| import org.eclipse.jdt.core.IAccessRule; |
| import org.eclipse.jdt.core.IClassFile; |
| import org.eclipse.jdt.core.IClasspathAttribute; |
| import org.eclipse.jdt.core.IClasspathContainer; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaModel; |
| import org.eclipse.jdt.core.IJavaModelStatus; |
| import org.eclipse.jdt.core.IJavaModelStatusConstants; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.IParent; |
| import org.eclipse.jdt.core.IProblemRequestor; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.ITypeRoot; |
| import org.eclipse.jdt.core.JavaConventions; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.WorkingCopyOwner; |
| import org.eclipse.jdt.core.compiler.CharOperation; |
| import org.eclipse.jdt.core.compiler.CompilationParticipant; |
| import org.eclipse.jdt.core.compiler.IProblem; |
| import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants; |
| import org.eclipse.jdt.internal.codeassist.CompletionEngine; |
| import org.eclipse.jdt.internal.codeassist.SelectionEngine; |
| import org.eclipse.jdt.internal.compiler.AbstractAnnotationProcessorManager; |
| import org.eclipse.jdt.internal.compiler.Compiler; |
| import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; |
| import org.eclipse.jdt.internal.compiler.env.AccessRestriction; |
| import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; |
| import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; |
| import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt; |
| import org.eclipse.jdt.internal.compiler.util.JRTUtil; |
| import org.eclipse.jdt.internal.compiler.util.ObjectVector; |
| import org.eclipse.jdt.internal.core.DeltaProcessor.RootInfo; |
| import org.eclipse.jdt.internal.core.JavaProjectElementInfo.ProjectCache; |
| import org.eclipse.jdt.internal.core.builder.JavaBuilder; |
| import org.eclipse.jdt.internal.core.dom.SourceRangeVerifier; |
| import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEventStore; |
| import org.eclipse.jdt.internal.core.hierarchy.TypeHierarchy; |
| import org.eclipse.jdt.internal.core.nd.IReader; |
| import org.eclipse.jdt.internal.core.nd.Nd; |
| import org.eclipse.jdt.internal.core.nd.db.Database; |
| import org.eclipse.jdt.internal.core.nd.indexer.Indexer; |
| import org.eclipse.jdt.internal.core.nd.java.JavaIndex; |
| import org.eclipse.jdt.internal.core.nd.java.NdResourceFile; |
| 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.IJob; |
| 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.formatter.DefaultCodeFormatter; |
| import org.eclipse.osgi.service.debug.DebugOptions; |
| import org.eclipse.osgi.service.debug.DebugOptionsListener; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.ServiceRegistration; |
| 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 ServiceRegistration<DebugOptionsListener> DEBUG_REGISTRATION; |
| private static final String NON_CHAINING_JARS_CACHE = "nonChainingJarsCache"; //$NON-NLS-1$ |
| private static final String EXTERNAL_FILES_CACHE = "externalFilesCache"; //$NON-NLS-1$ |
| private static final String ASSUMED_EXTERNAL_FILES_CACHE = "assumedExternalFilesCache"; //$NON-NLS-1$ |
| |
| public static enum ArchiveValidity { |
| BAD_FORMAT, UNABLE_TO_READ, FILE_NOT_FOUND, VALID; |
| |
| public boolean isValid() { |
| return this == VALID; |
| } |
| } |
| |
| /** |
| * Define a zip cache object. |
| */ |
| static class ZipCache { |
| private Map<Object, ZipFile> map; |
| Object owner; |
| |
| ZipCache(Object owner) { |
| this.map = new HashMap<>(); |
| this.owner = owner; |
| } |
| |
| public void flush() { |
| Thread currentThread = Thread.currentThread(); |
| Iterator<ZipFile> iterator = this.map.values().iterator(); |
| while (iterator.hasNext()) { |
| ZipFile zipFile = iterator.next(); |
| try { |
| if (JavaModelManager.ZIP_ACCESS_VERBOSE) { |
| System.out.println("(" + currentThread + ") [ZipCache[" + this.owner //$NON-NLS-1$//$NON-NLS-2$ |
| + "].flush()] Closing ZipFile on " + zipFile.getName()); //$NON-NLS-1$ |
| } |
| zipFile.close(); |
| } catch (IOException e) { |
| // problem occured closing zip file: cannot do much more |
| JavaCore.getPlugin().getLog().log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, "Error closing " + zipFile.getName(), e)); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| public ZipFile getCache(IPath path) { |
| return this.map.get(path); |
| } |
| |
| public void setCache(IPath path, ZipFile zipFile) { |
| ZipFile old = this.map.put(path, zipFile); |
| if(old != null) { |
| if (JavaModelManager.ZIP_ACCESS_VERBOSE) { |
| Thread currentThread = Thread.currentThread(); |
| System.out.println("(" + currentThread + ") [ZipCache[" + this.owner //$NON-NLS-1$//$NON-NLS-2$ |
| + "].setCache()] leaked ZipFile on " + old.getName() + " for path: " + path); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |
| } |
| /** |
| * Unique handle onto the JavaModel |
| */ |
| final JavaModel javaModel = new JavaModel(); |
| |
| /** |
| * Classpath variables pool |
| */ |
| public HashMap<String, IPath> variables = new HashMap<>(5); |
| public HashSet<String> variablesWithInitializer = new HashSet<>(5); |
| public HashMap<String, String> deprecatedVariables = new HashMap<>(5); |
| public HashSet<String> readOnlyVariables = new HashSet<>(5); |
| public HashMap<String, IPath> previousSessionVariables = new HashMap<>(5); |
| private ThreadLocal<Set<String>> variableInitializationInProgress = new ThreadLocal<>(); |
| |
| /** |
| * Classpath containers pool |
| */ |
| public HashMap<IJavaProject, Map<IPath, IClasspathContainer>> containers = new HashMap<>(5); |
| public HashMap<IJavaProject, Map<IPath, IClasspathContainer>> previousSessionContainers = new HashMap<>(5); |
| private ThreadLocal<Map<IJavaProject, Set<IPath>>> containerInitializationInProgress = new ThreadLocal<>(); |
| ThreadLocal<Map<IJavaProject, Map<IPath, IClasspathContainer>>> 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 Object batchContainerInitializationsLock = new Object(); |
| |
| public BatchInitializationMonitor batchContainerInitializationsProgress = new BatchInitializationMonitor(); |
| public Hashtable<String, ClasspathContainerInitializer> containerInitializersCache = new Hashtable<>(5); |
| |
| /* |
| * A HashSet that contains the IJavaProject whose classpath is being resolved. |
| */ |
| private ThreadLocal<Set<IJavaProject>> 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<IPath, String> 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$ |
| |
| /** |
| * Name of the JVM parameter to specify how many compilation units must be handled at once by the builder. |
| * The default value is represented by <code>AbstractImageBuilder#MAX_AT_ONCE</code>. |
| */ |
| public static final String MAX_COMPILED_UNITS_AT_ONCE = "maxCompiledUnitsAtOnce"; //$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() { |
| @Override |
| public IClasspathEntry[] getClasspathEntries() { return null; } |
| @Override |
| public String getDescription() { return "Container Initialization In Progress"; } //$NON-NLS-1$ |
| @Override |
| public int getKind() { return 0; } |
| @Override |
| public IPath getPath() { return null; } |
| @Override |
| public String toString() { return getDescription(); } |
| }; |
| |
| private static final String DEBUG = JavaCore.PLUGIN_ID + "/debug"; //$NON-NLS-1$ |
| 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_CLASSPATH = JavaCore.PLUGIN_ID + "/debug/javamodel/classpath" ; //$NON-NLS-1$ |
| private static final String JAVAMODEL_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel" ; //$NON-NLS-1$ |
| private static final String JAVAMODEL_INVALID_ARCHIVES = JavaCore.PLUGIN_ID + "/debug/javamodel/invalid_archives" ; //$NON-NLS-1$ |
| private static final String JAVAMODELCACHE_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel/cache" ; //$NON-NLS-1$ |
| private static final String JAVAMODELCACHE_INSERTIONS_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel/insertions" ; //$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 DOM_AST_DEBUG = JavaCore.PLUGIN_ID + "/debug/dom/ast" ; //$NON-NLS-1$ |
| private static final String DOM_AST_DEBUG_THROW = JavaCore.PLUGIN_ID + "/debug/dom/ast/throw" ; //$NON-NLS-1$ |
| private static final String DOM_REWRITE_DEBUG = JavaCore.PLUGIN_ID + "/debug/dom/rewrite" ; //$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$ |
| private static final String INDEX_DEBUG_LARGE_CHUNKS = JavaCore.PLUGIN_ID + "/debug/index/freespacetest" ; //$NON-NLS-1$ |
| private static final String INDEX_DEBUG_PAGE_CACHE = JavaCore.PLUGIN_ID + "/debug/index/pagecache" ; //$NON-NLS-1$ |
| private static final String INDEX_INDEXER_DEBUG = JavaCore.PLUGIN_ID + "/debug/index/indexer" ; //$NON-NLS-1$ |
| private static final String INDEX_INDEXER_INSERTIONS = JavaCore.PLUGIN_ID + "/debug/index/insertions" ; //$NON-NLS-1$ |
| private static final String INDEX_INDEXER_SCHEDULING = JavaCore.PLUGIN_ID + "/debug/index/scheduling" ; //$NON-NLS-1$ |
| private static final String INDEX_INDEXER_SELFTEST = JavaCore.PLUGIN_ID + "/debug/index/selftest" ; //$NON-NLS-1$ |
| private static final String INDEX_LOCKS_DEBUG = JavaCore.PLUGIN_ID + "/debug/index/locks" ; //$NON-NLS-1$ |
| private static final String INDEX_INDEXER_SPACE = JavaCore.PLUGIN_ID + "/debug/index/space" ; //$NON-NLS-1$ |
| private static final String INDEX_INDEXER_TIMING = JavaCore.PLUGIN_ID + "/debug/index/timing" ; //$NON-NLS-1$ |
| private static final String INDEX_INDEXER_LOG_SIZE_MEGS = JavaCore.PLUGIN_ID + "/debug/index/logsizemegs"; //$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$ |
| |
| 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<String> optionNames = new HashSet<>(20); |
| Map<String, String[]> deprecatedOptions = new HashMap<>(); |
| Hashtable<String, String> 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 = 11 ; // 1.1 to 1.8 and 9, 10 |
| |
| /* |
| * 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<String> 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() { |
| @Override |
| public void handleException(Throwable exception) { |
| Util.log(exception, "Exception occurred while creating compilation participant"); //$NON-NLS-1$ |
| } |
| @Override |
| 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<String> 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<IConfigurationElement> modifyingEnv = new ArrayList<>(); |
| final ArrayList<IConfigurationElement> creatingProblems = new ArrayList<>(); |
| final ArrayList<IConfigurationElement> 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 |
| * 1.8 -> 7 |
| * 9 -> 8 |
| * null -> 0 |
| */ |
| private int indexForSourceLevel(String sourceLevel) { |
| if (sourceLevel == null) return 0; |
| int majVersion = (int) (CompilerOptions.versionToJdkLevel(sourceLevel) >>> 16); |
| if (majVersion > ClassFileConstants.MAJOR_VERSION_1_2) { |
| return (majVersion - ClassFileConstants.MAJOR_VERSION_1_1); |
| } |
| // all other cases including ClassFileConstants.MAJOR_VERSION_1_1 |
| return 0; |
| } |
| |
| private int sortParticipants(ArrayList<IConfigurationElement> group, IConfigurationElement[] configElements, int index) { |
| int size = group.size(); |
| if (size == 0) return index; |
| Object[] elements = group.toArray(); |
| Util.sort(elements, new Util.Comparer() { |
| @Override |
| 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<Boolean> 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<IPath, IClasspathContainer> projectContainers = this.containers.get(project); |
| if (projectContainers == null){ |
| return null; |
| } |
| IClasspathContainer container = projectContainers.get(containerPath); |
| return container; |
| } |
| |
| synchronized boolean containerIsSet(IJavaProject project, IPath containerPath) { |
| Map<IPath, IClasspathContainer> projectContainers = this.containers.get(project); |
| if (projectContainers == null){ |
| return false; |
| } |
| IClasspathContainer container = projectContainers.get(containerPath); |
| return container != null; |
| } |
| |
| public synchronized IClasspathContainer containerGetDefaultToPreviousSession(IJavaProject project, IPath containerPath) { |
| Map<IPath, IClasspathContainer> projectContainers = this.containers.get(project); |
| if (projectContainers == null) |
| return getPreviousSessionContainer(containerPath, project); |
| IClasspathContainer container = projectContainers.get(containerPath); |
| if (container == null) |
| return getPreviousSessionContainer(containerPath, project); |
| return container; |
| } |
| |
| private boolean containerIsInitializationInProgress(IJavaProject project, IPath containerPath) { |
| Map<IJavaProject, Set<IPath>> initializations = this.containerInitializationInProgress.get(); |
| if (initializations == null) |
| return false; |
| Set<IPath> projectInitializations = initializations.get(project); |
| if (projectInitializations == null) |
| return false; |
| return projectInitializations.contains(containerPath); |
| } |
| |
| private void containerAddInitializationInProgress(IJavaProject project, IPath containerPath) { |
| Map<IJavaProject, Set<IPath>> initializations = this.containerInitializationInProgress.get(); |
| if (initializations == null) |
| this.containerInitializationInProgress.set(initializations = new HashMap<>()); |
| Set<IPath> projectInitializations = 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<IJavaProject, Map<IPath, IClasspathContainer>> perProjectContainers = this.containersBeingInitialized.get(); |
| if (perProjectContainers == null) |
| this.containersBeingInitialized.set(perProjectContainers = new HashMap<>()); |
| Map<IPath, IClasspathContainer> perPathContainers = perProjectContainers.get(project); |
| if (perPathContainers == null) |
| perProjectContainers.put(project, perPathContainers = new HashMap<>()); |
| perPathContainers.put(containerPath, container); |
| } |
| |
| public IClasspathContainer containerBeingInitializedGet(IJavaProject project, IPath containerPath) { |
| Map<IJavaProject, Map<IPath, IClasspathContainer>> perProjectContainers = this.containersBeingInitialized.get(); |
| if (perProjectContainers == null) |
| return null; |
| Map<IPath, IClasspathContainer> perPathContainers = perProjectContainers.get(project); |
| if (perPathContainers == null) |
| return null; |
| return perPathContainers.get(containerPath); |
| } |
| |
| public IClasspathContainer containerBeingInitializedRemove(IJavaProject project, IPath containerPath) { |
| Map<IJavaProject, Map<IPath, IClasspathContainer>> perProjectContainers = this.containersBeingInitialized.get(); |
| if (perProjectContainers == null) |
| return null; |
| Map<IPath, IClasspathContainer> perPathContainers = perProjectContainers.get(project); |
| if (perPathContainers == null) |
| return null; |
| IClasspathContainer container = perPathContainers.remove(containerPath); |
| if (perPathContainers.size() == 0) |
| perProjectContainers.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<IPath, IClasspathContainer> projectContainers = 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<IPath, IClasspathContainer> previousContainers = 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<IJavaProject, Set<IPath>> initializations = 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(){ |
| @Override |
| 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(){ |
| @Override |
| 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(){ |
| @Override |
| 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(){ |
| @Override |
| 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$ |
| ); |
| } |
| |
| void containerRemoveInitializationInProgress(IJavaProject project, IPath containerPath) { |
| Map<IJavaProject, Set<IPath>> initializations = this.containerInitializationInProgress.get(); |
| if (initializations == null) |
| return; |
| Set<IPath> projectInitializations = 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<Map<IPath, IClasspathContainer>> projectIterator = this.containers.values().iterator(); |
| while (projectIterator.hasNext()){ |
| Map<IPath, IClasspathContainer> projectContainers = projectIterator.next(); |
| if (projectContainers != null){ |
| Iterator<IPath> containerIterator = projectContainers.keySet().iterator(); |
| while (containerIterator.hasNext()){ |
| IPath containerPath = 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); |
| } |
| String fileName = file.getName(); |
| if (TypeConstants.MODULE_INFO_CLASS_NAME_STRING.equals(fileName)) |
| return pkg.getModularClassFile(); |
| 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<HashMap<IJavaElement, Object>> temporaryCache = new ThreadLocal<>(); |
| |
| /** |
| * Set of elements which are out of sync with their buffers. |
| */ |
| protected HashSet<Openable> 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<IProject, PerProjectInfo> 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 HashMap<WorkingCopyOwner, Map<CompilationUnit, PerWorkingCopyInfo>> perWorkingCopyInfos = new HashMap<>(5); |
| |
| /** |
| * A weak set of the known search scopes. |
| */ |
| protected WeakHashMap<AbstractSearchScope, ?> 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<IPath, IClasspathEntry> rootPathToRawEntries; // reverse map from a package fragment root's path to the raw entry |
| public Map<IPath, IClasspathEntry> rootPathToResolvedEntries; // map from a package fragment root's path to the resolved entry |
| public IPath outputLocation; |
| public Map<IPath, ObjectVector> jrtRoots; // A map between a JRT file system (as a string) and the package fragment roots found in it. |
| |
| public IEclipsePreferences preferences; |
| public Hashtable<String, String> options; |
| public Hashtable<String, Map<String, IType>> secondaryTypes; |
| /** |
| * The temporary structure used while indexing, previously known as INDEXED_SECONDARY_TYPES entry |
| */ |
| volatile Map<IFile, Map<String, Map<String, IType>>> indexingSecondaryCache; |
| |
| |
| // NB: PackageFragment#getAttachedJavadoc uses this map differently |
| // and stores String data, not JavadocContents as values |
| public LRUCache<IJavaElement, Object> 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; |
| Hashtable<IPath, Long> externalTimeStamps = manager.deltaState.getExternalLibTimeStamps(); |
| Map<IPath, List<RootInfo>> 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<IPath, Long> 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, Long.valueOf(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<IPath, IClasspathEntry> newRootPathToRawEntries, Map<IPath, IClasspathEntry> newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus, boolean addClasspathChange) { |
| if (DEBUG_CLASSPATH) { |
| System.out.println("Setting resolved classpath for " + this.project.getFullPath()); //$NON-NLS-1$ |
| if (newResolvedClasspath == null) { |
| System.out.println("New classpath = null"); //$NON-NLS-1$ |
| } else { |
| for (IClasspathEntry next : newResolvedClasspath) { |
| System.out.println(" " + next); //$NON-NLS-1$ |
| } |
| } |
| } |
| 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<IPath, IClasspathEntry> newRootPathToRawEntries, Map<IPath, IClasspathEntry> 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<IPath, IClasspathEntry> newRootPathToRawEntries, Map<IPath, IClasspathEntry> 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); |
| } |
| |
| public synchronized void setJrtPackageRoots(IPath jrtPath, ObjectVector roots) { |
| if (this.jrtRoots == null) this.jrtRoots = new HashMap<>(); |
| this.jrtRoots.put(jrtPath, roots); |
| } |
| |
| /** |
| * 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; |
| } |
| |
| @Override |
| 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; |
| } |
| @Override |
| public void acceptProblem(IProblem problem) { |
| IProblemRequestor requestor = getProblemRequestor(); |
| if (requestor == null) return; |
| requestor.acceptProblem(problem); |
| } |
| @Override |
| public void beginReporting() { |
| IProblemRequestor requestor = getProblemRequestor(); |
| if (requestor == null) return; |
| requestor.beginReporting(); |
| } |
| @Override |
| 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; |
| } |
| @Override |
| public boolean isActive() { |
| IProblemRequestor requestor = getProblemRequestor(); |
| return requestor != null && requestor.isActive(); |
| } |
| @Override |
| 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 DEBUG_CLASSPATH = false; |
| public static boolean DEBUG_INVALID_ARCHIVES = 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; |
| public static boolean JRT_ACCESS_VERBOSE = false; |
| |
| /** |
| * A cache of opened zip files per thread. |
| * (for a given thread, the object value is a HashMap from IPath to java.io.ZipFile) |
| */ |
| private ThreadLocal<ZipCache> zipFiles = new ThreadLocal<>(); |
| |
| private UserLibraryManager userLibraryManager; |
| |
| private ModuleSourcePathManager modulePathManager; |
| /* |
| * A set of IPaths for jars that are known to not contain a chaining (through MANIFEST.MF) to another library |
| */ |
| private Set<IPath> nonChainingJars; |
| |
| // The amount of time from when an invalid archive is first sensed until that state is considered stale. |
| private static long INVALID_ARCHIVE_TTL_MILLISECONDS = 2 * 60 * 1000; |
| |
| private static class InvalidArchiveInfo { |
| /** |
| * Time at which this entry will be removed from the invalid archive list. |
| */ |
| final long evictionTimestamp; |
| |
| /** |
| * Reason the entry was added to the invalid archive list. |
| */ |
| final ArchiveValidity reason; |
| |
| InvalidArchiveInfo(long evictionTimestamp, ArchiveValidity reason) { |
| this.evictionTimestamp = evictionTimestamp; |
| this.reason = reason; |
| } |
| } |
| |
| /* |
| * A map of IPaths for jars that are known to be invalid (such as not being in a valid/known format), to an eviction timestamp. |
| * Synchronize on invalidArchivesMutex before accessing. |
| */ |
| private final Map<IPath, InvalidArchiveInfo> invalidArchives = new HashMap<>(); |
| private final Object invalidArchivesMutex = new Object(); |
| |
| /* |
| * A set of IPaths for files that are known to be external to the workspace. |
| * Need not be referenced by the classpath. |
| */ |
| private Set<IPath> externalFiles; |
| |
| /* |
| * A set of IPaths for files that do not exist on the file system but are assumed to be |
| * external archives (rather than external folders). |
| */ |
| private Set<IPath> assumedExternalFiles; |
| |
| /** |
| * 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) |
| */ |
| @Override |
| 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) || |
| propertyName.equals(JavaCore.CORE_MAIN_ONLY_PROJECT_HAS_TEST_ONLY_DEPENDENCY) || |
| propertyName.equals(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM) || |
| propertyName.equals(JavaCore.CORE_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE)) { |
| JavaModelManager manager = JavaModelManager.getJavaModelManager(); |
| IJavaModel model = manager.getJavaModel(); |
| IJavaProject[] jProjects; |
| try { |
| jProjects = model.getJavaProjects(); |
| IProject[] projects = new IProject[jProjects.length]; |
| for (int i = 0, pl = jProjects.length; i < pl; i++) { |
| JavaProject javaProject = (JavaProject) jProjects[i]; |
| projects[i] = javaProject.getProject(); |
| manager.deltaState.addClasspathValidation(javaProject); |
| } |
| manager.touchProjects(projects, null); |
| } 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() { |
| @Override |
| public void added(IEclipsePreferences.NodeChangeEvent event) { |
| // do nothing |
| } |
| @Override |
| 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() { |
| @Override |
| public void added(IEclipsePreferences.NodeChangeEvent event) { |
| // do nothing |
| } |
| @Override |
| 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.externalFiles = loadClasspathListCache(EXTERNAL_FILES_CACHE); |
| this.assumedExternalFiles = loadClasspathListCache(ASSUMED_EXTERNAL_FILES_CACHE); |
| String includeContainerReferencedLib = System.getProperty(RESOLVE_REFERENCED_LIBRARIES_FOR_CONTAINERS); |
| this.resolveReferencedLibrariesForContainers = TRUE.equalsIgnoreCase(includeContainerReferencedLib); |
| } |
| } |
| |
| /** |
| * @deprecated |
| */ |
| private void addDeprecatedOptions(Hashtable<String, String> 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, ArchiveValidity reason) { |
| if (DEBUG_INVALID_ARCHIVES) { |
| System.out.println("Invalid JAR cache: adding " + path + ", reason: " + reason); //$NON-NLS-1$//$NON-NLS-2$ |
| } |
| synchronized (this.invalidArchivesMutex) { |
| this.invalidArchives.put(path, new InvalidArchiveInfo(System.currentTimeMillis() + INVALID_ARCHIVE_TTL_MILLISECONDS, reason)); |
| } |
| } |
| |
| /** |
| * Adds a path to the external files cache. It is the responsibility of callers to |
| * determine the file's existence, as determined by {@link File#isFile()}. |
| */ |
| public void addExternalFile(IPath path) { |
| // unlikely to be null |
| if (this.externalFiles == null) { |
| this.externalFiles = Collections.synchronizedSet(new HashSet<IPath>()); |
| } |
| if(this.externalFiles != null) { |
| this.externalFiles.add(path); |
| } |
| } |
| |
| /** |
| * Starts caching ZipFiles. |
| * Ignores if there are already clients. |
| */ |
| public void cacheZipFiles(Object owner) { |
| ZipCache zipCache = this.zipFiles.get(); |
| if (zipCache != null) { |
| return; |
| } |
| // the owner will be responsible for flushing the cache |
| this.zipFiles.set(new ZipCache(owner)); |
| } |
| |
| public void closeZipFile(ZipFile zipFile) { |
| if (zipFile == null) return; |
| if (this.zipFiles.get() != null) { |
| if (JavaModelManager.ZIP_ACCESS_VERBOSE) { |
| System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.closeZipFile(ZipFile)] NOT closed ZipFile (cache exist!) on " +zipFile.getName()); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| 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 |
| JavaCore.getPlugin().getLog().log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, "Error closing " + zipFile.getName(), e)); //$NON-NLS-1$ |
| } |
| } |
| |
| public static void registerDebugOptionsListener(BundleContext context) { |
| // register debug options listener |
| Hashtable<String, String> properties = new Hashtable<>(2); |
| properties.put(DebugOptions.LISTENER_SYMBOLICNAME, JavaCore.PLUGIN_ID); |
| DEBUG_REGISTRATION = context.registerService(DebugOptionsListener.class, new DebugOptionsListener() { |
| @Override |
| public void optionsChanged(DebugOptions options) { |
| boolean debug = options.getBooleanOption(DEBUG, false); |
| BufferManager.VERBOSE = debug && options.getBooleanOption(BUFFER_MANAGER_DEBUG, false); |
| JavaBuilder.DEBUG = debug && options.getBooleanOption(BUILDER_DEBUG, false); |
| Compiler.DEBUG = debug && options.getBooleanOption(COMPILER_DEBUG, false); |
| JavaBuilder.SHOW_STATS = debug && options.getBooleanOption(BUILDER_STATS_DEBUG, false); |
| CompletionEngine.DEBUG = debug && options.getBooleanOption(COMPLETION_DEBUG, false); |
| JavaModelManager.CP_RESOLVE_VERBOSE = debug && options.getBooleanOption(CP_RESOLVE_DEBUG, false); |
| JavaModelManager.CP_RESOLVE_VERBOSE_ADVANCED = debug && options.getBooleanOption(CP_RESOLVE_ADVANCED_DEBUG, false); |
| JavaModelManager.CP_RESOLVE_VERBOSE_FAILURE = debug && options.getBooleanOption(CP_RESOLVE_FAILURE_DEBUG, false); |
| DeltaProcessor.DEBUG = debug && options.getBooleanOption(DELTA_DEBUG, false); |
| DeltaProcessor.VERBOSE = debug && options.getBooleanOption(DELTA_DEBUG_VERBOSE, false); |
| SourceRangeVerifier.DEBUG = debug && options.getBooleanOption(DOM_AST_DEBUG, false); |
| SourceRangeVerifier.DEBUG_THROW = debug && options.getBooleanOption(DOM_AST_DEBUG_THROW, false); |
| SourceRangeVerifier.DEBUG |= SourceRangeVerifier.DEBUG_THROW; |
| RewriteEventStore.DEBUG = debug && options.getBooleanOption(DOM_REWRITE_DEBUG, false); |
| TypeHierarchy.DEBUG = debug && options.getBooleanOption(HIERARCHY_DEBUG, false); |
| JobManager.VERBOSE = debug && options.getBooleanOption(INDEX_MANAGER_DEBUG, false); |
| IndexManager.DEBUG = debug && options.getBooleanOption(INDEX_MANAGER_ADVANCED_DEBUG, false); |
| JavaModelManager.DEBUG_CLASSPATH = debug && options.getBooleanOption(JAVAMODEL_CLASSPATH, false); |
| JavaModelManager.DEBUG_INVALID_ARCHIVES = debug && options.getBooleanOption(JAVAMODEL_INVALID_ARCHIVES, false); |
| JavaModelManager.VERBOSE = debug && options.getBooleanOption(JAVAMODEL_DEBUG, false); |
| JavaModelCache.VERBOSE = debug && options.getBooleanOption(JAVAMODELCACHE_DEBUG, false); |
| JavaModelCache.DEBUG_CACHE_INSERTIONS = debug && options.getBooleanOption(JAVAMODELCACHE_INSERTIONS_DEBUG, false); |
| JavaModelOperation.POST_ACTION_VERBOSE = debug && options.getBooleanOption(POST_ACTION_DEBUG, false); |
| NameLookup.VERBOSE = debug && options.getBooleanOption(RESOLUTION_DEBUG, false); |
| BasicSearchEngine.VERBOSE = debug && options.getBooleanOption(SEARCH_DEBUG, false); |
| SelectionEngine.DEBUG = debug && options.getBooleanOption(SELECTION_DEBUG, false); |
| JavaModelManager.ZIP_ACCESS_VERBOSE = debug && options.getBooleanOption(ZIP_ACCESS_DEBUG, false); |
| SourceMapper.VERBOSE = debug && options.getBooleanOption(SOURCE_MAPPER_DEBUG_VERBOSE, false); |
| DefaultCodeFormatter.DEBUG = debug && options.getBooleanOption(FORMATTER_DEBUG, false); |
| Database.DEBUG_FREE_SPACE = debug && options.getBooleanOption(INDEX_DEBUG_LARGE_CHUNKS, false); |
| Database.DEBUG_PAGE_CACHE = debug && options.getBooleanOption(INDEX_DEBUG_PAGE_CACHE, false); |
| Indexer.DEBUG = debug && options.getBooleanOption(INDEX_INDEXER_DEBUG, false); |
| Indexer.DEBUG_INSERTIONS = debug && options.getBooleanOption(INDEX_INDEXER_INSERTIONS, false); |
| Indexer.DEBUG_ALLOCATIONS = debug && options.getBooleanOption(INDEX_INDEXER_SPACE, false); |
| Indexer.DEBUG_TIMING = debug && options.getBooleanOption(INDEX_INDEXER_TIMING, false); |
| Indexer.DEBUG_SCHEDULING = debug && options.getBooleanOption(INDEX_INDEXER_SCHEDULING, false); |
| Indexer.DEBUG_SELFTEST = debug && options.getBooleanOption(INDEX_INDEXER_SELFTEST, false); |
| Indexer.DEBUG_LOG_SIZE_MB = debug ? options.getIntegerOption(INDEX_INDEXER_LOG_SIZE_MEGS, 0) : 0; |
| Nd.sDEBUG_LOCKS = debug && options.getBooleanOption(INDEX_LOCKS_DEBUG, false); |
| |
| // 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); |
| } |
| } |
| }, properties); |
| } |
| |
| public static void unregisterDebugOptionsListener() { |
| // unregister debug options listener |
| DEBUG_REGISTRATION.unregister(); |
| DEBUG_REGISTRATION = null; |
| } |
| |
| /* |
| * 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() { |
| @Override |
| public void handleException(Throwable exception) { |
| Util.log(exception, "Exception occurred while loading annotation processor manager"); //$NON-NLS-1$ |
| } |
| @Override |
| 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<CompilationUnit, PerWorkingCopyInfo> workingCopyToInfos = this.perWorkingCopyInfos.get(owner); |
| if (workingCopyToInfos == null) return -1; |
| |
| info = 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 |
| */ |
| @Override |
| 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 = this.zipFiles.get(); |
| if (zipCache == null) { |
| if (JavaModelManager.ZIP_ACCESS_VERBOSE) { |
| System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.flushZipFiles(String)] NOT found cache for " + owner); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| 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(); |
| } else { |
| if (JavaModelManager.ZIP_ACCESS_VERBOSE) { |
| System.out.println("(" + Thread.currentThread() //$NON-NLS-1$ |
| + ") [JavaModelManager.flushZipFiles(String)] NOT closed cache, wrong owner, expected: " //$NON-NLS-1$ |
| + zipCache.owner + ", got: " + owner); //$NON-NLS-1$ |
| } |
| } |
| } |
| |
| /* |
| * 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; |
| |
| LinkedHashSet<IPath> pathToReferencedEntries = new LinkedHashSet<>(referencedEntries.length); |
| for (int index = 0; index < referencedEntries.length; index++) { |
| |
| if (pathToReferencedEntries.contains(referencedEntries[index].getPath())) |
| continue; |
| |
| IClasspathEntry persistedEntry = null; |
| if ((persistedEntry = 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<Openable> 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<IJavaElement, Object> tempCache = 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<IJavaProject> getExternalWorkingCopyProjects() { |
| synchronized (this.perWorkingCopyInfos) { |
| HashSet<IJavaProject> result = null; |
| Iterator<Map<CompilationUnit, PerWorkingCopyInfo>> values = this.perWorkingCopyInfos.values().iterator(); |
| while (values.hasNext()) { |
| Map<CompilationUnit, PerWorkingCopyInfo> ownerCopies = values.next(); |
| Iterator<CompilationUnit> workingCopies = ownerCopies.keySet().iterator(); |
| while (workingCopies.hasNext()) { |
| ICompilationUnit workingCopy = 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<String, String> getDefaultOptions(){ |
| |
| Hashtable<String, String> 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<String> iterator = this.optionNames.iterator(); |
| while (iterator.hasNext()) { |
| String propertyName = 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 = 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 = 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<String, String> getOptions() { |
| |
| // return cached options if already computed |
| Hashtable<String, String> cachedOptions; // use a local variable to avoid race condition (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=256329 ) |
| if ((cachedOptions = this.optionsCache) != null) { |
| return new Hashtable<>(cachedOptions); |
| } |
| if (!Platform.isRunning()) { |
| this.optionsCache = getDefaultOptionsNoInitialization(); |
| return new Hashtable<>(this.optionsCache); |
| } |
| // init |
| Hashtable<String, String> options = new Hashtable<>(10); |
| IPreferencesService service = Platform.getPreferencesService(); |
| |
| // set options using preferences service lookup |
| Iterator<String> iterator = this.optionNames.iterator(); |
| while (iterator.hasNext()) { |
| String propertyName = iterator.next(); |
| String propertyValue = service.get(propertyName, null, this.preferencesLookup); |
| if (propertyValue != null) { |
| options.put(propertyName, propertyValue); |
| } |
| } |
| |
| // set deprecated options using preferences service lookup |
| Iterator<Entry<String, String[]>> deprecatedEntries = this.deprecatedOptions.entrySet().iterator(); |
| while (deprecatedEntries.hasNext()) { |
| Entry<String, String[]> entry = deprecatedEntries.next(); |
| String propertyName = entry.getKey(); |
| String propertyValue = service.get(propertyName, null, this.preferencesLookup); |
| if (propertyValue != null) { |
| options.put(propertyName, propertyValue); |
| String[] compatibleOptions = entry.getValue(); |
| for (int co=0, length=compatibleOptions.length; co < length; co++) { |
| String compatibleOption = compatibleOptions[co]; |
| if (!options.containsKey(compatibleOption)) |
| options.put(compatibleOption, propertyValue); |
| } |
| } |
| } |
| |
| // get encoding through resource plugin |
| options.put(JavaCore.CORE_ENCODING, JavaCore.getEncoding()); |
| |
| // backward compatibility |
| addDeprecatedOptions(options); |
| |
| Util.fixTaskTags(options); |
| // store built map in cache |
| this.optionsCache = new Hashtable<>(options); |
| // return built map |
| return options; |
| } |
| |
| // Do not modify without modifying getDefaultOptions() |
| private Hashtable<String, String> getDefaultOptionsNoInitialization() { |
| Map<String, String> 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_MAIN_ONLY_PROJECT_HAS_TEST_ONLY_DEPENDENCY, JavaCore.ERROR); |
| defaultOptionsMap.put(JavaCore.CORE_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE, JavaCore.ERROR); |
| 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_SUBSTRING_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= 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<CompilationUnit, PerWorkingCopyInfo> workingCopyToInfos = this.perWorkingCopyInfos.get(owner); |
| if (workingCopyToInfos == null && create) { |
| workingCopyToInfos = new HashMap<>(); |
| this.perWorkingCopyInfos.put(owner, workingCopyToInfos); |
| } |
| |
| PerWorkingCopyInfo info = workingCopyToInfos == null ? null : 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<IPath, IClasspathContainer> previousContainerValues = this.previousSessionContainers.get(project); |
| if (previousContainerValues != null){ |
| IClasspathContainer previousContainer = 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 = 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<IJavaElement, Object> getTemporaryCache() { |
| HashMap<IJavaElement, Object> result = 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<String> 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<String> 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; |
| } |
| |
| public static ModuleSourcePathManager getModulePathManager() { |
| if (MANAGER.modulePathManager == null) { |
| ModuleSourcePathManager modulePathManager = new ModuleSourcePathManager(); |
| synchronized(MANAGER) { |
| if (MANAGER.modulePathManager == null) { // ensure another library manager was not set while creating the instance above |
| MANAGER.modulePathManager = modulePathManager; |
| } |
| } |
| } |
| return MANAGER.modulePathManager; |
| } |
| /* |
| * 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<CompilationUnit, PerWorkingCopyInfo> workingCopyToInfos = 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<PerWorkingCopyInfo> iterator = workingCopyToInfos.values().iterator(); |
| while(iterator.hasNext()) { |
| result[index++] = iterator.next().getWorkingCopy(); |
| } |
| return result; |
| } |
| } |
| |
| public JavaWorkspaceScope getWorkspaceScope() { |
| if (this.workspaceScope == null) { |
| this.workspaceScope = new JavaWorkspaceScope(); |
| } |
| return this.workspaceScope; |
| } |
| |
| public static boolean isJrt(IPath path) { |
| return path.toString().endsWith(JRTUtil.JRT_FS_JAR); |
| } |
| |
| public static boolean isJrt(String path) { |
| return isJrt(new Path(path)); |
| } |
| |
| public void verifyArchiveContent(IPath path) throws CoreException { |
| // TODO: we haven't finalized what path the JRT is represented by. Don't attempt to validate it. |
| if (isJrt(path)) { |
| return; |
| } |
| throwExceptionIfArchiveInvalid(path); |
| // Check if we can determine the archive's validity by examining the index |
| if (JavaIndex.isEnabled()) { |
| JavaIndex index = JavaIndex.getIndex(); |
| String location = JavaModelManager.getLocalFile(path).getAbsolutePath(); |
| try (IReader reader = index.getNd().acquireReadLock()) { |
| NdResourceFile resourceFile = index.getResourceFile(location.toCharArray()); |
| if (index.isUpToDate(resourceFile)) { |
| // We have this file in the index and the index is up-to-date, so we can determine the file's |
| // validity without touching the filesystem. |
| if (resourceFile.isCorruptedZipFile()) { |
| throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, |
| Messages.status_IOException, new ZipException())); |
| } |
| return; |
| } |
| } |
| } |
| |
| 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. The |
| * cause will be a {@link ZipException} if the file was corrupt, a |
| * {@link FileNotFoundException} if the file does not exist, or a |
| * {@link IOException} if we were unable to read the file. |
| */ |
| public ZipFile getZipFile(IPath path) throws CoreException { |
| return getZipFile(path, true); |
| } |
| |
| /** |
| * For use in the JDT unit tests only. Used for testing error handling. Causes an |
| * {@link IOException} to be thrown in {@link #getZipFile} whenever it attempts to |
| * read a zip file. |
| * |
| * @noreference This field is not intended to be referenced by clients. |
| */ |
| public static boolean throwIoExceptionsInGetZipFile = false; |
| |
| public ZipFile getZipFile(IPath path, boolean checkInvalidArchiveCache) throws CoreException { |
| if (checkInvalidArchiveCache) { |
| throwExceptionIfArchiveInvalid(path); |
| } |
| ZipCache zipCache; |
| ZipFile zipFile; |
| if ((zipCache = this.zipFiles.get()) != null |
| && (zipFile = zipCache.getCache(path)) != null) { |
| return zipFile; |
| } |
| File localFile = getLocalFile(path); |
| |
| try { |
| if (ZIP_ACCESS_VERBOSE) { |
| System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + localFile ); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| if (throwIoExceptionsInGetZipFile) { |
| throw new IOException(); |
| } |
| zipFile = new ZipFile(localFile); |
| if (zipCache != null) { |
| zipCache.setCache(path, zipFile); |
| } |
| return zipFile; |
| } catch (IOException e) { |
| ArchiveValidity reason; |
| |
| if (e instanceof ZipException) { |
| reason = ArchiveValidity.BAD_FORMAT; |
| } else if (e instanceof FileNotFoundException) { |
| reason = ArchiveValidity.FILE_NOT_FOUND; |
| } else { |
| reason = ArchiveValidity.UNABLE_TO_READ; |
| } |
| addInvalidArchive(path, reason); |
| throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e)); |
| } |
| } |
| |
| public static File getLocalFile(IPath path) throws CoreException { |
| 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(); |
| } |
| return localFile; |
| } |
| |
| private void throwExceptionIfArchiveInvalid(IPath path) throws CoreException { |
| ArchiveValidity validity = getArchiveValidity(path); |
| IOException reason; |
| switch (validity) { |
| case BAD_FORMAT: reason = new ZipException(); break; |
| case FILE_NOT_FOUND: reason = new FileNotFoundException(); break; |
| case UNABLE_TO_READ: reason = new IOException(); break; |
| default: reason = null; |
| } |
| if (reason != null) { |
| throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, reason)); |
| } |
| } |
| |
| /* |
| * 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<IJavaProject, Set<IPath>> 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()); |
| Set<IPath> paths = 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) { |
| Set<IPath> containerPaths = 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() { |
| @Override |
| public void run(IProgressMonitor monitor) throws CoreException { |
| try { |
| // Collect all containers |
| Set<Entry<IJavaProject, Set<IPath>>> entrySet = allContainerPaths.entrySet(); |
| int length = entrySet.size(); |
| if (monitor != null) |
| monitor.beginTask("", length); //$NON-NLS-1$ |
| Set<Entry<IJavaProject, Set<IPath>>> entries = new HashSet<>(entrySet); // clone as the following will have a side effect |
| for (Entry<IJavaProject, Set<IPath>> entry : entries) { |
| IJavaProject javaProject = entry.getKey(); |
| Set<IPath> pathSet = 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]; |
| synchronized(JavaModelManager.this.batchContainerInitializationsLock) { |
| if (containerIsSet(javaProject, path)) { |
| // another thread has concurrently initialized the container. |
| continue; |
| } |
| } |
| initializeContainer(javaProject, path); |
| IClasspathContainer container = containerBeingInitializedGet(javaProject, path); |
| if (container != null) { |
| synchronized(JavaModelManager.this.batchContainerInitializationsLock) { |
| if (containerIsSet(javaProject, path)) { |
| // another thread has concurrently initialized the container. |
| containerBeingInitializedRemove(javaProject, path); |
| containerRemoveInitializationInProgress(javaProject, path); |
| } else { |
| containerPut(javaProject, path, container); |
| } |
| } |
| } |
| } |
| if (monitor != null) |
| monitor.worked(1); |
| } |
| |
| // Set all containers |
| Map<IJavaProject, Map<IPath, IClasspathContainer>> perProjectContainers = JavaModelManager.this.containersBeingInitialized.get(); |
| // Note that during the operation below new containers could be added to the map, |
| // so we should loop until containersBeingInitialized will be empty |
| while (perProjectContainers != null && !perProjectContainers.isEmpty()) { |
| initKnownContainers(perProjectContainers, monitor); |
| } |
| JavaModelManager.this.containersBeingInitialized.set(null); |
| } finally { |
| if (monitor != null) |
| monitor.done(); |
| } |
| } |
| |
| private void initKnownContainers(Map<IJavaProject, Map<IPath, IClasspathContainer>> perProjectContainers, IProgressMonitor monitor) |
| throws JavaModelException { |
| Iterator<Entry<IJavaProject, Map<IPath, IClasspathContainer>>> entriesIterator = perProjectContainers.entrySet().iterator(); |
| List<SetContainerOperation> operations = new ArrayList<>(); |
| while (entriesIterator.hasNext()) { |
| Entry<IJavaProject, Map<IPath, IClasspathContainer>> entry = entriesIterator.next(); |
| IJavaProject project = entry.getKey(); |
| Map<IPath, IClasspathContainer> perPathContainers = entry.getValue(); |
| Iterator<Entry<IPath, IClasspathContainer>> containersIterator = perPathContainers.entrySet().iterator(); |
| while (containersIterator.hasNext()) { |
| Entry<IPath, IClasspathContainer> containerEntry = containersIterator.next(); |
| IPath containerPath = containerEntry.getKey(); |
| IClasspathContainer container = containerEntry.getValue(); |
| SetContainerOperation operation = new SetContainerOperation(containerPath, new IJavaProject[] {project}, new IClasspathContainer[] {container}); |
| operations.add(operation); |
| } |
| } |
| // operation.runOperation() below could put something into the map again |
| // so we clear the map to make sure we only see new content there |
| perProjectContainers.clear(); |
| for (SetContainerOperation operation : operations) { |
| operation.runOperation(monitor); |
| } |
| } |
| }; |
| 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() { |
| @Override |
| 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() { |
| @Override |
| public void added(IEclipsePreferences.NodeChangeEvent event) { |
| // do nothing |
| } |
| @Override |
| 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() { |
| @Override |
| public void added(IEclipsePreferences.NodeChangeEvent event) { |
| // do nothing |
| } |
| @Override |
| 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) { |
| return (String) this.stringSymbols.add(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; |
| */ |
| } |
| |
| void touchProjects(final IProject[] projectsToTouch, IProgressMonitor progressMonitor) throws JavaModelException { |
| WorkspaceJob touchJob = new WorkspaceJob(Messages.synchronizing_projects_job) { |
| @Override |
| public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException { |
| SubMonitor subMonitor = SubMonitor.convert(monitor, projectsToTouch.length); |
| for (IProject iProject : projectsToTouch) { |
| if (JavaBuilder.DEBUG) { |
| System.out.println("Touching project " + iProject.getName()); //$NON-NLS-1$ |
| } |
| if (iProject.isAccessible()) { |
| iProject.touch(subMonitor.split(1)); |
| } |
| } |
| return Status.OK_STATUS; |
| } |
| |
| @Override |
| public boolean belongsTo(Object family) { |
| return ResourcesPlugin.FAMILY_MANUAL_REFRESH == family; |
| } |
| }; |
| touchJob.schedule(); |
| } |
| |
| private Set<IJavaProject> getClasspathBeingResolved() { |
| Set<IJavaProject> result = 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 ArchiveValidity getArchiveValidity(IPath path) { |
| InvalidArchiveInfo invalidArchiveInfo; |
| synchronized (this.invalidArchivesMutex) { |
| invalidArchiveInfo = this.invalidArchives.get(path); |
| } |
| if (invalidArchiveInfo == null) |
| return ArchiveValidity.VALID; |
| long now = System.currentTimeMillis(); |
| |
| // If the TTL for this cache entry has expired, directly check whether the archive is still invalid. |
| // If it transitioned to being valid, remove it from the cache and force an update to project caches. |
| if (now > invalidArchiveInfo.evictionTimestamp) { |
| try { |
| getZipFile(path, false); |
| removeFromInvalidArchiveCache(path); |
| } catch (CoreException e) { |
| // Archive is still invalid, fall through to reporting it is invalid. |
| } |
| // Retry the test from the start, now that we have an up-to-date result |
| return getArchiveValidity(path); |
| } |
| return invalidArchiveInfo.reason; |
| } |
| |
| public void removeFromInvalidArchiveCache(IPath path) { |
| synchronized(this.invalidArchivesMutex) { |
| if (this.invalidArchives.remove(path) != null) { |
| if (DEBUG_INVALID_ARCHIVES) { |
| System.out.println("Invalid JAR cache: removed " + path); //$NON-NLS-1$ |
| } |
| try { |
| // Bug 455042: Force an update of the JavaProjectElementInfo project caches. |
| for (IJavaProject project : getJavaModel().getJavaProjects()) { |
| if (project.findPackageFragmentRoot(path) != null) { |
| ((JavaProject) project).resetCaches(); |
| } |
| } |
| } catch (JavaModelException e) { |
| Util.log(e, "Unable to retrieve the Java model."); //$NON-NLS-1$ |
| } |
| } |
| } |
| } |
| |
| /** |
| * Returns the cached value for whether the file referred to by <code>path</code> exists |
| * and is a file, as determined by the return value of {@link File#isFile()}. |
| */ |
| public boolean isExternalFile(IPath path) { |
| return this.externalFiles != null && this.externalFiles.contains(path); |
| } |
| |
| /** |
| * Removes the cached state of a single entry in the externalFiles cache. |
| */ |
| public void clearExternalFileState(IPath path) { |
| if (this.externalFiles != null) { |
| this.externalFiles.remove(path); |
| } |
| } |
| |
| /** |
| * Resets the entire externalFiles cache. |
| */ |
| public void resetExternalFilesCache() { |
| if (this.externalFiles != null) { |
| this.externalFiles.clear(); |
| } |
| } |
| |
| /** |
| * Returns whether the provided {@link IPath} appears to be an external file, |
| * which is true if the path does not represent an internal resource, does not |
| * exist on the file system, and does have a file extension (this is the definition |
| * provided by {@link ExternalFoldersManager#isExternalFolderPath}). |
| */ |
| public boolean isAssumedExternalFile(IPath path) { |
| if (this.assumedExternalFiles == null) { |
| return false; |
| } |
| return this.assumedExternalFiles.contains(path); |
| } |
| |
| /** |
| * Adds the provided {@link IPath} to the list of assumed external files. |
| */ |
| public void addAssumedExternalFile(IPath path) { |
| this.assumedExternalFiles.add(path); |
| } |
| |
| public void setClasspathBeingResolved(IJavaProject project, boolean classpathIsResolved) { |
| if (classpathIsResolved) { |
| getClasspathBeingResolved().add(project); |
| } else { |
| getClasspathBeingResolved().remove(project); |
| } |
| } |
| |
| private Set<IPath> loadClasspathListCache(String cacheName) { |
| Set<IPath> 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 JavaModelManager " + cacheName + " file"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } 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<IPath> 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<IPath> 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<IPath> getClasspathListCache(String cacheName) throws CoreException { |
| if (cacheName == NON_CHAINING_JARS_CACHE) |
| return getNonChainingJarsCache(); |
| else if (cacheName == EXTERNAL_FILES_CACHE) |
| return this.externalFiles; |
| else if (cacheName == ASSUMED_EXTERNAL_FILES_CACHE) |
| return this.assumedExternalFiles; |
| 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; |
| } |
| |
| @Override |
| public IClasspathEntry[] getClasspathEntries() { |
| return this.entries; |
| } |
| |
| @Override |
| public String getDescription() { |
| return "Persisted container [" + this.containerPath //$NON-NLS-1$ |
| + " for project [" + this.project.getElementName() //$NON-NLS-1$ |
| + "]]"; //$NON-NLS-1$ |
| } |
| |
| @Override |
| public int getKind() { |
| return 0; |
| } |
| |
| @Override |
| public IPath getPath() { |
| return this.containerPath; |
| } |
| |
| @Override |
| 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<String, IPath> allPaths; |
| |
| 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 getAccessRuleForProblemId(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<IPath, IClasspathContainer> oldContainers = 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 = 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<String, IPath> 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<IJavaElement, Object> tempCache = this.temporaryCache.get(); |
| if (tempCache != null) { |
| Object result = tempCache.get(element); |
| if (result != null) { |
| return result; |
| } |
| } |
| return this.cache.peekAtInfo(element); |
| } |
| |
| /** |
| * @see ISaveParticipant |
| */ |
| @Override |
| 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 if the info is not already present in the cache. |
| * If the info is already present in the cache, it depends upon the forceAdd parameter. |
| * If forceAdd is false it just returns the existing info and if true, this element and it's children are closed and then |
| * this particular info is added to the cache. |
| */ |
| protected synchronized Object putInfos(IJavaElement openedElement, Object newInfo, boolean forceAdd, Map<IJavaElement, Object> newElements) { |
| // remove existing children as the are replaced with the new children contained in newElements |
| Object existingInfo = this.cache.peekAtInfo(openedElement); |
| if (existingInfo != null && !forceAdd) { |
| // If forceAdd is false, then it could mean that the particular element |
| // wasn't in cache at that point of time, but would have got added through |
| // another thread. In that case, removing the children could remove it's own |
| // children. So, we should not remove the children but return the already existing |
| // info. |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=372687 |
| return existingInfo; |
| } |
| if (openedElement instanceof IParent) { |
| closeChildren(existingInfo); |
| } |
| |
| // Need to put any JarPackageFragmentRoot in first. |
| // This is due to the way the LRU cache flushes entries. |
| // When a JarPackageFragment is flushed from the LRU cache, the entire |
| // jar is flushed by removing the JarPackageFragmentRoot and all of its |
| // children (see ElementCache.close()). If we flush the JarPackageFragment |
| // when its JarPackageFragmentRoot is not in the cache and the root is about to be |
| // added (during the 'while' loop), we will end up in an inconsistent state. |
| // Subsequent resolution against package in the jar would fail as a result. |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=102422 |
| // (theodora) |
| for(Iterator<Entry<IJavaElement, Object>> it = newElements.entrySet().iterator(); it.hasNext(); ) { |
| Entry<IJavaElement, Object> entry = it.next(); |
| IJavaElement element = entry.getKey(); |
| if (element instanceof JarPackageFragmentRoot) { |
| JavaElementInfo info = (JavaElementInfo) entry.getValue(); |
| it.remove(); |
| this.cache.putInfo(element, info); |
| } |
| } |
| |
| Iterator<Entry<IJavaElement, Object>> iterator = newElements.entrySet().iterator(); |
| while (iterator.hasNext()) { |
| Entry<IJavaElement, Object> entry = iterator.next(); |
| this.cache.putInfo(entry.getKey(), entry.getValue()); |
| } |
| return newInfo; |
| } |
| |
| private void closeChildren(Object info) { |
| if (info instanceof JavaElementInfo) { |
| IJavaElement[] children = ((JavaElementInfo)info).getChildren(); |
| for (int i = 0, size = children.length; i < size; ++i) { |
| JavaElement child = (JavaElement) children[i]; |
| try { |
| child.close(); |
| } catch (JavaModelException e) { |
| // ignore |
| } |
| } |
| } |
| } |
| |
| /** |
| * Remember the info for the jar binary type |
| * @param info instanceof IBinaryType or {@link JavaModelCache#NON_EXISTING_JAR_TYPE_INFO} |
| */ |
| 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() { |
| @Override |
| public IClasspathEntry[] getClasspathEntries() { |
| return containerEntries; |
| } |
| @Override |
| public String getDescription() { |
| return "Persisted container ["+containerPath+" for project ["+ project.getElementName()+"]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ |
| } |
| @Override |
| public int getKind() { |
| return 0; |
| } |
| @Override |
| public IPath getPath() { |
| return containerPath; |
| } |
| @Override |
| public String toString() { |
| return getDescription(); |
| } |
| |
| }; |
| if (addToContainerValues) { |
| getJavaModelManager().containerPut(project, containerPath, container); |
| } |
| Map<IPath, IClasspathContainer> projectContainers = 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 = JavaModelCache.getElementType(element); |
| 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; |
| } |
| |
| void removeFromJarTypeCache(BinaryType type) { |
| this.cache.removeFromJarTypeCache(type); |
| } |
| |
| public void removePerProjectInfo(JavaProject javaProject, boolean removeExtJarInfo) { |
| synchronized(this.perProjectInfos) { // use the perProjectInfo collection as its own lock |
| IProject project = javaProject.getProject(); |
| PerProjectInfo info= 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= 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= 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 (DEBUG_INVALID_ARCHIVES) { |
| synchronized(this.invalidArchivesMutex) { |
| if (!this.invalidArchives.isEmpty()) { |
| System.out.println("Invalid JAR cache: clearing cache"); //$NON-NLS-1$ |
| } |
| } |
| } |
| synchronized(this.invalidArchivesMutex) { |
| this.invalidArchives.clear(); |
| } |
| if (this.externalFiles != null) |
| this.externalFiles.clear(); |
| if (this.assumedExternalFiles != null) |
| this.assumedExternalFiles.clear(); |
| } |
| |
| /* |
| * Resets the temporary cache for newly created elements to null. |
| */ |
| public void resetTemporaryCache() { |
| this.temporaryCache.set(null); |
| } |
| |
| /** |
| * @see ISaveParticipant |
| */ |
| @Override |
| 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<IPath> pathCache = getClasspathListCache(cacheName); |
| synchronized (pathCache) { |
| out.writeInt(pathCache.size()); |
| Iterator<IPath> entries = pathCache.iterator(); |
| while (entries.hasNext()) { |
| IPath path = 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<String, IPath> varsToSave = null; |
| Iterator<Entry<String, IPath>> iterator = JavaModelManager.this.variables.entrySet().iterator(); |
| IEclipsePreferences defaultPreferences = getDefaultPreferences(); |
| while (iterator.hasNext()) { |
| Entry<String, IPath> entry = iterator.next(); |
| String varName = 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<IPath, IClasspathContainer> containerMap) |
| throws IOException { |
| saveInt(containerMap.size()); |
| |
| for (Iterator<Entry<IPath, IClasspathContainer>> i = containerMap.entrySet().iterator(); i.hasNext();) { |
| Entry<IPath, IClasspathContainer> entry = i.next(); |
| IPath path = entry.getKey(); |
| IClasspathContainer container = 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<IPath, IClasspathContainer> containerMap = 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<String, IPath> map) throws IOException { |
| saveInt(map.size()); |
| |
| for (Iterator<Entry<String, IPath>> i = map.entrySet().iterator(); i.hasNext();) { |
| Entry<String, IPath> entry = i.next(); |
| String varName = entry.getKey(); |
| IPath varPath = entry.getValue(); |
| |
| saveString(varName); |
| savePath(varPath); |
| } |
| } |
| } |
| |
| private void traceVariableAndContainers(String action, long start) { |
| |
| Long delta = Long.valueOf(System.currentTimeMillis() - start); |
| Long length = Long.valueOf(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 |
| */ |
| @Override |
| 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, invalid jar and external file caches on full save |
| saveClasspathListCache(NON_CHAINING_JARS_CACHE); |
| saveClasspathListCache(EXTERNAL_FILES_CACHE); |
| saveClasspathListCache(ASSUMED_EXTERNAL_FILES_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<IStatus> vStats= null; // lazy initialized |
| ArrayList<PerProjectInfo> values = null; |
| synchronized(this.perProjectInfos) { |
| values = new ArrayList<>(this.perProjectInfos.values()); |
| } |
| Iterator<PerProjectInfo> iterator = values.iterator(); |
| while (iterator.hasNext()) { |
| try { |
| PerProjectInfo info = 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. |
| * |
| * @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 instanceof IFile) { |
| if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(path)) { |
| 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) |
| Map<IFile, Map<String, Map<String, IType>>> indexedSecondaryTypes; |
| if (projectInfo.secondaryTypes == null) { |
| projectInfo.secondaryTypes = new Hashtable<>(3); |
| indexedSecondaryTypes = new HashMap<>(3); |
| projectInfo.indexingSecondaryCache = indexedSecondaryTypes; |
| } else { |
| indexedSecondaryTypes = projectInfo.indexingSecondaryCache; |
| if (indexedSecondaryTypes == null) { |
| indexedSecondaryTypes = new HashMap<>(3); |
| projectInfo.indexingSecondaryCache = indexedSecondaryTypes; |
| } |
| } |
| // Store the secondary type in temporary cache (these are just handles => no problem to create it now...) |
| Map<String, Map<String, IType>> allTypes = indexedSecondaryTypes.get(resource); |
| if (allTypes == null) { |
| allTypes = new HashMap<>(3); |
| indexedSecondaryTypes.put((IFile) 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(); |
| Map<String, IType> packageTypes = 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<Entry<IFile, Map<String, Map<String, IType>>>> entries = indexedSecondaryTypes.entrySet().iterator(); |
| while (entries.hasNext()) { |
| Entry<IFile, Map<String, Map<String, IType>>> entry = entries.next(); |
| IFile file = 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. |
| * <p> |
| * This cache is an <code>Hashtable<String, HashMap<String, IType>></code>: |
| * <ul> |
| * <li>key: package name |
| * <li>value: |
| * <ul> |
| * <li>key: type name |
| * <li>value: java model handle for the secondary type |
| * </ul> |
| * </ul> |
| * Hashtable was used to protect callers from possible concurrent access. |
| * </p> |
| * Note, 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<String, Map<String, IType>> 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<IFile, Map<String, Map<String, IType>>> indexingSecondaryCache = projectInfo.secondaryTypes == null ? null : projectInfo.indexingSecondaryCache; |
| 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 |
| try { |
| this.indexManager.performConcurrentJob(new IJob() { |
| @Override |
| public boolean belongsTo(String jobFamily) { |
| return true; |
| } |
| |
| @Override |
| public void cancel() { |
| // job is cancelled through progress |
| } |
| |
| @Override |
| public void ensureReadyToRun() { |
| // always ready |
| } |
| |
| @Override |
| public boolean execute(IProgressMonitor progress) { |
| return progress == null || !progress.isCanceled(); |
| } |
| |
| @Override |
| public String getJobFamily() { |
| return ""; //$NON-NLS-1$ |
| } |
| |
| }, IJob.WaitUntilReady, monitor); |
| } catch (OperationCanceledException oce) { |
| return projectInfo.secondaryTypes; |
| } |
| } |
| |
| // Indexing is finished => merge caches and return result |
| return secondaryTypesMerging(projectInfo); |
| } |
| |
| /* |
| * Return secondary types cache merged with new secondary types created while indexing |
| * Note that merge result is directly stored in given parameter map. |
| */ |
| private Map<String, Map<String, IType>> secondaryTypesMerging(PerProjectInfo projectInfo) { |
| Map<String, Map<String, IType>> secondaryTypes = projectInfo.secondaryTypes; |
| if (VERBOSE) { |
| Util.verbose("JavaModelManager.getSecondaryTypesMerged()"); //$NON-NLS-1$ |
| Util.verbose(" - current cache to merge:"); //$NON-NLS-1$ |
| Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypes.entrySet().iterator(); |
| while (entries.hasNext()) { |
| Entry<String, Map<String, IType>> entry = entries.next(); |
| String packName = 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) |
| Map<IFile, Map<String, Map<String, IType>>> indexedSecondaryTypes = projectInfo.indexingSecondaryCache; |
| projectInfo.indexingSecondaryCache = null; |
| if (indexedSecondaryTypes == null) { |
| return secondaryTypes; |
| } |
| |
| // Merge indexing cache in secondary types one |
| Iterator<Entry<IFile, Map<String, Map<String, IType>>>> entries = indexedSecondaryTypes.entrySet().iterator(); |
| while (entries.hasNext()) { |
| Entry<IFile, Map<String, Map<String, IType>>> entry = entries.next(); |
| IFile file = entry.getKey(); |
| |
| // Remove all secondary types of indexed file from cache |
| secondaryTypesRemoving(secondaryTypes, file); |
| |
| // Add all indexing file secondary types in given secondary types cache |
| Map<String, Map<String, IType>> fileSecondaryTypes = entry.getValue(); |
| Iterator<Entry<String, Map<String, IType>>> entries2 = fileSecondaryTypes.entrySet().iterator(); |
| while (entries2.hasNext()) { |
| Entry<String, Map<String, IType>> entry2 = entries2.next(); |
| String packageName = entry2.getKey(); |
| Map<String, IType> cachedTypes = secondaryTypes.get(packageName); |
| if (cachedTypes == null) { |
| secondaryTypes.put(packageName, entry2.getValue()); |
| } else { |
| Map<String, IType> types = entry2.getValue(); |
| Iterator<Entry<String, IType>> entries3 = types.entrySet().iterator(); |
| while (entries3.hasNext()) { |
| Entry<String, IType> entry3 = entries3.next(); |
| String typeName = entry3.getKey(); |
| cachedTypes.put(typeName, entry3.getValue()); |
| } |
| } |
| } |
| } |
| if (VERBOSE) { |
| Util.verbose(" - secondary types cache merged:"); //$NON-NLS-1$ |
| Iterator<Entry<String, Map<String, IType>>> entries2 = secondaryTypes.entrySet().iterator(); |
| while (entries.hasNext()) { |
| Entry<String, Map<String, IType>> entry = entries2.next(); |
| String packName = 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 static Map<String, Map<String, IType>> 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<String, Map<String, String>> secondaryTypesSearch = new Hashtable<>(3); |
| IRestrictedAccessTypeRequestor nameRequestor = new IRestrictedAccessTypeRequestor() { |
| @Override |
| 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$ |
| Map<String, String> types = secondaryTypesSearch.get(key); |
| if (types == null) types = new HashMap<>(3); |
| types.put(new String(simpleTypeName), path); |
| secondaryTypesSearch.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 |
| final Hashtable<String, Map<String, IType>> secondaryTypes = new Hashtable<>(secondaryTypesSearch.size()); |
| for (Entry<String, Map<String, String>> packageEntry : secondaryTypesSearch.entrySet()) { |
| String packageName = packageEntry.getKey(); |
| Map<String, String> types = packageEntry.getValue(); |
| Map<String, IType> tempTypes = new HashMap<>(types.size()); |
| for (Entry<String, String> entry : types.entrySet()) { |
| String typeName = entry.getKey(); |
| String path = 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); |
| tempTypes.put(typeName, type); |
| } |
| } |
| secondaryTypes.put(packageName, tempTypes); |
| } |
| |
| // Store result in per project info cache if still null or there's still an indexing cache (may have been set by another thread...) |
| if (projectInfo.secondaryTypes == null || projectInfo.indexingSecondaryCache != 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<Entry<String, Map<String, IType>>> entries = secondaryTypes.entrySet().iterator(); |
| while (entries.hasNext()) { |
| Entry<String, Map<String, IType>> entry = entries.next(); |
| String qualifiedName = 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 |
| Map<IFile, Map<String, Map<String, IType>>> indexingCache = projectInfo.indexingSecondaryCache; |
| 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.indexingSecondaryCache = new HashMap<>(); |
| } |
| return; |
| } |
| if (indexingCache != null) { |
| Set<IFile> keys = indexingCache.keySet(); |
| int filesSize = keys.size(), filesCount = 0; |
| IFile[] removed = null; |
| Iterator<IFile> cachedFiles = keys.iterator(); |
| while (cachedFiles.hasNext()) { |
| IFile cachedFile = 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(Map<String, Map<String, IType>> secondaryTypesMap, IFile file) { |
| if (VERBOSE) { |
| StringBuffer buffer = new StringBuffer("JavaModelManager.removeSecondaryTypesFromMap("); //$NON-NLS-1$ |
| Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypesMap.entrySet().iterator(); |
| while (entries.hasNext()) { |
| Entry<String, Map<String, IType>> entry = entries.next(); |
| String qualifiedName = entry.getKey(); |
| buffer.append(qualifiedName+':'+ entry.getValue()); |
| } |
| buffer.append(','); |
| buffer.append(file.getFullPath()); |
| buffer.append(')'); |
| Util.verbose(buffer.toString()); |
| } |
| Set<Entry<String, Map<String, IType>>> packageEntries = secondaryTypesMap.entrySet(); |
| int packagesSize = packageEntries.size(), removedPackagesCount = 0; |
| String[] removedPackages = null; |
| Iterator<Entry<String, Map<String, IType>>> packages = packageEntries.iterator(); |
| while (packages.hasNext()) { |
| Entry<String, Map<String, IType>> entry = packages.next(); |
| String packName = entry.getKey(); |
| Map<String, IType> types = entry.getValue(); |
| Set<Entry<String, IType>> nameEntries = types.entrySet(); |
| int namesSize = nameEntries.size(), removedNamesCount = 0; |
| String[] removedNames = null; |
| Iterator<Entry<String, IType>> names = nameEntries.iterator(); |
| while (names.hasNext()) { |
| Entry<String, IType> entry2 = names.next(); |
| String typeName = 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<Entry<String, Map<String, IType>>> entries = secondaryTypesMap.entrySet().iterator(); |
| while (entries.hasNext()) { |
| Entry<String, Map<String, IType>> entry = entries.next(); |
| String qualifiedName = 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<String, String> 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 |
| * @param otherOptions more options being stored, used to avoid conflict between deprecated option and its compatible |
| * @return <code>true</code> if the preferences have been changed, |
| * <code>false</code> otherwise. |
| */ |
| public boolean storePreference(String optionName, String optionValue, IEclipsePreferences eclipsePreferences, Map<String, String> otherOptions) { |
| 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 = this.deprecatedOptions.get(optionName); |
| for (int co=0, length=compatibleOptions.length; co < length; co++) { |
| if (otherOptions != null && otherOptions.containsKey(compatibleOptions[co])) |
| continue; // don't overwrite explicit value of otherOptions at compatibleOptions[co] |
| if (optionValue == null) { |
| eclipsePreferences.remove(compatibleOptions[co]); |
| } else { |
| eclipsePreferences.put(compatibleOptions[co], optionValue); |
| } |
| } |
| break; |
| default: |
| return false; |
| } |
| return true; |
| } |
| |
| public void setOptions(Hashtable<String, String> newOptions) { |
| Hashtable<String, String> 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<String> keys = newOptions.keys(); |
| while (keys.hasMoreElements()){ |
| String key = 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 = newOptions.get(key); |
| String defaultValue = defaultPreferences.get(key, null); |
| // Store value in preferences |
| if (defaultValue != null && defaultValue.equals(value)) { |
| value = null; |
| } |
| storePreference(key, value, instancePreferences, newOptions); |
| } |
| try { |
| // persist options |
| instancePreferences.flush(); |
| } catch(BackingStoreException e) { |
| // ignore |
| } |
| } |
| // update cache |
| Util.fixTaskTags(cachedValue); |
| this.optionsCache = cachedValue; |
| } |
| |
| public void startup() throws CoreException { |
| try { |
| // 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() { |
| @Override |
| 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() { |
| @Override |
| 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); |
| |
| Indexer.getInstance().addListener(this.deltaState); |
| |
| // listen to resource changes affecting external annotations |
| ExternalAnnotationTracker.start(workspace); |
| |
| 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) { |
| @Override |
| 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() { |
| @Override |
| 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) { |
| try { |
| shutdown(); |
| } catch (RuntimeException e2) { |
| e.addSuppressed(e2); |
| } |
| 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); |
| |
| ExternalAnnotationTracker.shutdown(workspace); |
| |
| // Stop listening to content-type changes |
| IContentTypeManager contentTypeManager = Platform.getContentTypeManager(); |
| if (contentTypeManager != null) { |
| contentTypeManager.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 |
| Set<String> initializations = variableInitializationInProgress(); |
| if (initializations.contains(variableName)) { |
| return VARIABLE_INITIALIZATION_IN_PROGRESS; |
| } |
| return this.variables.get(variableName); |
| } |
| |
| private synchronized IPath variableGetDefaultToPreviousSession(String variableName){ |
| IPath variablePath = 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 Set<String> variableInitializationInProgress() { |
| Set<String> initializations = 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<String> vars = this.variables.keySet().iterator(); |
| int index = 0; |
| while (vars.hasNext()) { |
| result[index++] = vars.next(); |
| } |
| return result; |
| } |
| |
| public synchronized void variablePut(String variableName, IPath variablePath){ |
| |
| // set/unset the initialization in progress |
| Set<String> 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; |
| } |
| |
| @Override |
| 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 ElementCache<ITypeRoot>.Stats debugNewOpenableCacheStats() { |
| return this.cache.openableCache.new Stats(); |
| } |
| |
| public int getOpenableCacheSize() { |
| return this.cache.openableCache.getSpaceLimit(); |
| } |
| |
| /** |
| * Get a cached access rule, or when the cache did not contain the rule, creates a new one. |
| * |
| * @param filePattern the file pattern this access rule should match |
| * @param kind one of {@link IAccessRule#K_ACCESSIBLE}, {@link IAccessRule#K_DISCOURAGED}, |
| * or {@link IAccessRule#K_NON_ACCESSIBLE}, optionally combined with |
| * {@link IAccessRule#IGNORE_IF_BETTER} |
| * @return an access rule |
| */ |
| public IAccessRule getAccessRule(IPath filePattern, int kind) { |
| ClasspathAccessRule rule = new ClasspathAccessRule(filePattern, kind); |
| return getFromCache(rule); |
| } |
| |
| /** |
| * Used only for loading rules from disk. |
| */ |
| public ClasspathAccessRule getAccessRuleForProblemId(char [] filePattern, int problemId) { |
| ClasspathAccessRule rule = new ClasspathAccessRule(filePattern, problemId); |
| return getFromCache(rule); |
| } |
| |
| private ClasspathAccessRule getFromCache(ClasspathAccessRule rule) { |
| ClasspathAccessRule cachedRule = this.cache.accessRuleCache.get(rule); |
| if (cachedRule != null) { |
| return cachedRule; |
| } |
| this.cache.accessRuleCache.put(rule, rule); |
| return rule; |
| } |
| } |