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<String, HashMap<String, IType>></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