1 /*******************************************************************************
2  * Copyright (c) 2000, 2020 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Theodora Yeung (tyeung@bea.com) - ensure that JarPackageFragmentRoot make it into cache
14  *                                                           before its contents
15  *                                                           (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=102422)
16  *     Stephan Herrmann - Contributions for
17  *								Bug 346010 - [model] strange initialization dependency in OptionTests
18  *								Bug 440477 - [null] Infrastructure for feeding external annotations into compilation
19  *     Terry Parker <tparker@google.com> - DeltaProcessor misses state changes in archive files, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=357425
20  *     Thirumala Reddy Mutchukota <thirumala@google.com> - Contribution to bug: https://bugs.eclipse.org/bugs/show_bug.cgi?id=411423
21  *     Terry Parker <tparker@google.com> - [performance] Low hit rates in JavaModel caches - https://bugs.eclipse.org/421165
22  *     Terry Parker <tparker@google.com> - Enable the Java model caches to recover from IO errors - https://bugs.eclipse.org/455042
23  *     Gábor Kövesdán - Contribution for Bug 350000 - [content assist] Include non-prefix matches in auto-complete suggestions
24  *     Karsten Thoms - Bug 532505 - Reduce memory footprint of ClasspathAccessRule
25  *******************************************************************************/
26 package org.eclipse.jdt.internal.core;
27 
28 import java.io.BufferedInputStream;
29 import java.io.BufferedOutputStream;
30 import java.io.DataInputStream;
31 import java.io.DataOutputStream;
32 import java.io.File;
33 import java.io.FileInputStream;
34 import java.io.FileNotFoundException;
35 import java.io.FileOutputStream;
36 import java.io.IOException;
37 import java.io.StringReader;
38 import java.net.URI;
39 import java.text.MessageFormat;
40 import java.util.ArrayList;
41 import java.util.Collections;
42 import java.util.Enumeration;
43 import java.util.HashMap;
44 import java.util.HashSet;
45 import java.util.Hashtable;
46 import java.util.Iterator;
47 import java.util.LinkedHashSet;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.Map.Entry;
51 import java.util.Set;
52 import java.util.WeakHashMap;
53 import java.util.zip.ZipException;
54 import java.util.zip.ZipFile;
55 
56 import javax.xml.parsers.DocumentBuilder;
57 import javax.xml.parsers.DocumentBuilderFactory;
58 import javax.xml.parsers.ParserConfigurationException;
59 
60 import org.eclipse.core.resources.IFile;
61 import org.eclipse.core.resources.IFolder;
62 import org.eclipse.core.resources.IProject;
63 import org.eclipse.core.resources.IResource;
64 import org.eclipse.core.resources.IResourceChangeEvent;
65 import org.eclipse.core.resources.ISaveContext;
66 import org.eclipse.core.resources.ISaveParticipant;
67 import org.eclipse.core.resources.ISavedState;
68 import org.eclipse.core.resources.IWorkspace;
69 import org.eclipse.core.resources.IWorkspaceDescription;
70 import org.eclipse.core.resources.IWorkspaceRoot;
71 import org.eclipse.core.resources.IWorkspaceRunnable;
72 import org.eclipse.core.resources.ResourcesPlugin;
73 import org.eclipse.core.resources.WorkspaceJob;
74 import org.eclipse.core.runtime.CoreException;
75 import org.eclipse.core.runtime.IConfigurationElement;
76 import org.eclipse.core.runtime.IExtension;
77 import org.eclipse.core.runtime.IExtensionPoint;
78 import org.eclipse.core.runtime.IPath;
79 import org.eclipse.core.runtime.IProgressMonitor;
80 import org.eclipse.core.runtime.ISafeRunnable;
81 import org.eclipse.core.runtime.IStatus;
82 import org.eclipse.core.runtime.MultiStatus;
83 import org.eclipse.core.runtime.OperationCanceledException;
84 import org.eclipse.core.runtime.Path;
85 import org.eclipse.core.runtime.PerformanceStats;
86 import org.eclipse.core.runtime.Platform;
87 import org.eclipse.core.runtime.Plugin;
88 import org.eclipse.core.runtime.QualifiedName;
89 import org.eclipse.core.runtime.SafeRunner;
90 import org.eclipse.core.runtime.Status;
91 import org.eclipse.core.runtime.SubMonitor;
92 import org.eclipse.core.runtime.content.IContentTypeManager;
93 import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent;
94 import org.eclipse.core.runtime.content.IContentTypeManager.IContentTypeChangeListener;
95 import org.eclipse.core.runtime.jobs.Job;
96 import org.eclipse.core.runtime.preferences.DefaultScope;
97 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
98 import org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent;
99 import org.eclipse.core.runtime.preferences.IPreferencesService;
100 import org.eclipse.core.runtime.preferences.InstanceScope;
101 import org.eclipse.jdt.core.ClasspathContainerInitializer;
102 import org.eclipse.jdt.core.IAccessRule;
103 import org.eclipse.jdt.core.IClassFile;
104 import org.eclipse.jdt.core.IClasspathAttribute;
105 import org.eclipse.jdt.core.IClasspathContainer;
106 import org.eclipse.jdt.core.IClasspathEntry;
107 import org.eclipse.jdt.core.ICompilationUnit;
108 import org.eclipse.jdt.core.IJavaElement;
109 import org.eclipse.jdt.core.IJavaModel;
110 import org.eclipse.jdt.core.IJavaModelStatus;
111 import org.eclipse.jdt.core.IJavaModelStatusConstants;
112 import org.eclipse.jdt.core.IJavaProject;
113 import org.eclipse.jdt.core.IPackageFragment;
114 import org.eclipse.jdt.core.IPackageFragmentRoot;
115 import org.eclipse.jdt.core.IParent;
116 import org.eclipse.jdt.core.IProblemRequestor;
117 import org.eclipse.jdt.core.IType;
118 import org.eclipse.jdt.core.ITypeRoot;
119 import org.eclipse.jdt.core.JavaConventions;
120 import org.eclipse.jdt.core.JavaCore;
121 import org.eclipse.jdt.core.JavaModelException;
122 import org.eclipse.jdt.core.WorkingCopyOwner;
123 import org.eclipse.jdt.core.compiler.CharOperation;
124 import org.eclipse.jdt.core.compiler.CompilationParticipant;
125 import org.eclipse.jdt.core.compiler.IProblem;
126 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
127 import org.eclipse.jdt.internal.codeassist.CompletionEngine;
128 import org.eclipse.jdt.internal.codeassist.SelectionEngine;
129 import org.eclipse.jdt.internal.compiler.AbstractAnnotationProcessorManager;
130 import org.eclipse.jdt.internal.compiler.Compiler;
131 import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
132 import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
133 import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
134 import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
135 import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt;
136 import org.eclipse.jdt.internal.compiler.util.JRTUtil;
137 import org.eclipse.jdt.internal.compiler.util.ObjectVector;
138 import org.eclipse.jdt.internal.core.DeltaProcessor.RootInfo;
139 import org.eclipse.jdt.internal.core.JavaProjectElementInfo.ProjectCache;
140 import org.eclipse.jdt.internal.core.builder.JavaBuilder;
141 import org.eclipse.jdt.internal.core.dom.SourceRangeVerifier;
142 import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEventStore;
143 import org.eclipse.jdt.internal.core.hierarchy.TypeHierarchy;
144 import org.eclipse.jdt.internal.core.nd.IReader;
145 import org.eclipse.jdt.internal.core.nd.Nd;
146 import org.eclipse.jdt.internal.core.nd.db.Database;
147 import org.eclipse.jdt.internal.core.nd.indexer.Indexer;
148 import org.eclipse.jdt.internal.core.nd.java.JavaIndex;
149 import org.eclipse.jdt.internal.core.nd.java.NdResourceFile;
150 import org.eclipse.jdt.internal.core.search.AbstractSearchScope;
151 import org.eclipse.jdt.internal.core.search.BasicSearchEngine;
152 import org.eclipse.jdt.internal.core.search.IRestrictedAccessTypeRequestor;
153 import org.eclipse.jdt.internal.core.search.JavaWorkspaceScope;
154 import org.eclipse.jdt.internal.core.search.indexing.IndexManager;
155 import org.eclipse.jdt.internal.core.search.processing.IJob;
156 import org.eclipse.jdt.internal.core.search.processing.JobManager;
157 import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject;
158 import org.eclipse.jdt.internal.core.util.LRUCache;
159 import org.eclipse.jdt.internal.core.util.Messages;
160 import org.eclipse.jdt.internal.core.util.Util;
161 import org.eclipse.jdt.internal.core.util.WeakHashSet;
162 import org.eclipse.jdt.internal.core.util.WeakHashSetOfCharArray;
163 import org.eclipse.jdt.internal.formatter.DefaultCodeFormatter;
164 import org.eclipse.osgi.service.debug.DebugOptions;
165 import org.eclipse.osgi.service.debug.DebugOptionsListener;
166 import org.osgi.framework.BundleContext;
167 import org.osgi.framework.ServiceRegistration;
168 import org.osgi.service.prefs.BackingStoreException;
169 import org.w3c.dom.Element;
170 import org.w3c.dom.Node;
171 import org.w3c.dom.NodeList;
172 import org.xml.sax.InputSource;
173 import org.xml.sax.SAXException;
174 
175 /**
176  * The <code>JavaModelManager</code> manages instances of <code>IJavaModel</code>.
177  * <code>IElementChangedListener</code>s register with the <code>JavaModelManager</code>,
178  * and receive <code>ElementChangedEvent</code>s for all <code>IJavaModel</code>s.
179  * <p>
180  * The single instance of <code>JavaModelManager</code> is available from
181  * the static method <code>JavaModelManager.getJavaModelManager()</code>.
182  */
183 public class JavaModelManager implements ISaveParticipant, IContentTypeChangeListener {
184 	private static ServiceRegistration<DebugOptionsListener> DEBUG_REGISTRATION;
185 	private static final String NON_CHAINING_JARS_CACHE = "nonChainingJarsCache"; //$NON-NLS-1$
186 	private static final String EXTERNAL_FILES_CACHE = "externalFilesCache";  //$NON-NLS-1$
187 	private static final String ASSUMED_EXTERNAL_FILES_CACHE = "assumedExternalFilesCache";  //$NON-NLS-1$
188 
189 	public static enum ArchiveValidity {
190 		BAD_FORMAT, UNABLE_TO_READ, FILE_NOT_FOUND, VALID;
191 
isValid()192 		public boolean isValid() {
193 			return this == VALID;
194 		}
195 	}
196 
197 	/**
198 	 * Define a zip cache object.
199 	 */
200 	static class ZipCache {
201 		private Map<Object, ZipFile> map;
202 		Object owner;
203 
ZipCache(Object owner)204 		ZipCache(Object owner) {
205 			this.map = new HashMap<>();
206 			this.owner = owner;
207 		}
208 
flush()209 		public void flush() {
210 			Thread currentThread = Thread.currentThread();
211 			Iterator<ZipFile> iterator = this.map.values().iterator();
212 			while (iterator.hasNext()) {
213 				ZipFile zipFile = iterator.next();
214 				try {
215 					if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
216 						System.out.println("(" + currentThread + ") [ZipCache[" + this.owner //$NON-NLS-1$//$NON-NLS-2$
217 								+ "].flush()] Closing ZipFile on " + zipFile.getName()); //$NON-NLS-1$
218 					}
219 					zipFile.close();
220 				} catch (IOException e) {
221 					// problem occured closing zip file: cannot do much more
222 					JavaCore.getPlugin().getLog().log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, "Error closing " + zipFile.getName(), e)); //$NON-NLS-1$
223 				}
224 			}
225 		}
226 
getCache(IPath path)227 		public ZipFile getCache(IPath path) {
228 			return this.map.get(path);
229 		}
230 
setCache(IPath path, ZipFile zipFile)231 		public void setCache(IPath path, ZipFile zipFile) {
232 			ZipFile old = this.map.put(path, zipFile);
233 			if(old != null) {
234 				if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
235 					Thread currentThread = Thread.currentThread();
236 					System.out.println("(" + currentThread + ") [ZipCache[" + this.owner //$NON-NLS-1$//$NON-NLS-2$
237 							+ "].setCache()] leaked ZipFile on " + old.getName() + " for path: " + path); //$NON-NLS-1$ //$NON-NLS-2$
238 				}
239 			}
240 		}
241 	}
242 	/**
243 	 * Unique handle onto the JavaModel
244 	 */
245 	final JavaModel javaModel = new JavaModel();
246 
247 	/**
248 	 * Classpath variables pool
249 	 */
250 	public HashMap<String, IPath> variables = new HashMap<>(5);
251 	public HashSet<String> variablesWithInitializer = new HashSet<>(5);
252 	public HashMap<String, String> deprecatedVariables = new HashMap<>(5);
253 	public HashSet<String> readOnlyVariables = new HashSet<>(5);
254 	public HashMap<String, IPath> previousSessionVariables = new HashMap<>(5);
255 	private ThreadLocal<Set<String>> variableInitializationInProgress = new ThreadLocal<>();
256 
257 	/**
258 	 * Classpath containers pool
259 	 */
260 	public HashMap<IJavaProject, Map<IPath, IClasspathContainer>> containers = new HashMap<>(5);
261 	public HashMap<IJavaProject, Map<IPath, IClasspathContainer>> previousSessionContainers = new HashMap<>(5);
262 	private ThreadLocal<Map<IJavaProject, Set<IPath>>> containerInitializationInProgress = new ThreadLocal<>();
263 	ThreadLocal<Map<IJavaProject, Map<IPath, IClasspathContainer>>> containersBeingInitialized = new ThreadLocal<>();
264 
265 	public static final int NO_BATCH_INITIALIZATION = 0;
266 	public static final int NEED_BATCH_INITIALIZATION = 1;
267 	public static final int BATCH_INITIALIZATION_IN_PROGRESS = 2;
268 	public static final int BATCH_INITIALIZATION_FINISHED = 3;
269 	public int batchContainerInitializations = NO_BATCH_INITIALIZATION;
270 	public Object batchContainerInitializationsLock = new Object();
271 
272 	public BatchInitializationMonitor batchContainerInitializationsProgress = new BatchInitializationMonitor();
273 	public Hashtable<String, ClasspathContainerInitializer> containerInitializersCache = new Hashtable<>(5);
274 
275 	/*
276 	 * A HashSet that contains the IJavaProject whose classpath is being resolved.
277 	 */
278 	private ThreadLocal<Set<IJavaProject>> classpathsBeingResolved = new ThreadLocal<>();
279 
280 	/*
281 	 * The unique workspace scope
282 	 */
283 	public JavaWorkspaceScope workspaceScope;
284 
285 	/*
286 	 * Pools of symbols used in the Java model.
287 	 * Used as a replacement for String#intern() that could prevent garbage collection of strings on some VMs.
288 	 */
289 	private WeakHashSet stringSymbols = new WeakHashSet(5);
290 	private WeakHashSetOfCharArray charArraySymbols = new WeakHashSetOfCharArray(5);
291 
292 	/*
293 	 * Extension used to construct Java 6 annotation processor managers
294 	 */
295 	private IConfigurationElement annotationProcessorManagerFactory = null;
296 
297 	/*
298 	 * Map from a package fragment root's path to a source attachment property (source path + ATTACHMENT_PROPERTY_DELIMITER + source root path)
299 	 */
300 	public Map<IPath, String> rootPathToAttachments = new Hashtable<>();
301 
302 	public final static String CP_VARIABLE_PREFERENCES_PREFIX = JavaCore.PLUGIN_ID+".classpathVariable."; //$NON-NLS-1$
303 	public final static String CP_CONTAINER_PREFERENCES_PREFIX = JavaCore.PLUGIN_ID+".classpathContainer."; //$NON-NLS-1$
304 	public final static String CP_USERLIBRARY_PREFERENCES_PREFIX = JavaCore.PLUGIN_ID+".userLibrary."; //$NON-NLS-1$
305 	public final static String CP_ENTRY_IGNORE = "##<cp entry ignore>##"; //$NON-NLS-1$
306 	public final static IPath CP_ENTRY_IGNORE_PATH = new Path(CP_ENTRY_IGNORE);
307 	public final static String TRUE = "true"; //$NON-NLS-1$
308 
309 	private final static int VARIABLES_AND_CONTAINERS_FILE_VERSION = 2;
310 
311 	/**
312 	 * Name of the extension point for contributing classpath variable initializers
313 	 */
314 	public static final String CPVARIABLE_INITIALIZER_EXTPOINT_ID = "classpathVariableInitializer" ; //$NON-NLS-1$
315 
316 	/**
317 	 * Name of the extension point for contributing classpath container initializers
318 	 */
319 	public static final String CPCONTAINER_INITIALIZER_EXTPOINT_ID = "classpathContainerInitializer" ; //$NON-NLS-1$
320 
321 	/**
322 	 * Name of the extension point for contributing a source code formatter
323 	 */
324 	public static final String FORMATTER_EXTPOINT_ID = "codeFormatter" ; //$NON-NLS-1$
325 
326 	/**
327 	 * Name of the extension point for contributing a compilation participant
328 	 */
329 	public static final String COMPILATION_PARTICIPANT_EXTPOINT_ID = "compilationParticipant" ; //$NON-NLS-1$
330 
331 	/**
332 	 * Name of the extension point for contributing the Java 6 annotation processor manager
333 	 */
334 	public static final String ANNOTATION_PROCESSOR_MANAGER_EXTPOINT_ID = "annotationProcessorManager" ;  //$NON-NLS-1$
335 
336 	/**
337 	 * Name of the JVM parameter to specify whether or not referenced JAR should be resolved for container libraries.
338 	 */
339 	private static final String RESOLVE_REFERENCED_LIBRARIES_FOR_CONTAINERS = "resolveReferencedLibrariesForContainers"; //$NON-NLS-1$
340 
341 	/**
342 	 * Name of the JVM parameter to specify how many compilation units must be handled at once by the builder.
343 	 * The default value is represented by <code>AbstractImageBuilder#MAX_AT_ONCE</code>.
344 	 */
345 	public static final String MAX_COMPILED_UNITS_AT_ONCE = "maxCompiledUnitsAtOnce"; //$NON-NLS-1$
346 
347 	/**
348 	 * Special value used for recognizing ongoing initialization and breaking initialization cycles
349 	 */
350 	public final static IPath VARIABLE_INITIALIZATION_IN_PROGRESS = new Path("Variable Initialization In Progress"); //$NON-NLS-1$
351 	public final static IClasspathContainer CONTAINER_INITIALIZATION_IN_PROGRESS = new IClasspathContainer() {
352 		@Override
353 		public IClasspathEntry[] getClasspathEntries() { return null; }
354 		@Override
355 		public String getDescription() { return "Container Initialization In Progress"; } //$NON-NLS-1$
356 		@Override
357 		public int getKind() { return 0; }
358 		@Override
359 		public IPath getPath() { return null; }
360 		@Override
361 		public String toString() { return getDescription(); }
362 	};
363 
364 	private static final String DEBUG = JavaCore.PLUGIN_ID + "/debug"; //$NON-NLS-1$
365 	private static final String BUFFER_MANAGER_DEBUG = JavaCore.PLUGIN_ID + "/debug/buffermanager" ; //$NON-NLS-1$
366 	private static final String INDEX_MANAGER_DEBUG = JavaCore.PLUGIN_ID + "/debug/indexmanager" ; //$NON-NLS-1$
367 	private static final String INDEX_MANAGER_ADVANCED_DEBUG = JavaCore.PLUGIN_ID + "/debug/indexmanager/advanced" ; //$NON-NLS-1$
368 	private static final String COMPILER_DEBUG = JavaCore.PLUGIN_ID + "/debug/compiler" ; //$NON-NLS-1$
369 	private static final String JAVAMODEL_CLASSPATH = JavaCore.PLUGIN_ID + "/debug/javamodel/classpath" ; //$NON-NLS-1$
370 	private static final String JAVAMODEL_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel" ; //$NON-NLS-1$
371 	private static final String JAVAMODEL_INVALID_ARCHIVES = JavaCore.PLUGIN_ID + "/debug/javamodel/invalid_archives" ; //$NON-NLS-1$
372 	private static final String JAVAMODELCACHE_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel/cache" ; //$NON-NLS-1$
373 	private static final String JAVAMODELCACHE_INSERTIONS_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel/insertions" ; //$NON-NLS-1$
374 	private static final String CP_RESOLVE_DEBUG = JavaCore.PLUGIN_ID + "/debug/cpresolution" ; //$NON-NLS-1$
375 	private static final String CP_RESOLVE_ADVANCED_DEBUG = JavaCore.PLUGIN_ID + "/debug/cpresolution/advanced" ; //$NON-NLS-1$
376 	private static final String CP_RESOLVE_FAILURE_DEBUG = JavaCore.PLUGIN_ID + "/debug/cpresolution/failure" ; //$NON-NLS-1$
377 	private static final String ZIP_ACCESS_DEBUG = JavaCore.PLUGIN_ID + "/debug/zipaccess" ; //$NON-NLS-1$
378 	private static final String DELTA_DEBUG =JavaCore.PLUGIN_ID + "/debug/javadelta" ; //$NON-NLS-1$
379 	private static final String DELTA_DEBUG_VERBOSE =JavaCore.PLUGIN_ID + "/debug/javadelta/verbose" ; //$NON-NLS-1$
380 	private static final String DOM_AST_DEBUG = JavaCore.PLUGIN_ID + "/debug/dom/ast" ; //$NON-NLS-1$
381 	private static final String DOM_AST_DEBUG_THROW = JavaCore.PLUGIN_ID + "/debug/dom/ast/throw" ; //$NON-NLS-1$
382 	private static final String DOM_REWRITE_DEBUG = JavaCore.PLUGIN_ID + "/debug/dom/rewrite" ; //$NON-NLS-1$
383 	private static final String HIERARCHY_DEBUG = JavaCore.PLUGIN_ID + "/debug/hierarchy" ; //$NON-NLS-1$
384 	private static final String POST_ACTION_DEBUG = JavaCore.PLUGIN_ID + "/debug/postaction" ; //$NON-NLS-1$
385 	private static final String BUILDER_DEBUG = JavaCore.PLUGIN_ID + "/debug/builder" ; //$NON-NLS-1$
386 	private static final String BUILDER_STATS_DEBUG = JavaCore.PLUGIN_ID + "/debug/builder/stats" ; //$NON-NLS-1$
387 	private static final String COMPLETION_DEBUG = JavaCore.PLUGIN_ID + "/debug/completion" ; //$NON-NLS-1$
388 	private static final String RESOLUTION_DEBUG = JavaCore.PLUGIN_ID + "/debug/resolution" ; //$NON-NLS-1$
389 	private static final String SELECTION_DEBUG = JavaCore.PLUGIN_ID + "/debug/selection" ; //$NON-NLS-1$
390 	private static final String SEARCH_DEBUG = JavaCore.PLUGIN_ID + "/debug/search" ; //$NON-NLS-1$
391 	private static final String SOURCE_MAPPER_DEBUG_VERBOSE = JavaCore.PLUGIN_ID + "/debug/sourcemapper" ; //$NON-NLS-1$
392 	private static final String FORMATTER_DEBUG = JavaCore.PLUGIN_ID + "/debug/formatter" ; //$NON-NLS-1$
393 	private static final String INDEX_DEBUG_LARGE_CHUNKS = JavaCore.PLUGIN_ID + "/debug/index/freespacetest" ; //$NON-NLS-1$
394 	private static final String INDEX_DEBUG_PAGE_CACHE = JavaCore.PLUGIN_ID + "/debug/index/pagecache" ; //$NON-NLS-1$
395 	private static final String INDEX_INDEXER_DEBUG = JavaCore.PLUGIN_ID + "/debug/index/indexer" ; //$NON-NLS-1$
396 	private static final String INDEX_INDEXER_INSERTIONS = JavaCore.PLUGIN_ID + "/debug/index/insertions" ; //$NON-NLS-1$
397 	private static final String INDEX_INDEXER_SCHEDULING = JavaCore.PLUGIN_ID + "/debug/index/scheduling" ; //$NON-NLS-1$
398 	private static final String INDEX_INDEXER_SELFTEST = JavaCore.PLUGIN_ID + "/debug/index/selftest" ; //$NON-NLS-1$
399 	private static final String INDEX_LOCKS_DEBUG = JavaCore.PLUGIN_ID + "/debug/index/locks" ; //$NON-NLS-1$
400 	private static final String INDEX_INDEXER_SPACE = JavaCore.PLUGIN_ID + "/debug/index/space" ; //$NON-NLS-1$
401 	private static final String INDEX_INDEXER_TIMING = JavaCore.PLUGIN_ID + "/debug/index/timing" ; //$NON-NLS-1$
402 	private static final String INDEX_INDEXER_LOG_SIZE_MEGS = JavaCore.PLUGIN_ID + "/debug/index/logsizemegs"; //$NON-NLS-1$
403 
404 	public static final String COMPLETION_PERF = JavaCore.PLUGIN_ID + "/perf/completion" ; //$NON-NLS-1$
405 	public static final String SELECTION_PERF = JavaCore.PLUGIN_ID + "/perf/selection" ; //$NON-NLS-1$
406 	public static final String DELTA_LISTENER_PERF = JavaCore.PLUGIN_ID + "/perf/javadeltalistener" ; //$NON-NLS-1$
407 	public static final String VARIABLE_INITIALIZER_PERF = JavaCore.PLUGIN_ID + "/perf/variableinitializer" ; //$NON-NLS-1$
408 	public static final String CONTAINER_INITIALIZER_PERF = JavaCore.PLUGIN_ID + "/perf/containerinitializer" ; //$NON-NLS-1$
409 	public static final String RECONCILE_PERF = JavaCore.PLUGIN_ID + "/perf/reconcile" ; //$NON-NLS-1$
410 
411 	public static boolean PERF_VARIABLE_INITIALIZER = false;
412 	public static boolean PERF_CONTAINER_INITIALIZER = false;
413 	// Non-static, which will give it a chance to retain the default when and if JavaModelManager is restarted.
414 	boolean resolveReferencedLibrariesForContainers = false;
415 
416 	public final static ICompilationUnit[] NO_WORKING_COPY = new ICompilationUnit[0];
417 
418 	// Options
419 	private final static int UNKNOWN_OPTION = 0;
420 	private final static int DEPRECATED_OPTION = 1;
421 	private final static int VALID_OPTION = 2;
422 	HashSet<String> optionNames = new HashSet<>(20);
423 	Map<String, String[]> deprecatedOptions = new HashMap<>();
424 	Hashtable<String, String> optionsCache;
425 
426 	// Preferences
427 	public final IEclipsePreferences[] preferencesLookup = new IEclipsePreferences[2];
428 	static final int PREF_INSTANCE = 0;
429 	static final int PREF_DEFAULT = 1;
430 
431 	static final Object[][] NO_PARTICIPANTS = new Object[0][];
432 
433 	public static class CompilationParticipants {
434 
435 		static final int MAX_SOURCE_LEVEL = JavaCore.getAllVersions().size() - 1; // All except VERSION_CLDC_1_1
436 
437 		/*
438 		 * The registered compilation participants (a table from int (source level) to Object[])
439 		 * The Object array contains first IConfigurationElements when not resolved yet, then
440 		 * it contains CompilationParticipants.
441 		 */
442 		private Object[][] registeredParticipants = null;
443 		private HashSet<String> managedMarkerTypes;
444 
getCompilationParticipants(IJavaProject project)445 		public CompilationParticipant[] getCompilationParticipants(IJavaProject project) {
446 			final Object[][] participantsPerSource = getRegisteredParticipants();
447 			if (participantsPerSource == NO_PARTICIPANTS)
448 				return null;
449 			String sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true/*inherit options*/);
450 			final int sourceLevelIndex = indexForSourceLevel(sourceLevel);
451 			final Object[] participants = participantsPerSource[sourceLevelIndex];
452 			int length = participants.length;
453 			CompilationParticipant[] result = new CompilationParticipant[length];
454 			int index = 0;
455 			for (int i = 0; i < length; i++) {
456 				if (participants[i] instanceof IConfigurationElement) {
457 					final IConfigurationElement configElement = (IConfigurationElement) participants[i];
458 					final int participantIndex = i;
459 					SafeRunner.run(new ISafeRunnable() {
460 						@Override
461 						public void handleException(Throwable exception) {
462 							Util.log(exception, "Exception occurred while creating compilation participant"); //$NON-NLS-1$
463 						}
464 						@Override
465 						public void run() throws Exception {
466 							Object executableExtension = configElement.createExecutableExtension("class"); //$NON-NLS-1$
467 							for (int j = sourceLevelIndex; j < MAX_SOURCE_LEVEL; j++)
468 								participantsPerSource[j][participantIndex] = executableExtension;
469 						}
470 					});
471 				}
472 				CompilationParticipant participant;
473 				if ((participants[i] instanceof CompilationParticipant) && (participant = (CompilationParticipant) participants[i]).isActive(project))
474 					result[index++] = participant;
475 			}
476 			if (index == 0)
477 				return null;
478 			if (index < length)
479 				System.arraycopy(result, 0, result = new CompilationParticipant[index], 0, index);
480 			return result;
481 		}
482 
managedMarkerTypes()483 		public HashSet<String> managedMarkerTypes() {
484 			if (this.managedMarkerTypes == null) {
485 				// force extension points to be read
486 				getRegisteredParticipants();
487 			}
488 			return this.managedMarkerTypes;
489 		}
490 
getRegisteredParticipants()491 		private synchronized Object[][] getRegisteredParticipants() {
492 			if (this.registeredParticipants != null) {
493 				return this.registeredParticipants;
494 			}
495 			this.managedMarkerTypes = new HashSet<>();
496 			IExtensionPoint extension = Platform.getExtensionRegistry().getExtensionPoint(JavaCore.PLUGIN_ID, COMPILATION_PARTICIPANT_EXTPOINT_ID);
497 			if (extension == null)
498 				return this.registeredParticipants = NO_PARTICIPANTS;
499 			final ArrayList<IConfigurationElement> modifyingEnv = new ArrayList<>();
500 			final ArrayList<IConfigurationElement> creatingProblems = new ArrayList<>();
501 			final ArrayList<IConfigurationElement> others = new ArrayList<>();
502 			IExtension[] extensions = extension.getExtensions();
503 			// for all extensions of this point...
504 			for(int i = 0; i < extensions.length; i++) {
505 				IConfigurationElement[] configElements = extensions[i].getConfigurationElements();
506 				// for all config elements named "compilationParticipant"
507 				for(int j = 0; j < configElements.length; j++) {
508 					final IConfigurationElement configElement = configElements[j];
509 					String elementName =configElement.getName();
510 					if (!("compilationParticipant".equals(elementName))) { //$NON-NLS-1$
511 						continue;
512 					}
513 					// add config element in the group it belongs to
514 					if (TRUE.equals(configElement.getAttribute("modifiesEnvironment"))) //$NON-NLS-1$
515 						modifyingEnv.add(configElement);
516 					else if (TRUE.equals(configElement.getAttribute("createsProblems"))) //$NON-NLS-1$
517 						creatingProblems.add(configElement);
518 					else
519 						others.add(configElement);
520 					// add managed marker types
521 					IConfigurationElement[] managedMarkers = configElement.getChildren("managedMarker"); //$NON-NLS-1$
522 					for (int k = 0, length = managedMarkers.length; k < length; k++) {
523 						IConfigurationElement element = managedMarkers[k];
524 						String markerType = element.getAttribute("markerType"); //$NON-NLS-1$
525 						if (markerType != null)
526 							this.managedMarkerTypes.add(markerType);
527 					}
528 				}
529 			}
530 			int size = modifyingEnv.size() + creatingProblems.size() + others.size();
531 			if (size == 0)
532 				return this.registeredParticipants = NO_PARTICIPANTS;
533 
534 			// sort config elements in each group
535 			IConfigurationElement[] configElements = new IConfigurationElement[size];
536 			int index = 0;
537 			index = sortParticipants(modifyingEnv, configElements, index);
538 			index = sortParticipants(creatingProblems, configElements, index);
539 			index = sortParticipants(others, configElements, index);
540 
541 			// create result table
542 			Object[][] result = new Object[MAX_SOURCE_LEVEL][];
543 			int length = configElements.length;
544 			for (int i = 0; i < MAX_SOURCE_LEVEL; i++) {
545 				result[i] = new Object[length];
546 			}
547 			for (int i = 0; i < length; i++) {
548 				String sourceLevel = configElements[i].getAttribute("requiredSourceLevel"); //$NON-NLS-1$
549 				int sourceLevelIndex = indexForSourceLevel(sourceLevel);
550 				for (int j = sourceLevelIndex; j < MAX_SOURCE_LEVEL; j++) {
551 					result[j][i] = configElements[i];
552 				}
553 			}
554 			return this.registeredParticipants = result;
555 		}
556 
557 		/*
558 		 * 1.1 -> 0
559 		 * 1.2 -> 1
560 		 * ...
561 		 * 1.6 -> 5
562 		 * 1.7 -> 6
563 		 * 1.8 -> 7
564 		 * 9 -> 8
565 		 * null -> 0
566 		 */
indexForSourceLevel(String sourceLevel)567 		private int indexForSourceLevel(String sourceLevel) {
568 			if (sourceLevel == null) return 0;
569 			int majVersion = (int) (CompilerOptions.versionToJdkLevel(sourceLevel) >>> 16);
570 			if (majVersion > ClassFileConstants.MAJOR_VERSION_1_2) {
571 				return (majVersion - ClassFileConstants.MAJOR_VERSION_1_1);
572 			}
573 			// all other cases including ClassFileConstants.MAJOR_VERSION_1_1
574 			return 0;
575 		}
576 
sortParticipants(ArrayList<IConfigurationElement> group, IConfigurationElement[] configElements, int index)577 		private int sortParticipants(ArrayList<IConfigurationElement> group, IConfigurationElement[] configElements, int index) {
578 			int size = group.size();
579 			if (size == 0) return index;
580 			Object[] elements = group.toArray();
581 			Util.sort(elements, new Util.Comparer() {
582 				@Override
583 				public int compare(Object a, Object b) {
584 					if (a == b) return 0;
585 					String id = ((IConfigurationElement) a).getAttribute("id"); //$NON-NLS-1$
586 					if (id == null) return -1;
587 					IConfigurationElement[] requiredElements = ((IConfigurationElement) b).getChildren("requires"); //$NON-NLS-1$
588 					for (int i = 0, length = requiredElements.length; i < length; i++) {
589 						IConfigurationElement required = requiredElements[i];
590 						if (id.equals(required.getAttribute("id"))) //$NON-NLS-1$
591 							return 1;
592 					}
593 					return -1;
594 				}
595 			});
596 			for (int i = 0; i < size; i++)
597 				configElements[index+i] = (IConfigurationElement) elements[i];
598 			return index + size;
599 		}
600 	}
601 
602 	public final CompilationParticipants compilationParticipants = new CompilationParticipants();
603 
604 	/* whether an AbortCompilationUnit should be thrown when the source of a compilation unit cannot be retrieved */
605 	public ThreadLocal<Boolean> abortOnMissingSource = new ThreadLocal<>();
606 
607 	private ExternalFoldersManager externalFoldersManager = ExternalFoldersManager.getExternalFoldersManager();
608 
609 	/**
610 	 * Returns whether the given full path (for a package) conflicts with the output location
611 	 * of the given project.
612 	 */
conflictsWithOutputLocation(IPath folderPath, JavaProject project)613 	public static boolean conflictsWithOutputLocation(IPath folderPath, JavaProject project) {
614 		try {
615 			IPath outputLocation = project.getOutputLocation();
616 			if (outputLocation == null) {
617 				// in doubt, there is a conflict
618 				return true;
619 			}
620 			if (outputLocation.isPrefixOf(folderPath)) {
621 				// only allow nesting in project's output if there is a corresponding source folder
622 				// or if the project's output is not used (in other words, if all source folders have their custom output)
623 				IClasspathEntry[] classpath = project.getResolvedClasspath();
624 				boolean isOutputUsed = false;
625 				for (int i = 0, length = classpath.length; i < length; i++) {
626 					IClasspathEntry entry = classpath[i];
627 					if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
628 						if (entry.getPath().equals(outputLocation)) {
629 							return false;
630 						}
631 						if (entry.getOutputLocation() == null) {
632 							isOutputUsed = true;
633 						}
634 					}
635 				}
636 				return isOutputUsed;
637 			}
638 			return false;
639 		} catch (JavaModelException e) {
640 			// in doubt, there is a conflict
641 			return true;
642 		}
643 	}
644 
containerGet(IJavaProject project, IPath containerPath)645 	public synchronized IClasspathContainer containerGet(IJavaProject project, IPath containerPath) {
646 		// check initialization in progress first
647 		if (containerIsInitializationInProgress(project, containerPath)) {
648 			return CONTAINER_INITIALIZATION_IN_PROGRESS;
649 		}
650 
651 		Map<IPath, IClasspathContainer> projectContainers = this.containers.get(project);
652 		if (projectContainers == null){
653 			return null;
654 		}
655 		IClasspathContainer container = projectContainers.get(containerPath);
656 		return container;
657 	}
658 
containerIsSet(IJavaProject project, IPath containerPath)659 	synchronized boolean containerIsSet(IJavaProject project, IPath containerPath) {
660 		Map<IPath, IClasspathContainer> projectContainers = this.containers.get(project);
661 		if (projectContainers == null){
662 			return false;
663 		}
664 		IClasspathContainer container = projectContainers.get(containerPath);
665 		return container != null;
666 	}
667 
containerGetDefaultToPreviousSession(IJavaProject project, IPath containerPath)668 	public synchronized IClasspathContainer containerGetDefaultToPreviousSession(IJavaProject project, IPath containerPath) {
669 		Map<IPath, IClasspathContainer> projectContainers = this.containers.get(project);
670 		if (projectContainers == null)
671 			return getPreviousSessionContainer(containerPath, project);
672 		IClasspathContainer container = projectContainers.get(containerPath);
673 		if (container == null)
674 			return getPreviousSessionContainer(containerPath, project);
675 		return container;
676 	}
677 
containerIsInitializationInProgress(IJavaProject project, IPath containerPath)678 	private boolean containerIsInitializationInProgress(IJavaProject project, IPath containerPath) {
679 		Map<IJavaProject, Set<IPath>> initializations = this.containerInitializationInProgress.get();
680 		if (initializations == null)
681 			return false;
682 		Set<IPath> projectInitializations = initializations.get(project);
683 		if (projectInitializations == null)
684 			return false;
685 		return projectInitializations.contains(containerPath);
686 	}
687 
containerAddInitializationInProgress(IJavaProject project, IPath containerPath)688 	private void containerAddInitializationInProgress(IJavaProject project, IPath containerPath) {
689 		Map<IJavaProject, Set<IPath>> initializations = this.containerInitializationInProgress.get();
690 		if (initializations == null)
691 			this.containerInitializationInProgress.set(initializations = new HashMap<>());
692 		Set<IPath> projectInitializations = initializations.get(project);
693 		if (projectInitializations == null)
694 			initializations.put(project, projectInitializations = new HashSet<>());
695 		projectInitializations.add(containerPath);
696 	}
697 
containerBeingInitializedPut(IJavaProject project, IPath containerPath, IClasspathContainer container)698 	public void containerBeingInitializedPut(IJavaProject project, IPath containerPath, IClasspathContainer container) {
699 		Map<IJavaProject, Map<IPath, IClasspathContainer>> perProjectContainers = this.containersBeingInitialized.get();
700 		if (perProjectContainers == null)
701 			this.containersBeingInitialized.set(perProjectContainers = new HashMap<>());
702 		Map<IPath, IClasspathContainer> perPathContainers = perProjectContainers.get(project);
703 		if (perPathContainers == null)
704 			perProjectContainers.put(project, perPathContainers = new HashMap<>());
705 		perPathContainers.put(containerPath, container);
706 	}
707 
containerBeingInitializedGet(IJavaProject project, IPath containerPath)708 	public IClasspathContainer containerBeingInitializedGet(IJavaProject project, IPath containerPath) {
709 		Map<IJavaProject, Map<IPath, IClasspathContainer>> perProjectContainers = this.containersBeingInitialized.get();
710 		if (perProjectContainers == null)
711 			return null;
712 		Map<IPath, IClasspathContainer> perPathContainers = perProjectContainers.get(project);
713 		if (perPathContainers == null)
714 			return null;
715 		return perPathContainers.get(containerPath);
716 	}
717 
containerBeingInitializedRemove(IJavaProject project, IPath containerPath)718 	public IClasspathContainer containerBeingInitializedRemove(IJavaProject project, IPath containerPath) {
719 		Map<IJavaProject, Map<IPath, IClasspathContainer>> perProjectContainers = this.containersBeingInitialized.get();
720 		if (perProjectContainers == null)
721 			return null;
722 		Map<IPath, IClasspathContainer> perPathContainers = perProjectContainers.get(project);
723 		if (perPathContainers == null)
724 			return null;
725 		IClasspathContainer container = perPathContainers.remove(containerPath);
726 		if (perPathContainers.size() == 0)
727 			perProjectContainers.remove(project);
728 		if (perProjectContainers.size() == 0)
729 			this.containersBeingInitialized.set(null);
730 		return container;
731 	}
732 
containerPut(IJavaProject project, IPath containerPath, IClasspathContainer container)733 	public synchronized void containerPut(IJavaProject project, IPath containerPath, IClasspathContainer container){
734 
735 		// set/unset the initialization in progress
736 		if (container == CONTAINER_INITIALIZATION_IN_PROGRESS) {
737 			containerAddInitializationInProgress(project, containerPath);
738 
739 			// do not write out intermediate initialization value
740 			return;
741 		} else {
742 			containerRemoveInitializationInProgress(project, containerPath);
743 
744 			Map<IPath, IClasspathContainer> projectContainers = this.containers.get(project);
745  			if (projectContainers == null){
746 				projectContainers = new HashMap<>(1);
747 				this.containers.put(project, projectContainers);
748 			}
749 
750 			if (container == null) {
751 				projectContainers.remove(containerPath);
752 			} else {
753   				projectContainers.put(containerPath, container);
754 			}
755 			// discard obsoleted information about previous session
756 			Map<IPath, IClasspathContainer> previousContainers = this.previousSessionContainers.get(project);
757 			if (previousContainers != null){
758 				previousContainers.remove(containerPath);
759 			}
760 		}
761 		// container values are persisted in preferences during save operations, see #saving(ISaveContext)
762 	}
763 
764 	/*
765 	 * The given project is being removed. Remove all containers for this project from the cache.
766 	 */
containerRemove(IJavaProject project)767 	public synchronized void containerRemove(IJavaProject project) {
768 		Map<IJavaProject, Set<IPath>> initializations = this.containerInitializationInProgress.get();
769 		if (initializations != null) {
770 			initializations.remove(project);
771 		}
772 		this.containers.remove(project);
773 	}
774 
containerPutIfInitializingWithSameEntries(IPath containerPath, IJavaProject[] projects, IClasspathContainer[] respectiveContainers)775 	public boolean containerPutIfInitializingWithSameEntries(IPath containerPath, IJavaProject[] projects, IClasspathContainer[] respectiveContainers) {
776 		int projectLength = projects.length;
777 		if (projectLength != 1)
778 			return false;
779 		final IClasspathContainer container = respectiveContainers[0];
780 		IJavaProject project = projects[0];
781 		// optimize only if initializing, otherwise we are in a regular setContainer(...) call
782 		if (!containerIsInitializationInProgress(project, containerPath))
783 			return false;
784 		IClasspathContainer previousContainer = containerGetDefaultToPreviousSession(project, containerPath);
785 		if (container == null) {
786 			if (previousContainer == null) {
787 				containerPut(project, containerPath, null);
788 				return true;
789 			}
790 			return false;
791 		}
792 		final IClasspathEntry[] newEntries = container.getClasspathEntries();
793 		if (previousContainer == null)
794 			if (newEntries.length == 0) {
795 				containerPut(project, containerPath, container);
796 				return true;
797 			} else {
798 				if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
799 					verbose_missbehaving_container(containerPath, projects, respectiveContainers, container, newEntries, null/*no old entries*/);
800 				return false;
801 			}
802 		final IClasspathEntry[] oldEntries = previousContainer.getClasspathEntries();
803 		if (oldEntries.length != newEntries.length) {
804 			if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
805 				verbose_missbehaving_container(containerPath, projects, respectiveContainers, container, newEntries, oldEntries);
806 			return false;
807 		}
808 		for (int i = 0, length = newEntries.length; i < length; i++) {
809 			if (newEntries[i] == null) {
810 				if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
811 					verbose_missbehaving_container(project, containerPath, newEntries);
812 				return false;
813 			}
814 			if (!newEntries[i].equals(oldEntries[i])) {
815 				if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
816 					verbose_missbehaving_container(containerPath, projects, respectiveContainers, container, newEntries, oldEntries);
817 				return false;
818 			}
819 		}
820 		containerPut(project, containerPath, container);
821 		return true;
822 	}
823 
verbose_missbehaving_container( IPath containerPath, IJavaProject[] projects, IClasspathContainer[] respectiveContainers, final IClasspathContainer container, final IClasspathEntry[] newEntries, final IClasspathEntry[] oldEntries)824 	private void verbose_missbehaving_container(
825 			IPath containerPath,
826 			IJavaProject[] projects,
827 			IClasspathContainer[] respectiveContainers,
828 			final IClasspathContainer container,
829 			final IClasspathEntry[] newEntries,
830 			final IClasspathEntry[] oldEntries) {
831 		Util.verbose(
832 			"CPContainer SET  - missbehaving container\n" + //$NON-NLS-1$
833 			"	container path: " + containerPath + '\n' + //$NON-NLS-1$
834 			"	projects: {" +//$NON-NLS-1$
835 			org.eclipse.jdt.internal.compiler.util.Util.toString(
836 				projects,
837 				new org.eclipse.jdt.internal.compiler.util.Util.Displayable(){
838 					@Override
839 					public String displayString(Object o) { return ((IJavaProject) o).getElementName(); }
840 				}) +
841 			"}\n	values on previous session: {\n"  +//$NON-NLS-1$
842 			org.eclipse.jdt.internal.compiler.util.Util.toString(
843 				respectiveContainers,
844 				new org.eclipse.jdt.internal.compiler.util.Util.Displayable(){
845 					@Override
846 					public String displayString(Object o) {
847 						StringBuffer buffer = new StringBuffer("		"); //$NON-NLS-1$
848 						if (o == null) {
849 							buffer.append("<null>"); //$NON-NLS-1$
850 							return buffer.toString();
851 						}
852 						buffer.append(container.getDescription());
853 						buffer.append(" {\n"); //$NON-NLS-1$
854 						if (oldEntries == null) {
855 							buffer.append(" 			"); //$NON-NLS-1$
856 							buffer.append("<null>\n"); //$NON-NLS-1$
857 						} else {
858 							for (int j = 0; j < oldEntries.length; j++){
859 								buffer.append(" 			"); //$NON-NLS-1$
860 								buffer.append(oldEntries[j]);
861 								buffer.append('\n');
862 							}
863 						}
864 						buffer.append(" 		}"); //$NON-NLS-1$
865 						return buffer.toString();
866 					}
867 				}) +
868 			"}\n	new values: {\n"  +//$NON-NLS-1$
869 			org.eclipse.jdt.internal.compiler.util.Util.toString(
870 				respectiveContainers,
871 				new org.eclipse.jdt.internal.compiler.util.Util.Displayable(){
872 					@Override
873 					public String displayString(Object o) {
874 						StringBuffer buffer = new StringBuffer("		"); //$NON-NLS-1$
875 						if (o == null) {
876 							buffer.append("<null>"); //$NON-NLS-1$
877 							return buffer.toString();
878 						}
879 						buffer.append(container.getDescription());
880 						buffer.append(" {\n"); //$NON-NLS-1$
881 						for (int j = 0; j < newEntries.length; j++){
882 							buffer.append(" 			"); //$NON-NLS-1$
883 							buffer.append(newEntries[j]);
884 							buffer.append('\n');
885 						}
886 						buffer.append(" 		}"); //$NON-NLS-1$
887 						return buffer.toString();
888 					}
889 				}) +
890 			"\n	}"); //$NON-NLS-1$
891 	}
892 
verbose_missbehaving_container(IJavaProject project, IPath containerPath, IClasspathEntry[] classpathEntries)893 	void verbose_missbehaving_container(IJavaProject project, IPath containerPath, IClasspathEntry[] classpathEntries) {
894 		Util.verbose(
895 			"CPContainer GET - missbehaving container (returning null classpath entry)\n" + //$NON-NLS-1$
896 			"	project: " + project.getElementName() + '\n' + //$NON-NLS-1$
897 			"	container path: " + containerPath + '\n' + //$NON-NLS-1$
898 			"	classpath entries: {\n" + //$NON-NLS-1$
899 			org.eclipse.jdt.internal.compiler.util.Util.toString(
900 				classpathEntries,
901 				new org.eclipse.jdt.internal.compiler.util.Util.Displayable(){
902 					@Override
903 					public String displayString(Object o) {
904 						StringBuffer buffer = new StringBuffer("		"); //$NON-NLS-1$
905 						if (o == null) {
906 							buffer.append("<null>"); //$NON-NLS-1$
907 							return buffer.toString();
908 						}
909 						buffer.append(o);
910 						return buffer.toString();
911 					}
912 				}) +
913 			"\n	}" //$NON-NLS-1$
914 		);
915 	}
916 
verbose_missbehaving_container_null_entries(IJavaProject project, IPath containerPath)917 	void verbose_missbehaving_container_null_entries(IJavaProject project, IPath containerPath) {
918 		Util.verbose(
919 			"CPContainer GET - missbehaving container (returning null as classpath entries)\n" + //$NON-NLS-1$
920 			"	project: " + project.getElementName() + '\n' + //$NON-NLS-1$
921 			"	container path: " + containerPath + '\n' + //$NON-NLS-1$
922 			"	classpath entries: <null>" //$NON-NLS-1$
923 		);
924 	}
925 
containerRemoveInitializationInProgress(IJavaProject project, IPath containerPath)926 	void containerRemoveInitializationInProgress(IJavaProject project, IPath containerPath) {
927 		Map<IJavaProject, Set<IPath>> initializations = this.containerInitializationInProgress.get();
928 		if (initializations == null)
929 			return;
930 		Set<IPath> projectInitializations = initializations.get(project);
931 		if (projectInitializations == null)
932 			return;
933 		projectInitializations.remove(containerPath);
934 		if (projectInitializations.size() == 0)
935 			initializations.remove(project);
936 		if (initializations.size() == 0)
937 			this.containerInitializationInProgress.set(null);
938 	}
939 
containersReset(String[] containerIDs)940 	private synchronized void containersReset(String[] containerIDs) {
941 		for (int i = 0; i < containerIDs.length; i++) {
942 			String containerID = containerIDs[i];
943 			Iterator<Map<IPath, IClasspathContainer>> projectIterator = this.containers.values().iterator();
944 			while (projectIterator.hasNext()){
945 				Map<IPath, IClasspathContainer> projectContainers = projectIterator.next();
946 				if (projectContainers != null){
947 					Iterator<IPath> containerIterator = projectContainers.keySet().iterator();
948 					while (containerIterator.hasNext()){
949 						IPath containerPath = containerIterator.next();
950 						if (containerID.equals(containerPath.segment(0))) { // registered container
951 							projectContainers.put(containerPath, null); // reset container value, but leave entry in Map
952 						}
953 					}
954 				}
955 			}
956 		}
957 	}
958 
959 	/**
960 	 * Returns the Java element corresponding to the given resource, or
961 	 * <code>null</code> if unable to associate the given resource
962 	 * with a Java element.
963 	 * <p>
964 	 * The resource must be one of:<ul>
965 	 *	<li>a project - the element returned is the corresponding <code>IJavaProject</code></li>
966 	 *	<li>a <code>.java</code> file - the element returned is the corresponding <code>ICompilationUnit</code></li>
967 	 *	<li>a <code>.class</code> file - the element returned is the corresponding <code>IClassFile</code></li>
968 	 *	<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>
969 	 *  <li>a folder - the element returned is the corresponding <code>IPackageFragmentRoot</code>
970 	 *			or <code>IPackageFragment</code></li>
971 	 *  <li>the workspace root resource - the element returned is the <code>IJavaModel</code></li>
972 	 *	</ul>
973 	 * <p>
974 	 * Creating a Java element has the side effect of creating and opening all of the
975 	 * element's parents if they are not yet open.
976 	 */
create(IResource resource, IJavaProject project)977 	public static IJavaElement create(IResource resource, IJavaProject project) {
978 		if (resource == null) {
979 			return null;
980 		}
981 		int type = resource.getType();
982 		switch (type) {
983 			case IResource.PROJECT :
984 				return JavaCore.create((IProject) resource);
985 			case IResource.FILE :
986 				return create((IFile) resource, project);
987 			case IResource.FOLDER :
988 				return create((IFolder) resource, project);
989 			case IResource.ROOT :
990 				return JavaCore.create((IWorkspaceRoot) resource);
991 			default :
992 				return null;
993 		}
994 	}
995 
996 	/**
997 	 * Returns the Java element corresponding to the given file, its project being the given
998 	 * project.
999 	 * Returns <code>null</code> if unable to associate the given file
1000 	 * with a Java element.
1001 	 *
1002 	 * <p>The file must be one of:<ul>
1003 	 *	<li>a <code>.java</code> file - the element returned is the corresponding <code>ICompilationUnit</code></li>
1004 	 *	<li>a <code>.class</code> file - the element returned is the corresponding <code>IClassFile</code></li>
1005 	 *	<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>
1006 	 *	</ul>
1007 	 * <p>
1008 	 * Creating a Java element has the side effect of creating and opening all of the
1009 	 * element's parents if they are not yet open.
1010 	 */
create(IFile file, IJavaProject project)1011 	public static IJavaElement create(IFile file, IJavaProject project) {
1012 		if (file == null) {
1013 			return null;
1014 		}
1015 		if (project == null) {
1016 			project = JavaCore.create(file.getProject());
1017 		}
1018 
1019 		if (file.getFileExtension() != null) {
1020 			String name = file.getName();
1021 			if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(name))
1022 				return createCompilationUnitFrom(file, project);
1023 			if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(name))
1024 				return createClassFileFrom(file, project);
1025 			return createJarPackageFragmentRootFrom(file, project);
1026 		}
1027 		return null;
1028 	}
1029 
1030 	/**
1031 	 * Returns the package fragment or package fragment root corresponding to the given folder,
1032 	 * its parent or great parent being the given project.
1033 	 * or <code>null</code> if unable to associate the given folder with a Java element.
1034 	 * <p>
1035 	 * Note that a package fragment root is returned rather than a default package.
1036 	 * <p>
1037 	 * Creating a Java element has the side effect of creating and opening all of the
1038 	 * element's parents if they are not yet open.
1039 	 */
create(IFolder folder, IJavaProject project)1040 	public static IJavaElement create(IFolder folder, IJavaProject project) {
1041 		if (folder == null) {
1042 			return null;
1043 		}
1044 		IJavaElement element;
1045 		if (project == null) {
1046 			project = JavaCore.create(folder.getProject());
1047 			element = determineIfOnClasspath(folder, project);
1048 			if (element == null) {
1049 				// walk all projects and find one that have the given folder on its classpath
1050 				IJavaProject[] projects;
1051 				try {
1052 					projects = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects();
1053 				} catch (JavaModelException e) {
1054 					return null;
1055 				}
1056 				for (int i = 0, length = projects.length; i < length; i++) {
1057 					project = projects[i];
1058 					element = determineIfOnClasspath(folder, project);
1059 					if (element != null)
1060 						break;
1061 				}
1062 			}
1063 		} else {
1064 			element = determineIfOnClasspath(folder, project);
1065 		}
1066 		return element;
1067 	}
1068 
1069 	/**
1070 	 * Creates and returns a class file element for the given <code>.class</code> file,
1071 	 * its project being the given project. Returns <code>null</code> if unable
1072 	 * to recognize the class file.
1073 	 */
createClassFileFrom(IFile file, IJavaProject project )1074 	public static IClassFile createClassFileFrom(IFile file, IJavaProject project ) {
1075 		if (file == null) {
1076 			return null;
1077 		}
1078 		if (project == null) {
1079 			project = JavaCore.create(file.getProject());
1080 		}
1081 		IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file, project);
1082 		if (pkg == null) {
1083 			// fix for 1FVS7WE
1084 			// not on classpath - make the root its folder, and a default package
1085 			PackageFragmentRoot root = (PackageFragmentRoot) project.getPackageFragmentRoot(file.getParent());
1086 			pkg = root.getPackageFragment(CharOperation.NO_STRINGS);
1087 		}
1088 		String fileName = file.getName();
1089 		if (TypeConstants.MODULE_INFO_CLASS_NAME_STRING.equals(fileName))
1090 			return pkg.getModularClassFile();
1091 		return pkg.getClassFile(file.getName());
1092 	}
1093 
1094 	/**
1095 	 * Creates and returns a compilation unit element for the given <code>.java</code>
1096 	 * file, its project being the given project. Returns <code>null</code> if unable
1097 	 * to recognize the compilation unit.
1098 	 */
createCompilationUnitFrom(IFile file, IJavaProject project)1099 	public static ICompilationUnit createCompilationUnitFrom(IFile file, IJavaProject project) {
1100 
1101 		if (file == null) return null;
1102 
1103 		if (project == null) {
1104 			project = JavaCore.create(file.getProject());
1105 		}
1106 		IPackageFragment pkg = (IPackageFragment) determineIfOnClasspath(file, project);
1107 		if (pkg == null) {
1108 			// not on classpath - make the root its folder, and a default package
1109 			PackageFragmentRoot root = (PackageFragmentRoot) project.getPackageFragmentRoot(file.getParent());
1110 			pkg = root.getPackageFragment(CharOperation.NO_STRINGS);
1111 
1112 			if (VERBOSE){
1113 				System.out.println("WARNING : creating unit element outside classpath ("+ Thread.currentThread()+"): " + file.getFullPath()); //$NON-NLS-1$//$NON-NLS-2$
1114 			}
1115 		}
1116 		return pkg.getCompilationUnit(file.getName());
1117 	}
1118 
1119 	/**
1120 	 * Creates and returns a handle for the given JAR file, its project being the given project.
1121 	 * The Java model associated with the JAR's project may be
1122 	 * created as a side effect.
1123 	 * Returns <code>null</code> if unable to create a JAR package fragment root.
1124 	 * (for example, if the JAR file represents a non-Java resource)
1125 	 */
createJarPackageFragmentRootFrom(IFile file, IJavaProject project)1126 	public static IPackageFragmentRoot createJarPackageFragmentRootFrom(IFile file, IJavaProject project) {
1127 		if (file == null) {
1128 			return null;
1129 		}
1130 		if (project == null) {
1131 			project = JavaCore.create(file.getProject());
1132 		}
1133 
1134 		// Create a jar package fragment root only if on the classpath
1135 		IPath resourcePath = file.getFullPath();
1136 		try {
1137 			IClasspathEntry entry = ((JavaProject)project).getClasspathEntryFor(resourcePath);
1138 			if (entry != null) {
1139 				return project.getPackageFragmentRoot(file);
1140 			}
1141 		} catch (JavaModelException e) {
1142 			// project doesn't exist: return null
1143 		}
1144 		return null;
1145 	}
1146 
1147 	/**
1148 	 * Returns the package fragment root represented by the resource, or
1149 	 * the package fragment the given resource is located in, or <code>null</code>
1150 	 * if the given resource is not on the classpath of the given project.
1151 	 */
determineIfOnClasspath(IResource resource, IJavaProject project)1152 	public static IJavaElement determineIfOnClasspath(IResource resource, IJavaProject project) {
1153 		IPath resourcePath = resource.getFullPath();
1154 		boolean isExternal = ExternalFoldersManager.isInternalPathForExternalFolder(resourcePath);
1155 		if (isExternal)
1156 			resourcePath = resource.getLocation();
1157 
1158 		try {
1159 			JavaProjectElementInfo projectInfo = (JavaProjectElementInfo) getJavaModelManager().getInfo(project);
1160 			ProjectCache projectCache = projectInfo == null ? null : projectInfo.projectCache;
1161 			HashtableOfArrayToObject allPkgFragmentsCache = projectCache == null ? null : projectCache.allPkgFragmentsCache;
1162 			boolean isJavaLike = org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(resourcePath.lastSegment());
1163 			IClasspathEntry[] entries = isJavaLike ? project.getRawClasspath() // JAVA file can only live inside SRC folder (on the raw path)
1164 					: ((JavaProject)project).getResolvedClasspath();
1165 
1166 			int length	= entries.length;
1167 			if (length > 0) {
1168 				String sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true);
1169 				String complianceLevel = project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
1170 				for (int i = 0; i < length; i++) {
1171 					IClasspathEntry entry = entries[i];
1172 					if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) continue;
1173 					IPath rootPath = entry.getPath();
1174 					if (rootPath.equals(resourcePath)) {
1175 						if (isJavaLike)
1176 							return null;
1177 						return project.getPackageFragmentRoot(resource);
1178 					} else if (rootPath.isPrefixOf(resourcePath)) {
1179 						// allow creation of package fragment if it contains a .java file that is included
1180 						if (!Util.isExcluded(resource, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars())) {
1181 							// given we have a resource child of the root, it cannot be a JAR pkg root
1182 							PackageFragmentRoot root =
1183 								isExternal ?
1184 									new ExternalPackageFragmentRoot(rootPath, (JavaProject) project) :
1185 									(PackageFragmentRoot) ((JavaProject) project).getFolderPackageFragmentRoot(rootPath);
1186 							if (root == null) return null;
1187 							IPath pkgPath = resourcePath.removeFirstSegments(rootPath.segmentCount());
1188 
1189 							if (resource.getType() == IResource.FILE) {
1190 								// if the resource is a file, then remove the last segment which
1191 								// is the file name in the package
1192 								pkgPath = pkgPath.removeLastSegments(1);
1193 							}
1194 							String[] pkgName = pkgPath.segments();
1195 
1196 							// if package name is in the cache, then it has already been validated
1197 							// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=133141)
1198 							if (allPkgFragmentsCache != null && allPkgFragmentsCache.containsKey(pkgName))
1199 								return root.getPackageFragment(pkgName);
1200 
1201 							if (pkgName.length != 0 && JavaConventions.validatePackageName(Util.packageName(pkgPath, sourceLevel, complianceLevel), sourceLevel, complianceLevel).getSeverity() == IStatus.ERROR) {
1202 								return null;
1203 							}
1204 							return root.getPackageFragment(pkgName);
1205 						}
1206 					}
1207 				}
1208 			}
1209 		} catch (JavaModelException npe) {
1210 			return null;
1211 		}
1212 		return null;
1213 	}
1214 
1215 	/**
1216 	 * The singleton manager
1217 	 */
1218 	private static JavaModelManager MANAGER= new JavaModelManager();
1219 
1220 	/**
1221 	 * Infos cache.
1222 	 */
1223 	private JavaModelCache cache;
1224 
1225 	/*
1226 	 * Temporary cache of newly opened elements
1227 	 */
1228 	private ThreadLocal<HashMap<IJavaElement, Object>> temporaryCache = new ThreadLocal<>();
1229 
1230 	/**
1231 	 * Set of elements which are out of sync with their buffers.
1232 	 */
1233 	protected HashSet<Openable> elementsOutOfSynchWithBuffers = new HashSet<>(11);
1234 
1235 	/**
1236 	 * Holds the state used for delta processing.
1237 	 */
1238 	public DeltaProcessingState deltaState = new DeltaProcessingState();
1239 
1240 	public IndexManager indexManager = null;
1241 
1242 	/**
1243 	 * Table from IProject to PerProjectInfo.
1244 	 * NOTE: this object itself is used as a lock to synchronize creation/removal of per project infos
1245 	 */
1246 	protected Map<IProject, PerProjectInfo> perProjectInfos = new HashMap<>(5);
1247 
1248 	/**
1249 	 * Table from WorkingCopyOwner to a table of ICompilationUnit (working copy handle) to PerWorkingCopyInfo.
1250 	 * NOTE: this object itself is used as a lock to synchronize creation/removal of per working copy infos
1251 	 */
1252 	protected HashMap<WorkingCopyOwner, Map<CompilationUnit, PerWorkingCopyInfo>> perWorkingCopyInfos = new HashMap<>(5);
1253 
1254 	/**
1255 	 * A weak set of the known search scopes.
1256 	 */
1257 	protected WeakHashMap<AbstractSearchScope, ?> searchScopes = new WeakHashMap<>();
1258 
1259 	public static class PerProjectInfo {
1260 		private static final int JAVADOC_CACHE_INITIAL_SIZE = 10;
1261 
1262 		static final IJavaModelStatus NEED_RESOLUTION = new JavaModelStatus();
1263 
1264 		public IProject project;
1265 		public Object savedState;
1266 		public boolean triedRead;
1267 		public IClasspathEntry[] rawClasspath;
1268 		public IClasspathEntry[] referencedEntries;
1269 		public IJavaModelStatus rawClasspathStatus;
1270 		public int rawTimeStamp = 0;
1271 		public boolean writtingRawClasspath = false;
1272 		public IClasspathEntry[] resolvedClasspath;
1273 		public IJavaModelStatus unresolvedEntryStatus;
1274 		public Map<IPath, IClasspathEntry> rootPathToRawEntries; // reverse map from a package fragment root's path to the raw entry
1275 		public Map<IPath, IClasspathEntry> rootPathToResolvedEntries; // map from a package fragment root's path to the resolved entry
1276 		public IPath outputLocation;
1277 		public Map<IPath, ObjectVector> jrtRoots; // A map between a JRT file system (as a string) and the package fragment roots found in it.
1278 
1279 		public IEclipsePreferences preferences;
1280 		public Hashtable<String, String> options;
1281 		public Hashtable<String, Map<String, IType>> secondaryTypes;
1282 		/**
1283 		 * The temporary structure used while indexing, previously known as INDEXED_SECONDARY_TYPES entry
1284 		 */
1285 		volatile Map<IFile, Map<String, Map<String, IType>>> indexingSecondaryCache;
1286 
1287 
1288 		// NB: PackageFragment#getAttachedJavadoc uses this map differently
1289 		// and stores String data, not JavadocContents as values
1290 		public LRUCache<IJavaElement, Object> javadocCache;
1291 
PerProjectInfo(IProject project)1292 		public PerProjectInfo(IProject project) {
1293 
1294 			this.triedRead = false;
1295 			this.savedState = null;
1296 			this.project = project;
1297 			this.javadocCache = new LRUCache<>(JAVADOC_CACHE_INITIAL_SIZE);
1298 		}
1299 
getResolvedClasspath()1300 		public synchronized IClasspathEntry[] getResolvedClasspath() {
1301 			if (this.unresolvedEntryStatus == NEED_RESOLUTION)
1302 				return null;
1303 			return this.resolvedClasspath;
1304 		}
1305 
forgetExternalTimestampsAndIndexes()1306 		public void forgetExternalTimestampsAndIndexes() {
1307 			IClasspathEntry[] classpath = this.resolvedClasspath;
1308 			if (classpath == null) return;
1309 			JavaModelManager manager = JavaModelManager.getJavaModelManager();
1310 			IndexManager indexManager = manager.indexManager;
1311 			Hashtable<IPath, Long> externalTimeStamps = manager.deltaState.getExternalLibTimeStamps();
1312 			Map<IPath, List<RootInfo>> rootInfos = JavaModelManager.getDeltaState().otherRoots;
1313 			for (int i = 0, length = classpath.length; i < length; i++) {
1314 				IClasspathEntry entry = classpath[i];
1315 				if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
1316 					IPath path = entry.getPath();
1317 					if (rootInfos.get(path) == null) {
1318 						externalTimeStamps.remove(path);
1319 						indexManager.removeIndex(path); // force reindexing on next reference (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=250083 )
1320 					}
1321 				}
1322 			}
1323 		}
1324 
rememberExternalLibTimestamps()1325 		public void rememberExternalLibTimestamps() {
1326 			IClasspathEntry[] classpath = this.resolvedClasspath;
1327 			if (classpath == null) return;
1328 			Map<IPath, Long> externalTimeStamps = JavaModelManager.getJavaModelManager().deltaState.getExternalLibTimeStamps();
1329 			for (int i = 0, length = classpath.length; i < length; i++) {
1330 				IClasspathEntry entry = classpath[i];
1331 				if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
1332 					IPath path = entry.getPath();
1333 					if (externalTimeStamps.get(path) == null) {
1334 						Object target = JavaModel.getExternalTarget(path, true);
1335 						if (target instanceof File) {
1336 							long timestamp = DeltaProcessor.getTimeStamp((java.io.File)target);
1337 							externalTimeStamps.put(path, Long.valueOf(timestamp));
1338 						}
1339 					}
1340 				}
1341 			}
1342 		}
1343 
resetResolvedClasspath()1344 		public synchronized ClasspathChange resetResolvedClasspath() {
1345 			// clear non-chaining jars cache and invalid jars cache
1346 			JavaModelManager.getJavaModelManager().resetClasspathListCache();
1347 
1348 			// null out resolved information
1349 			return setResolvedClasspath(null, null, null, null, this.rawTimeStamp, true/*add classpath change*/);
1350 		}
1351 
setClasspath(IClasspathEntry[] newRawClasspath, IClasspathEntry[] referencedEntries, IPath newOutputLocation, IJavaModelStatus newRawClasspathStatus, IClasspathEntry[] newResolvedClasspath, Map<IPath, IClasspathEntry> newRootPathToRawEntries, Map<IPath, IClasspathEntry> newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus, boolean addClasspathChange)1352 		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) {
1353 			if (DEBUG_CLASSPATH) {
1354 				System.out.println("Setting resolved classpath for " + this.project.getFullPath()); //$NON-NLS-1$
1355 				if (newResolvedClasspath == null) {
1356 					System.out.println("New classpath = null"); //$NON-NLS-1$
1357 				} else {
1358 					for (IClasspathEntry next : newResolvedClasspath) {
1359 						System.out.println("    " + next); //$NON-NLS-1$
1360 					}
1361 				}
1362 			}
1363 			ClasspathChange classpathChange = addClasspathChange ? addClasspathChange() : null;
1364 
1365 			if (referencedEntries != null)	this.referencedEntries = referencedEntries;
1366 			if (this.referencedEntries == null) this.referencedEntries = ClasspathEntry.NO_ENTRIES;
1367 			this.rawClasspath = newRawClasspath;
1368 			this.outputLocation = newOutputLocation;
1369 			this.rawClasspathStatus = newRawClasspathStatus;
1370 			this.resolvedClasspath = newResolvedClasspath;
1371 			this.rootPathToRawEntries = newRootPathToRawEntries;
1372 			this.rootPathToResolvedEntries = newRootPathToResolvedEntries;
1373 			this.unresolvedEntryStatus = newUnresolvedEntryStatus;
1374 			this.javadocCache = new LRUCache<>(JAVADOC_CACHE_INITIAL_SIZE);
1375 
1376 			return classpathChange;
1377 		}
1378 
addClasspathChange()1379 		protected ClasspathChange addClasspathChange() {
1380 			// remember old info
1381 			JavaModelManager manager = JavaModelManager.getJavaModelManager();
1382 			ClasspathChange classpathChange = manager.deltaState.addClasspathChange(this.project, this.rawClasspath, this.outputLocation, this.resolvedClasspath);
1383 			return classpathChange;
1384 		}
1385 
setRawClasspath(IClasspathEntry[] newRawClasspath, IPath newOutputLocation, IJavaModelStatus newRawClasspathStatus)1386 		public ClasspathChange setRawClasspath(IClasspathEntry[] newRawClasspath, IPath newOutputLocation, IJavaModelStatus newRawClasspathStatus) {
1387 			return setRawClasspath(newRawClasspath, null, newOutputLocation, newRawClasspathStatus);
1388 		}
1389 
setRawClasspath(IClasspathEntry[] newRawClasspath, IClasspathEntry[] referencedEntries, IPath newOutputLocation, IJavaModelStatus newRawClasspathStatus)1390 		public synchronized ClasspathChange setRawClasspath(IClasspathEntry[] newRawClasspath, IClasspathEntry[] referencedEntries, IPath newOutputLocation, IJavaModelStatus newRawClasspathStatus) {
1391 			this.rawTimeStamp++;
1392 			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*/);
1393 		}
1394 
setResolvedClasspath(IClasspathEntry[] newResolvedClasspath, Map<IPath, IClasspathEntry> newRootPathToRawEntries, Map<IPath, IClasspathEntry> newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus, int timeStamp, boolean addClasspathChange)1395 		public ClasspathChange setResolvedClasspath(IClasspathEntry[] newResolvedClasspath, Map<IPath, IClasspathEntry> newRootPathToRawEntries, Map<IPath, IClasspathEntry> newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus, int timeStamp, boolean addClasspathChange) {
1396 			return setResolvedClasspath(newResolvedClasspath, null, newRootPathToRawEntries, newRootPathToResolvedEntries, newUnresolvedEntryStatus, timeStamp, addClasspathChange);
1397 		}
1398 
setResolvedClasspath(IClasspathEntry[] newResolvedClasspath, IClasspathEntry[] referencedEntries, Map<IPath, IClasspathEntry> newRootPathToRawEntries, Map<IPath, IClasspathEntry> newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus, int timeStamp, boolean addClasspathChange)1399 		public synchronized ClasspathChange setResolvedClasspath(IClasspathEntry[] newResolvedClasspath, IClasspathEntry[] referencedEntries, Map<IPath, IClasspathEntry> newRootPathToRawEntries, Map<IPath, IClasspathEntry> newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus, int timeStamp, boolean addClasspathChange) {
1400 			if (this.rawTimeStamp != timeStamp)
1401 				return null;
1402 			return setClasspath(this.rawClasspath, referencedEntries, this.outputLocation, this.rawClasspathStatus, newResolvedClasspath, newRootPathToRawEntries, newRootPathToResolvedEntries, newUnresolvedEntryStatus, addClasspathChange);
1403 		}
1404 
setJrtPackageRoots(IPath jrtPath, ObjectVector roots)1405 		public synchronized void setJrtPackageRoots(IPath jrtPath, ObjectVector roots) {
1406 			if (this.jrtRoots == null) this.jrtRoots = new HashMap<>();
1407 			this.jrtRoots.put(jrtPath, roots);
1408 		}
1409 
1410 		/**
1411 		 * Reads the classpath and caches the entries. Returns a two-dimensional array, where the number of elements in the row is fixed to 2.
1412 		 * 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
1413 		 * by the client earlier. See {@link IJavaProject#getReferencedClasspathEntries()} for more details.
1414 		 *
1415 		 */
readAndCacheClasspath(JavaProject javaProject)1416 		public synchronized IClasspathEntry[][] readAndCacheClasspath(JavaProject javaProject) {
1417 			// read file entries and update status
1418 			IClasspathEntry[][] classpath;
1419 			IJavaModelStatus status;
1420 			try {
1421 				classpath = javaProject.readFileEntriesWithException(null/*not interested in unknown elements*/);
1422 				status = JavaModelStatus.VERIFIED_OK;
1423 			} catch (CoreException e) {
1424 				classpath = new IClasspathEntry[][]{JavaProject.INVALID_CLASSPATH, ClasspathEntry.NO_ENTRIES};
1425 				status =
1426 					new JavaModelStatus(
1427 						IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
1428 						Messages.bind(Messages.classpath_cannotReadClasspathFile, javaProject.getElementName()));
1429 			} catch (IOException e) {
1430 				classpath = new IClasspathEntry[][]{JavaProject.INVALID_CLASSPATH, ClasspathEntry.NO_ENTRIES};
1431 				if (Messages.file_badFormat.equals(e.getMessage()))
1432 					status =
1433 						new JavaModelStatus(
1434 							IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
1435 							Messages.bind(Messages.classpath_xmlFormatError, javaProject.getElementName(), Messages.file_badFormat));
1436 				else
1437 					status =
1438 						new JavaModelStatus(
1439 							IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
1440 							Messages.bind(Messages.classpath_cannotReadClasspathFile, javaProject.getElementName()));
1441 			} catch (ClasspathEntry.AssertionFailedException e) {
1442 				classpath = new IClasspathEntry[][]{JavaProject.INVALID_CLASSPATH, ClasspathEntry.NO_ENTRIES};
1443 				status =
1444 					new JavaModelStatus(
1445 						IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT,
1446 						Messages.bind(Messages.classpath_illegalEntryInClasspathFile, new String[] {javaProject.getElementName(), e.getMessage()}));
1447 			}
1448 
1449 			// extract out the output location
1450 			int rawClasspathLength = classpath[0].length;
1451 			IPath output = null;
1452 			if (rawClasspathLength > 0) {
1453 				IClasspathEntry entry = classpath[0][rawClasspathLength - 1];
1454 				if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
1455 					output = entry.getPath();
1456 					IClasspathEntry[] copy = new IClasspathEntry[rawClasspathLength - 1];
1457 					System.arraycopy(classpath[0], 0, copy, 0, copy.length);
1458 					classpath[0] = copy;
1459 				}
1460 			}
1461 
1462 			// store new raw classpath, new output and new status, and null out resolved info
1463 			setRawClasspath(classpath[0], classpath[1], output, status);
1464 
1465 			return classpath;
1466 		}
1467 
1468 		@Override
toString()1469 		public String toString() {
1470 			StringBuffer buffer = new StringBuffer();
1471 			buffer.append("Info for "); //$NON-NLS-1$
1472 			buffer.append(this.project.getFullPath());
1473 			buffer.append("\nRaw classpath:\n"); //$NON-NLS-1$
1474 			if (this.rawClasspath == null) {
1475 				buffer.append("  <null>\n"); //$NON-NLS-1$
1476 			} else {
1477 				for (int i = 0, length = this.rawClasspath.length; i < length; i++) {
1478 					buffer.append("  "); //$NON-NLS-1$
1479 					buffer.append(this.rawClasspath[i]);
1480 					buffer.append('\n');
1481 				}
1482 			}
1483 			buffer.append("Resolved classpath:\n"); //$NON-NLS-1$
1484 			IClasspathEntry[] resolvedCP = this.resolvedClasspath;
1485 			if (resolvedCP == null) {
1486 				buffer.append("  <null>\n"); //$NON-NLS-1$
1487 			} else {
1488 				for (int i = 0, length = resolvedCP.length; i < length; i++) {
1489 					buffer.append("  "); //$NON-NLS-1$
1490 					buffer.append(resolvedCP[i]);
1491 					buffer.append('\n');
1492 				}
1493 			}
1494 			buffer.append("Resolved classpath status: "); //$NON-NLS-1$
1495 			if (this.unresolvedEntryStatus == NEED_RESOLUTION)
1496 				buffer.append("NEED RESOLUTION"); //$NON-NLS-1$
1497 			else
1498 				buffer.append(this.unresolvedEntryStatus == null ? "<null>\n" : this.unresolvedEntryStatus.toString()); //$NON-NLS-1$
1499 			buffer.append("Output location:\n  "); //$NON-NLS-1$
1500 			if (this.outputLocation == null) {
1501 				buffer.append("<null>"); //$NON-NLS-1$
1502 			} else {
1503 				buffer.append(this.outputLocation);
1504 			}
1505 			return buffer.toString();
1506 		}
1507 
writeAndCacheClasspath( JavaProject javaProject, final IClasspathEntry[] newRawClasspath, IClasspathEntry[] newReferencedEntries, final IPath newOutputLocation)1508 		public boolean writeAndCacheClasspath(
1509 				JavaProject javaProject,
1510 				final IClasspathEntry[] newRawClasspath,
1511 				IClasspathEntry[] newReferencedEntries,
1512 				final IPath newOutputLocation) throws JavaModelException {
1513 			try {
1514 				this.writtingRawClasspath = true;
1515 				if (newReferencedEntries == null) newReferencedEntries = this.referencedEntries;
1516 
1517 				// write .classpath
1518 				if (!javaProject.writeFileEntries(newRawClasspath, newReferencedEntries,  newOutputLocation)) {
1519 					return false;
1520 				}
1521 				// store new raw classpath, new output and new status, and null out resolved info
1522 				setRawClasspath(newRawClasspath, newReferencedEntries, newOutputLocation, JavaModelStatus.VERIFIED_OK);
1523 			} finally {
1524 				this.writtingRawClasspath = false;
1525 			}
1526 			return true;
1527 		}
1528 
writeAndCacheClasspath(JavaProject javaProject, final IClasspathEntry[] newRawClasspath, final IPath newOutputLocation)1529 		public boolean writeAndCacheClasspath(JavaProject javaProject, final IClasspathEntry[] newRawClasspath, final IPath newOutputLocation) throws JavaModelException {
1530 			return writeAndCacheClasspath(javaProject, newRawClasspath, null, newOutputLocation);
1531 		}
1532 
1533 	}
1534 
1535 	public static class PerWorkingCopyInfo implements IProblemRequestor {
1536 		int useCount = 0;
1537 		IProblemRequestor problemRequestor;
1538 		CompilationUnit workingCopy;
PerWorkingCopyInfo(CompilationUnit workingCopy, IProblemRequestor problemRequestor)1539 		public PerWorkingCopyInfo(CompilationUnit workingCopy, IProblemRequestor problemRequestor) {
1540 			this.workingCopy = workingCopy;
1541 			this.problemRequestor = problemRequestor;
1542 		}
1543 		@Override
acceptProblem(IProblem problem)1544 		public void acceptProblem(IProblem problem) {
1545 			IProblemRequestor requestor = getProblemRequestor();
1546 			if (requestor == null) return;
1547 			requestor.acceptProblem(problem);
1548 		}
1549 		@Override
beginReporting()1550 		public void beginReporting() {
1551 			IProblemRequestor requestor = getProblemRequestor();
1552 			if (requestor == null) return;
1553 			requestor.beginReporting();
1554 		}
1555 		@Override
endReporting()1556 		public void endReporting() {
1557 			IProblemRequestor requestor = getProblemRequestor();
1558 			if (requestor == null) return;
1559 			requestor.endReporting();
1560 		}
getProblemRequestor()1561 		public IProblemRequestor getProblemRequestor() {
1562 			if (this.problemRequestor == null && this.workingCopy.owner != null) {
1563 				return this.workingCopy.owner.getProblemRequestor(this.workingCopy);
1564 			}
1565 			return this.problemRequestor;
1566 		}
getWorkingCopy()1567 		public ICompilationUnit getWorkingCopy() {
1568 			return this.workingCopy;
1569 		}
1570 		@Override
isActive()1571 		public boolean isActive() {
1572 			IProblemRequestor requestor = getProblemRequestor();
1573 			return requestor != null && requestor.isActive();
1574 		}
1575 		@Override
toString()1576 		public String toString() {
1577 			StringBuffer buffer = new StringBuffer();
1578 			buffer.append("Info for "); //$NON-NLS-1$
1579 			buffer.append(((JavaElement)this.workingCopy).toStringWithAncestors());
1580 			buffer.append("\nUse count = "); //$NON-NLS-1$
1581 			buffer.append(this.useCount);
1582 			buffer.append("\nProblem requestor:\n  "); //$NON-NLS-1$
1583 			buffer.append(this.problemRequestor);
1584 			if (this.problemRequestor == null) {
1585 				IProblemRequestor requestor = getProblemRequestor();
1586 				buffer.append("\nOwner problem requestor:\n  "); //$NON-NLS-1$
1587 				buffer.append(requestor);
1588 			}
1589 			return buffer.toString();
1590 		}
1591 	}
1592 
1593 	public static boolean VERBOSE = false;
1594 	public static boolean DEBUG_CLASSPATH = false;
1595 	public static boolean DEBUG_INVALID_ARCHIVES = false;
1596 	public static boolean CP_RESOLVE_VERBOSE = false;
1597 	public static boolean CP_RESOLVE_VERBOSE_ADVANCED = false;
1598 	public static boolean CP_RESOLVE_VERBOSE_FAILURE = false;
1599 	public static boolean ZIP_ACCESS_VERBOSE = false;
1600 	public static boolean JRT_ACCESS_VERBOSE = false;
1601 
1602 	/**
1603 	 * A cache of opened zip files per thread.
1604 	 * (for a given thread, the object value is a HashMap from IPath to java.io.ZipFile)
1605 	 */
1606 	private ThreadLocal<ZipCache> zipFiles = new ThreadLocal<>();
1607 
1608 	private UserLibraryManager userLibraryManager;
1609 
1610 	private ModuleSourcePathManager modulePathManager;
1611 	/*
1612 	 * A set of IPaths for jars that are known to not contain a chaining (through MANIFEST.MF) to another library
1613 	 */
1614 	private Set<IPath> nonChainingJars;
1615 
1616 	// The amount of time from when an invalid archive is first sensed until that state is considered stale.
1617 	private static long INVALID_ARCHIVE_TTL_MILLISECONDS = 2 * 60 * 1000;
1618 
1619 	private static class InvalidArchiveInfo {
1620 		/**
1621 		 * Time at which this entry will be removed from the invalid archive list.
1622 		 */
1623 		final long evictionTimestamp;
1624 
1625 		/**
1626 		 * Reason the entry was added to the invalid archive list.
1627 		 */
1628 		final ArchiveValidity reason;
1629 
InvalidArchiveInfo(long evictionTimestamp, ArchiveValidity reason)1630 		InvalidArchiveInfo(long evictionTimestamp, ArchiveValidity reason) {
1631 			this.evictionTimestamp = evictionTimestamp;
1632 			this.reason = reason;
1633 		}
1634 	}
1635 
1636 	/*
1637 	 * 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.
1638 	 * Synchronize on invalidArchivesMutex before accessing.
1639 	 */
1640 	private final Map<IPath, InvalidArchiveInfo> invalidArchives = new HashMap<>();
1641 	private final Object invalidArchivesMutex = new Object();
1642 
1643 	/*
1644 	 * A set of IPaths for files that are known to be external to the workspace.
1645 	 * Need not be referenced by the classpath.
1646 	 */
1647 	private Set<IPath> externalFiles;
1648 
1649 	/*
1650 	 * A set of IPaths for files that do not exist on the file system but are assumed to be
1651 	 * external archives (rather than external folders).
1652 	 */
1653 	private Set<IPath> assumedExternalFiles;
1654 
1655 	/**
1656 	 * Update the classpath variable cache
1657 	 */
1658 	public static class EclipsePreferencesListener implements IEclipsePreferences.IPreferenceChangeListener {
1659 		/**
1660          * @see org.eclipse.core.runtime.preferences.IEclipsePreferences.IPreferenceChangeListener#preferenceChange(org.eclipse.core.runtime.preferences.IEclipsePreferences.PreferenceChangeEvent)
1661          */
1662         @Override
preferenceChange(IEclipsePreferences.PreferenceChangeEvent event)1663 		public void preferenceChange(IEclipsePreferences.PreferenceChangeEvent event) {
1664         	String propertyName = event.getKey();
1665         	if (propertyName.startsWith(JavaCore.PLUGIN_ID)) {
1666 	        	if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)) {
1667 	        		String varName = propertyName.substring(CP_VARIABLE_PREFERENCES_PREFIX.length());
1668 	        		JavaModelManager manager = getJavaModelManager();
1669 	        		if (manager.variablesWithInitializer.contains(varName)) {
1670 	        			// revert preference value as we will not apply it to JavaCore classpath variable
1671 	        			String oldValue = (String) event.getOldValue();
1672 	        			if (oldValue == null) {
1673 	        				// unexpected old value => remove variable from set
1674 	        				manager.variablesWithInitializer.remove(varName);
1675 	        			} else {
1676 	        				manager.getInstancePreferences().put(varName, oldValue);
1677 	        			}
1678 	        		} else {
1679 	        			String newValue = (String)event.getNewValue();
1680 	        			IPath newPath;
1681 	        			if (newValue != null && !(newValue = newValue.trim()).equals(CP_ENTRY_IGNORE)) {
1682 	        				newPath = new Path(newValue);
1683 	        			} else {
1684 	        				newPath = null;
1685 	        			}
1686 	        			try {
1687 	        				SetVariablesOperation operation = new SetVariablesOperation(new String[] {varName}, new IPath[] {newPath}, false/*don't update preferences*/);
1688 	        				operation.runOperation(null/*no progress available*/);
1689 	        			} catch (JavaModelException e) {
1690 	        				Util.log(e, "Could not set classpath variable " + varName + " to " + newPath); //$NON-NLS-1$ //$NON-NLS-2$
1691 	        			}
1692 	        		}
1693 	        	} else if (propertyName.startsWith(CP_CONTAINER_PREFERENCES_PREFIX)) {
1694 	        		recreatePersistedContainer(propertyName, (String)event.getNewValue(), false);
1695 	        	} else if (propertyName.equals(JavaCore.CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER) ||
1696 					propertyName.equals(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER) ||
1697 					propertyName.equals(JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE) ||
1698 					propertyName.equals(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER) ||
1699 					propertyName.equals(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH) ||
1700 					propertyName.equals(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS) ||
1701 					propertyName.equals(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS) ||
1702 					propertyName.equals(JavaCore.CORE_INCOMPLETE_CLASSPATH) ||
1703 					propertyName.equals(JavaCore.CORE_CIRCULAR_CLASSPATH) ||
1704 					propertyName.equals(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL) ||
1705 					propertyName.equals(JavaCore.CORE_MAIN_ONLY_PROJECT_HAS_TEST_ONLY_DEPENDENCY) ||
1706 					propertyName.equals(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM) ||
1707 					propertyName.equals(JavaCore.CORE_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE)) {
1708 					JavaModelManager manager = JavaModelManager.getJavaModelManager();
1709 					IJavaModel model = manager.getJavaModel();
1710 					IJavaProject[] jProjects;
1711 					try {
1712 						jProjects = model.getJavaProjects();
1713 						IProject[] projects = new IProject[jProjects.length];
1714 						for (int i = 0, pl = jProjects.length; i < pl; i++) {
1715 							JavaProject javaProject = (JavaProject) jProjects[i];
1716 							projects[i] = javaProject.getProject();
1717 							manager.deltaState.addClasspathValidation(javaProject);
1718 						}
1719 						manager.touchProjects(projects, null);
1720 					} catch (JavaModelException e) {
1721 						// skip
1722 					}
1723 	        	} else if (propertyName.startsWith(CP_USERLIBRARY_PREFERENCES_PREFIX)) {
1724 					String libName = propertyName.substring(CP_USERLIBRARY_PREFERENCES_PREFIX.length());
1725 					UserLibraryManager manager = JavaModelManager.getUserLibraryManager();
1726 	        		manager.updateUserLibrary(libName, (String)event.getNewValue());
1727 	        	}
1728 	        }
1729         	// Reset all project caches (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=233568 )
1730         	try {
1731         		IJavaProject[] projects = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects();
1732 	        	for (int i = 0, length = projects.length; i < length; i++) {
1733 					((JavaProject) projects[i]).resetCaches();
1734 				}
1735         	} catch (JavaModelException e) {
1736         		// cannot retrieve Java projects
1737         	}
1738         }
1739 	}
1740 	/**
1741 	 * Listener on eclipse preferences changes.
1742 	 */
1743 	EclipsePreferencesListener instancePreferencesListener = new EclipsePreferencesListener();
1744 	/**
1745 	 * Listener on eclipse preferences default/instance node changes.
1746 	 */
1747 	IEclipsePreferences.INodeChangeListener instanceNodeListener = new IEclipsePreferences.INodeChangeListener() {
1748 		@Override
1749 		public void added(IEclipsePreferences.NodeChangeEvent event) {
1750 			// do nothing
1751 		}
1752 		@Override
1753 		public void removed(IEclipsePreferences.NodeChangeEvent event) {
1754 			if (event.getChild() == JavaModelManager.this.preferencesLookup[PREF_INSTANCE]) {
1755 				JavaModelManager.this.preferencesLookup[PREF_INSTANCE] = InstanceScope.INSTANCE.getNode(JavaCore.PLUGIN_ID);
1756 				JavaModelManager.this.preferencesLookup[PREF_INSTANCE].addPreferenceChangeListener(new EclipsePreferencesListener());
1757 			}
1758 		}
1759 	};
1760 	IEclipsePreferences.INodeChangeListener defaultNodeListener = new IEclipsePreferences.INodeChangeListener() {
1761 		@Override
1762 		public void added(IEclipsePreferences.NodeChangeEvent event) {
1763 			// do nothing
1764 		}
1765 		@Override
1766 		public void removed(IEclipsePreferences.NodeChangeEvent event) {
1767 			if (event.getChild() == JavaModelManager.this.preferencesLookup[PREF_DEFAULT]) {
1768 				JavaModelManager.this.preferencesLookup[PREF_DEFAULT] = DefaultScope.INSTANCE.getNode(JavaCore.PLUGIN_ID);
1769 			}
1770 		}
1771 	};
1772 	/**
1773 	 * Listener on properties changes.
1774 	 */
1775 	IEclipsePreferences.IPreferenceChangeListener propertyListener;
1776 	IEclipsePreferences.IPreferenceChangeListener resourcesPropertyListener;
1777 
1778 	/**
1779 	 * Constructs a new JavaModelManager
1780 	 */
JavaModelManager()1781 	private JavaModelManager() {
1782 		// singleton: prevent others from creating a new instance
1783 		/*
1784 		 * It is required to initialize all fields that depends on a headless environment
1785 		 * only if the platform is running. Otherwise this breaks the ability to use
1786 		 * ASTParser in a non-headless environment.
1787 		 */
1788 		if (Platform.isRunning()) {
1789 			this.indexManager = new IndexManager();
1790 			this.nonChainingJars = loadClasspathListCache(NON_CHAINING_JARS_CACHE);
1791 			this.externalFiles = loadClasspathListCache(EXTERNAL_FILES_CACHE);
1792 			this.assumedExternalFiles = loadClasspathListCache(ASSUMED_EXTERNAL_FILES_CACHE);
1793 			String includeContainerReferencedLib = System.getProperty(RESOLVE_REFERENCED_LIBRARIES_FOR_CONTAINERS);
1794 			this.resolveReferencedLibrariesForContainers = TRUE.equalsIgnoreCase(includeContainerReferencedLib);
1795 		}
1796 	}
1797 
1798 	/**
1799 	 * @deprecated
1800 	 */
addDeprecatedOptions(Hashtable<String, String> options)1801 	private void addDeprecatedOptions(Hashtable<String, String> options) {
1802 		options.put(JavaCore.COMPILER_PB_INVALID_IMPORT, JavaCore.ERROR);
1803 		options.put(JavaCore.COMPILER_PB_UNREACHABLE_CODE, JavaCore.ERROR);
1804 	}
1805 
addNonChainingJar(IPath path)1806 	public void addNonChainingJar(IPath path) {
1807 		if (this.nonChainingJars != null)
1808 			this.nonChainingJars.add(path);
1809 	}
1810 
addInvalidArchive(IPath path, ArchiveValidity reason)1811 	public void addInvalidArchive(IPath path, ArchiveValidity reason) {
1812 		if (DEBUG_INVALID_ARCHIVES) {
1813 			System.out.println("Invalid JAR cache: adding " + path + ", reason: " + reason);  //$NON-NLS-1$//$NON-NLS-2$
1814 		}
1815 		synchronized (this.invalidArchivesMutex) {
1816 			this.invalidArchives.put(path, new InvalidArchiveInfo(System.currentTimeMillis() + INVALID_ARCHIVE_TTL_MILLISECONDS, reason));
1817 		}
1818 	}
1819 
1820 	/**
1821 	 * Adds a path to the external files cache. It is the responsibility of callers to
1822 	 * determine the file's existence, as determined by  {@link File#isFile()}.
1823 	 */
addExternalFile(IPath path)1824 	public void addExternalFile(IPath path) {
1825 		// unlikely to be null
1826 		if (this.externalFiles == null) {
1827 			this.externalFiles = Collections.synchronizedSet(new HashSet<IPath>());
1828 		}
1829 		if(this.externalFiles != null) {
1830 			this.externalFiles.add(path);
1831 		}
1832 	}
1833 
1834 	/**
1835 	 * Starts caching ZipFiles.
1836 	 * Ignores if there are already clients.
1837 	 */
cacheZipFiles(Object owner)1838 	public void cacheZipFiles(Object owner) {
1839 		ZipCache zipCache = this.zipFiles.get();
1840 		if (zipCache != null) {
1841 			return;
1842 		}
1843 		// the owner will be responsible for flushing the cache
1844 		this.zipFiles.set(new ZipCache(owner));
1845 	}
1846 
closeZipFile(ZipFile zipFile)1847 	public void closeZipFile(ZipFile zipFile) {
1848 		if (zipFile == null) return;
1849 		if (this.zipFiles.get() != null) {
1850 			if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
1851 				System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.closeZipFile(ZipFile)] NOT closed ZipFile (cache exist!) on " +zipFile.getName()); //$NON-NLS-1$	//$NON-NLS-2$
1852 			}
1853 			return; // zip file will be closed by call to flushZipFiles
1854 		}
1855 		try {
1856 			if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
1857 				System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.closeZipFile(ZipFile)] Closing ZipFile on " +zipFile.getName()); //$NON-NLS-1$	//$NON-NLS-2$
1858 			}
1859 			zipFile.close();
1860 		} catch (IOException e) {
1861 			// problem occured closing zip file: cannot do much more
1862 			JavaCore.getPlugin().getLog().log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, "Error closing " + zipFile.getName(), e)); //$NON-NLS-1$
1863 		}
1864 	}
1865 
registerDebugOptionsListener(BundleContext context)1866 	public static void registerDebugOptionsListener(BundleContext context) {
1867 		// register debug options listener
1868 		Hashtable<String, String> properties = new Hashtable<>(2);
1869 		properties.put(DebugOptions.LISTENER_SYMBOLICNAME, JavaCore.PLUGIN_ID);
1870 		DEBUG_REGISTRATION = context.registerService(DebugOptionsListener.class, new DebugOptionsListener() {
1871 			@Override
1872 			public void optionsChanged(DebugOptions options) {
1873 				boolean debug = options.getBooleanOption(DEBUG, false);
1874 				BufferManager.VERBOSE = debug && options.getBooleanOption(BUFFER_MANAGER_DEBUG, false);
1875 				JavaBuilder.DEBUG = debug && options.getBooleanOption(BUILDER_DEBUG, false);
1876 				Compiler.DEBUG = debug && options.getBooleanOption(COMPILER_DEBUG, false);
1877 				JavaBuilder.SHOW_STATS = debug && options.getBooleanOption(BUILDER_STATS_DEBUG, false);
1878 				CompletionEngine.DEBUG = debug && options.getBooleanOption(COMPLETION_DEBUG, false);
1879 				JavaModelManager.CP_RESOLVE_VERBOSE = debug && options.getBooleanOption(CP_RESOLVE_DEBUG, false);
1880 				JavaModelManager.CP_RESOLVE_VERBOSE_ADVANCED = debug && options.getBooleanOption(CP_RESOLVE_ADVANCED_DEBUG, false);
1881 				JavaModelManager.CP_RESOLVE_VERBOSE_FAILURE = debug && options.getBooleanOption(CP_RESOLVE_FAILURE_DEBUG, false);
1882 				DeltaProcessor.DEBUG = debug && options.getBooleanOption(DELTA_DEBUG, false);
1883 				DeltaProcessor.VERBOSE = debug && options.getBooleanOption(DELTA_DEBUG_VERBOSE, false);
1884 				SourceRangeVerifier.DEBUG = debug && options.getBooleanOption(DOM_AST_DEBUG, false);
1885 				SourceRangeVerifier.DEBUG_THROW = debug && options.getBooleanOption(DOM_AST_DEBUG_THROW, false);
1886 				SourceRangeVerifier.DEBUG |= SourceRangeVerifier.DEBUG_THROW;
1887 				RewriteEventStore.DEBUG = debug && options.getBooleanOption(DOM_REWRITE_DEBUG, false);
1888 				TypeHierarchy.DEBUG = debug && options.getBooleanOption(HIERARCHY_DEBUG, false);
1889 				JobManager.VERBOSE = debug && options.getBooleanOption(INDEX_MANAGER_DEBUG, false);
1890 				IndexManager.DEBUG = debug && options.getBooleanOption(INDEX_MANAGER_ADVANCED_DEBUG, false);
1891 				JavaModelManager.DEBUG_CLASSPATH = debug && options.getBooleanOption(JAVAMODEL_CLASSPATH, false);
1892 				JavaModelManager.DEBUG_INVALID_ARCHIVES = debug && options.getBooleanOption(JAVAMODEL_INVALID_ARCHIVES, false);
1893 				JavaModelManager.VERBOSE = debug && options.getBooleanOption(JAVAMODEL_DEBUG, false);
1894 				JavaModelCache.VERBOSE = debug && options.getBooleanOption(JAVAMODELCACHE_DEBUG, false);
1895 				JavaModelCache.DEBUG_CACHE_INSERTIONS = debug && options.getBooleanOption(JAVAMODELCACHE_INSERTIONS_DEBUG, false);
1896 				JavaModelOperation.POST_ACTION_VERBOSE = debug && options.getBooleanOption(POST_ACTION_DEBUG, false);
1897 				NameLookup.VERBOSE = debug && options.getBooleanOption(RESOLUTION_DEBUG, false);
1898 				BasicSearchEngine.VERBOSE = debug && options.getBooleanOption(SEARCH_DEBUG, false);
1899 				SelectionEngine.DEBUG = debug && options.getBooleanOption(SELECTION_DEBUG, false);
1900 				JavaModelManager.ZIP_ACCESS_VERBOSE = debug && options.getBooleanOption(ZIP_ACCESS_DEBUG, false);
1901 				SourceMapper.VERBOSE = debug && options.getBooleanOption(SOURCE_MAPPER_DEBUG_VERBOSE, false);
1902 				DefaultCodeFormatter.DEBUG = debug && options.getBooleanOption(FORMATTER_DEBUG, false);
1903 				Database.DEBUG_FREE_SPACE = debug && options.getBooleanOption(INDEX_DEBUG_LARGE_CHUNKS, false);
1904 				Database.DEBUG_PAGE_CACHE = debug && options.getBooleanOption(INDEX_DEBUG_PAGE_CACHE, false);
1905 				Indexer.DEBUG = debug && options.getBooleanOption(INDEX_INDEXER_DEBUG, false);
1906 				Indexer.DEBUG_INSERTIONS = debug  && options.getBooleanOption(INDEX_INDEXER_INSERTIONS, false);
1907 				Indexer.DEBUG_ALLOCATIONS = debug && options.getBooleanOption(INDEX_INDEXER_SPACE, false);
1908 				Indexer.DEBUG_TIMING = debug && options.getBooleanOption(INDEX_INDEXER_TIMING, false);
1909 				Indexer.DEBUG_SCHEDULING = debug && options.getBooleanOption(INDEX_INDEXER_SCHEDULING, false);
1910 				Indexer.DEBUG_SELFTEST = debug && options.getBooleanOption(INDEX_INDEXER_SELFTEST, false);
1911 				Indexer.DEBUG_LOG_SIZE_MB = debug ? options.getIntegerOption(INDEX_INDEXER_LOG_SIZE_MEGS, 0) : 0;
1912 				Nd.sDEBUG_LOCKS = debug && options.getBooleanOption(INDEX_LOCKS_DEBUG, false);
1913 
1914 				// configure performance options
1915 				if(PerformanceStats.ENABLED) {
1916 					CompletionEngine.PERF = PerformanceStats.isEnabled(COMPLETION_PERF);
1917 					SelectionEngine.PERF = PerformanceStats.isEnabled(SELECTION_PERF);
1918 					DeltaProcessor.PERF = PerformanceStats.isEnabled(DELTA_LISTENER_PERF);
1919 					JavaModelManager.PERF_VARIABLE_INITIALIZER = PerformanceStats.isEnabled(VARIABLE_INITIALIZER_PERF);
1920 					JavaModelManager.PERF_CONTAINER_INITIALIZER = PerformanceStats.isEnabled(CONTAINER_INITIALIZER_PERF);
1921 					ReconcileWorkingCopyOperation.PERF = PerformanceStats.isEnabled(RECONCILE_PERF);
1922 				}
1923 			}
1924 		}, properties);
1925 	}
1926 
unregisterDebugOptionsListener()1927 	public static void unregisterDebugOptionsListener() {
1928 		// unregister debug options listener
1929 		DEBUG_REGISTRATION.unregister();
1930 		DEBUG_REGISTRATION = null;
1931 	}
1932 
1933 	/*
1934 	 * Return a new Java 6 annotation processor manager.  The manager will need to
1935 	 * be configured before it can be used.  Returns null if a manager cannot be
1936 	 * created, i.e. if the current VM does not support Java 6 annotation processing.
1937 	 */
createAnnotationProcessorManager()1938 	public AbstractAnnotationProcessorManager createAnnotationProcessorManager() {
1939 		synchronized(this) {
1940 			if (this.annotationProcessorManagerFactory == null) {
1941 				IExtensionPoint extension = Platform.getExtensionRegistry().getExtensionPoint(JavaCore.PLUGIN_ID, ANNOTATION_PROCESSOR_MANAGER_EXTPOINT_ID);
1942 				if (extension == null)
1943 					return null;
1944 				IExtension[] extensions = extension.getExtensions();
1945 				for(int i = 0; i < extensions.length; i++) {
1946 					if (i > 0) {
1947 						Util.log(null, "An annotation processor manager is already registered: ignoring " + extensions[i].getUniqueIdentifier()); //$NON-NLS-1$
1948 						break;
1949 					}
1950 					IConfigurationElement[] configElements = extensions[i].getConfigurationElements();
1951 					for(int j = 0; j < configElements.length; j++) {
1952 						final IConfigurationElement configElement = configElements[j];
1953 						if ("annotationProcessorManager".equals(configElement.getName())) { //$NON-NLS-1$
1954 							this.annotationProcessorManagerFactory = configElement;
1955 							break;
1956 						}
1957 					}
1958 				}
1959 			}
1960 		}
1961 
1962 		if (this.annotationProcessorManagerFactory == null) {
1963 			return null;
1964 		}
1965 		final AbstractAnnotationProcessorManager[] apm = new AbstractAnnotationProcessorManager[1];
1966 		apm[0] = null;
1967 		final IConfigurationElement factory = this.annotationProcessorManagerFactory;
1968 		SafeRunner.run(new ISafeRunnable() {
1969 			@Override
1970 			public void handleException(Throwable exception) {
1971 				Util.log(exception, "Exception occurred while loading annotation processor manager"); //$NON-NLS-1$
1972 			}
1973 			@Override
1974 			public void run() throws Exception {
1975 				Object executableExtension = factory.createExecutableExtension("class"); //$NON-NLS-1$
1976 				if (executableExtension instanceof AbstractAnnotationProcessorManager) {
1977 					apm[0] = (AbstractAnnotationProcessorManager) executableExtension;
1978 				}
1979 			}
1980 		});
1981 		return apm[0];
1982 	}
1983 
1984 	/*
1985 	 * Discards the per working copy info for the given working copy (making it a compilation unit)
1986 	 * if its use count was 1. Otherwise, just decrement the use count.
1987 	 * If the working copy is primary, computes the delta between its state and the original compilation unit
1988 	 * and register it.
1989 	 * Close the working copy, its buffer and remove it from the shared working copy table.
1990 	 * Ignore if no per-working copy info existed.
1991 	 * NOTE: it must NOT be synchronized as it may interact with the element info cache (if useCount is decremented to 0), see bug 50667.
1992 	 * Returns the new use count (or -1 if it didn't exist).
1993 	 */
discardPerWorkingCopyInfo(CompilationUnit workingCopy)1994 	public int discardPerWorkingCopyInfo(CompilationUnit workingCopy) throws JavaModelException {
1995 
1996 		// create the delta builder (this remembers the current content of the working copy)
1997 		// outside the perWorkingCopyInfos lock (see bug 50667)
1998 		JavaElementDeltaBuilder deltaBuilder = null;
1999 		if (workingCopy.isPrimary() && workingCopy.hasUnsavedChanges()) {
2000 			deltaBuilder = new JavaElementDeltaBuilder(workingCopy);
2001 		}
2002 		PerWorkingCopyInfo info = null;
2003 		synchronized(this.perWorkingCopyInfos) {
2004 			WorkingCopyOwner owner = workingCopy.owner;
2005 			Map<CompilationUnit, PerWorkingCopyInfo> workingCopyToInfos = this.perWorkingCopyInfos.get(owner);
2006 			if (workingCopyToInfos == null) return -1;
2007 
2008 			info = workingCopyToInfos.get(workingCopy);
2009 			if (info == null) return -1;
2010 
2011 			if (--info.useCount == 0) {
2012 				// remove per working copy info
2013 				workingCopyToInfos.remove(workingCopy);
2014 				if (workingCopyToInfos.isEmpty()) {
2015 					this.perWorkingCopyInfos.remove(owner);
2016 				}
2017 			}
2018 		}
2019 		if (info.useCount == 0) { // info cannot be null here (check was done above)
2020 			// remove infos + close buffer (since no longer working copy)
2021 			// outside the perWorkingCopyInfos lock (see bug 50667)
2022 			removeInfoAndChildren(workingCopy);
2023 			workingCopy.closeBuffer();
2024 
2025 			// compute the delta if needed and register it if there are changes
2026 			if (deltaBuilder != null) {
2027 				deltaBuilder.buildDeltas();
2028 				if (deltaBuilder.delta != null) {
2029 					getDeltaProcessor().registerJavaModelDelta(deltaBuilder.delta);
2030 				}
2031 			}
2032 		}
2033 		return info.useCount;
2034 	}
2035 
2036 	/**
2037 	 * @see ISaveParticipant
2038 	 */
2039 	@Override
doneSaving(ISaveContext context)2040 	public void doneSaving(ISaveContext context){
2041 		// nothing to do for jdt.core
2042 	}
2043 
2044 	/**
2045 	 * Flushes ZipFiles cache if there are no more clients.
2046 	 */
flushZipFiles(Object owner)2047 	public void flushZipFiles(Object owner) {
2048 		ZipCache zipCache = this.zipFiles.get();
2049 		if (zipCache == null) {
2050 			if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
2051 				System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.flushZipFiles(String)] NOT found cache for " + owner); //$NON-NLS-1$	//$NON-NLS-2$
2052 			}
2053 			return;
2054 		}
2055 		// the owner will be responsible for flushing the cache
2056 		// we want to check object identity to make sure this is the owner that created the cache
2057 		if (zipCache.owner == owner) {
2058 			this.zipFiles.set(null);
2059 			zipCache.flush();
2060 		} else {
2061 			if (JavaModelManager.ZIP_ACCESS_VERBOSE) {
2062 				System.out.println("(" + Thread.currentThread() //$NON-NLS-1$
2063 						+ ") [JavaModelManager.flushZipFiles(String)] NOT closed cache, wrong owner, expected: " //$NON-NLS-1$
2064 						+ zipCache.owner + ", got: " + owner); //$NON-NLS-1$
2065 			}
2066 		}
2067 	}
2068 
2069 	/*
2070 	 * Returns true if forcing batch initialization was successful.
2071 	 * Returns false if batch initialization is already running.
2072 	 */
forceBatchInitializations(boolean initAfterLoad)2073 	public synchronized boolean forceBatchInitializations(boolean initAfterLoad) {
2074 		switch (this.batchContainerInitializations) {
2075 		case NO_BATCH_INITIALIZATION:
2076 			this.batchContainerInitializations = NEED_BATCH_INITIALIZATION;
2077 			return true;
2078 		case BATCH_INITIALIZATION_FINISHED:
2079 			if (initAfterLoad)
2080 				return false; // no need to initialize again
2081 			this.batchContainerInitializations = NEED_BATCH_INITIALIZATION;
2082 			return true;
2083 		}
2084 		return false;
2085 	}
2086 
batchContainerInitializations()2087 	private synchronized boolean batchContainerInitializations() {
2088 		switch (this.batchContainerInitializations) {
2089 		case NEED_BATCH_INITIALIZATION:
2090 			this.batchContainerInitializations = BATCH_INITIALIZATION_IN_PROGRESS;
2091 			return true;
2092 		case BATCH_INITIALIZATION_IN_PROGRESS:
2093 			return true;
2094 		}
2095 		return false;
2096 	}
2097 
batchInitializationFinished()2098 	private synchronized void batchInitializationFinished() {
2099 		this.batchContainerInitializations = BATCH_INITIALIZATION_FINISHED;
2100 	}
2101 
getClasspathContainer(final IPath containerPath, final IJavaProject project)2102 	public IClasspathContainer getClasspathContainer(final IPath containerPath, final IJavaProject project) throws JavaModelException {
2103 
2104 		IClasspathContainer container = containerGet(project, containerPath);
2105 
2106 		if (container == null) {
2107 			if (batchContainerInitializations()) {
2108 				// avoid deep recursion while initializing container on workspace restart
2109 				// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=60437)
2110 				try {
2111 					container = initializeAllContainers(project, containerPath);
2112 				} finally {
2113 					batchInitializationFinished();
2114 				}
2115 			} else {
2116 				container = initializeContainer(project, containerPath);
2117 				containerBeingInitializedRemove(project, containerPath);
2118 				SetContainerOperation operation = new SetContainerOperation(containerPath, new IJavaProject[] {project}, new IClasspathContainer[] {container});
2119 				operation.runOperation(null);
2120 			}
2121 		}
2122 		return container;
2123 	}
2124 
getReferencedClasspathEntries(IClasspathEntry libraryEntry, IJavaProject project)2125 	public IClasspathEntry[] getReferencedClasspathEntries(IClasspathEntry libraryEntry, IJavaProject project) {
2126 
2127 		IClasspathEntry[] referencedEntries = ((ClasspathEntry)libraryEntry).resolvedChainedLibraries();
2128 
2129 		if (project == null)
2130 			return referencedEntries;
2131 
2132 		PerProjectInfo perProjectInfo = getPerProjectInfo(project.getProject(), false);
2133 		if(perProjectInfo == null)
2134 			return referencedEntries;
2135 
2136 		LinkedHashSet<IPath> pathToReferencedEntries = new LinkedHashSet<>(referencedEntries.length);
2137 		for (int index = 0; index < referencedEntries.length; index++) {
2138 
2139 			if (pathToReferencedEntries.contains(referencedEntries[index].getPath()))
2140 				continue;
2141 
2142 			IClasspathEntry persistedEntry = null;
2143 			if ((persistedEntry = perProjectInfo.rootPathToResolvedEntries.get(referencedEntries[index].getPath())) != null) {
2144 				// TODO: reconsider this - may want to copy the values instead of reference assignment?
2145 				referencedEntries[index] = persistedEntry;
2146 			}
2147 			pathToReferencedEntries.add(referencedEntries[index].getPath());
2148 		}
2149 		return referencedEntries;
2150 	}
2151 
getDeltaProcessor()2152 	public DeltaProcessor getDeltaProcessor() {
2153 		return this.deltaState.getDeltaProcessor();
2154 	}
2155 
getDeltaState()2156 	public static DeltaProcessingState getDeltaState() {
2157 		return MANAGER.deltaState;
2158 	}
2159 
2160 	/**
2161 	 * Returns the set of elements which are out of synch with their buffers.
2162 	 */
getElementsOutOfSynchWithBuffers()2163 	protected HashSet<Openable> getElementsOutOfSynchWithBuffers() {
2164 		return this.elementsOutOfSynchWithBuffers;
2165 	}
2166 
getExternalManager()2167 	public static ExternalFoldersManager getExternalManager() {
2168 		return MANAGER.externalFoldersManager;
2169 	}
2170 
getIndexManager()2171 	public static IndexManager getIndexManager() {
2172 		return MANAGER.indexManager;
2173 	}
2174 
2175 	/**
2176 	 *  Returns the info for the element.
2177 	 */
getInfo(IJavaElement element)2178 	public synchronized Object getInfo(IJavaElement element) {
2179 		HashMap<IJavaElement, Object> tempCache = this.temporaryCache.get();
2180 		if (tempCache != null) {
2181 			Object result = tempCache.get(element);
2182 			if (result != null) {
2183 				return result;
2184 			}
2185 		}
2186 		return this.cache.getInfo(element);
2187 	}
2188 
2189 	/**
2190 	 *  Returns the existing element in the cache that is equal to the given element.
2191 	 */
getExistingElement(IJavaElement element)2192 	public synchronized IJavaElement getExistingElement(IJavaElement element) {
2193 		return this.cache.getExistingElement(element);
2194 	}
2195 
getExternalWorkingCopyProjects()2196 	public HashSet<IJavaProject> getExternalWorkingCopyProjects() {
2197 		synchronized (this.perWorkingCopyInfos) {
2198 			HashSet<IJavaProject> result = null;
2199 			Iterator<Map<CompilationUnit, PerWorkingCopyInfo>> values = this.perWorkingCopyInfos.values().iterator();
2200 			while (values.hasNext()) {
2201 				Map<CompilationUnit, PerWorkingCopyInfo> ownerCopies = values.next();
2202 				Iterator<CompilationUnit> workingCopies = ownerCopies.keySet().iterator();
2203 				while (workingCopies.hasNext()) {
2204 					ICompilationUnit workingCopy = workingCopies.next();
2205 					IJavaProject project = workingCopy.getJavaProject();
2206 					if (project.getElementName().equals(ExternalJavaProject.EXTERNAL_PROJECT_NAME)) {
2207 						if (result == null)
2208 							result = new HashSet<>();
2209 						result.add(project);
2210 					}
2211 				}
2212 			}
2213 			return result;
2214 		}
2215 	}
2216 
2217 	/**
2218 	 * Get workspace eclipse preference for JavaCore plug-in.
2219 	 */
getInstancePreferences()2220 	public IEclipsePreferences getInstancePreferences() {
2221 		return this.preferencesLookup[PREF_INSTANCE];
2222 	}
2223 
2224 	// If modified, also modify the method getDefaultOptionsNoInitialization()
getDefaultOptions()2225 	public Hashtable<String, String> getDefaultOptions(){
2226 
2227 		Hashtable<String, String> defaultOptions = new Hashtable<>(10);
2228 
2229 		// see JavaCorePreferenceInitializer#initializeDefaultPluginPreferences() for changing default settings
2230 		// If modified, also modify the method getDefaultOptionsNoInitialization()
2231 		IEclipsePreferences defaultPreferences = getDefaultPreferences();
2232 
2233 		// initialize preferences to their default
2234 		Iterator<String> iterator = this.optionNames.iterator();
2235 		while (iterator.hasNext()) {
2236 		    String propertyName = iterator.next();
2237 		    String value = defaultPreferences.get(propertyName, null);
2238 		    if (value != null) defaultOptions.put(propertyName, value);
2239 		}
2240 		// get encoding through resource plugin
2241 		defaultOptions.put(JavaCore.CORE_ENCODING, JavaCore.getEncoding());
2242 		// backward compatibility
2243 		addDeprecatedOptions(defaultOptions);
2244 
2245 		return defaultOptions;
2246 	}
2247 
2248 	/**
2249 	 * Get default eclipse preference for JavaCore plugin.
2250 	 */
getDefaultPreferences()2251 	public IEclipsePreferences getDefaultPreferences() {
2252 		return this.preferencesLookup[PREF_DEFAULT];
2253 	}
2254 
2255 	/**
2256 	 * Returns the handle to the active Java Model.
2257 	 */
getJavaModel()2258 	public final JavaModel getJavaModel() {
2259 		return this.javaModel;
2260 	}
2261 
2262 	/**
2263 	 * Returns the singleton JavaModelManager
2264 	 */
getJavaModelManager()2265 	public final static JavaModelManager getJavaModelManager() {
2266 		return MANAGER;
2267 	}
2268 
2269 	/**
2270 	 * Returns the last built state for the given project, or null if there is none.
2271 	 * Deserializes the state if necessary.
2272 	 *
2273 	 * For use by image builder and evaluation support only
2274 	 */
getLastBuiltState(IProject project, IProgressMonitor monitor)2275 	public Object getLastBuiltState(IProject project, IProgressMonitor monitor) {
2276 		if (!JavaProject.hasJavaNature(project)) {
2277 			if (JavaBuilder.DEBUG)
2278 				System.out.println(project + " is not a Java project"); //$NON-NLS-1$
2279 			return null; // should never be requested on non-Java projects
2280 		}
2281 		PerProjectInfo info = getPerProjectInfo(project, true/*create if missing*/);
2282 		if (!info.triedRead) {
2283 			info.triedRead = true;
2284 			try {
2285 				if (monitor != null)
2286 					monitor.subTask(Messages.bind(Messages.build_readStateProgress, project.getName()));
2287 				info.savedState = readState(project);
2288 			} catch (CoreException e) {
2289 				Util.log(e, "Exception while reading last build state for: " + project); //$NON-NLS-1$
2290 			}
2291 		}
2292 		return info.savedState;
2293 	}
2294 
getOption(String optionName)2295 	public String getOption(String optionName) {
2296 
2297 		if (JavaCore.CORE_ENCODING.equals(optionName)){
2298 			return JavaCore.getEncoding();
2299 		}
2300 		// backward compatibility
2301 		if (isDeprecatedOption(optionName)) {
2302 			return JavaCore.ERROR;
2303 		}
2304 		int optionLevel = getOptionLevel(optionName);
2305 		if (optionLevel != UNKNOWN_OPTION){
2306 			IPreferencesService service = Platform.getPreferencesService();
2307 			String value = service.get(optionName, null, this.preferencesLookup);
2308 			if (value == null && optionLevel == DEPRECATED_OPTION) {
2309 				// May be a deprecated option, retrieve the new value in compatible options
2310 				String[] compatibleOptions = this.deprecatedOptions.get(optionName);
2311 				value = service.get(compatibleOptions[0], null, this.preferencesLookup);
2312 			}
2313 			return value==null ? null : value.trim();
2314 		}
2315 		return null;
2316 	}
2317 
2318 	/**
2319 	 * Returns the value of the given option for the given Eclipse preferences.
2320 	 * If no value was already set, then inherits from the global options if specified.
2321 	 *
2322 	 * @param optionName The name of the option
2323 	 * @param inheritJavaCoreOptions Tells whether the value can be inherited from global JavaCore options
2324 	 * @param projectPreferences The eclipse preferences from which to get the value
2325 	 * @return The value of the option. May be <code>null</code>
2326 	 */
getOption(String optionName, boolean inheritJavaCoreOptions, IEclipsePreferences projectPreferences)2327 	public String getOption(String optionName, boolean inheritJavaCoreOptions, IEclipsePreferences projectPreferences) {
2328 		// Return the option value depending on its level
2329 		switch (getOptionLevel(optionName)) {
2330 			case VALID_OPTION:
2331 				// Valid option, return the preference value
2332 				String javaCoreDefault = inheritJavaCoreOptions ? JavaCore.getOption(optionName) : null;
2333 				if (projectPreferences == null) return javaCoreDefault;
2334 				String value = projectPreferences.get(optionName, javaCoreDefault);
2335 				return value == null ? null : value.trim();
2336 			case DEPRECATED_OPTION:
2337 				// Return the deprecated option value if it was already set
2338 				String oldValue = projectPreferences.get(optionName, null);
2339 				if (oldValue != null) {
2340 					return oldValue.trim();
2341 				}
2342 				// Get the new compatible value
2343 				String[] compatibleOptions = this.deprecatedOptions.get(optionName);
2344 				String newDefault = inheritJavaCoreOptions ? JavaCore.getOption(compatibleOptions[0]) : null;
2345 				String newValue = projectPreferences.get(compatibleOptions[0], newDefault);
2346 				return newValue == null ? null : newValue.trim();
2347 		}
2348 		return null;
2349 	}
2350 
2351 	/**
2352 	 * Returns whether an option name is known or not.
2353 	 *
2354 	 * @param optionName The name of the option
2355 	 * @return <code>true</code> when the option name is either
2356 	 * {@link #VALID_OPTION valid} or {@link #DEPRECATED_OPTION deprecated},
2357 	 * <code>false</code> otherwise.
2358 	 */
knowsOption(String optionName)2359 	public boolean knowsOption(String optionName) {
2360 		boolean knownOption = this.optionNames.contains(optionName);
2361 		if (!knownOption) {
2362 			knownOption = this.deprecatedOptions.get(optionName) != null;
2363 		}
2364 		return knownOption;
2365 	}
2366 
2367 	/**
2368 	 * Returns the level of the given option.
2369 	 *
2370 	 * @param optionName The name of the option
2371 	 * @return The level of the option as an int which may have the following
2372 	 * values:
2373 	 * <ul>
2374 	 * <li>{@link #UNKNOWN_OPTION}: the given option is unknown</li>
2375 	 * <li>{@link #DEPRECATED_OPTION}: the given option is deprecated</li>
2376 	 * <li>{@link #VALID_OPTION}: the given option is valid</li>
2377 	 * </ul>
2378 	 */
getOptionLevel(String optionName)2379 	public int getOptionLevel(String optionName) {
2380 		if (this.optionNames.contains(optionName)) {
2381 			return VALID_OPTION;
2382 		}
2383 		if (this.deprecatedOptions.get(optionName) != null) {
2384 			return DEPRECATED_OPTION;
2385 		}
2386 		return UNKNOWN_OPTION;
2387 	}
2388 
getOptions()2389 	public Hashtable<String, String> getOptions() {
2390 
2391 		// return cached options if already computed
2392 		Hashtable<String, String> cachedOptions; // use a local variable to avoid race condition (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=256329 )
2393 		if ((cachedOptions = this.optionsCache) != null) {
2394 			return new Hashtable<>(cachedOptions);
2395 		}
2396 		if (!Platform.isRunning()) {
2397 			this.optionsCache = getDefaultOptionsNoInitialization();
2398 			return new Hashtable<>(this.optionsCache);
2399 		}
2400 		// init
2401 		Hashtable<String, String> options = new Hashtable<>(10);
2402 		IPreferencesService service = Platform.getPreferencesService();
2403 
2404 		// set options using preferences service lookup
2405 		Iterator<String> iterator = this.optionNames.iterator();
2406 		while (iterator.hasNext()) {
2407 			String propertyName = iterator.next();
2408 			String propertyValue = service.get(propertyName, null, this.preferencesLookup);
2409 			if (propertyValue != null) {
2410 				options.put(propertyName, propertyValue);
2411 			}
2412 		}
2413 
2414 		// set deprecated options using preferences service lookup
2415 		Iterator<Entry<String, String[]>> deprecatedEntries = this.deprecatedOptions.entrySet().iterator();
2416 		while (deprecatedEntries.hasNext()) {
2417 			Entry<String, String[]> entry = deprecatedEntries.next();
2418 			String propertyName = entry.getKey();
2419 			String propertyValue = service.get(propertyName, null, this.preferencesLookup);
2420 			if (propertyValue != null) {
2421 				options.put(propertyName, propertyValue);
2422 				String[] compatibleOptions = entry.getValue();
2423 				for (int co=0, length=compatibleOptions.length; co < length; co++) {
2424 					String compatibleOption = compatibleOptions[co];
2425 					if (!options.containsKey(compatibleOption))
2426 						options.put(compatibleOption, propertyValue);
2427 				}
2428 			}
2429 		}
2430 
2431 		// get encoding through resource plugin
2432 		options.put(JavaCore.CORE_ENCODING, JavaCore.getEncoding());
2433 
2434 		// backward compatibility
2435 		addDeprecatedOptions(options);
2436 
2437 		Util.fixTaskTags(options);
2438 		// store built map in cache
2439 		this.optionsCache = new Hashtable<>(options);
2440 		// return built map
2441 		return options;
2442 	}
2443 
2444 	// Do not modify without modifying getDefaultOptions()
getDefaultOptionsNoInitialization()2445 	private Hashtable<String, String> getDefaultOptionsNoInitialization() {
2446 		Map<String, String> defaultOptionsMap = new CompilerOptions().getMap(); // compiler defaults
2447 
2448 		// Override some compiler defaults
2449 		defaultOptionsMap.put(JavaCore.COMPILER_LOCAL_VARIABLE_ATTR, JavaCore.GENERATE);
2450 		defaultOptionsMap.put(JavaCore.COMPILER_CODEGEN_UNUSED_LOCAL, JavaCore.PRESERVE);
2451 		defaultOptionsMap.put(JavaCore.COMPILER_TASK_TAGS, JavaCore.DEFAULT_TASK_TAGS);
2452 		defaultOptionsMap.put(JavaCore.COMPILER_TASK_PRIORITIES, JavaCore.DEFAULT_TASK_PRIORITIES);
2453 		defaultOptionsMap.put(JavaCore.COMPILER_TASK_CASE_SENSITIVE, JavaCore.ENABLED);
2454 		defaultOptionsMap.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED);
2455 		defaultOptionsMap.put(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, JavaCore.ERROR);
2456 
2457 		// Builder settings
2458 		defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER, ""); //$NON-NLS-1$
2459 		defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH, JavaCore.ABORT);
2460 		defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE, JavaCore.WARNING);
2461 		defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER, JavaCore.CLEAN);
2462 
2463 		// JavaCore settings
2464 		defaultOptionsMap.put(JavaCore.CORE_JAVA_BUILD_ORDER, JavaCore.IGNORE);
2465 		defaultOptionsMap.put(JavaCore.CORE_INCOMPLETE_CLASSPATH, JavaCore.ERROR);
2466 		defaultOptionsMap.put(JavaCore.CORE_CIRCULAR_CLASSPATH, JavaCore.ERROR);
2467 		defaultOptionsMap.put(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, JavaCore.IGNORE);
2468 		defaultOptionsMap.put(JavaCore.CORE_MAIN_ONLY_PROJECT_HAS_TEST_ONLY_DEPENDENCY, JavaCore.ERROR);
2469 		defaultOptionsMap.put(JavaCore.CORE_OUTPUT_LOCATION_OVERLAPPING_ANOTHER_SOURCE, JavaCore.ERROR);
2470 		defaultOptionsMap.put(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS, JavaCore.ENABLED);
2471 		defaultOptionsMap.put(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS, JavaCore.ENABLED);
2472 
2473 		// Formatter settings
2474 		defaultOptionsMap.putAll(DefaultCodeFormatterConstants.getEclipseDefaultSettings());
2475 
2476 		// CodeAssist settings
2477 		defaultOptionsMap.put(JavaCore.CODEASSIST_VISIBILITY_CHECK, JavaCore.DISABLED);
2478 		defaultOptionsMap.put(JavaCore.CODEASSIST_DEPRECATION_CHECK, JavaCore.DISABLED);
2479 		defaultOptionsMap.put(JavaCore.CODEASSIST_IMPLICIT_QUALIFICATION, JavaCore.DISABLED);
2480 		defaultOptionsMap.put(JavaCore.CODEASSIST_FIELD_PREFIXES, ""); //$NON-NLS-1$
2481 		defaultOptionsMap.put(JavaCore.CODEASSIST_STATIC_FIELD_PREFIXES, ""); //$NON-NLS-1$
2482 		defaultOptionsMap.put(JavaCore.CODEASSIST_STATIC_FINAL_FIELD_PREFIXES, ""); //$NON-NLS-1$
2483 		defaultOptionsMap.put(JavaCore.CODEASSIST_LOCAL_PREFIXES, ""); //$NON-NLS-1$
2484 		defaultOptionsMap.put(JavaCore.CODEASSIST_ARGUMENT_PREFIXES, ""); //$NON-NLS-1$
2485 		defaultOptionsMap.put(JavaCore.CODEASSIST_FIELD_SUFFIXES, ""); //$NON-NLS-1$
2486 		defaultOptionsMap.put(JavaCore.CODEASSIST_STATIC_FIELD_SUFFIXES, ""); //$NON-NLS-1$
2487 		defaultOptionsMap.put(JavaCore.CODEASSIST_STATIC_FINAL_FIELD_SUFFIXES, ""); //$NON-NLS-1$
2488 		defaultOptionsMap.put(JavaCore.CODEASSIST_LOCAL_SUFFIXES, ""); //$NON-NLS-1$
2489 		defaultOptionsMap.put(JavaCore.CODEASSIST_ARGUMENT_SUFFIXES, ""); //$NON-NLS-1$
2490 		defaultOptionsMap.put(JavaCore.CODEASSIST_FORBIDDEN_REFERENCE_CHECK, JavaCore.ENABLED);
2491 		defaultOptionsMap.put(JavaCore.CODEASSIST_DISCOURAGED_REFERENCE_CHECK, JavaCore.DISABLED);
2492 		defaultOptionsMap.put(JavaCore.CODEASSIST_CAMEL_CASE_MATCH, JavaCore.ENABLED);
2493 		defaultOptionsMap.put(JavaCore.CODEASSIST_SUBWORD_MATCH, JavaCore.ENABLED);
2494 		defaultOptionsMap.put(JavaCore.CODEASSIST_SUGGEST_STATIC_IMPORTS, JavaCore.ENABLED);
2495 
2496 		// Time out for parameter names
2497 		defaultOptionsMap.put(JavaCore.TIMEOUT_FOR_PARAMETER_NAME_FROM_ATTACHED_JAVADOC, "50"); //$NON-NLS-1$
2498 
2499 		return new Hashtable<>(defaultOptionsMap);
2500 	}
2501 
2502 	/*
2503 	 * Returns the per-project info for the given project. If specified, create the info if the info doesn't exist.
2504 	 */
getPerProjectInfo(IProject project, boolean create)2505 	public PerProjectInfo getPerProjectInfo(IProject project, boolean create) {
2506 		synchronized(this.perProjectInfos) { // use the perProjectInfo collection as its own lock
2507 			PerProjectInfo info= this.perProjectInfos.get(project);
2508 			if (info == null && create) {
2509 				info= new PerProjectInfo(project);
2510 				this.perProjectInfos.put(project, info);
2511 			}
2512 			return info;
2513 		}
2514 	}
2515 
2516 	/*
2517 	 * Returns  the per-project info for the given project.
2518 	 * If the info doesn't exist, check for the project existence and create the info.
2519 	 * @throws JavaModelException if the project doesn't exist.
2520 	 */
getPerProjectInfoCheckExistence(IProject project)2521 	public PerProjectInfo getPerProjectInfoCheckExistence(IProject project) throws JavaModelException {
2522 		JavaModelManager.PerProjectInfo info = getPerProjectInfo(project, false /* don't create info */);
2523 		if (info == null) {
2524 			if (!JavaProject.hasJavaNature(project)) {
2525 				throw ((JavaProject)JavaCore.create(project)).newNotPresentException();
2526 			}
2527 			info = getPerProjectInfo(project, true /* create info */);
2528 		}
2529 		return info;
2530 	}
2531 
2532 	/*
2533 	 * Returns the per-working copy info for the given working copy at the given path.
2534 	 * If it doesn't exist and if create, add a new per-working copy info with the given problem requestor.
2535 	 * If recordUsage, increment the per-working copy info's use count.
2536 	 * Returns null if it doesn't exist and not create.
2537 	 */
getPerWorkingCopyInfo(CompilationUnit workingCopy,boolean create, boolean recordUsage, IProblemRequestor problemRequestor)2538 	public PerWorkingCopyInfo getPerWorkingCopyInfo(CompilationUnit workingCopy,boolean create, boolean recordUsage, IProblemRequestor problemRequestor) {
2539 		synchronized(this.perWorkingCopyInfos) { // use the perWorkingCopyInfo collection as its own lock
2540 			WorkingCopyOwner owner = workingCopy.owner;
2541 			Map<CompilationUnit, PerWorkingCopyInfo> workingCopyToInfos = this.perWorkingCopyInfos.get(owner);
2542 			if (workingCopyToInfos == null && create) {
2543 				workingCopyToInfos = new HashMap<>();
2544 				this.perWorkingCopyInfos.put(owner, workingCopyToInfos);
2545 			}
2546 
2547 			PerWorkingCopyInfo info = workingCopyToInfos == null ? null : workingCopyToInfos.get(workingCopy);
2548 			if (info == null && create) {
2549 				info= new PerWorkingCopyInfo(workingCopy, problemRequestor);
2550 				workingCopyToInfos.put(workingCopy, info);
2551 			}
2552 			if (info != null && recordUsage) info.useCount++;
2553 			return info;
2554 		}
2555 	}
2556 
2557 	/**
2558 	 * Returns a persisted container from previous session if any. Note that it is not the original container from previous
2559 	 * session (i.e. it did not get serialized) but rather a summary of its entries recreated for CP initialization purpose.
2560 	 * As such it should not be stored into container caches.
2561 	 */
getPreviousSessionContainer(IPath containerPath, IJavaProject project)2562 	public IClasspathContainer getPreviousSessionContainer(IPath containerPath, IJavaProject project) {
2563 			Map<IPath, IClasspathContainer> previousContainerValues = this.previousSessionContainers.get(project);
2564 			if (previousContainerValues != null){
2565 			    IClasspathContainer previousContainer = previousContainerValues.get(containerPath);
2566 			    if (previousContainer != null) {
2567 					if (JavaModelManager.CP_RESOLVE_VERBOSE_ADVANCED)
2568 						verbose_reentering_project_container_access(containerPath, project, previousContainer);
2569 					return previousContainer;
2570 			    }
2571 			}
2572 		    return null; // break cycle if none found
2573 	}
2574 
verbose_reentering_project_container_access( IPath containerPath, IJavaProject project, IClasspathContainer previousContainer)2575 	private void verbose_reentering_project_container_access(	IPath containerPath, IJavaProject project, IClasspathContainer previousContainer) {
2576 		StringBuffer buffer = new StringBuffer();
2577 		buffer.append("CPContainer INIT - reentering access to project container during its initialization, will see previous value\n"); //$NON-NLS-1$
2578 		buffer.append("	project: " + project.getElementName() + '\n'); //$NON-NLS-1$
2579 		buffer.append("	container path: " + containerPath + '\n'); //$NON-NLS-1$
2580 		buffer.append("	previous value: "); //$NON-NLS-1$
2581 		buffer.append(previousContainer.getDescription());
2582 		buffer.append(" {\n"); //$NON-NLS-1$
2583 		IClasspathEntry[] entries = previousContainer.getClasspathEntries();
2584 		if (entries != null){
2585 			for (int j = 0; j < entries.length; j++){
2586 				buffer.append(" 		"); //$NON-NLS-1$
2587 				buffer.append(entries[j]);
2588 				buffer.append('\n');
2589 			}
2590 		}
2591 		buffer.append(" 	}"); //$NON-NLS-1$
2592 		Util.verbose(buffer.toString());
2593 		new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$
2594 	}
2595 
2596 	/**
2597 	 * Returns a persisted container from previous session if any
2598 	 */
getPreviousSessionVariable(String variableName)2599 	public IPath getPreviousSessionVariable(String variableName) {
2600 		IPath previousPath = this.previousSessionVariables.get(variableName);
2601 		if (previousPath != null){
2602 			if (CP_RESOLVE_VERBOSE_ADVANCED)
2603 				verbose_reentering_variable_access(variableName, previousPath);
2604 			return previousPath;
2605 		}
2606 	    return null; // break cycle
2607 	}
2608 
verbose_reentering_variable_access(String variableName, IPath previousPath)2609 	private void verbose_reentering_variable_access(String variableName, IPath previousPath) {
2610 		Util.verbose(
2611 			"CPVariable INIT - reentering access to variable during its initialization, will see previous value\n" + //$NON-NLS-1$
2612 			"	variable: "+ variableName + '\n' + //$NON-NLS-1$
2613 			"	previous value: " + previousPath); //$NON-NLS-1$
2614 		new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$
2615 	}
2616 
2617 	/**
2618 	 * Returns the temporary cache for newly opened elements for the current thread.
2619 	 * Creates it if not already created.
2620 	 */
getTemporaryCache()2621 	public HashMap<IJavaElement, Object> getTemporaryCache() {
2622 		HashMap<IJavaElement, Object> result = this.temporaryCache.get();
2623 		if (result == null) {
2624 			result = new HashMap<>();
2625 			this.temporaryCache.set(result);
2626 		}
2627 		return result;
2628 	}
2629 
getVariableAndContainersFile()2630 	private File getVariableAndContainersFile() {
2631 		return JavaCore.getPlugin().getStateLocation().append("variablesAndContainers.dat").toFile(); //$NON-NLS-1$
2632 	}
2633 
2634 	/**
2635  	 * Returns the name of the variables for which an CP variable initializer is registered through an extension point
2636  	 */
getRegisteredVariableNames()2637 	public static String[] getRegisteredVariableNames(){
2638 
2639 		Plugin jdtCorePlugin = JavaCore.getPlugin();
2640 		if (jdtCorePlugin == null) return null;
2641 
2642 		ArrayList<String> variableList = new ArrayList<>(5);
2643 		IExtensionPoint extension = Platform.getExtensionRegistry().getExtensionPoint(JavaCore.PLUGIN_ID, JavaModelManager.CPVARIABLE_INITIALIZER_EXTPOINT_ID);
2644 		if (extension != null) {
2645 			IExtension[] extensions =  extension.getExtensions();
2646 			for(int i = 0; i < extensions.length; i++){
2647 				IConfigurationElement [] configElements = extensions[i].getConfigurationElements();
2648 				for(int j = 0; j < configElements.length; j++){
2649 					String varAttribute = configElements[j].getAttribute("variable"); //$NON-NLS-1$
2650 					if (varAttribute != null) variableList.add(varAttribute);
2651 				}
2652 			}
2653 		}
2654 		String[] variableNames = new String[variableList.size()];
2655 		variableList.toArray(variableNames);
2656 		return variableNames;
2657 	}
2658 
2659 	/**
2660  	 * Returns the name of the container IDs for which an CP container initializer is registered through an extension point
2661  	 */
getRegisteredContainerIDs()2662 	public static String[] getRegisteredContainerIDs(){
2663 
2664 		Plugin jdtCorePlugin = JavaCore.getPlugin();
2665 		if (jdtCorePlugin == null) return null;
2666 
2667 		ArrayList<String> containerIDList = new ArrayList<>(5);
2668 		IExtensionPoint extension = Platform.getExtensionRegistry().getExtensionPoint(JavaCore.PLUGIN_ID, JavaModelManager.CPCONTAINER_INITIALIZER_EXTPOINT_ID);
2669 		if (extension != null) {
2670 			IExtension[] extensions =  extension.getExtensions();
2671 			for(int i = 0; i < extensions.length; i++){
2672 				IConfigurationElement [] configElements = extensions[i].getConfigurationElements();
2673 				for(int j = 0; j < configElements.length; j++){
2674 					String idAttribute = configElements[j].getAttribute("id"); //$NON-NLS-1$
2675 					if (idAttribute != null) containerIDList.add(idAttribute);
2676 				}
2677 			}
2678 		}
2679 		String[] containerIDs = new String[containerIDList.size()];
2680 		containerIDList.toArray(containerIDs);
2681 		return containerIDs;
2682 	}
2683 
resolveVariableEntry(IClasspathEntry entry, boolean usePreviousSession)2684 	public IClasspathEntry resolveVariableEntry(IClasspathEntry entry, boolean usePreviousSession) {
2685 
2686 		if (entry.getEntryKind() != IClasspathEntry.CPE_VARIABLE)
2687 			return entry;
2688 
2689 		IPath resolvedPath = getResolvedVariablePath(entry.getPath(), usePreviousSession);
2690 		if (resolvedPath == null)
2691 			return null;
2692 		// By passing a null reference path, we keep it relative to workspace root.
2693 		resolvedPath = ClasspathEntry.resolveDotDot(null, resolvedPath);
2694 
2695 		Object target = JavaModel.getTarget(resolvedPath, false);
2696 		if (target == null)
2697 			return null;
2698 
2699 		// inside the workspace
2700 		if (target instanceof IResource) {
2701 			IResource resolvedResource = (IResource) target;
2702 			switch (resolvedResource.getType()) {
2703 
2704 				case IResource.PROJECT :
2705 					// internal project
2706 					return JavaCore.newProjectEntry(
2707 							resolvedPath,
2708 							entry.getAccessRules(),
2709 							entry.combineAccessRules(),
2710 							entry.getExtraAttributes(),
2711 							entry.isExported());
2712 				case IResource.FILE :
2713 					// internal binary archive
2714 					return JavaCore.newLibraryEntry(
2715 							resolvedPath,
2716 							getResolvedVariablePath(entry.getSourceAttachmentPath(), usePreviousSession),
2717 							getResolvedVariablePath(entry.getSourceAttachmentRootPath(), usePreviousSession),
2718 							entry.getAccessRules(),
2719 							entry.getExtraAttributes(),
2720 							entry.isExported());
2721 				case IResource.FOLDER :
2722 					// internal binary folder
2723 					return JavaCore.newLibraryEntry(
2724 							resolvedPath,
2725 							getResolvedVariablePath(entry.getSourceAttachmentPath(), usePreviousSession),
2726 							getResolvedVariablePath(entry.getSourceAttachmentRootPath(), usePreviousSession),
2727 							entry.getAccessRules(),
2728 							entry.getExtraAttributes(),
2729 							entry.isExported());
2730 			}
2731 		}
2732 		if (target instanceof File) {
2733 			File externalFile = JavaModel.getFile(target);
2734 			if (externalFile != null) {
2735 				// external binary archive
2736 				return JavaCore.newLibraryEntry(
2737 						resolvedPath,
2738 						getResolvedVariablePath(entry.getSourceAttachmentPath(), usePreviousSession),
2739 						getResolvedVariablePath(entry.getSourceAttachmentRootPath(), usePreviousSession),
2740 						entry.getAccessRules(),
2741 						entry.getExtraAttributes(),
2742 						entry.isExported());
2743 			} else {
2744 				// non-existing file
2745 				if (resolvedPath.isAbsolute()){
2746 					return JavaCore.newLibraryEntry(
2747 							resolvedPath,
2748 							getResolvedVariablePath(entry.getSourceAttachmentPath(), usePreviousSession),
2749 							getResolvedVariablePath(entry.getSourceAttachmentRootPath(), usePreviousSession),
2750 							entry.getAccessRules(),
2751 							entry.getExtraAttributes(),
2752 							entry.isExported());
2753 				}
2754 			}
2755 		}
2756 		return null;
2757 	}
2758 
getResolvedVariablePath(IPath variablePath, boolean usePreviousSession)2759 	public IPath getResolvedVariablePath(IPath variablePath, boolean usePreviousSession) {
2760 
2761 		if (variablePath == null)
2762 			return null;
2763 		int count = variablePath.segmentCount();
2764 		if (count == 0)
2765 			return null;
2766 
2767 		// lookup variable
2768 		String variableName = variablePath.segment(0);
2769 		IPath resolvedPath = usePreviousSession ? getPreviousSessionVariable(variableName) : JavaCore.getClasspathVariable(variableName);
2770 		if (resolvedPath == null)
2771 			return null;
2772 
2773 		// append path suffix
2774 		if (count > 1) {
2775 			resolvedPath = resolvedPath.append(variablePath.removeFirstSegments(1));
2776 		}
2777 		return resolvedPath;
2778 	}
2779 
2780 	/**
2781 	 * Returns the File to use for saving and restoring the last built state for the given project.
2782 	 */
getSerializationFile(IProject project)2783 	private File getSerializationFile(IProject project) {
2784 		if (!project.exists()) return null;
2785 		IPath workingLocation = project.getWorkingLocation(JavaCore.PLUGIN_ID);
2786 		return workingLocation.append("state.dat").toFile(); //$NON-NLS-1$
2787 	}
2788 
getUserLibraryManager()2789 	public static UserLibraryManager getUserLibraryManager() {
2790 		if (MANAGER.userLibraryManager == null) {
2791 			UserLibraryManager libraryManager = new UserLibraryManager();
2792 			synchronized(MANAGER) {
2793 				if (MANAGER.userLibraryManager == null) { // ensure another library manager was not set while creating the instance above
2794 					MANAGER.userLibraryManager = libraryManager;
2795 				}
2796 			}
2797 		}
2798 		return MANAGER.userLibraryManager;
2799 	}
2800 
getModulePathManager()2801 	public static ModuleSourcePathManager getModulePathManager() {
2802 		if (MANAGER.modulePathManager == null) {
2803 			ModuleSourcePathManager modulePathManager = new ModuleSourcePathManager();
2804 			synchronized(MANAGER) {
2805 				if (MANAGER.modulePathManager == null) { // ensure another library manager was not set while creating the instance above
2806 					MANAGER.modulePathManager = modulePathManager;
2807 				}
2808 			}
2809 		}
2810 		return MANAGER.modulePathManager;
2811 	}
2812 	/*
2813 	 * Returns all the working copies which have the given owner.
2814 	 * Adds the working copies of the primary owner if specified.
2815 	 * Returns null if it has none.
2816 	 */
getWorkingCopies(WorkingCopyOwner owner, boolean addPrimary)2817 	public ICompilationUnit[] getWorkingCopies(WorkingCopyOwner owner, boolean addPrimary) {
2818 		synchronized(this.perWorkingCopyInfos) {
2819 			ICompilationUnit[] primaryWCs = addPrimary && owner != DefaultWorkingCopyOwner.PRIMARY
2820 				? getWorkingCopies(DefaultWorkingCopyOwner.PRIMARY, false)
2821 				: null;
2822 			Map<CompilationUnit, PerWorkingCopyInfo> workingCopyToInfos = this.perWorkingCopyInfos.get(owner);
2823 			if (workingCopyToInfos == null) return primaryWCs;
2824 			int primaryLength = primaryWCs == null ? 0 : primaryWCs.length;
2825 			int size = workingCopyToInfos.size(); // note size is > 0 otherwise pathToPerWorkingCopyInfos would be null
2826 			ICompilationUnit[] result = new ICompilationUnit[primaryLength + size];
2827 			int index = 0;
2828 			if (primaryWCs != null) {
2829 				for (int i = 0; i < primaryLength; i++) {
2830 					ICompilationUnit primaryWorkingCopy = primaryWCs[i];
2831 					ICompilationUnit workingCopy = new CompilationUnit((PackageFragment) primaryWorkingCopy.getParent(), primaryWorkingCopy.getElementName(), owner);
2832 					if (!workingCopyToInfos.containsKey(workingCopy))
2833 						result[index++] = primaryWorkingCopy;
2834 				}
2835 				if (index != primaryLength)
2836 					System.arraycopy(result, 0, result = new ICompilationUnit[index+size], 0, index);
2837 			}
2838 			Iterator<PerWorkingCopyInfo> iterator = workingCopyToInfos.values().iterator();
2839 			while(iterator.hasNext()) {
2840 				result[index++] = iterator.next().getWorkingCopy();
2841 			}
2842 			return result;
2843 		}
2844 	}
2845 
getWorkspaceScope()2846 	public JavaWorkspaceScope getWorkspaceScope() {
2847 		if (this.workspaceScope == null) {
2848 			this.workspaceScope = new JavaWorkspaceScope();
2849 		}
2850 		return this.workspaceScope;
2851 	}
2852 
isJrt(IPath path)2853 	public static boolean isJrt(IPath path) {
2854 		return path.toString().endsWith(JRTUtil.JRT_FS_JAR);
2855 	}
2856 
isJrt(String path)2857 	public static boolean isJrt(String path) {
2858 		return isJrt(new Path(path));
2859 	}
2860 
verifyArchiveContent(IPath path)2861 	public void verifyArchiveContent(IPath path) throws CoreException {
2862 		// TODO: we haven't finalized what path the JRT is represented by. Don't attempt to validate it.
2863 		if (isJrt(path)) {
2864 			return;
2865 		}
2866 		throwExceptionIfArchiveInvalid(path);
2867 		// Check if we can determine the archive's validity by examining the index
2868 		if (JavaIndex.isEnabled()) {
2869 			JavaIndex index = JavaIndex.getIndex();
2870 			String location = JavaModelManager.getLocalFile(path).getAbsolutePath();
2871 			try (IReader reader = index.getNd().acquireReadLock()) {
2872 				NdResourceFile resourceFile = index.getResourceFile(location.toCharArray());
2873 				if (index.isUpToDate(resourceFile)) {
2874 					// We have this file in the index and the index is up-to-date, so we can determine the file's
2875 					// validity without touching the filesystem.
2876 					if (resourceFile.isCorruptedZipFile()) {
2877 						throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1,
2878 								Messages.status_IOException, new ZipException()));
2879 					}
2880 					return;
2881 				}
2882 			}
2883 		}
2884 
2885 		ZipFile file = getZipFile(path);
2886 		closeZipFile(file);
2887 	}
2888 
2889 	/**
2890 	 * Returns the open ZipFile at the given path. If the ZipFile
2891 	 * does not yet exist, it is created, opened, and added to the cache
2892 	 * of open ZipFiles.
2893 	 *
2894 	 * The path must be a file system path if representing an external
2895 	 * zip/jar, or it must be an absolute workspace relative path if
2896 	 * representing a zip/jar inside the workspace.
2897 	 *
2898 	 * @exception CoreException If unable to create/open the ZipFile. The
2899 	 * cause will be a {@link ZipException} if the file was corrupt, a
2900 	 * {@link FileNotFoundException} if the file does not exist, or a
2901 	 * {@link IOException} if we were unable to read the file.
2902 	 */
getZipFile(IPath path)2903 	public ZipFile getZipFile(IPath path) throws CoreException {
2904 		return getZipFile(path, true);
2905 	}
2906 
2907 	/**
2908 	 * For use in the JDT unit tests only. Used for testing error handling. Causes an
2909 	 * {@link IOException} to be thrown in {@link #getZipFile} whenever it attempts to
2910 	 * read a zip file.
2911 	 *
2912 	 * @noreference This field is not intended to be referenced by clients.
2913 	 */
2914 	public static boolean throwIoExceptionsInGetZipFile = false;
2915 
getZipFile(IPath path, boolean checkInvalidArchiveCache)2916 	public ZipFile getZipFile(IPath path, boolean checkInvalidArchiveCache) throws CoreException {
2917 		if (checkInvalidArchiveCache) {
2918 			throwExceptionIfArchiveInvalid(path);
2919 		}
2920 		ZipCache zipCache;
2921 		ZipFile zipFile;
2922 		if ((zipCache = this.zipFiles.get()) != null
2923 				&& (zipFile = zipCache.getCache(path)) != null) {
2924 			return zipFile;
2925 		}
2926 		File localFile = getLocalFile(path);
2927 
2928 		try {
2929 			if (ZIP_ACCESS_VERBOSE) {
2930 				System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + localFile ); //$NON-NLS-1$ //$NON-NLS-2$
2931 			}
2932 			if (throwIoExceptionsInGetZipFile) {
2933 				throw new IOException();
2934 			}
2935 			zipFile = new ZipFile(localFile);
2936 			if (zipCache != null) {
2937 				zipCache.setCache(path, zipFile);
2938 			}
2939 			return zipFile;
2940 		} catch (IOException e) {
2941 			ArchiveValidity reason;
2942 
2943 			if (e instanceof ZipException) {
2944 				reason = ArchiveValidity.BAD_FORMAT;
2945 			} else if (e instanceof FileNotFoundException) {
2946 				reason = ArchiveValidity.FILE_NOT_FOUND;
2947 			} else {
2948 				reason = ArchiveValidity.UNABLE_TO_READ;
2949 			}
2950 			addInvalidArchive(path, reason);
2951 			throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e));
2952 		}
2953 	}
2954 
getLocalFile(IPath path)2955 	public static File getLocalFile(IPath path) throws CoreException {
2956 		File localFile = null;
2957 		IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
2958 		IResource file = root.findMember(path);
2959 		if (file != null) {
2960 			// internal resource
2961 			URI location;
2962 			if (file.getType() != IResource.FILE || (location = file.getLocationURI()) == null) {
2963 				throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.bind(Messages.file_notFound, path.toString()), null));
2964 			}
2965 			localFile = Util.toLocalFile(location, null/*no progress availaible*/);
2966 			if (localFile == null)
2967 				throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.bind(Messages.file_notFound, path.toString()), null));
2968 		} else {
2969 			// external resource -> it is ok to use toFile()
2970 			localFile= path.toFile();
2971 		}
2972 		return localFile;
2973 	}
2974 
throwExceptionIfArchiveInvalid(IPath path)2975 	private void throwExceptionIfArchiveInvalid(IPath path) throws CoreException {
2976 		ArchiveValidity validity = getArchiveValidity(path);
2977 		IOException reason;
2978 		switch (validity) {
2979 			case BAD_FORMAT: reason = new ZipException("Bad format in archive: " + path); break; //$NON-NLS-1$
2980 			case FILE_NOT_FOUND: reason = new FileNotFoundException("Archive not found for path: " + path); break; //$NON-NLS-1$
2981 			case UNABLE_TO_READ: reason = new IOException("Unable to read archive: " + path); break; //$NON-NLS-1$
2982 			default: reason = null;
2983 		}
2984 		if (reason != null) {
2985 			throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, reason));
2986 		}
2987 	}
2988 
2989 	/*
2990 	 * Returns whether there is a temporary cache for the current thread.
2991 	 */
hasTemporaryCache()2992 	public boolean hasTemporaryCache() {
2993 		return this.temporaryCache.get() != null;
2994 	}
2995 
2996 	/*
2997 	 * Initialize all container at the same time as the given container.
2998 	 * Return the container for the given path and project.
2999 	 */
initializeAllContainers(IJavaProject javaProjectToInit, IPath containerToInit)3000 	private IClasspathContainer initializeAllContainers(IJavaProject javaProjectToInit, IPath containerToInit) throws JavaModelException {
3001 		if (CP_RESOLVE_VERBOSE_ADVANCED)
3002 			verbose_batching_containers_initialization(javaProjectToInit, containerToInit);
3003 
3004 		// collect all container paths
3005 		final HashMap<IJavaProject, Set<IPath>> allContainerPaths = new HashMap<>();
3006 		IProject[] projects = ResourcesPlugin.getWorkspace().getRoot().getProjects();
3007 		for (int i = 0, length = projects.length; i < length; i++) {
3008 			IProject project = projects[i];
3009 			if (!JavaProject.hasJavaNature(project)) continue;
3010 			IJavaProject javaProject = new JavaProject(project, getJavaModel());
3011 			Set<IPath> paths = allContainerPaths.get(javaProject);
3012 			IClasspathEntry[] rawClasspath = javaProject.getRawClasspath();
3013 			for (int j = 0, length2 = rawClasspath.length; j < length2; j++) {
3014 				IClasspathEntry entry = rawClasspath[j];
3015 				IPath path = entry.getPath();
3016 				if (entry.getEntryKind() == IClasspathEntry.CPE_CONTAINER
3017 						&& containerGet(javaProject, path) == null) {
3018 					if (paths == null) {
3019 						paths = new HashSet<>();
3020 						allContainerPaths.put(javaProject, paths);
3021 					}
3022 					paths.add(path);
3023 				}
3024 			}
3025 			/* TODO (frederic) put back when JDT/UI dummy project will be thrown away...
3026 			 * See https://bugs.eclipse.org/bugs/show_bug.cgi?id=97524
3027 			 *
3028 			if (javaProject.equals(javaProjectToInit)) {
3029 				if (paths == null) {
3030 					paths = new HashSet();
3031 					allContainerPaths.put(javaProject, paths);
3032 				}
3033 				paths.add(containerToInit);
3034 			}
3035 			*/
3036 		}
3037 		// TODO (frederic) remove following block when JDT/UI dummy project will be thrown away...
3038 		if (javaProjectToInit != null) {
3039 			Set<IPath> containerPaths = allContainerPaths.get(javaProjectToInit);
3040 			if (containerPaths == null) {
3041 				containerPaths = new HashSet<>();
3042 				allContainerPaths.put(javaProjectToInit, containerPaths);
3043 			}
3044 			containerPaths.add(containerToInit);
3045 		}
3046 		// end block
3047 
3048 		// initialize all containers
3049 		boolean ok = false;
3050 		try {
3051 			// if possible run inside an IWokspaceRunnable with AVOID_UPATE to avoid unwanted builds
3052 			// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=118507)
3053 			IWorkspaceRunnable runnable =
3054 				new IWorkspaceRunnable() {
3055 					@Override
3056 					public void run(IProgressMonitor monitor) throws CoreException {
3057 						try {
3058 							// Collect all containers
3059 							Set<Entry<IJavaProject, Set<IPath>>> entrySet = allContainerPaths.entrySet();
3060 							int length = entrySet.size();
3061 							if (monitor != null)
3062 								monitor.beginTask("", length); //$NON-NLS-1$
3063 							Set<Entry<IJavaProject, Set<IPath>>> entries = new HashSet<>(entrySet);  // clone as the following will have a side effect
3064 							for (Entry<IJavaProject, Set<IPath>> entry : entries) {
3065 								IJavaProject javaProject = entry.getKey();
3066 								Set<IPath> pathSet = entry.getValue();
3067 								if (pathSet == null) continue;
3068 								int length2 = pathSet.size();
3069 								IPath[] paths = new IPath[length2];
3070 								pathSet.toArray(paths); // clone as the following will have a side effect
3071 								for (int j = 0; j < length2; j++) {
3072 									IPath path = paths[j];
3073 									synchronized(JavaModelManager.this.batchContainerInitializationsLock) {
3074 										if (containerIsSet(javaProject, path)) {
3075 											// another thread has concurrently initialized the container.
3076 											continue;
3077 										}
3078 									}
3079 									initializeContainer(javaProject, path);
3080 									IClasspathContainer container = containerBeingInitializedGet(javaProject, path);
3081 									if (container != null) {
3082 										synchronized(JavaModelManager.this.batchContainerInitializationsLock) {
3083 											if (containerIsSet(javaProject, path)) {
3084 												// another thread has concurrently initialized the container.
3085 												containerBeingInitializedRemove(javaProject, path);
3086 												containerRemoveInitializationInProgress(javaProject, path);
3087 											} else {
3088 												containerPut(javaProject, path, container);
3089 											}
3090 										}
3091 									}
3092 								}
3093 								if (monitor != null)
3094 									monitor.worked(1);
3095 							}
3096 
3097 							// Set all containers
3098 							Map<IJavaProject, Map<IPath, IClasspathContainer>> perProjectContainers = JavaModelManager.this.containersBeingInitialized.get();
3099 							// Note that during the operation below new containers could be added to the map,
3100 							// so we should loop until containersBeingInitialized will be empty
3101 							while (perProjectContainers != null && !perProjectContainers.isEmpty()) {
3102 								initKnownContainers(perProjectContainers, monitor);
3103 							}
3104 							JavaModelManager.this.containersBeingInitialized.set(null);
3105 						} finally {
3106 							if (monitor != null)
3107 								monitor.done();
3108 						}
3109 					}
3110 
3111 					private void initKnownContainers(Map<IJavaProject, Map<IPath, IClasspathContainer>> perProjectContainers, IProgressMonitor monitor)
3112 							throws JavaModelException {
3113 						Iterator<Entry<IJavaProject, Map<IPath, IClasspathContainer>>> entriesIterator = perProjectContainers.entrySet().iterator();
3114 						List<SetContainerOperation> operations = new ArrayList<>();
3115 						while (entriesIterator.hasNext()) {
3116 							Entry<IJavaProject, Map<IPath, IClasspathContainer>> entry = entriesIterator.next();
3117 							IJavaProject project = entry.getKey();
3118 							Map<IPath, IClasspathContainer> perPathContainers = entry.getValue();
3119 							Iterator<Entry<IPath, IClasspathContainer>> containersIterator = perPathContainers.entrySet().iterator();
3120 							while (containersIterator.hasNext()) {
3121 								Entry<IPath, IClasspathContainer> containerEntry = containersIterator.next();
3122 								IPath containerPath = containerEntry.getKey();
3123 								IClasspathContainer container = containerEntry.getValue();
3124 								SetContainerOperation operation = new SetContainerOperation(containerPath, new IJavaProject[] {project}, new IClasspathContainer[] {container});
3125 								operations.add(operation);
3126 							}
3127 						}
3128 						// operation.runOperation() below could put something into the map again
3129 						// so we clear the map to make sure we only see new content there
3130 						perProjectContainers.clear();
3131 						for (SetContainerOperation operation : operations) {
3132 							operation.runOperation(monitor);
3133 						}
3134 					}
3135 				};
3136 			IProgressMonitor monitor = this.batchContainerInitializationsProgress;
3137 			IWorkspace workspace = ResourcesPlugin.getWorkspace();
3138 			if (workspace.isTreeLocked())
3139 				runnable.run(monitor);
3140 			else
3141 				workspace.run(
3142 					runnable,
3143 					null/*don't take any lock*/,
3144 					IWorkspace.AVOID_UPDATE,
3145 					monitor);
3146 			ok = true;
3147 		} catch (CoreException e) {
3148 			// ignore
3149 			Util.log(e, "Exception while initializing all containers"); //$NON-NLS-1$
3150 		} finally {
3151 			if (!ok) {
3152 				// if we're being traversed by an exception, ensure that that containers are
3153 				// no longer marked as initialization in progress
3154 				// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=66437)
3155 				this.containerInitializationInProgress.set(null);
3156 			}
3157 		}
3158 
3159 		return containerGet(javaProjectToInit, containerToInit);
3160 	}
3161 
verbose_batching_containers_initialization(IJavaProject javaProjectToInit, IPath containerToInit)3162 	private void verbose_batching_containers_initialization(IJavaProject javaProjectToInit, IPath containerToInit) {
3163 		Util.verbose(
3164 			"CPContainer INIT - batching containers initialization\n" + //$NON-NLS-1$
3165 			"	project to init: " + (javaProjectToInit == null ? "null" : javaProjectToInit.getElementName()) + '\n' + //$NON-NLS-1$ //$NON-NLS-2$
3166 			"	container path to init: " + containerToInit); //$NON-NLS-1$
3167 	}
3168 
initializeContainer(IJavaProject project, IPath containerPath)3169 	IClasspathContainer initializeContainer(IJavaProject project, IPath containerPath) throws JavaModelException {
3170 
3171 		IProgressMonitor monitor = this.batchContainerInitializationsProgress;
3172 		if (monitor != null && monitor.isCanceled())
3173 			throw new OperationCanceledException();
3174 
3175 		IClasspathContainer container = null;
3176 		final ClasspathContainerInitializer initializer = JavaCore.getClasspathContainerInitializer(containerPath.segment(0));
3177 		if (initializer != null){
3178 			if (CP_RESOLVE_VERBOSE)
3179 				verbose_triggering_container_initialization(project, containerPath, initializer);
3180 			if (CP_RESOLVE_VERBOSE_ADVANCED)
3181 				verbose_triggering_container_initialization_invocation_trace();
3182 			PerformanceStats stats = null;
3183 			if(JavaModelManager.PERF_CONTAINER_INITIALIZER) {
3184 				stats = PerformanceStats.getStats(JavaModelManager.CONTAINER_INITIALIZER_PERF, this);
3185 				stats.startRun(containerPath + " of " + project.getPath()); //$NON-NLS-1$
3186 			}
3187 			containerPut(project, containerPath, CONTAINER_INITIALIZATION_IN_PROGRESS); // avoid initialization cycles
3188 			boolean ok = false;
3189 			try {
3190 				if (monitor != null)
3191 					monitor.subTask(Messages.bind(Messages.javamodel_configuring, initializer.getDescription(containerPath, project)));
3192 
3193 				// let OperationCanceledException go through
3194 				// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59363)
3195 				initializer.initialize(containerPath, project);
3196 
3197 				if (monitor != null)
3198 					monitor.subTask(""); //$NON-NLS-1$
3199 
3200 				// retrieve value (if initialization was successful)
3201 				container = containerBeingInitializedGet(project, containerPath);
3202 				if (container == null && containerGet(project, containerPath) == CONTAINER_INITIALIZATION_IN_PROGRESS) {
3203 					// initializer failed to do its job: redirect to the failure container
3204 					container = initializer.getFailureContainer(containerPath, project);
3205 					if (container == null) {
3206 						if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
3207 							verbose_container_null_failure_container(project, containerPath, initializer);
3208 						return null; // break cycle
3209 					}
3210 					if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
3211 						verbose_container_using_failure_container(project, containerPath, initializer);
3212 					containerPut(project, containerPath, container);
3213 				}
3214 				ok = true;
3215 			} catch (CoreException e) {
3216 				if (e instanceof JavaModelException) {
3217 					throw (JavaModelException) e;
3218 				} else {
3219 					throw new JavaModelException(e);
3220 				}
3221 			} catch (RuntimeException | Error e) {
3222 				if (JavaModelManager.CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
3223 					e.printStackTrace();
3224 				throw e;
3225 			} finally {
3226 				if(JavaModelManager.PERF_CONTAINER_INITIALIZER) {
3227 					stats.endRun();
3228 				}
3229 				if (!ok) {
3230 					// just remove initialization in progress and keep previous session container so as to avoid a full build
3231 					// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=92588
3232 					containerRemoveInitializationInProgress(project, containerPath);
3233 					if (CP_RESOLVE_VERBOSE || CP_RESOLVE_VERBOSE_FAILURE)
3234 						verbose_container_initialization_failed(project, containerPath, container, initializer);
3235 				}
3236 			}
3237 			if (CP_RESOLVE_VERBOSE_ADVANCED)
3238 				verbose_container_value_after_initialization(project, containerPath, container);
3239 		} else {
3240 			// create a dummy initializer and get the default failure container
3241 			container = (new ClasspathContainerInitializer() {
3242 				@Override
3243 				public void initialize(IPath path, IJavaProject javaProject) throws CoreException {
3244 					// not used
3245 				}
3246 			}).getFailureContainer(containerPath, project);
3247 			if (CP_RESOLVE_VERBOSE_ADVANCED || CP_RESOLVE_VERBOSE_FAILURE)
3248 				verbose_no_container_initializer_found(project, containerPath);
3249 		}
3250 		return container;
3251 	}
3252 
verbose_no_container_initializer_found(IJavaProject project, IPath containerPath)3253 	private void verbose_no_container_initializer_found(IJavaProject project, IPath containerPath) {
3254 		Util.verbose(
3255 			"CPContainer INIT - no initializer found\n" + //$NON-NLS-1$
3256 			"	project: " + project.getElementName() + '\n' + //$NON-NLS-1$
3257 			"	container path: " + containerPath); //$NON-NLS-1$
3258 	}
3259 
verbose_container_value_after_initialization(IJavaProject project, IPath containerPath, IClasspathContainer container)3260 	private void verbose_container_value_after_initialization(IJavaProject project, IPath containerPath, IClasspathContainer container) {
3261 		StringBuffer buffer = new StringBuffer();
3262 		buffer.append("CPContainer INIT - after resolution\n"); //$NON-NLS-1$
3263 		buffer.append("	project: " + project.getElementName() + '\n'); //$NON-NLS-1$
3264 		buffer.append("	container path: " + containerPath + '\n'); //$NON-NLS-1$
3265 		if (container != null){
3266 			buffer.append("	container: "+container.getDescription()+" {\n"); //$NON-NLS-2$//$NON-NLS-1$
3267 			IClasspathEntry[] entries = container.getClasspathEntries();
3268 			if (entries != null){
3269 				for (int i = 0; i < entries.length; i++) {
3270 					buffer.append("		" + entries[i] + '\n'); //$NON-NLS-1$
3271 				}
3272 			}
3273 			buffer.append("	}");//$NON-NLS-1$
3274 		} else {
3275 			buffer.append("	container: {unbound}");//$NON-NLS-1$
3276 		}
3277 		Util.verbose(buffer.toString());
3278 	}
3279 
verbose_container_initialization_failed(IJavaProject project, IPath containerPath, IClasspathContainer container, ClasspathContainerInitializer initializer)3280 	private void verbose_container_initialization_failed(IJavaProject project, IPath containerPath, IClasspathContainer container, ClasspathContainerInitializer initializer) {
3281 		if (container == CONTAINER_INITIALIZATION_IN_PROGRESS) {
3282 			Util.verbose(
3283 				"CPContainer INIT - FAILED (initializer did not initialize container)\n" + //$NON-NLS-1$
3284 				"	project: " + project.getElementName() + '\n' + //$NON-NLS-1$
3285 				"	container path: " + containerPath + '\n' + //$NON-NLS-1$
3286 				"	initializer: " + initializer); //$NON-NLS-1$
3287 
3288 		} else {
3289 			Util.verbose(
3290 				"CPContainer INIT - FAILED (see exception above)\n" + //$NON-NLS-1$
3291 				"	project: " + project.getElementName() + '\n' + //$NON-NLS-1$
3292 				"	container path: " + containerPath + '\n' + //$NON-NLS-1$
3293 				"	initializer: " + initializer); //$NON-NLS-1$
3294 		}
3295 	}
3296 
verbose_container_null_failure_container(IJavaProject project, IPath containerPath, ClasspathContainerInitializer initializer)3297 	private void verbose_container_null_failure_container(IJavaProject project, IPath containerPath,  ClasspathContainerInitializer initializer) {
3298 		Util.verbose(
3299 			"CPContainer INIT - FAILED (and failure container is null)\n" + //$NON-NLS-1$
3300 			"	project: " + project.getElementName() + '\n' + //$NON-NLS-1$
3301 			"	container path: " + containerPath + '\n' + //$NON-NLS-1$
3302 			"	initializer: " + initializer); //$NON-NLS-1$
3303 	}
3304 
verbose_container_using_failure_container(IJavaProject project, IPath containerPath, ClasspathContainerInitializer initializer)3305 	private void verbose_container_using_failure_container(IJavaProject project, IPath containerPath,  ClasspathContainerInitializer initializer) {
3306 		Util.verbose(
3307 			"CPContainer INIT - FAILED (using failure container)\n" + //$NON-NLS-1$
3308 			"	project: " + project.getElementName() + '\n' + //$NON-NLS-1$
3309 			"	container path: " + containerPath + '\n' + //$NON-NLS-1$
3310 			"	initializer: " + initializer); //$NON-NLS-1$
3311 	}
3312 
verbose_triggering_container_initialization(IJavaProject project, IPath containerPath, ClasspathContainerInitializer initializer)3313 	private void verbose_triggering_container_initialization(IJavaProject project, IPath containerPath,  ClasspathContainerInitializer initializer) {
3314 		Util.verbose(
3315 			"CPContainer INIT - triggering initialization\n" + //$NON-NLS-1$
3316 			"	project: " + project.getElementName() + '\n' + //$NON-NLS-1$
3317 			"	container path: " + containerPath + '\n' + //$NON-NLS-1$
3318 			"	initializer: " + initializer); //$NON-NLS-1$
3319 	}
3320 
verbose_triggering_container_initialization_invocation_trace()3321 	private void verbose_triggering_container_initialization_invocation_trace() {
3322 		Util.verbose(
3323 			"CPContainer INIT - triggering initialization\n" + //$NON-NLS-1$
3324 			"	invocation trace:"); //$NON-NLS-1$
3325 		new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$
3326 	}
3327 
3328 	/**
3329 	 * Initialize preferences lookups for JavaCore plug-in.
3330 	 */
initializePreferences()3331 	public void initializePreferences() {
3332 
3333 		// Create lookups
3334 		this.preferencesLookup[PREF_INSTANCE] = InstanceScope.INSTANCE.getNode(JavaCore.PLUGIN_ID);
3335 		this.preferencesLookup[PREF_DEFAULT] = DefaultScope.INSTANCE.getNode(JavaCore.PLUGIN_ID);
3336 
3337 		// Listen to instance preferences node removal from parent in order to refresh stored one
3338 		this.instanceNodeListener = new IEclipsePreferences.INodeChangeListener() {
3339 			@Override
3340 			public void added(IEclipsePreferences.NodeChangeEvent event) {
3341 				// do nothing
3342 			}
3343 			@Override
3344 			public void removed(IEclipsePreferences.NodeChangeEvent event) {
3345 				if (event.getChild() == JavaModelManager.this.preferencesLookup[PREF_INSTANCE]) {
3346 					JavaModelManager.this.preferencesLookup[PREF_INSTANCE] = InstanceScope.INSTANCE.getNode(JavaCore.PLUGIN_ID);
3347 					JavaModelManager.this.preferencesLookup[PREF_INSTANCE].addPreferenceChangeListener(new EclipsePreferencesListener());
3348 				}
3349 			}
3350 		};
3351 		((IEclipsePreferences) this.preferencesLookup[PREF_INSTANCE].parent()).addNodeChangeListener(this.instanceNodeListener);
3352 		this.preferencesLookup[PREF_INSTANCE].addPreferenceChangeListener(this.instancePreferencesListener = new EclipsePreferencesListener());
3353 
3354 		// Listen to default preferences node removal from parent in order to refresh stored one
3355 		this.defaultNodeListener = new IEclipsePreferences.INodeChangeListener() {
3356 			@Override
3357 			public void added(IEclipsePreferences.NodeChangeEvent event) {
3358 				// do nothing
3359 			}
3360 			@Override
3361 			public void removed(IEclipsePreferences.NodeChangeEvent event) {
3362 				if (event.getChild() == JavaModelManager.this.preferencesLookup[PREF_DEFAULT]) {
3363 					JavaModelManager.this.preferencesLookup[PREF_DEFAULT] = DefaultScope.INSTANCE.getNode(JavaCore.PLUGIN_ID);
3364 				}
3365 			}
3366 		};
3367 		((IEclipsePreferences) this.preferencesLookup[PREF_DEFAULT].parent()).addNodeChangeListener(this.defaultNodeListener);
3368 	}
3369 
intern(char[] array)3370 	public synchronized char[] intern(char[] array) {
3371 		return this.charArraySymbols.add(array);
3372 	}
3373 
intern(String s)3374 	public synchronized String intern(String s) {
3375 		return (String) this.stringSymbols.add(s);
3376 
3377 		// Note1: String#intern() cannot be used as on some VMs this prevents the string from being garbage collected
3378 		// Note 2: Instead of using a WeakHashset, one could use a WeakHashMap with the following implementation
3379 		// 			   This would costs more per entry (one Entry object and one WeakReference more))
3380 
3381 		/*
3382 		WeakReference reference = (WeakReference) this.symbols.get(s);
3383 		String existing;
3384 		if (reference != null && (existing = (String) reference.get()) != null)
3385 			return existing;
3386 		this.symbols.put(s, new WeakReference(s));
3387 		return s;
3388 		*/
3389 	}
3390 
touchProjects(final IProject[] projectsToTouch, IProgressMonitor progressMonitor)3391 	void touchProjects(final IProject[] projectsToTouch, IProgressMonitor progressMonitor) throws JavaModelException {
3392 		WorkspaceJob touchJob = new WorkspaceJob(Messages.synchronizing_projects_job) {
3393 			@Override
3394 			public IStatus runInWorkspace(IProgressMonitor monitor) throws CoreException {
3395 				SubMonitor subMonitor = SubMonitor.convert(monitor, projectsToTouch.length);
3396 				for (IProject iProject : projectsToTouch) {
3397 					if (JavaBuilder.DEBUG) {
3398 						System.out.println("Touching project " + iProject.getName()); //$NON-NLS-1$
3399 					}
3400 					if (iProject.isAccessible()) {
3401 						iProject.touch(subMonitor.split(1));
3402 					}
3403 				}
3404 				return Status.OK_STATUS;
3405 			}
3406 
3407 			@Override
3408 			public boolean belongsTo(Object family) {
3409 				return ResourcesPlugin.FAMILY_MANUAL_REFRESH == family;
3410 			}
3411 		};
3412 		touchJob.schedule();
3413 	}
3414 
getClasspathBeingResolved()3415 	private Set<IJavaProject> getClasspathBeingResolved() {
3416 	    Set<IJavaProject> result = this.classpathsBeingResolved.get();
3417 	    if (result == null) {
3418 	        result = new HashSet<>();
3419 	        this.classpathsBeingResolved.set(result);
3420 	    }
3421 	    return result;
3422 	}
3423 
isClasspathBeingResolved(IJavaProject project)3424 	public boolean isClasspathBeingResolved(IJavaProject project) {
3425 	    return getClasspathBeingResolved().contains(project);
3426 	}
3427 
3428 	/**
3429 	 * @deprecated
3430 	 */
isDeprecatedOption(String optionName)3431 	private boolean isDeprecatedOption(String optionName) {
3432 		return JavaCore.COMPILER_PB_INVALID_IMPORT.equals(optionName)
3433 				|| JavaCore.COMPILER_PB_UNREACHABLE_CODE.equals(optionName);
3434 	}
3435 
isNonChainingJar(IPath path)3436 	public boolean isNonChainingJar(IPath path) {
3437 		return this.nonChainingJars != null && this.nonChainingJars.contains(path);
3438 	}
3439 
getArchiveValidity(IPath path)3440 	public ArchiveValidity getArchiveValidity(IPath path) {
3441 		InvalidArchiveInfo invalidArchiveInfo;
3442 		synchronized (this.invalidArchivesMutex) {
3443 			invalidArchiveInfo = this.invalidArchives.get(path);
3444 		}
3445 		if (invalidArchiveInfo == null)
3446 			return ArchiveValidity.VALID;
3447 		long now = System.currentTimeMillis();
3448 
3449 		// If the TTL for this cache entry has expired, directly check whether the archive is still invalid.
3450 		// If it transitioned to being valid, remove it from the cache and force an update to project caches.
3451 		if (now > invalidArchiveInfo.evictionTimestamp) {
3452 			try {
3453 				getZipFile(path, false);
3454 				removeFromInvalidArchiveCache(path);
3455 			} catch (CoreException e) {
3456 				// Archive is still invalid, fall through to reporting it is invalid.
3457 			}
3458 			// Retry the test from the start, now that we have an up-to-date result
3459 			return getArchiveValidity(path);
3460 		}
3461 		return invalidArchiveInfo.reason;
3462 	}
3463 
removeFromInvalidArchiveCache(IPath path)3464 	public void removeFromInvalidArchiveCache(IPath path) {
3465 		synchronized(this.invalidArchivesMutex) {
3466 			if (this.invalidArchives.remove(path) != null) {
3467 				if (DEBUG_INVALID_ARCHIVES) {
3468 					System.out.println("Invalid JAR cache: removed " + path);  //$NON-NLS-1$
3469 				}
3470 				try {
3471 					// Bug 455042: Force an update of the JavaProjectElementInfo project caches.
3472 					for (IJavaProject project : getJavaModel().getJavaProjects()) {
3473 						if (project.findPackageFragmentRoot(path) != null) {
3474 							((JavaProject) project).resetCaches();
3475 						}
3476 					}
3477 				} catch (JavaModelException e) {
3478 					Util.log(e, "Unable to retrieve the Java model."); //$NON-NLS-1$
3479 				}
3480 			}
3481 		}
3482 	}
3483 
3484 	/**
3485 	 * Returns the cached value for whether the file referred to by <code>path</code> exists
3486 	 * and is a file, as determined by the return value of {@link File#isFile()}.
3487 	 */
isExternalFile(IPath path)3488 	public boolean isExternalFile(IPath path) {
3489 		return this.externalFiles != null && this.externalFiles.contains(path);
3490 	}
3491 
3492 	/**
3493 	 * Removes the cached state of a single entry in the externalFiles cache.
3494 	 */
clearExternalFileState(IPath path)3495 	public void clearExternalFileState(IPath path) {
3496 		if (this.externalFiles != null) {
3497 			this.externalFiles.remove(path);
3498 		}
3499 	}
3500 
3501 	/**
3502 	 * Resets the entire externalFiles cache.
3503 	 */
resetExternalFilesCache()3504 	public void resetExternalFilesCache() {
3505 		if (this.externalFiles != null) {
3506 			this.externalFiles.clear();
3507 		}
3508 	}
3509 
3510 	/**
3511 	 * Returns whether the provided {@link IPath} appears to be an external file,
3512 	 * which is true if the path does not represent an internal resource, does not
3513 	 * exist on the file system, and does have a file extension (this is the definition
3514 	 * provided by {@link ExternalFoldersManager#isExternalFolderPath}).
3515 	 */
isAssumedExternalFile(IPath path)3516 	public boolean isAssumedExternalFile(IPath path) {
3517 		if (this.assumedExternalFiles == null) {
3518 			return false;
3519 		}
3520 		return this.assumedExternalFiles.contains(path);
3521 	}
3522 
3523 	/**
3524 	 * Adds the provided {@link IPath} to the list of assumed external files.
3525 	 */
addAssumedExternalFile(IPath path)3526 	public void addAssumedExternalFile(IPath path) {
3527 		this.assumedExternalFiles.add(path);
3528 	}
3529 
setClasspathBeingResolved(IJavaProject project, boolean classpathIsResolved)3530 	public void setClasspathBeingResolved(IJavaProject project, boolean classpathIsResolved) {
3531 	    if (classpathIsResolved) {
3532 	        getClasspathBeingResolved().add(project);
3533 	    } else {
3534 	        getClasspathBeingResolved().remove(project);
3535 	    }
3536 	}
3537 
loadClasspathListCache(String cacheName)3538 	private Set<IPath> loadClasspathListCache(String cacheName) {
3539 		Set<IPath> pathCache = new HashSet<>();
3540 		File cacheFile = getClasspathListFile(cacheName);
3541 		DataInputStream in = null;
3542 		try {
3543 			in = new DataInputStream(new BufferedInputStream(new FileInputStream(cacheFile)));
3544 			int size = in.readInt();
3545 			while (size-- > 0) {
3546 				String path = in.readUTF();
3547 				pathCache.add(Path.fromPortableString(path));
3548 			}
3549 		} catch (IOException e) {
3550 			if (cacheFile.exists())
3551 				Util.log(e, "Unable to read JavaModelManager " + cacheName + " file"); //$NON-NLS-1$ //$NON-NLS-2$
3552 		} finally {
3553 			if (in != null) {
3554 				try {
3555 					in.close();
3556 				} catch (IOException e) {
3557 					// nothing we can do: ignore
3558 				}
3559 			}
3560 		}
3561 		return Collections.synchronizedSet(pathCache);
3562 	}
3563 
getClasspathListFile(String fileName)3564 	private File getClasspathListFile(String fileName) {
3565 		return JavaCore.getPlugin().getStateLocation().append(fileName).toFile();
3566 	}
3567 
getNonChainingJarsCache()3568 	private Set<IPath> getNonChainingJarsCache() throws CoreException {
3569 		// Even if there is one entry in the cache, just return it. It may not be
3570 		// the complete cache, but avoid going through all the projects to populate the cache.
3571 		if (this.nonChainingJars != null && this.nonChainingJars.size() > 0) {
3572 			return this.nonChainingJars;
3573 		}
3574 		Set<IPath> result = new HashSet<>();
3575 		IJavaProject[] projects = getJavaModel().getJavaProjects();
3576 		for (int i = 0, length = projects.length; i < length; i++) {
3577 			IJavaProject javaProject = projects[i];
3578 			IClasspathEntry[] classpath = ((JavaProject) javaProject).getResolvedClasspath();
3579 			for (int j = 0, length2 = classpath.length; j < length2; j++) {
3580 				IClasspathEntry entry = classpath[j];
3581 				IPath path;
3582 				if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY
3583 					&& !result.contains(path = entry.getPath())
3584 					&& ClasspathEntry.resolvedChainedLibraries(path).length == 0) {
3585 						result.add(path);
3586 				}
3587 			}
3588 		}
3589 		this.nonChainingJars = Collections.synchronizedSet(result);
3590 		return this.nonChainingJars;
3591 	}
3592 
getClasspathListCache(String cacheName)3593 	private Set<IPath> getClasspathListCache(String cacheName) throws CoreException {
3594 		if (cacheName == NON_CHAINING_JARS_CACHE)
3595 			return getNonChainingJarsCache();
3596 		else if (cacheName == EXTERNAL_FILES_CACHE)
3597 			return this.externalFiles;
3598 		else if (cacheName == ASSUMED_EXTERNAL_FILES_CACHE)
3599 			return this.assumedExternalFiles;
3600 		else
3601 			return null;
3602 	}
3603 
loadVariablesAndContainers()3604 	public void loadVariablesAndContainers() throws CoreException {
3605 		// backward compatibility, consider persistent property
3606 		QualifiedName qName = new QualifiedName(JavaCore.PLUGIN_ID, "variables"); //$NON-NLS-1$
3607 		String xmlString = ResourcesPlugin.getWorkspace().getRoot().getPersistentProperty(qName);
3608 
3609 		try {
3610 			if (xmlString != null){
3611 				StringReader reader = new StringReader(xmlString);
3612 				Element cpElement;
3613 				try {
3614 					DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
3615 					cpElement = parser.parse(new InputSource(reader)).getDocumentElement();
3616 				} catch(SAXException | ParserConfigurationException e){
3617 					return;
3618 				} finally {
3619 					reader.close();
3620 				}
3621 				if (cpElement == null) return;
3622 				if (!cpElement.getNodeName().equalsIgnoreCase("variables")) { //$NON-NLS-1$
3623 					return;
3624 				}
3625 
3626 				NodeList list= cpElement.getChildNodes();
3627 				int length= list.getLength();
3628 				for (int i= 0; i < length; ++i) {
3629 					Node node= list.item(i);
3630 					short type= node.getNodeType();
3631 					if (type == Node.ELEMENT_NODE) {
3632 						Element element= (Element) node;
3633 						if (element.getNodeName().equalsIgnoreCase("variable")) { //$NON-NLS-1$
3634 							variablePut(
3635 								element.getAttribute("name"), //$NON-NLS-1$
3636 								new Path(element.getAttribute("path"))); //$NON-NLS-1$
3637 						}
3638 					}
3639 				}
3640 			}
3641 		} catch(IOException e){
3642 			// problem loading xml file: nothing we can do
3643 		} finally {
3644 			if (xmlString != null){
3645 				ResourcesPlugin.getWorkspace().getRoot().setPersistentProperty(qName, null); // flush old one
3646 			}
3647 		}
3648 
3649 		// backward compatibility, load variables and containers from preferences into cache
3650 		loadVariablesAndContainers(getDefaultPreferences());
3651 		loadVariablesAndContainers(getInstancePreferences());
3652 
3653 		// load variables and containers from saved file into cache
3654 		File file = getVariableAndContainersFile();
3655 		DataInputStream in = null;
3656 		try {
3657 			in = new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
3658 			switch (in.readInt()) {
3659 				case 2 :
3660 					new VariablesAndContainersLoadHelper(in).load();
3661 					break;
3662 				case 1 : // backward compatibility, load old format
3663 					// variables
3664 					int size = in.readInt();
3665 					while (size-- > 0) {
3666 						String varName = in.readUTF();
3667 						String pathString = in.readUTF();
3668 						if (CP_ENTRY_IGNORE.equals(pathString))
3669 							continue;
3670 						IPath varPath = Path.fromPortableString(pathString);
3671 						this.variables.put(varName, varPath);
3672 						this.previousSessionVariables.put(varName, varPath);
3673 					}
3674 
3675 					// containers
3676 					IJavaModel model = getJavaModel();
3677 					int projectSize = in.readInt();
3678 					while (projectSize-- > 0) {
3679 						String projectName = in.readUTF();
3680 						IJavaProject project = model.getJavaProject(projectName);
3681 						int containerSize = in.readInt();
3682 						while (containerSize-- > 0) {
3683 							IPath containerPath = Path.fromPortableString(in.readUTF());
3684 							int length = in.readInt();
3685 							byte[] containerString = new byte[length];
3686 							in.readFully(containerString);
3687 							recreatePersistedContainer(project, containerPath, new String(containerString), true/*add to container values*/);
3688 						}
3689 					}
3690 					break;
3691 			}
3692 		} catch (IOException e) {
3693 			if (file.exists())
3694 				Util.log(e, "Unable to read variable and containers file"); //$NON-NLS-1$
3695 		} catch (RuntimeException e) {
3696 			if (file.exists())
3697 				Util.log(e, "Unable to read variable and containers file (file is corrupt)"); //$NON-NLS-1$
3698 		} finally {
3699 			if (in != null) {
3700 				try {
3701 					in.close();
3702 				} catch (IOException e) {
3703 					// nothing we can do: ignore
3704 				}
3705 			}
3706 		}
3707 
3708 		// override persisted values for variables which have a registered initializer
3709 		String[] registeredVariables = getRegisteredVariableNames();
3710 		for (int i = 0; i < registeredVariables.length; i++) {
3711 			String varName = registeredVariables[i];
3712 			this.variables.put(varName, null); // reset variable, but leave its entry in the Map, so it will be part of variable names.
3713 		}
3714 		// override persisted values for containers which have a registered initializer
3715 		containersReset(getRegisteredContainerIDs());
3716 	}
3717 
loadVariablesAndContainers(IEclipsePreferences preferences)3718 	private void loadVariablesAndContainers(IEclipsePreferences preferences) {
3719 		try {
3720 			// only get variable from preferences not set to their default
3721 			String[] propertyNames = preferences.keys();
3722 			int variablePrefixLength = CP_VARIABLE_PREFERENCES_PREFIX.length();
3723 			for (int i = 0; i < propertyNames.length; i++){
3724 				String propertyName = propertyNames[i];
3725 				if (propertyName.startsWith(CP_VARIABLE_PREFERENCES_PREFIX)){
3726 					String varName = propertyName.substring(variablePrefixLength);
3727 					String propertyValue = preferences.get(propertyName, null);
3728 					if (propertyValue != null) {
3729 						String pathString = propertyValue.trim();
3730 
3731 						if (CP_ENTRY_IGNORE.equals(pathString)) {
3732 							// cleanup old preferences
3733 							preferences.remove(propertyName);
3734 							continue;
3735 						}
3736 
3737 						// add variable to table
3738 						IPath varPath = new Path(pathString);
3739 						this.variables.put(varName, varPath);
3740 						this.previousSessionVariables.put(varName, varPath);
3741 					}
3742 				} else if (propertyName.startsWith(CP_CONTAINER_PREFERENCES_PREFIX)){
3743 					String propertyValue = preferences.get(propertyName, null);
3744 					if (propertyValue != null) {
3745 						// cleanup old preferences
3746 						preferences.remove(propertyName);
3747 
3748 						// recreate container
3749 						recreatePersistedContainer(propertyName, propertyValue, true/*add to container values*/);
3750 					}
3751 				}
3752 			}
3753 		} catch (BackingStoreException e1) {
3754 			// TODO (frederic) see if it's necessary to report this failure...
3755 		}
3756 	}
3757 
3758 	private static final class PersistedClasspathContainer implements
3759 			IClasspathContainer {
3760 
3761 		private final IPath containerPath;
3762 
3763 		private final IClasspathEntry[] entries;
3764 
3765 		private final IJavaProject project;
3766 
PersistedClasspathContainer(IJavaProject project, IPath containerPath, IClasspathEntry[] entries)3767 		PersistedClasspathContainer(IJavaProject project, IPath containerPath,
3768 				IClasspathEntry[] entries) {
3769 			super();
3770 			this.containerPath = containerPath;
3771 			this.entries = entries;
3772 			this.project = project;
3773 		}
3774 
3775 		@Override
getClasspathEntries()3776 		public IClasspathEntry[] getClasspathEntries() {
3777 			return this.entries;
3778 		}
3779 
3780 		@Override
getDescription()3781 		public String getDescription() {
3782 			return "Persisted container [" + this.containerPath //$NON-NLS-1$
3783 					+ " for project [" + this.project.getElementName() //$NON-NLS-1$
3784 					+ "]]"; //$NON-NLS-1$
3785 		}
3786 
3787 		@Override
getKind()3788 		public int getKind() {
3789 			return 0;
3790 		}
3791 
3792 		@Override
getPath()3793 		public IPath getPath() {
3794 			return this.containerPath;
3795 		}
3796 
3797 		@Override
toString()3798 		public String toString() {
3799 			return getDescription();
3800 		}
3801 	}
3802 
3803 	private final class VariablesAndContainersLoadHelper {
3804 
3805 		private static final int ARRAY_INCREMENT = 200;
3806 
3807 		private IClasspathEntry[] allClasspathEntries;
3808 		private int allClasspathEntryCount;
3809 
3810 		private final Map<String, IPath> allPaths;
3811 
3812 		private String[] allStrings;
3813 		private int allStringsCount;
3814 
3815 		private final DataInputStream in;
3816 
VariablesAndContainersLoadHelper(DataInputStream in)3817 		VariablesAndContainersLoadHelper(DataInputStream in) {
3818 			super();
3819 			this.allClasspathEntries = null;
3820 			this.allClasspathEntryCount = 0;
3821 			this.allPaths = new HashMap<>();
3822 			this.allStrings = null;
3823 			this.allStringsCount = 0;
3824 			this.in = in;
3825 		}
3826 
load()3827 		void load() throws IOException {
3828 			loadProjects(getJavaModel());
3829 			loadVariables();
3830 		}
3831 
loadAccessRule()3832 		private IAccessRule loadAccessRule() throws IOException {
3833 			int problemId = loadInt();
3834 			IPath pattern = loadPath();
3835 			return getAccessRuleForProblemId(pattern.toString().toCharArray(), problemId);
3836 		}
3837 
loadAccessRules()3838 		private IAccessRule[] loadAccessRules() throws IOException {
3839 			int count = loadInt();
3840 
3841 			if (count == 0)
3842 				return ClasspathEntry.NO_ACCESS_RULES;
3843 
3844 			IAccessRule[] rules = new IAccessRule[count];
3845 
3846 			for (int i = 0; i < count; ++i)
3847 				rules[i] = loadAccessRule();
3848 
3849 			return rules;
3850 		}
3851 
loadAttribute()3852 		private IClasspathAttribute loadAttribute() throws IOException {
3853 			String name = loadString();
3854 			String value = loadString();
3855 
3856 			return new ClasspathAttribute(name, value);
3857 		}
3858 
loadAttributes()3859 		private IClasspathAttribute[] loadAttributes() throws IOException {
3860 			int count = loadInt();
3861 
3862 			if (count == 0)
3863 				return ClasspathEntry.NO_EXTRA_ATTRIBUTES;
3864 
3865 			IClasspathAttribute[] attributes = new IClasspathAttribute[count];
3866 
3867 			for (int i = 0; i < count; ++i)
3868 				attributes[i] = loadAttribute();
3869 
3870 			return attributes;
3871 		}
3872 
loadBoolean()3873 		private boolean loadBoolean() throws IOException {
3874 			return this.in.readBoolean();
3875 		}
3876 
loadClasspathEntries()3877 		private IClasspathEntry[] loadClasspathEntries() throws IOException {
3878 			int count = loadInt();
3879 			IClasspathEntry[] entries = new IClasspathEntry[count];
3880 
3881 			for (int i = 0; i < count; ++i)
3882 				entries[i] = loadClasspathEntry();
3883 
3884 			return entries;
3885 		}
3886 
loadClasspathEntry()3887 		private IClasspathEntry loadClasspathEntry() throws IOException {
3888 			int id = loadInt();
3889 
3890 			if (id < 0 || id > this.allClasspathEntryCount)
3891 				throw new IOException("Unexpected classpathentry id"); //$NON-NLS-1$
3892 
3893 			if (id < this.allClasspathEntryCount)
3894 				return this.allClasspathEntries[id];
3895 
3896 			int contentKind = loadInt();
3897 			int entryKind = loadInt();
3898 			IPath path = loadPath();
3899 			IPath[] inclusionPatterns = loadPaths();
3900 			IPath[] exclusionPatterns = loadPaths();
3901 			IPath sourceAttachmentPath = loadPath();
3902 			IPath sourceAttachmentRootPath = loadPath();
3903 			IPath specificOutputLocation = loadPath();
3904 			boolean isExported = loadBoolean();
3905 			IAccessRule[] accessRules = loadAccessRules();
3906 			boolean combineAccessRules = loadBoolean();
3907 			IClasspathAttribute[] extraAttributes = loadAttributes();
3908 
3909 			IClasspathEntry entry = new ClasspathEntry(contentKind, entryKind,
3910 					path, inclusionPatterns, exclusionPatterns,
3911 					sourceAttachmentPath, sourceAttachmentRootPath,
3912 					specificOutputLocation, isExported, accessRules,
3913 					combineAccessRules, extraAttributes);
3914 
3915 			IClasspathEntry[] array = this.allClasspathEntries;
3916 
3917 			if (array == null || id == array.length) {
3918 				array = new IClasspathEntry[id + ARRAY_INCREMENT];
3919 
3920 				if (id != 0)
3921 					System.arraycopy(this.allClasspathEntries, 0, array, 0, id);
3922 
3923 				this.allClasspathEntries = array;
3924 			}
3925 
3926 			array[id] = entry;
3927 			this.allClasspathEntryCount = id + 1;
3928 
3929 			return entry;
3930 		}
3931 
loadContainers(IJavaProject project)3932 		private void loadContainers(IJavaProject project) throws IOException {
3933 			boolean projectIsAccessible = project.getProject().isAccessible();
3934 			int count = loadInt();
3935 			for (int i = 0; i < count; ++i) {
3936 				IPath path = loadPath();
3937 				IClasspathEntry[] entries = loadClasspathEntries();
3938 
3939 				if (!projectIsAccessible)
3940 					// avoid leaking deleted project's persisted container,
3941 					// but still read the container as it is is part of the file format
3942 					continue;
3943 
3944 				IClasspathContainer container = new PersistedClasspathContainer(project, path, entries);
3945 
3946 				containerPut(project, path, container);
3947 
3948 				Map<IPath, IClasspathContainer> oldContainers = JavaModelManager.this.previousSessionContainers.get(project);
3949 
3950 				if (oldContainers == null) {
3951 					oldContainers = new HashMap<>();
3952 					JavaModelManager.this.previousSessionContainers.put(project, oldContainers);
3953 				}
3954 
3955 				oldContainers.put(path, container);
3956 			}
3957 		}
3958 
loadInt()3959 		private int loadInt() throws IOException {
3960 			return this.in.readInt();
3961 		}
3962 
loadPath()3963 		private IPath loadPath() throws IOException {
3964 			if (loadBoolean())
3965 				return null;
3966 
3967 			String portableString = loadString();
3968 			IPath path = this.allPaths.get(portableString);
3969 
3970 			if (path == null) {
3971 				path = Path.fromPortableString(portableString);
3972 				this.allPaths.put(portableString, path);
3973 			}
3974 
3975 			return path;
3976 		}
3977 
loadPaths()3978 		private IPath[] loadPaths() throws IOException {
3979 			int count = loadInt();
3980 			IPath[] pathArray = new IPath[count];
3981 
3982 			for (int i = 0; i < count; ++i)
3983 				pathArray[i] = loadPath();
3984 
3985 			return pathArray;
3986 		}
3987 
loadProjects(IJavaModel model)3988 		private void loadProjects(IJavaModel model) throws IOException {
3989 			int count = loadInt();
3990 
3991 			for (int i = 0; i < count; ++i) {
3992 				String projectName = loadString();
3993 
3994 				loadContainers(model.getJavaProject(projectName));
3995 			}
3996 		}
3997 
loadString()3998 		private String loadString() throws IOException {
3999 			int id = loadInt();
4000 
4001 			if (id < 0 || id > this.allStringsCount)
4002 				throw new IOException("Unexpected string id"); //$NON-NLS-1$
4003 
4004 			if (id < this.allStringsCount)
4005 				return this.allStrings[id];
4006 
4007 			String string = this.in.readUTF();
4008 			String[] array = this.allStrings;
4009 
4010 			if (array == null || id == array.length) {
4011 				array = new String[id + ARRAY_INCREMENT];
4012 
4013 				if (id != 0)
4014 					System.arraycopy(this.allStrings, 0, array, 0, id);
4015 
4016 				this.allStrings = array;
4017 			}
4018 
4019 			array[id] = string;
4020 			this.allStringsCount = id + 1;
4021 
4022 			return string;
4023 		}
4024 
loadVariables()4025 		private void loadVariables() throws IOException {
4026 			int size = loadInt();
4027 			Map<String, IPath> loadedVars = new HashMap<>(size);
4028 
4029 			for (int i = 0; i < size; ++i) {
4030 				String varName = loadString();
4031 				IPath varPath = loadPath();
4032 
4033 				if (varPath != null)
4034 					loadedVars.put(varName, varPath);
4035 			}
4036 
4037 			JavaModelManager.this.previousSessionVariables.putAll(loadedVars);
4038 			JavaModelManager.this.variables.putAll(loadedVars);
4039 		}
4040 	}
4041 
4042 	/**
4043 	 *  Returns the info for this element without
4044 	 *  disturbing the cache ordering.
4045 	 */
peekAtInfo(IJavaElement element)4046 	protected synchronized Object peekAtInfo(IJavaElement element) {
4047 		HashMap<IJavaElement, Object> tempCache = this.temporaryCache.get();
4048 		if (tempCache != null) {
4049 			Object result = tempCache.get(element);
4050 			if (result != null) {
4051 				return result;
4052 			}
4053 		}
4054 		return this.cache.peekAtInfo(element);
4055 	}
4056 
4057 	/**
4058 	 * @see ISaveParticipant
4059 	 */
4060 	@Override
prepareToSave(ISaveContext context)4061 	public void prepareToSave(ISaveContext context) /*throws CoreException*/ {
4062 		// nothing to do
4063 	}
4064 	/*
4065 	 * Puts the infos in the given map (keys are IJavaElements and values are JavaElementInfos)
4066 	 * in the Java model cache in an atomic way if the info is not already present in the cache.
4067 	 * If the info is already present in the cache, it depends upon the forceAdd parameter.
4068 	 * If forceAdd is false it just returns the existing info and if true, this element and it's children are closed and then
4069 	 * this particular info is added to the cache.
4070 	 */
putInfos(IJavaElement openedElement, Object newInfo, boolean forceAdd, Map<IJavaElement, Object> newElements)4071 	protected synchronized Object putInfos(IJavaElement openedElement, Object newInfo, boolean forceAdd, Map<IJavaElement, Object> newElements) {
4072 		// remove existing children as the are replaced with the new children contained in newElements
4073 		Object existingInfo = this.cache.peekAtInfo(openedElement);
4074 		if (existingInfo != null && !forceAdd) {
4075 			// If forceAdd is false, then it could mean that the particular element
4076 			// wasn't in cache at that point of time, but would have got added through
4077 			// another thread. In that case, removing the children could remove it's own
4078 			// children. So, we should not remove the children but return the already existing
4079 			// info.
4080 			// https://bugs.eclipse.org/bugs/show_bug.cgi?id=372687
4081 			return existingInfo;
4082 		}
4083 		if (openedElement instanceof IParent) {
4084 			closeChildren(existingInfo);
4085 		}
4086 
4087 		// Need to put any JarPackageFragmentRoot in first.
4088 		// This is due to the way the LRU cache flushes entries.
4089 		// When a JarPackageFragment is flushed from the LRU cache, the entire
4090 		// jar is flushed by removing the JarPackageFragmentRoot and all of its
4091 		// children (see ElementCache.close()). If we flush the JarPackageFragment
4092 		// when its JarPackageFragmentRoot is not in the cache and the root is about to be
4093 		// added (during the 'while' loop), we will end up in an inconsistent state.
4094 		// Subsequent resolution against package in the jar would fail as a result.
4095 		// https://bugs.eclipse.org/bugs/show_bug.cgi?id=102422
4096 		// (theodora)
4097 		for(Iterator<Entry<IJavaElement, Object>> it = newElements.entrySet().iterator(); it.hasNext(); ) {
4098 			Entry<IJavaElement, Object> entry = it.next();
4099 			IJavaElement element = entry.getKey();
4100 			if (element instanceof JarPackageFragmentRoot) {
4101 				JavaElementInfo info = (JavaElementInfo) entry.getValue();
4102 				it.remove();
4103 				this.cache.putInfo(element, info);
4104 			}
4105 		}
4106 
4107 		Iterator<Entry<IJavaElement, Object>> iterator = newElements.entrySet().iterator();
4108 		while (iterator.hasNext()) {
4109 			Entry<IJavaElement, Object> entry = iterator.next();
4110 			this.cache.putInfo(entry.getKey(), entry.getValue());
4111 		}
4112 		return newInfo;
4113 	}
4114 
closeChildren(Object info)4115 	private void closeChildren(Object info) {
4116 		if (info instanceof JavaElementInfo) {
4117 			IJavaElement[] children = ((JavaElementInfo)info).getChildren();
4118 			for (int i = 0, size = children.length; i < size; ++i) {
4119 				JavaElement child = (JavaElement) children[i];
4120 				try {
4121 					child.close();
4122 				} catch (JavaModelException e) {
4123 					// ignore
4124 				}
4125 			}
4126 		}
4127 	}
4128 
4129 	/**
4130 	 * Remember the info for the jar binary type
4131 	 * @param info instanceof IBinaryType or {@link JavaModelCache#NON_EXISTING_JAR_TYPE_INFO}
4132 	 */
putJarTypeInfo(IJavaElement type, Object info)4133 	protected synchronized void putJarTypeInfo(IJavaElement type, Object info) {
4134 		this.cache.jarTypeCache.put(type, info);
4135 	}
4136 
4137 	/**
4138 	 * Reads the build state for the relevant project.
4139 	 */
readState(IProject project)4140 	protected Object readState(IProject project) throws CoreException {
4141 		File file = getSerializationFile(project);
4142 		if (file != null && file.exists()) {
4143 			try {
4144 				DataInputStream in= new DataInputStream(new BufferedInputStream(new FileInputStream(file)));
4145 				try {
4146 					String pluginID= in.readUTF();
4147 					if (!pluginID.equals(JavaCore.PLUGIN_ID))
4148 						throw new IOException(Messages.build_wrongFileFormat);
4149 					String kind= in.readUTF();
4150 					if (!kind.equals("STATE")) //$NON-NLS-1$
4151 						throw new IOException(Messages.build_wrongFileFormat);
4152 					if (in.readBoolean())
4153 						return JavaBuilder.readState(project, in);
4154 					if (JavaBuilder.DEBUG)
4155 						System.out.println("Saved state thinks last build failed for " + project.getName()); //$NON-NLS-1$
4156 				} finally {
4157 					in.close();
4158 				}
4159 			} catch (Exception e) {
4160 				e.printStackTrace();
4161 				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$
4162 			}
4163 		} else if (JavaBuilder.DEBUG) {
4164 			if (file == null)
4165 				System.out.println("Project does not exist: " + project); //$NON-NLS-1$
4166 			else
4167 				System.out.println("Build state file " + file.getPath() + " does not exist"); //$NON-NLS-1$ //$NON-NLS-2$
4168 		}
4169 		return null;
4170 	}
4171 
recreatePersistedContainer(String propertyName, String containerString, boolean addToContainerValues)4172 	public static void recreatePersistedContainer(String propertyName, String containerString, boolean addToContainerValues) {
4173 		int containerPrefixLength = CP_CONTAINER_PREFERENCES_PREFIX.length();
4174 		int index = propertyName.indexOf('|', containerPrefixLength);
4175 		if (containerString != null) containerString = containerString.trim();
4176 		if (index > 0) {
4177 			String projectName = propertyName.substring(containerPrefixLength, index).trim();
4178 			IJavaProject project = getJavaModelManager().getJavaModel().getJavaProject(projectName);
4179 			IPath containerPath = new Path(propertyName.substring(index+1).trim());
4180 			recreatePersistedContainer(project, containerPath, containerString, addToContainerValues);
4181 		}
4182 	}
4183 
recreatePersistedContainer(final IJavaProject project, final IPath containerPath, String containerString, boolean addToContainerValues)4184 	private static void recreatePersistedContainer(final IJavaProject project, final IPath containerPath, String containerString, boolean addToContainerValues) {
4185 		if (!project.getProject().isAccessible()) return; // avoid leaking deleted project's persisted container
4186 		if (containerString == null) {
4187 			getJavaModelManager().containerPut(project, containerPath, null);
4188 		} else {
4189 			IClasspathEntry[] entries;
4190 			try {
4191 				entries = ((JavaProject) project).decodeClasspath(containerString, null/*not interested in unknown elements*/)[0];
4192 			} catch (IOException e) {
4193 				Util.log(e, "Could not recreate persisted container: \n" + containerString); //$NON-NLS-1$
4194 				entries = JavaProject.INVALID_CLASSPATH;
4195 			}
4196 			if (entries != JavaProject.INVALID_CLASSPATH) {
4197 				final IClasspathEntry[] containerEntries = entries;
4198 				IClasspathContainer container = new IClasspathContainer() {
4199 					@Override
4200 					public IClasspathEntry[] getClasspathEntries() {
4201 						return containerEntries;
4202 					}
4203 					@Override
4204 					public String getDescription() {
4205 						return "Persisted container ["+containerPath+" for project ["+ project.getElementName()+"]"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$
4206 					}
4207 					@Override
4208 					public int getKind() {
4209 						return 0;
4210 					}
4211 					@Override
4212 					public IPath getPath() {
4213 						return containerPath;
4214 					}
4215 					@Override
4216 					public String toString() {
4217 						return getDescription();
4218 					}
4219 
4220 				};
4221 				if (addToContainerValues) {
4222 					getJavaModelManager().containerPut(project, containerPath, container);
4223 				}
4224 				Map<IPath, IClasspathContainer> projectContainers = getJavaModelManager().previousSessionContainers.get(project);
4225 				if (projectContainers == null){
4226 					projectContainers = new HashMap<>(1);
4227 					getJavaModelManager().previousSessionContainers.put(project, projectContainers);
4228 				}
4229 				projectContainers.put(containerPath, container);
4230 			}
4231 		}
4232 	}
4233 
4234 	/**
4235 	 * Remembers the given scope in a weak set
4236 	 * (so no need to remove it: it will be removed by the garbage collector)
4237 	 */
rememberScope(AbstractSearchScope scope)4238 	public void rememberScope(AbstractSearchScope scope) {
4239 		// NB: The value has to be null so as to not create a strong reference on the scope
4240 		this.searchScopes.put(scope, null);
4241 	}
4242 
4243 	/*
4244 	 * Removes all cached info for the given element (including all children)
4245 	 * from the cache.
4246 	 * Returns the info for the given element, or null if it was closed.
4247 	 */
removeInfoAndChildren(JavaElement element)4248 	public synchronized Object removeInfoAndChildren(JavaElement element) throws JavaModelException {
4249 		Object info = this.cache.peekAtInfo(element);
4250 		if (info != null) {
4251 			boolean wasVerbose = false;
4252 			try {
4253 				if (JavaModelCache.VERBOSE) {
4254 					String elementType = JavaModelCache.getElementType(element);
4255 					System.out.println(Thread.currentThread() + " CLOSING "+ elementType + " " + element.toStringWithAncestors());  //$NON-NLS-1$//$NON-NLS-2$
4256 					wasVerbose = true;
4257 					JavaModelCache.VERBOSE = false;
4258 				}
4259 				element.closing(info);
4260 				if (element instanceof IParent) {
4261 					closeChildren(info);
4262 				}
4263 				this.cache.removeInfo(element);
4264 				if (wasVerbose) {
4265 					System.out.println(this.cache.toStringFillingRation("-> ")); //$NON-NLS-1$
4266 				}
4267 			} finally {
4268 				JavaModelCache.VERBOSE = wasVerbose;
4269 			}
4270 			return info;
4271 		}
4272 		return null;
4273 	}
4274 
removeFromJarTypeCache(BinaryType type)4275 	void removeFromJarTypeCache(BinaryType type) {
4276 		this.cache.removeFromJarTypeCache(type);
4277 	}
4278 
removePerProjectInfo(JavaProject javaProject, boolean removeExtJarInfo)4279 	public void removePerProjectInfo(JavaProject javaProject, boolean removeExtJarInfo) {
4280 		synchronized(this.perProjectInfos) { // use the perProjectInfo collection as its own lock
4281 			IProject project = javaProject.getProject();
4282 			PerProjectInfo info= this.perProjectInfos.get(project);
4283 			if (info != null) {
4284 				this.perProjectInfos.remove(project);
4285 				if (removeExtJarInfo) {
4286 					info.forgetExternalTimestampsAndIndexes();
4287 				}
4288 			}
4289 		}
4290 		resetClasspathListCache();
4291 	}
4292 
4293 	/*
4294 	 * Reset project options stored in info cache.
4295 	 */
resetProjectOptions(JavaProject javaProject)4296 	public void resetProjectOptions(JavaProject javaProject) {
4297 		synchronized(this.perProjectInfos) { // use the perProjectInfo collection as its own lock
4298 			IProject project = javaProject.getProject();
4299 			PerProjectInfo info= this.perProjectInfos.get(project);
4300 			if (info != null) {
4301 				info.options = null;
4302 			}
4303 		}
4304 	}
4305 
4306 	/*
4307 	 * Reset project preferences stored in info cache.
4308 	 */
resetProjectPreferences(JavaProject javaProject)4309 	public void resetProjectPreferences(JavaProject javaProject) {
4310 		synchronized(this.perProjectInfos) { // use the perProjectInfo collection as its own lock
4311 			IProject project = javaProject.getProject();
4312 			PerProjectInfo info= this.perProjectInfos.get(project);
4313 			if (info != null) {
4314 				info.preferences = null;
4315 			}
4316 		}
4317 	}
4318 
doNotUse()4319 	public static final void doNotUse() {
4320 		// used by tests to simulate a startup
4321 		MANAGER.deltaState.doNotUse();
4322 		MANAGER = new JavaModelManager();
4323 	}
4324 
4325 	/*
4326 	 * Resets the cache that holds on binary type in jar files
4327 	 */
resetJarTypeCache()4328 	protected synchronized void resetJarTypeCache() {
4329 		this.cache.resetJarTypeCache();
4330 	}
4331 
resetClasspathListCache()4332 	public void resetClasspathListCache() {
4333 		if (this.nonChainingJars != null)
4334 			this.nonChainingJars.clear();
4335 		if (DEBUG_INVALID_ARCHIVES) {
4336 			synchronized(this.invalidArchivesMutex) {
4337 				if (!this.invalidArchives.isEmpty()) {
4338 					System.out.println("Invalid JAR cache: clearing cache"); //$NON-NLS-1$
4339 				}
4340 			}
4341 		}
4342 		synchronized(this.invalidArchivesMutex) {
4343 			this.invalidArchives.clear();
4344 		}
4345 		if (this.externalFiles != null)
4346 			this.externalFiles.clear();
4347 		if (this.assumedExternalFiles != null)
4348 			this.assumedExternalFiles.clear();
4349 	}
4350 
4351 	/*
4352 	 * Resets the temporary cache for newly created elements to null.
4353 	 */
resetTemporaryCache()4354 	public void resetTemporaryCache() {
4355 		this.temporaryCache.set(null);
4356 	}
4357 
4358 	/**
4359 	 * @see ISaveParticipant
4360 	 */
4361 	@Override
rollback(ISaveContext context)4362 	public void rollback(ISaveContext context){
4363 		// nothing to do
4364 	}
4365 
saveState(PerProjectInfo info, ISaveContext context)4366 	private void saveState(PerProjectInfo info, ISaveContext context) throws CoreException {
4367 
4368 		// passed this point, save actions are non trivial
4369 		if (context.getKind() == ISaveContext.SNAPSHOT) return;
4370 
4371 		// save built state
4372 		if (info.triedRead) saveBuiltState(info);
4373 	}
4374 
4375 	/**
4376 	 * Saves the built state for the project.
4377 	 */
saveBuiltState(PerProjectInfo info)4378 	private void saveBuiltState(PerProjectInfo info) throws CoreException {
4379 		if (JavaBuilder.DEBUG)
4380 			System.out.println(Messages.bind(Messages.build_saveStateProgress, info.project.getName()));
4381 		File file = getSerializationFile(info.project);
4382 		if (file == null) return;
4383 		long t = System.currentTimeMillis();
4384 		try {
4385 			DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
4386 			try {
4387 				out.writeUTF(JavaCore.PLUGIN_ID);
4388 				out.writeUTF("STATE"); //$NON-NLS-1$
4389 				if (info.savedState == null) {
4390 					out.writeBoolean(false);
4391 				} else {
4392 					out.writeBoolean(true);
4393 					JavaBuilder.writeState(info.savedState, out);
4394 				}
4395 			} finally {
4396 				out.close();
4397 			}
4398 		} catch (RuntimeException | IOException e) {
4399 			try {
4400 				file.delete();
4401 			} catch(SecurityException se) {
4402 				// could not delete file: cannot do much more
4403 			}
4404 			throw new CoreException(
4405 				new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, Platform.PLUGIN_ERROR,
4406 					Messages.bind(Messages.build_cannotSaveState, info.project.getName()), e));
4407 		}
4408 		if (JavaBuilder.DEBUG) {
4409 			t = System.currentTimeMillis() - t;
4410 			System.out.println(Messages.bind(Messages.build_saveStateComplete, String.valueOf(t)));
4411 		}
4412 	}
4413 
saveClasspathListCache(String cacheName)4414 	private void saveClasspathListCache(String cacheName) throws CoreException {
4415 		File file = getClasspathListFile(cacheName);
4416 		DataOutputStream out = null;
4417 		try {
4418 			out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
4419 			Set<IPath> pathCache = getClasspathListCache(cacheName);
4420 			synchronized (pathCache) {
4421 				out.writeInt(pathCache.size());
4422 				Iterator<IPath> entries = pathCache.iterator();
4423 				while (entries.hasNext()) {
4424 					IPath path = entries.next();
4425 					out.writeUTF(path.toPortableString());
4426 				}
4427 			}
4428 		} catch (IOException e) {
4429 			IStatus status = new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.ERROR, "Problems while saving non-chaining jar cache", e); //$NON-NLS-1$
4430 			throw new CoreException(status);
4431 		} finally {
4432 			if (out != null) {
4433 				try {
4434 					out.close();
4435 				} catch (IOException e) {
4436 					// nothing we can do: ignore
4437 				}
4438 			}
4439 		}
4440 	}
4441 
saveVariablesAndContainers(ISaveContext context)4442 	private void saveVariablesAndContainers(ISaveContext context) throws CoreException {
4443 		File file = getVariableAndContainersFile();
4444 		DataOutputStream out = null;
4445 		try {
4446 			out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
4447 			out.writeInt(VARIABLES_AND_CONTAINERS_FILE_VERSION);
4448 			new VariablesAndContainersSaveHelper(out).save(context);
4449 		} catch (IOException e) {
4450 			IStatus status = new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, IStatus.ERROR, "Problems while saving variables and containers", e); //$NON-NLS-1$
4451 			throw new CoreException(status);
4452 		} finally {
4453 			if (out != null) {
4454 				try {
4455 					out.close();
4456 				} catch (IOException e) {
4457 					// nothing we can do: ignore
4458 				}
4459 			}
4460 		}
4461 	}
4462 
4463 	private final class VariablesAndContainersSaveHelper {
4464 
4465 		private final HashtableOfObjectToInt classpathEntryIds; // IClasspathEntry -> int
4466 		private final DataOutputStream out;
4467 		private final HashtableOfObjectToInt stringIds; // Strings -> int
4468 
VariablesAndContainersSaveHelper(DataOutputStream out)4469 		VariablesAndContainersSaveHelper(DataOutputStream out) {
4470 			super();
4471 			this.classpathEntryIds = new HashtableOfObjectToInt();
4472 			this.out = out;
4473 			this.stringIds = new HashtableOfObjectToInt();
4474 		}
4475 
save(ISaveContext context)4476 		void save(ISaveContext context) throws IOException, JavaModelException {
4477 			saveProjects(getJavaModel().getJavaProjects());
4478 			// remove variables that should not be saved
4479 			HashMap<String, IPath> varsToSave = null;
4480 			Iterator<Entry<String, IPath>> iterator = JavaModelManager.this.variables.entrySet().iterator();
4481 			IEclipsePreferences defaultPreferences = getDefaultPreferences();
4482 			while (iterator.hasNext()) {
4483 				Entry<String, IPath> entry = iterator.next();
4484 				String varName = entry.getKey();
4485 				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
4486 						|| CP_ENTRY_IGNORE_PATH.equals(entry.getValue())) {
4487 
4488 					if (varsToSave == null)
4489 						varsToSave = new HashMap<>(JavaModelManager.this.variables);
4490 					varsToSave.remove(varName);
4491 				}
4492 			}
4493 			saveVariables(varsToSave != null ? varsToSave : JavaModelManager.this.variables);
4494 		}
4495 
saveAccessRule(ClasspathAccessRule rule)4496 		private void saveAccessRule(ClasspathAccessRule rule) throws IOException {
4497 			saveInt(rule.problemId);
4498 			savePath(rule.getPattern());
4499 		}
4500 
saveAccessRules(IAccessRule[] rules)4501 		private void saveAccessRules(IAccessRule[] rules) throws IOException {
4502 			int count = rules == null ? 0 : rules.length;
4503 
4504 			saveInt(count);
4505 			for (int i = 0; i < count; ++i)
4506 				saveAccessRule((ClasspathAccessRule) rules[i]);
4507 		}
4508 
saveAttribute(IClasspathAttribute attribute)4509 		private void saveAttribute(IClasspathAttribute attribute)
4510 				throws IOException {
4511 			saveString(attribute.getName());
4512 			saveString(attribute.getValue());
4513 		}
4514 
saveAttributes(IClasspathAttribute[] attributes)4515 		private void saveAttributes(IClasspathAttribute[] attributes)
4516 				throws IOException {
4517 			int count = attributes == null ? 0 : attributes.length;
4518 
4519 			saveInt(count);
4520 			for (int i = 0; i < count; ++i)
4521 				saveAttribute(attributes[i]);
4522 		}
4523 
saveClasspathEntries(IClasspathEntry[] entries)4524 		private void saveClasspathEntries(IClasspathEntry[] entries)
4525 				throws IOException {
4526 			int count = entries == null ? 0 : entries.length;
4527 
4528 			saveInt(count);
4529 			for (int i = 0; i < count; ++i)
4530 				saveClasspathEntry(entries[i]);
4531 		}
4532 
saveClasspathEntry(IClasspathEntry entry)4533 		private void saveClasspathEntry(IClasspathEntry entry)
4534 				throws IOException {
4535 			if (saveNewId(entry, this.classpathEntryIds)) {
4536 				saveInt(entry.getContentKind());
4537 				saveInt(entry.getEntryKind());
4538 				savePath(entry.getPath());
4539 				savePaths(entry.getInclusionPatterns());
4540 				savePaths(entry.getExclusionPatterns());
4541 				savePath(entry.getSourceAttachmentPath());
4542 				savePath(entry.getSourceAttachmentRootPath());
4543 				savePath(entry.getOutputLocation());
4544 				this.out.writeBoolean(entry.isExported());
4545 				saveAccessRules(entry.getAccessRules());
4546 				this.out.writeBoolean(entry.combineAccessRules());
4547 				saveAttributes(entry.getExtraAttributes());
4548 			}
4549 		}
4550 
saveContainers(IJavaProject project, Map<IPath, IClasspathContainer> containerMap)4551 		private void saveContainers(IJavaProject project, Map<IPath, IClasspathContainer> containerMap)
4552 				throws IOException {
4553 			saveInt(containerMap.size());
4554 
4555 			for (Iterator<Entry<IPath, IClasspathContainer>> i = containerMap.entrySet().iterator(); i.hasNext();) {
4556 				Entry<IPath, IClasspathContainer> entry = i.next();
4557 				IPath path = entry.getKey();
4558 				IClasspathContainer container = entry.getValue();
4559 				IClasspathEntry[] cpEntries = null;
4560 
4561 				if (container == null) {
4562 					// container has not been initialized yet, use previous
4563 					// session value
4564 					// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=73969)
4565 					container = getPreviousSessionContainer(path, project);
4566 				}
4567 
4568 				if (container != null)
4569 					cpEntries = container.getClasspathEntries();
4570 
4571 				savePath(path);
4572 				saveClasspathEntries(cpEntries);
4573 			}
4574 		}
4575 
saveInt(int value)4576 		private void saveInt(int value) throws IOException {
4577 			this.out.writeInt(value);
4578 		}
4579 
saveNewId(Object key, HashtableOfObjectToInt map)4580 		private boolean saveNewId(Object key, HashtableOfObjectToInt map) throws IOException {
4581 			int id = map.get(key);
4582 
4583 			if (id == -1) {
4584 				int newId = map.size();
4585 
4586 				map.put(key, newId);
4587 
4588 				saveInt(newId);
4589 
4590 				return true;
4591 			} else {
4592 				saveInt(id);
4593 
4594 				return false;
4595 			}
4596 		}
4597 
savePath(IPath path)4598 		private void savePath(IPath path) throws IOException {
4599 			if (path == null) {
4600 				this.out.writeBoolean(true);
4601 			} else {
4602 				this.out.writeBoolean(false);
4603 				saveString(path.toPortableString());
4604 			}
4605 		}
4606 
savePaths(IPath[] paths)4607 		private void savePaths(IPath[] paths) throws IOException {
4608 			int count = paths == null ? 0 : paths.length;
4609 
4610 			saveInt(count);
4611 			for (int i = 0; i < count; ++i)
4612 				savePath(paths[i]);
4613 		}
4614 
saveProjects(IJavaProject[] projects)4615 		private void saveProjects(IJavaProject[] projects) throws IOException,
4616 				JavaModelException {
4617 			int count = projects.length;
4618 
4619 			saveInt(count);
4620 
4621 			for (int i = 0; i < count; ++i) {
4622 				IJavaProject project = projects[i];
4623 
4624 				saveString(project.getElementName());
4625 
4626 				Map<IPath, IClasspathContainer> containerMap = JavaModelManager.this.containers.get(project);
4627 
4628 				if (containerMap == null) {
4629 					containerMap = Collections.EMPTY_MAP;
4630 				} else {
4631 					// clone while iterating
4632 					// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59638)
4633 					containerMap = new HashMap<>(containerMap);
4634 				}
4635 
4636 				saveContainers(project, containerMap);
4637 			}
4638 		}
4639 
saveString(String string)4640 		private void saveString(String string) throws IOException {
4641 			if (saveNewId(string, this.stringIds))
4642 				this.out.writeUTF(string);
4643 		}
4644 
saveVariables(Map<String, IPath> map)4645 		private void saveVariables(Map<String, IPath> map) throws IOException {
4646 			saveInt(map.size());
4647 
4648 			for (Iterator<Entry<String, IPath>> i = map.entrySet().iterator(); i.hasNext();) {
4649 				Entry<String, IPath> entry = i.next();
4650 				String varName = entry.getKey();
4651 				IPath varPath = entry.getValue();
4652 
4653 				saveString(varName);
4654 				savePath(varPath);
4655 			}
4656 		}
4657 	}
4658 
traceVariableAndContainers(String action, long start)4659 	private void traceVariableAndContainers(String action, long start) {
4660 
4661 		Long delta = Long.valueOf(System.currentTimeMillis() - start);
4662 		Long length = Long.valueOf(getVariableAndContainersFile().length());
4663 		String pattern = "{0} {1} bytes in variablesAndContainers.dat in {2}ms"; //$NON-NLS-1$
4664 		String message = MessageFormat.format(pattern, new Object[]{action, length, delta});
4665 
4666 		System.out.println(message);
4667 	}
4668 
4669 	/**
4670 	 * @see ISaveParticipant
4671 	 */
4672 	@Override
saving(ISaveContext context)4673 	public void saving(ISaveContext context) throws CoreException {
4674 
4675 	    long start = -1;
4676 		if (VERBOSE)
4677 			start = System.currentTimeMillis();
4678 
4679 		// save variable and container values on snapshot/full save
4680 		saveVariablesAndContainers(context);
4681 
4682 		if (VERBOSE)
4683 			traceVariableAndContainers("Saved", start); //$NON-NLS-1$
4684 
4685 		switch(context.getKind()) {
4686 			case ISaveContext.FULL_SAVE : {
4687 				// save non-chaining jar, invalid jar and external file caches on full save
4688 				saveClasspathListCache(NON_CHAINING_JARS_CACHE);
4689 				saveClasspathListCache(EXTERNAL_FILES_CACHE);
4690 				saveClasspathListCache(ASSUMED_EXTERNAL_FILES_CACHE);
4691 
4692 				// will need delta since this save (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38658)
4693 				context.needDelta();
4694 
4695 				// clean up indexes on workspace full save
4696 				// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=52347)
4697 				IndexManager manager = this.indexManager;
4698 				if (manager != null
4699 						// don't force initialization of workspace scope as we could be shutting down
4700 						// (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=93941)
4701 						&& this.workspaceScope != null) {
4702 					manager.cleanUpIndexes();
4703 				}
4704 			}
4705 			//$FALL-THROUGH$
4706 			case ISaveContext.SNAPSHOT : {
4707 				// clean up external folders on full save or snapshot
4708 				this.externalFoldersManager.cleanUp(null);
4709 			}
4710 		}
4711 
4712 		IProject savedProject = context.getProject();
4713 		if (savedProject != null) {
4714 			if (!JavaProject.hasJavaNature(savedProject)) return; // ignore
4715 			PerProjectInfo info = getPerProjectInfo(savedProject, true /* create info */);
4716 			saveState(info, context);
4717 			return;
4718 		}
4719 
4720 		ArrayList<IStatus> vStats= null; // lazy initialized
4721 		ArrayList<PerProjectInfo> values = null;
4722 		synchronized(this.perProjectInfos) {
4723 			values = new ArrayList<>(this.perProjectInfos.values());
4724 		}
4725 		Iterator<PerProjectInfo> iterator = values.iterator();
4726 		while (iterator.hasNext()) {
4727 			try {
4728 				PerProjectInfo info = iterator.next();
4729 				saveState(info, context);
4730 			} catch (CoreException e) {
4731 				if (vStats == null)
4732 					vStats= new ArrayList<>();
4733 				vStats.add(e.getStatus());
4734 			}
4735 		}
4736 		if (vStats != null) {
4737 			IStatus[] stats= new IStatus[vStats.size()];
4738 			vStats.toArray(stats);
4739 			throw new CoreException(new MultiStatus(JavaCore.PLUGIN_ID, IStatus.ERROR, stats, Messages.build_cannotSaveStates, null));
4740 		}
4741 
4742 		// save external libs timestamps
4743 		this.deltaState.saveExternalLibTimeStamps();
4744 
4745 	}
4746 
4747 	/**
4748 	 * Add a secondary type in temporary indexing cache for a project got from given path.
4749 	 *
4750 	 * Current secondary types cache is not modified as we want to wait that indexing
4751 	 * was finished before taking new secondary types into account.
4752 	 *
4753 	 * @see #secondaryTypes(IJavaProject, boolean, IProgressMonitor)
4754 	 */
secondaryTypeAdding(String path, char[] typeName, char[] packageName)4755 	public void secondaryTypeAdding(String path, char[] typeName, char[] packageName) {
4756 		if (VERBOSE) {
4757 			StringBuffer buffer = new StringBuffer("JavaModelManager.addSecondaryType("); //$NON-NLS-1$
4758 			buffer.append(path);
4759 			buffer.append(',');
4760 			buffer.append('[');
4761 			buffer.append(new String(packageName));
4762 			buffer.append('.');
4763 			buffer.append(new String(typeName));
4764 			buffer.append(']');
4765 			buffer.append(')');
4766 			Util.verbose(buffer.toString());
4767 		}
4768 		IWorkspaceRoot wRoot = ResourcesPlugin.getWorkspace().getRoot();
4769 		IResource resource = wRoot.findMember(path);
4770 		if (resource instanceof IFile) {
4771 			if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(path)) {
4772 				IProject project = resource.getProject();
4773 				try {
4774 					PerProjectInfo projectInfo = getPerProjectInfoCheckExistence(project);
4775 					// Get or create map to cache secondary types while indexing (can be not synchronized as indexing insure a non-concurrent usage)
4776 					Map<IFile, Map<String, Map<String, IType>>> indexedSecondaryTypes;
4777 					if (projectInfo.secondaryTypes == null) {
4778 						projectInfo.secondaryTypes = new Hashtable<>(3);
4779 						indexedSecondaryTypes = new HashMap<>(3);
4780 						projectInfo.indexingSecondaryCache = indexedSecondaryTypes;
4781 					} else {
4782 						indexedSecondaryTypes = projectInfo.indexingSecondaryCache;
4783 						if (indexedSecondaryTypes == null) {
4784 							indexedSecondaryTypes = new HashMap<>(3);
4785 							projectInfo.indexingSecondaryCache = indexedSecondaryTypes;
4786 						}
4787 					}
4788 					// Store the secondary type in temporary cache (these are just handles => no problem to create it now...)
4789 					Map<String, Map<String, IType>> allTypes = indexedSecondaryTypes.get(resource);
4790 					if (allTypes == null) {
4791 						allTypes = new HashMap<>(3);
4792 						indexedSecondaryTypes.put((IFile) resource, allTypes);
4793 					}
4794 					ICompilationUnit unit = JavaModelManager.createCompilationUnitFrom((IFile)resource, null);
4795 					if (unit != null) {
4796 						String typeString = new String(typeName);
4797 						IType type = unit.getType(typeString);
4798 						// String packageString = new String(packageName);
4799 						// use package fragment name instead of parameter as it may be invalid...
4800 						// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=186781
4801 						String packageString = type.getPackageFragment().getElementName();
4802 						Map<String, IType> packageTypes = allTypes.get(packageString);
4803 						if (packageTypes == null) {
4804 							packageTypes = new HashMap<>(3);
4805 							allTypes.put(packageString, packageTypes);
4806 						}
4807 						packageTypes.put(typeString, type);
4808 					}
4809 					if (VERBOSE) {
4810 						Util.verbose("	- indexing cache:"); //$NON-NLS-1$
4811 						Iterator<Entry<IFile, Map<String, Map<String, IType>>>> entries = indexedSecondaryTypes.entrySet().iterator();
4812 						while (entries.hasNext()) {
4813 							Entry<IFile, Map<String, Map<String, IType>>> entry = entries.next();
4814 							IFile file = entry.getKey();
4815 							Util.verbose("		+ "+file.getFullPath()+':'+ entry.getValue()); //$NON-NLS-1$
4816 						}
4817 					}
4818 				}
4819 				catch (JavaModelException jme) {
4820 					// do nothing
4821 				}
4822 			}
4823 		}
4824 	}
4825 
4826 	/**
4827 	 * Get all secondary types for a project and store result in per project info cache.
4828 	 * <p>
4829 	 * This cache is an <code>Hashtable&lt;String, HashMap&lt;String, IType&gt;&gt;</code>:
4830 	 *  <ul>
4831 	 * 	<li>key: package name
4832 	 * 	<li>value:
4833 	 * 		<ul>
4834 	 * 		<li>key: type name
4835 	 * 		<li>value: java model handle for the secondary type
4836 	 * 		</ul>
4837 	 * </ul>
4838 	 * Hashtable was used to protect callers from possible concurrent access.
4839 	 * </p>
4840 	 * Note, if indexing is not finished and caller does
4841 	 * not wait for the end of indexing, returned map is the current secondary
4842 	 * types cache content which may be invalid...
4843 	 *
4844 	 * @param project Project we want get secondary types from
4845 	 * @return HashMap Table of secondary type names->path for given project
4846 	 */
secondaryTypes(IJavaProject project, boolean waitForIndexes, IProgressMonitor monitor)4847 	public Map<String, Map<String, IType>> secondaryTypes(IJavaProject project, boolean waitForIndexes, IProgressMonitor monitor) throws JavaModelException {
4848 		if (VERBOSE) {
4849 			StringBuffer buffer = new StringBuffer("JavaModelManager.secondaryTypes("); //$NON-NLS-1$
4850 			buffer.append(project.getElementName());
4851 			buffer.append(',');
4852 			buffer.append(waitForIndexes);
4853 			buffer.append(')');
4854 			Util.verbose(buffer.toString());
4855 		}
4856 
4857 		// Return cache if not empty and there's no new secondary types created during indexing
4858 		final PerProjectInfo projectInfo = getPerProjectInfoCheckExistence(project.getProject());
4859 		Map<IFile, Map<String, Map<String, IType>>> indexingSecondaryCache = projectInfo.secondaryTypes == null ? null : projectInfo.indexingSecondaryCache;
4860 		if (projectInfo.secondaryTypes != null && indexingSecondaryCache == null) {
4861 			return projectInfo.secondaryTypes;
4862 		}
4863 
4864 		// Perform search request only if secondary types cache is not initialized yet (this will happen only once!)
4865 		if (projectInfo.secondaryTypes == null) {
4866 			return secondaryTypesSearching(project, waitForIndexes, monitor, projectInfo);
4867 		}
4868 
4869 		// New secondary types have been created while indexing secondary types cache
4870 		// => need to know whether the indexing is finished or not
4871 		boolean indexing = this.indexManager.awaitingJobsCount() > 0;
4872 		if (indexing) {
4873 			if (!waitForIndexes)  {
4874 				// Indexing is running but caller cannot wait => return current cache
4875 				return projectInfo.secondaryTypes;
4876 			}
4877 
4878 			// Wait for the end of indexing or a cancel
4879 			try {
4880 				this.indexManager.performConcurrentJob(new IJob() {
4881 					@Override
4882 					public boolean belongsTo(String jobFamily) {
4883 						return true;
4884 					}
4885 
4886 					@Override
4887 					public void cancel() {
4888 						// job is cancelled through progress
4889 					}
4890 
4891 					@Override
4892 					public void ensureReadyToRun() {
4893 						// always ready
4894 					}
4895 
4896 					@Override
4897 					public boolean execute(IProgressMonitor progress) {
4898 						return progress == null || !progress.isCanceled();
4899 					}
4900 
4901 					@Override
4902 					public String getJobFamily() {
4903 						return ""; //$NON-NLS-1$
4904 					}
4905 
4906 				}, IJob.WaitUntilReady, monitor);
4907 			} catch (OperationCanceledException oce) {
4908 				return projectInfo.secondaryTypes;
4909 			}
4910 		}
4911 
4912 		// Indexing is finished => merge caches and return result
4913 		return secondaryTypesMerging(projectInfo);
4914 	}
4915 
4916 	/*
4917 	 * Return secondary types cache merged with new secondary types created while indexing
4918 	 * Note that merge result is directly stored in given parameter map.
4919 	 */
secondaryTypesMerging(PerProjectInfo projectInfo)4920 	private Map<String, Map<String, IType>> secondaryTypesMerging(PerProjectInfo projectInfo) {
4921 		Map<String, Map<String, IType>> secondaryTypes = projectInfo.secondaryTypes;
4922 		if (VERBOSE) {
4923 			Util.verbose("JavaModelManager.getSecondaryTypesMerged()"); //$NON-NLS-1$
4924 			Util.verbose("	- current cache to merge:"); //$NON-NLS-1$
4925 			Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypes.entrySet().iterator();
4926 			while (entries.hasNext()) {
4927 				Entry<String, Map<String, IType>> entry = entries.next();
4928 				String packName = entry.getKey();
4929 				Util.verbose("		+ "+packName+':'+ entry.getValue() ); //$NON-NLS-1$
4930 			}
4931 		}
4932 
4933 		// Return current cache if there's no indexing cache (double check, this should not happen)
4934 		Map<IFile, Map<String, Map<String, IType>>> indexedSecondaryTypes = projectInfo.indexingSecondaryCache;
4935 		projectInfo.indexingSecondaryCache = null;
4936 		if (indexedSecondaryTypes == null) {
4937 			return secondaryTypes;
4938 		}
4939 
4940 		// Merge indexing cache in secondary types one
4941 		Iterator<Entry<IFile, Map<String, Map<String, IType>>>> entries = indexedSecondaryTypes.entrySet().iterator();
4942 		while (entries.hasNext()) {
4943 			Entry<IFile, Map<String, Map<String, IType>>> entry = entries.next();
4944 			IFile file = entry.getKey();
4945 
4946 			// Remove all secondary types of indexed file from cache
4947 			secondaryTypesRemoving(secondaryTypes, file);
4948 
4949 			// Add all indexing file secondary types in given secondary types cache
4950 			Map<String, Map<String, IType>> fileSecondaryTypes = entry.getValue();
4951 			Iterator<Entry<String, Map<String, IType>>> entries2 = fileSecondaryTypes.entrySet().iterator();
4952 			while (entries2.hasNext()) {
4953 				Entry<String, Map<String, IType>> entry2 = entries2.next();
4954 				String packageName = entry2.getKey();
4955 				Map<String, IType> cachedTypes = secondaryTypes.get(packageName);
4956 				if (cachedTypes == null) {
4957 					secondaryTypes.put(packageName, entry2.getValue());
4958 				} else {
4959 					Map<String, IType> types = entry2.getValue();
4960 					Iterator<Entry<String, IType>> entries3 = types.entrySet().iterator();
4961 					while (entries3.hasNext()) {
4962 						Entry<String, IType> entry3 = entries3.next();
4963 						String typeName = entry3.getKey();
4964 						cachedTypes.put(typeName, entry3.getValue());
4965 					}
4966 				}
4967 			}
4968 		}
4969 		if (VERBOSE) {
4970 			Util.verbose("	- secondary types cache merged:"); //$NON-NLS-1$
4971 			Iterator<Entry<String, Map<String, IType>>> entries2 = secondaryTypes.entrySet().iterator();
4972 			while (entries.hasNext()) {
4973 				Entry<String, Map<String, IType>> entry = entries2.next();
4974 				String packName = entry.getKey();
4975 				Util.verbose("		+ "+packName+':'+ entry.getValue()); //$NON-NLS-1$
4976 			}
4977 		}
4978 		return secondaryTypes;
4979 	}
4980 
4981 	/*
4982 	 * Perform search request to get all secondary types of a given project.
4983 	 * If not waiting for indexes and indexing is running, will return types found in current built indexes...
4984 	 */
secondaryTypesSearching(IJavaProject project, boolean waitForIndexes, IProgressMonitor monitor, final PerProjectInfo projectInfo)4985 	private static Map<String, Map<String, IType>> secondaryTypesSearching(IJavaProject project, boolean waitForIndexes, IProgressMonitor monitor, final PerProjectInfo projectInfo) throws JavaModelException {
4986 		if (VERBOSE || BasicSearchEngine.VERBOSE) {
4987 			StringBuffer buffer = new StringBuffer("JavaModelManager.secondaryTypesSearch("); //$NON-NLS-1$
4988 			buffer.append(project.getElementName());
4989 			buffer.append(',');
4990 			buffer.append(waitForIndexes);
4991 			buffer.append(')');
4992 			Util.verbose(buffer.toString());
4993 		}
4994 
4995 		final Hashtable<String, Map<String, String>> secondaryTypesSearch = new Hashtable<>(3);
4996 		IRestrictedAccessTypeRequestor nameRequestor = new IRestrictedAccessTypeRequestor() {
4997 			@Override
4998 			public void acceptType(int modifiers, char[] packageName, char[] simpleTypeName, char[][] enclosingTypeNames, String path, AccessRestriction access) {
4999 				String key = packageName==null ? "" : new String(packageName); //$NON-NLS-1$
5000 				Map<String, String> types = secondaryTypesSearch.get(key);
5001 				if (types == null) types = new HashMap<>(3);
5002 				types.put(new String(simpleTypeName), path);
5003 				secondaryTypesSearch.put(key, types);
5004 			}
5005 		};
5006 
5007 		// Build scope using prereq projects but only source folders
5008 		IPackageFragmentRoot[] allRoots = project.getAllPackageFragmentRoots();
5009 		int length = allRoots.length, size = 0;
5010 		IPackageFragmentRoot[] allSourceFolders = new IPackageFragmentRoot[length];
5011 		for (int i=0; i<length; i++) {
5012 			if (allRoots[i].getKind() == IPackageFragmentRoot.K_SOURCE) {
5013 				allSourceFolders[size++] = allRoots[i];
5014 			}
5015 		}
5016 		if (size < length) {
5017 			System.arraycopy(allSourceFolders, 0, allSourceFolders = new IPackageFragmentRoot[size], 0, size);
5018 		}
5019 
5020 		// Search all secondary types on scope
5021 		new BasicSearchEngine().searchAllSecondaryTypeNames(allSourceFolders, nameRequestor, waitForIndexes, monitor);
5022 
5023 		// Build types from paths
5024 		final Hashtable<String, Map<String, IType>> secondaryTypes = new Hashtable<>(secondaryTypesSearch.size());
5025 		for (Entry<String, Map<String, String>> packageEntry : secondaryTypesSearch.entrySet()) {
5026 			String packageName = packageEntry.getKey();
5027 			Map<String, String> types = packageEntry.getValue();
5028 			Map<String, IType> tempTypes = new HashMap<>(types.size());
5029 			for (Entry<String, String> entry : types.entrySet()) {
5030 				String typeName = entry.getKey();
5031 				String path = entry.getValue();
5032 				if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(path)) {
5033 					IFile file = ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(path));
5034 					ICompilationUnit unit = JavaModelManager.createCompilationUnitFrom(file, null);
5035 					IType type = unit.getType(typeName);
5036 					tempTypes.put(typeName, type);
5037 				}
5038 			}
5039 			secondaryTypes.put(packageName, tempTypes);
5040 		}
5041 
5042 		// Store result in per project info cache if still null or there's still an indexing cache (may have been set by another thread...)
5043 		if (projectInfo.secondaryTypes == null || projectInfo.indexingSecondaryCache != null) {
5044 			projectInfo.secondaryTypes = secondaryTypes;
5045 			if (VERBOSE || BasicSearchEngine.VERBOSE) {
5046 				System.out.print(Thread.currentThread() + "	-> secondary paths stored in cache: ");  //$NON-NLS-1$
5047 				System.out.println();
5048 				Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypes.entrySet().iterator();
5049 				while (entries.hasNext()) {
5050 					Entry<String, Map<String, IType>> entry = entries.next();
5051 					String qualifiedName = entry.getKey();
5052 					Util.verbose("		- "+qualifiedName+'-'+ entry.getValue()); //$NON-NLS-1$
5053 				}
5054 			}
5055 		}
5056 		return projectInfo.secondaryTypes;
5057 	}
5058 
5059 	/**
5060 	 * Remove from secondary types cache all types belonging to a given file.
5061 	 * Clean secondary types cache built while indexing if requested.
5062 	 *
5063 	 * Project's secondary types cache is found using file location.
5064 	 *
5065 	 * @param file File to remove
5066 	 */
secondaryTypesRemoving(IFile file, boolean cleanIndexCache)5067 	public void secondaryTypesRemoving(IFile file, boolean cleanIndexCache) {
5068 		if (VERBOSE) {
5069 			StringBuffer buffer = new StringBuffer("JavaModelManager.removeFromSecondaryTypesCache("); //$NON-NLS-1$
5070 			buffer.append(file.getName());
5071 			buffer.append(')');
5072 			Util.verbose(buffer.toString());
5073 		}
5074 		if (file != null) {
5075 			PerProjectInfo projectInfo = getPerProjectInfo(file.getProject(), false);
5076 			if (projectInfo != null && projectInfo.secondaryTypes != null) {
5077 				if (VERBOSE) {
5078 					Util.verbose("-> remove file from cache of project: "+file.getProject().getName()); //$NON-NLS-1$
5079 				}
5080 
5081 				// Clean current cache
5082 				secondaryTypesRemoving(projectInfo.secondaryTypes, file);
5083 
5084 				// Clean indexing cache if necessary
5085 				Map<IFile, Map<String, Map<String, IType>>> indexingCache = projectInfo.indexingSecondaryCache;
5086 				if (!cleanIndexCache) {
5087 					if (indexingCache == null) {
5088 						// Need to signify that secondary types indexing will happen before any request happens
5089 						// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=152841
5090 						projectInfo.indexingSecondaryCache = new HashMap<>();
5091 					}
5092 					return;
5093 				}
5094 				if (indexingCache != null) {
5095 					Set<IFile> keys = indexingCache.keySet();
5096 					int filesSize = keys.size(), filesCount = 0;
5097 					IFile[] removed = null;
5098 					Iterator<IFile> cachedFiles = keys.iterator();
5099 					while (cachedFiles.hasNext()) {
5100 						IFile cachedFile = cachedFiles.next();
5101 						if (file.equals(cachedFile)) {
5102 							if (removed == null) removed = new IFile[filesSize];
5103 							filesSize--;
5104 							removed[filesCount++] = cachedFile;
5105 						}
5106 					}
5107 					if (removed != null) {
5108 						for (int i=0; i<filesCount; i++) {
5109 							indexingCache.remove(removed[i]);
5110 						}
5111 					}
5112 				}
5113 			}
5114 		}
5115 	}
5116 
5117 	/*
5118 	 * Remove from a given cache map all secondary types belonging to a given file.
5119 	 * Note that there can have several secondary types per file...
5120 	 */
secondaryTypesRemoving(Map<String, Map<String, IType>> secondaryTypesMap, IFile file)5121 	private void secondaryTypesRemoving(Map<String, Map<String, IType>> secondaryTypesMap, IFile file) {
5122 		if (VERBOSE) {
5123 			StringBuffer buffer = new StringBuffer("JavaModelManager.removeSecondaryTypesFromMap("); //$NON-NLS-1$
5124 			Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypesMap.entrySet().iterator();
5125 			while (entries.hasNext()) {
5126 				Entry<String, Map<String, IType>> entry = entries.next();
5127 				String qualifiedName = entry.getKey();
5128 				buffer.append(qualifiedName+':'+ entry.getValue());
5129 			}
5130 			buffer.append(',');
5131 			buffer.append(file.getFullPath());
5132 			buffer.append(')');
5133 			Util.verbose(buffer.toString());
5134 		}
5135 		Set<Entry<String, Map<String, IType>>> packageEntries = secondaryTypesMap.entrySet();
5136 		int packagesSize = packageEntries.size(), removedPackagesCount = 0;
5137 		String[] removedPackages = null;
5138 		Iterator<Entry<String, Map<String, IType>>> packages = packageEntries.iterator();
5139 		while (packages.hasNext()) {
5140 			Entry<String, Map<String, IType>> entry = packages.next();
5141 			String packName = entry.getKey();
5142 			Map<String, IType> types = entry.getValue();
5143 			Set<Entry<String, IType>> nameEntries = types.entrySet();
5144 			int namesSize = nameEntries.size(), removedNamesCount = 0;
5145 			String[] removedNames = null;
5146 			Iterator<Entry<String, IType>> names = nameEntries.iterator();
5147 			while (names.hasNext()) {
5148 				Entry<String, IType> entry2 = names.next();
5149 				String typeName = entry2.getKey();
5150 				JavaElement type = (JavaElement) entry2.getValue();
5151 				if (file.equals(type.resource())) {
5152 					if (removedNames == null) removedNames = new String[namesSize];
5153 					namesSize--;
5154 					removedNames[removedNamesCount++] = typeName;
5155 				}
5156 			}
5157 			if (removedNames != null) {
5158 				for (int i=0; i<removedNamesCount; i++) {
5159 					types.remove(removedNames[i]);
5160 				}
5161 			}
5162 			if (types.size() == 0) {
5163 				if (removedPackages == null) removedPackages = new String[packagesSize];
5164 				packagesSize--;
5165 				removedPackages[removedPackagesCount++] = packName;
5166 			}
5167 		}
5168 		if (removedPackages != null) {
5169 			for (int i=0; i<removedPackagesCount; i++) {
5170 				secondaryTypesMap.remove(removedPackages[i]);
5171 			}
5172 		}
5173 		if (VERBOSE) {
5174 			Util.verbose("	- new secondary types map:"); //$NON-NLS-1$
5175 			Iterator<Entry<String, Map<String, IType>>> entries = secondaryTypesMap.entrySet().iterator();
5176 			while (entries.hasNext()) {
5177 				Entry<String, Map<String, IType>> entry = entries.next();
5178 				String qualifiedName = entry.getKey();
5179 				Util.verbose("		+ "+qualifiedName+':'+ entry.getValue()); //$NON-NLS-1$
5180 			}
5181 		}
5182 	}
5183 
5184 	/**
5185 	 * Record the order in which to build the java projects (batch build). This order is based
5186 	 * on the projects classpath settings.
5187 	 */
setBuildOrder(String[] javaBuildOrder)5188 	protected void setBuildOrder(String[] javaBuildOrder) throws JavaModelException {
5189 
5190 		// optional behaviour
5191 		// possible value of index 0 is Compute
5192 		if (!JavaCore.COMPUTE.equals(JavaCore.getOption(JavaCore.CORE_JAVA_BUILD_ORDER))) return; // cannot be customized at project level
5193 
5194 		if (javaBuildOrder == null || javaBuildOrder.length <= 1) return;
5195 
5196 		IWorkspace workspace = ResourcesPlugin.getWorkspace();
5197 		IWorkspaceDescription description = workspace.getDescription();
5198 		String[] wksBuildOrder = description.getBuildOrder();
5199 
5200 		String[] newOrder;
5201 		if (wksBuildOrder == null){
5202 			newOrder = javaBuildOrder;
5203 		} else {
5204 			// remove projects which are already mentionned in java builder order
5205 			int javaCount = javaBuildOrder.length;
5206 			HashMap<String, String> newSet = new HashMap<>(javaCount); // create a set for fast check
5207 			for (int i = 0; i < javaCount; i++){
5208 				newSet.put(javaBuildOrder[i], javaBuildOrder[i]);
5209 			}
5210 			int removed = 0;
5211 			int oldCount = wksBuildOrder.length;
5212 			for (int i = 0; i < oldCount; i++){
5213 				if (newSet.containsKey(wksBuildOrder[i])){
5214 					wksBuildOrder[i] = null;
5215 					removed++;
5216 				}
5217 			}
5218 			// add Java ones first
5219 			newOrder = new String[oldCount - removed + javaCount];
5220 			System.arraycopy(javaBuildOrder, 0, newOrder, 0, javaCount); // java projects are built first
5221 
5222 			// copy previous items in their respective order
5223 			int index = javaCount;
5224 			for (int i = 0; i < oldCount; i++){
5225 				if (wksBuildOrder[i] != null){
5226 					newOrder[index++] = wksBuildOrder[i];
5227 				}
5228 			}
5229 		}
5230 		// commit the new build order out
5231 		description.setBuildOrder(newOrder);
5232 		try {
5233 			workspace.setDescription(description);
5234 		} catch(CoreException e){
5235 			throw new JavaModelException(e);
5236 		}
5237 	}
5238 
5239 	/**
5240 	 * Sets the last built state for the given project, or null to reset it.
5241 	 */
setLastBuiltState(IProject project, Object state)5242 	public void setLastBuiltState(IProject project, Object state) {
5243 		if (JavaProject.hasJavaNature(project)) {
5244 			// should never be requested on non-Java projects
5245 			PerProjectInfo info = getPerProjectInfo(project, true /*create if missing*/);
5246 			info.triedRead = true; // no point trying to re-read once using setter
5247 			info.savedState = state;
5248 		}
5249 		if (state == null) { // delete state file to ensure a full build happens if the workspace crashes
5250 			try {
5251 				File file = getSerializationFile(project);
5252 				if (file != null && file.exists())
5253 					file.delete();
5254 			} catch(SecurityException se) {
5255 				// could not delete file: cannot do much more
5256 			}
5257 		}
5258 	}
5259 
5260 	/**
5261 	 * Store the preferences value for the given option name.
5262 	 *
5263 	 * @param optionName The name of the option
5264 	 * @param optionValue The value of the option. If <code>null</code>, then
5265 	 * 	the option will be removed from the preferences instead.
5266 	 * @param eclipsePreferences The eclipse preferences to be updated
5267 	 * @param otherOptions more options being stored, used to avoid conflict between deprecated option and its compatible
5268 	 * @return <code>true</code> if the preferences have been changed,
5269 	 * 	<code>false</code> otherwise.
5270 	 */
storePreference(String optionName, String optionValue, IEclipsePreferences eclipsePreferences, Map<String, String> otherOptions)5271 	public boolean storePreference(String optionName, String optionValue, IEclipsePreferences eclipsePreferences, Map<String, String> otherOptions) {
5272 		int optionLevel = this.getOptionLevel(optionName);
5273 		if (optionLevel == UNKNOWN_OPTION) return false; // unrecognized option
5274 
5275 		// Store option value
5276 		switch (optionLevel) {
5277 			case JavaModelManager.VALID_OPTION:
5278 				if (optionValue == null) {
5279 					eclipsePreferences.remove(optionName);
5280 				} else {
5281 					eclipsePreferences.put(optionName, optionValue);
5282 				}
5283 				break;
5284 			case JavaModelManager.DEPRECATED_OPTION:
5285 				// Try to migrate deprecated option
5286 				eclipsePreferences.remove(optionName); // get rid off old preference
5287 				String[] compatibleOptions = this.deprecatedOptions.get(optionName);
5288 				for (int co=0, length=compatibleOptions.length; co < length; co++) {
5289 					if (otherOptions != null && otherOptions.containsKey(compatibleOptions[co]))
5290 						continue; // don't overwrite explicit value of otherOptions at compatibleOptions[co]
5291 					if (optionValue == null) {
5292 						eclipsePreferences.remove(compatibleOptions[co]);
5293 					} else {
5294 						eclipsePreferences.put(compatibleOptions[co], optionValue);
5295 					}
5296 				}
5297 				break;
5298 			default:
5299 				return false;
5300 		}
5301 		return true;
5302 	}
5303 
setOptions(Hashtable<String, String> newOptions)5304 	public void setOptions(Hashtable<String, String> newOptions) {
5305 		Hashtable<String, String> cachedValue = newOptions == null ? null : new Hashtable<>(newOptions);
5306 		IEclipsePreferences defaultPreferences = getDefaultPreferences();
5307 		IEclipsePreferences instancePreferences = getInstancePreferences();
5308 
5309 		if (newOptions == null){
5310 			try {
5311 				instancePreferences.clear();
5312 			} catch(BackingStoreException e) {
5313 				// ignore
5314 			}
5315 		} else {
5316 			Enumeration<String> keys = newOptions.keys();
5317 			while (keys.hasMoreElements()){
5318 				String key = keys.nextElement();
5319 				int optionLevel = getOptionLevel(key);
5320 				if (optionLevel == UNKNOWN_OPTION) continue; // unrecognized option
5321 				if (key.equals(JavaCore.CORE_ENCODING)) {
5322 					if (cachedValue != null) {
5323 						cachedValue.put(key, JavaCore.getEncoding());
5324 					}
5325 					continue; // skipped, contributed by resource prefs
5326 				}
5327 				String value = newOptions.get(key);
5328 				String defaultValue = defaultPreferences.get(key, null);
5329 				// Store value in preferences
5330 				if (defaultValue != null && defaultValue.equals(value)) {
5331 					value = null;
5332 				}
5333 				storePreference(key, value, instancePreferences, newOptions);
5334 			}
5335 			try {
5336 				// persist options
5337 				instancePreferences.flush();
5338 			} catch(BackingStoreException e) {
5339 				// ignore
5340 			}
5341 		}
5342 		// update cache
5343 		Util.fixTaskTags(cachedValue);
5344 		this.optionsCache = cachedValue;
5345 	}
5346 
startup()5347 	public void startup() throws CoreException {
5348 		try {
5349 			// initialize Java model cache
5350 			this.cache = new JavaModelCache();
5351 
5352 			// request state folder creation (workaround 19885)
5353 			JavaCore.getPlugin().getStateLocation();
5354 
5355 			// Initialize eclipse preferences
5356 			initializePreferences();
5357 
5358 			// Listen to preference changes
5359 			this.propertyListener = new IEclipsePreferences.IPreferenceChangeListener() {
5360 				@Override
5361 				public void preferenceChange(PreferenceChangeEvent event) {
5362 					JavaModelManager.this.optionsCache = null;
5363 				}
5364 			};
5365 			InstanceScope.INSTANCE.getNode(JavaCore.PLUGIN_ID).addPreferenceChangeListener(this.propertyListener);
5366 
5367 			// listen for encoding changes (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=255501 )
5368 			this.resourcesPropertyListener = new IEclipsePreferences.IPreferenceChangeListener() {
5369 				@Override
5370 				public void preferenceChange(PreferenceChangeEvent event) {
5371 					if (ResourcesPlugin.PREF_ENCODING.equals(event.getKey())) {
5372 						JavaModelManager.this.optionsCache = null;
5373 					}
5374 				}
5375 			};
5376 			String resourcesPluginId = ResourcesPlugin.getPlugin().getBundle().getSymbolicName();
5377 			InstanceScope.INSTANCE.getNode(resourcesPluginId).addPreferenceChangeListener(this.resourcesPropertyListener);
5378 
5379 			// Listen to content-type changes
5380 			 Platform.getContentTypeManager().addContentTypeChangeListener(this);
5381 
5382 			// retrieve variable values
5383 			long start = -1;
5384 			if (VERBOSE)
5385 				start = System.currentTimeMillis();
5386 			loadVariablesAndContainers();
5387 			if (VERBOSE)
5388 				traceVariableAndContainers("Loaded", start); //$NON-NLS-1$
5389 
5390 			// listen for resource changes
5391 			this.deltaState.initializeRootsWithPreviousSession();
5392 			final IWorkspace workspace = ResourcesPlugin.getWorkspace();
5393 			workspace.addResourceChangeListener(
5394 				this.deltaState,
5395 				/* update spec in JavaCore#addPreProcessingResourceChangedListener(...) if adding more event types */
5396 				IResourceChangeEvent.PRE_BUILD
5397 					| IResourceChangeEvent.POST_BUILD
5398 					| IResourceChangeEvent.POST_CHANGE
5399 					| IResourceChangeEvent.PRE_DELETE
5400 					| IResourceChangeEvent.PRE_CLOSE
5401 					| IResourceChangeEvent.PRE_REFRESH);
5402 
5403 			// New index is disabled, see bug 544898
5404 			// Indexer.getInstance().addListener(this.deltaState);
5405 
5406 			// listen to resource changes affecting external annotations
5407 			ExternalAnnotationTracker.start(workspace);
5408 
5409 			startIndexing();
5410 
5411 			// process deltas since last activated in indexer thread so that indexes are up-to-date.
5412 			// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=38658
5413 			Job processSavedState = new Job(Messages.savedState_jobName) {
5414 				@Override
5415 				protected IStatus run(IProgressMonitor monitor) {
5416 					try {
5417 						// add save participant and process delta atomically
5418 						// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=59937
5419 						workspace.run(
5420 							new IWorkspaceRunnable() {
5421 								@Override
5422 								public void run(IProgressMonitor progress) throws CoreException {
5423 									ISavedState savedState = workspace.addSaveParticipant(JavaCore.PLUGIN_ID, JavaModelManager.this);
5424 									if (savedState != null) {
5425 										// the event type coming from the saved state is always POST_AUTO_BUILD
5426 										// force it to be POST_CHANGE so that the delta processor can handle it
5427 										JavaModelManager.this.deltaState.getDeltaProcessor().overridenEventType = IResourceChangeEvent.POST_CHANGE;
5428 										savedState.processResourceChangeEvents(JavaModelManager.this.deltaState);
5429 									}
5430 								}
5431 							},
5432 							monitor);
5433 					} catch (CoreException e) {
5434 						return e.getStatus();
5435 					}
5436 					return Status.OK_STATUS;
5437 				}
5438 			};
5439 			processSavedState.setSystem(true);
5440 			processSavedState.setPriority(Job.SHORT); // process asap
5441 			processSavedState.schedule();
5442 		} catch (RuntimeException e) {
5443 			try {
5444 				shutdown();
5445 			} catch (RuntimeException e2) {
5446 				e.addSuppressed(e2);
5447 			}
5448 			throw e;
5449 		}
5450 	}
5451 
5452 	/**
5453 	 * Initiate the background indexing process.
5454 	 * This should be deferred after the plug-in activation.
5455 	 */
startIndexing()5456 	private void startIndexing() {
5457 		if (this.indexManager != null) this.indexManager.reset();
5458 	}
5459 
shutdown()5460 	public void shutdown () {
5461 		IEclipsePreferences preferences = InstanceScope.INSTANCE.getNode(JavaCore.PLUGIN_ID);
5462 		try {
5463 			preferences.flush();
5464 		} catch (BackingStoreException e) {
5465 			Util.log(e, "Could not save JavaCore preferences"); //$NON-NLS-1$
5466 		}
5467 		IWorkspace workspace = ResourcesPlugin.getWorkspace();
5468 		workspace.removeResourceChangeListener(this.deltaState);
5469 		workspace.removeSaveParticipant(JavaCore.PLUGIN_ID);
5470 
5471 		ExternalAnnotationTracker.shutdown(workspace);
5472 
5473 		// Stop listening to content-type changes
5474 		IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
5475 		if (contentTypeManager != null) {
5476 			contentTypeManager.removeContentTypeChangeListener(this);
5477 		}
5478 
5479 		// Stop indexing
5480 		if (this.indexManager != null) {
5481 			this.indexManager.shutdown();
5482 		}
5483 
5484 		// Stop listening to preferences changes
5485 		preferences.removePreferenceChangeListener(this.propertyListener);
5486 		((IEclipsePreferences) this.preferencesLookup[PREF_DEFAULT].parent()).removeNodeChangeListener(this.defaultNodeListener);
5487 		this.preferencesLookup[PREF_DEFAULT] = null;
5488 		((IEclipsePreferences) this.preferencesLookup[PREF_INSTANCE].parent()).removeNodeChangeListener(this.instanceNodeListener);
5489 		this.preferencesLookup[PREF_INSTANCE].removePreferenceChangeListener(this.instancePreferencesListener);
5490 		this.preferencesLookup[PREF_INSTANCE] = null;
5491 		String resourcesPluginId = ResourcesPlugin.getPlugin().getBundle().getSymbolicName();
5492 		InstanceScope.INSTANCE.getNode(resourcesPluginId).removePreferenceChangeListener(this.resourcesPropertyListener);
5493 
5494 		// wait for the initialization job to finish
5495 		try {
5496 			Job.getJobManager().join(JavaCore.PLUGIN_ID, null);
5497 		} catch (InterruptedException e) {
5498 			// ignore
5499 		}
5500 
5501 		// Note: no need to close the Java model as this just removes Java element infos from the Java model cache
5502 	}
5503 
variableGet(String variableName)5504 	public synchronized IPath variableGet(String variableName){
5505 		// check initialization in progress first
5506 		Set<String> initializations = variableInitializationInProgress();
5507 		if (initializations.contains(variableName)) {
5508 			return VARIABLE_INITIALIZATION_IN_PROGRESS;
5509 		}
5510 		return this.variables.get(variableName);
5511 	}
5512 
variableGetDefaultToPreviousSession(String variableName)5513 	private synchronized IPath variableGetDefaultToPreviousSession(String variableName){
5514 		IPath variablePath = this.variables.get(variableName);
5515 		if (variablePath == null)
5516 			return getPreviousSessionVariable(variableName);
5517 		return variablePath;
5518 	}
5519 
5520 	/*
5521 	 * Returns the set of variable names that are being initialized in the current thread.
5522 	 */
variableInitializationInProgress()5523 	private Set<String> variableInitializationInProgress() {
5524 		Set<String> initializations = this.variableInitializationInProgress.get();
5525 		if (initializations == null) {
5526 			initializations = new HashSet<>();
5527 			this.variableInitializationInProgress.set(initializations);
5528 		}
5529 		return initializations;
5530 	}
5531 
variableNames()5532 	public synchronized String[] variableNames(){
5533 		int length = this.variables.size();
5534 		String[] result = new String[length];
5535 		Iterator<String> vars = this.variables.keySet().iterator();
5536 		int index = 0;
5537 		while (vars.hasNext()) {
5538 			result[index++] = vars.next();
5539 		}
5540 		return result;
5541 	}
5542 
variablePut(String variableName, IPath variablePath)5543 	public synchronized void variablePut(String variableName, IPath variablePath){
5544 
5545 		// set/unset the initialization in progress
5546 		Set<String> initializations = variableInitializationInProgress();
5547 		if (variablePath == VARIABLE_INITIALIZATION_IN_PROGRESS) {
5548 			initializations.add(variableName);
5549 
5550 			// do not write out intermediate initialization value
5551 			return;
5552 		} else {
5553 			initializations.remove(variableName);
5554 
5555 			// update cache - do not only rely on listener refresh
5556 			if (variablePath == null) {
5557 				// if path is null, record that the variable was removed to avoid asking the initializer to initialize it again
5558 				// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=112609
5559 				this.variables.put(variableName, CP_ENTRY_IGNORE_PATH);
5560 				// clean other variables caches
5561 				this.variablesWithInitializer.remove(variableName);
5562 				this.deprecatedVariables.remove(variableName);
5563 			} else {
5564 				this.variables.put(variableName, variablePath);
5565 			}
5566 			// discard obsoleted information about previous session
5567 			this.previousSessionVariables.remove(variableName);
5568 		}
5569 	}
5570 
variablePreferencesPut(String variableName, IPath variablePath)5571 	public void variablePreferencesPut(String variableName, IPath variablePath) {
5572 		String variableKey = CP_VARIABLE_PREFERENCES_PREFIX+variableName;
5573 		if (variablePath == null) {
5574 			getInstancePreferences().remove(variableKey);
5575 		} else {
5576 			getInstancePreferences().put(variableKey, variablePath.toString());
5577 		}
5578 		try {
5579 			getInstancePreferences().flush();
5580 		} catch (BackingStoreException e) {
5581 			// ignore exception
5582 		}
5583 	}
5584 
5585 	/*
5586 	 * Optimize startup case where 1 variable is initialized at a time with the same value as on shutdown.
5587 	 */
variablePutIfInitializingWithSameValue(String[] variableNames, IPath[] variablePaths)5588 	public boolean variablePutIfInitializingWithSameValue(String[] variableNames, IPath[] variablePaths) {
5589 		if (variableNames.length != 1)
5590 			return false;
5591 		String variableName = variableNames[0];
5592 		IPath oldPath = variableGetDefaultToPreviousSession(variableName);
5593 		if (oldPath == null)
5594 			return false;
5595 		IPath newPath = variablePaths[0];
5596 		if (!oldPath.equals(newPath))
5597 			return false;
5598 		variablePut(variableName, newPath);
5599 		return true;
5600 	}
5601 
5602 	@Override
contentTypeChanged(ContentTypeChangeEvent event)5603 	public void contentTypeChanged(ContentTypeChangeEvent event) {
5604 		Util.resetJavaLikeExtensions();
5605 
5606 		// Walk through projects to reset their secondary types cache
5607 		IJavaProject[] projects;
5608 		try {
5609 			projects = JavaModelManager.getJavaModelManager().getJavaModel().getJavaProjects();
5610 		} catch (JavaModelException e) {
5611 			return;
5612 		}
5613 		for (int i = 0, length = projects.length; i < length; i++) {
5614 			IJavaProject project = projects[i];
5615 			final PerProjectInfo projectInfo = getPerProjectInfo(project.getProject(), false /* don't create info */);
5616 			if (projectInfo != null) {
5617 				projectInfo.secondaryTypes = null;
5618 			}
5619 		}
5620 	}
5621 
cacheToString(String prefix)5622 	public synchronized String cacheToString(String prefix) {
5623 		return this.cache.toStringFillingRation(prefix);
5624 	}
5625 
debugNewOpenableCacheStats()5626 	public ElementCache<ITypeRoot>.Stats debugNewOpenableCacheStats() {
5627 		return this.cache.openableCache.new Stats();
5628 	}
5629 
getOpenableCacheSize()5630 	public int getOpenableCacheSize() {
5631 		return this.cache.openableCache.getSpaceLimit();
5632 	}
5633 
5634 	/**
5635 	 * Get a cached access rule, or when the cache did not contain the rule, creates a new one.
5636 	 *
5637 	 * @param filePattern the file pattern this access rule should match
5638 	 * @param kind one of {@link IAccessRule#K_ACCESSIBLE}, {@link IAccessRule#K_DISCOURAGED},
5639 	 *                     or {@link IAccessRule#K_NON_ACCESSIBLE}, optionally combined with
5640 	 *                     {@link IAccessRule#IGNORE_IF_BETTER}
5641 	 * @return an access rule
5642 	 */
getAccessRule(IPath filePattern, int kind)5643 	public IAccessRule getAccessRule(IPath filePattern, int kind) {
5644 		ClasspathAccessRule rule = new ClasspathAccessRule(filePattern, kind);
5645 		return getFromCache(rule);
5646 	}
5647 
5648 	/**
5649 	 * Used only for loading rules from disk.
5650 	 */
getAccessRuleForProblemId(char [] filePattern, int problemId)5651 	public ClasspathAccessRule getAccessRuleForProblemId(char [] filePattern, int problemId) {
5652 		ClasspathAccessRule rule = new ClasspathAccessRule(filePattern, problemId);
5653 		return getFromCache(rule);
5654 	}
5655 
getFromCache(ClasspathAccessRule rule)5656 	private ClasspathAccessRule getFromCache(ClasspathAccessRule rule) {
5657 		ClasspathAccessRule cachedRule = this.cache.accessRuleCache.get(rule);
5658 		if (cachedRule != null) {
5659 			return cachedRule;
5660 		}
5661 		this.cache.accessRuleCache.put(rule, rule);
5662 		return rule;
5663 	}
5664 }
5665