1 /******************************************************************************* 2 * Copyright (c) 2007, 2017 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 *******************************************************************************/ 14 package org.eclipse.pde.api.tools.internal.provisional; 15 16 import java.util.ArrayList; 17 import java.util.HashSet; 18 import java.util.Hashtable; 19 import java.util.Set; 20 import java.util.StringTokenizer; 21 22 import org.eclipse.core.resources.IProject; 23 import org.eclipse.core.resources.IResourceChangeEvent; 24 import org.eclipse.core.resources.ISaveContext; 25 import org.eclipse.core.resources.ISaveParticipant; 26 import org.eclipse.core.resources.IncrementalProjectBuilder; 27 import org.eclipse.core.resources.ProjectScope; 28 import org.eclipse.core.resources.ResourcesPlugin; 29 import org.eclipse.core.runtime.CoreException; 30 import org.eclipse.core.runtime.IStatus; 31 import org.eclipse.core.runtime.Platform; 32 import org.eclipse.core.runtime.Plugin; 33 import org.eclipse.core.runtime.Status; 34 import org.eclipse.core.runtime.preferences.DefaultScope; 35 import org.eclipse.core.runtime.preferences.IEclipsePreferences; 36 import org.eclipse.core.runtime.preferences.IPreferencesService; 37 import org.eclipse.core.runtime.preferences.IScopeContext; 38 import org.eclipse.core.runtime.preferences.InstanceScope; 39 import org.eclipse.jdt.core.ElementChangedEvent; 40 import org.eclipse.jdt.core.JavaCore; 41 import org.eclipse.osgi.service.debug.DebugOptions; 42 import org.eclipse.osgi.service.debug.DebugOptionsListener; 43 import org.eclipse.pde.api.tools.internal.ApiBaselineManager; 44 import org.eclipse.pde.api.tools.internal.ApiDescriptionManager; 45 import org.eclipse.pde.api.tools.internal.JavadocTagManager; 46 import org.eclipse.pde.api.tools.internal.SessionManager; 47 import org.eclipse.pde.api.tools.internal.WorkspaceDeltaProcessor; 48 import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes; 49 import org.eclipse.pde.api.tools.internal.util.FileManager; 50 import org.eclipse.pde.api.tools.internal.util.Util; 51 import org.eclipse.pde.core.target.NameVersionDescriptor; 52 import org.osgi.framework.Bundle; 53 import org.osgi.framework.BundleContext; 54 import org.osgi.framework.ServiceReference; 55 import org.osgi.service.prefs.BackingStoreException; 56 57 /** 58 * API Tools core plug-in. API tools can be run with or without an OSGi 59 * framework. 60 * 61 * @since 1.0.0 62 */ 63 public class ApiPlugin extends Plugin implements ISaveParticipant, DebugOptionsListener { 64 65 /** 66 * Constant representing the expected name for an execution environment 67 * description fragment Value is <code>org.eclipse.pde.api.tools.ee" 68 */ 69 private static final String EE_DESCRIPTION_PREFIX = "org.eclipse.pde.api.tools.ee"; //$NON-NLS-1$ 70 /** 71 * Constant representing the name of the javadoc tag extension point. Value 72 * is <code>apiJavadocTags</code> 73 */ 74 public static final String EXTENSION_JAVADOC_TAGS = "apiJavadocTags"; //$NON-NLS-1$ 75 /** 76 * The plug-in identifier of the PDE API tool support (value 77 * <code>"org.eclipse.pde.api.tools"</code>). 78 */ 79 public static final String PLUGIN_ID = "org.eclipse.pde.api.tools"; //$NON-NLS-1$ 80 /** 81 * The API Tools nature id (value 82 * <code>"org.eclipse.pde.api.tools.apiAnalysisNature"</code>). 83 */ 84 public static final String NATURE_ID = PLUGIN_ID + ".apiAnalysisNature"; //$NON-NLS-1$ 85 /** 86 * Status code indicating an unexpected internal error. 87 */ 88 public static final int INTERNAL_ERROR = 120; 89 /** 90 * Status code indicating an unexpected error 91 */ 92 public static final int ERROR = 121; 93 94 /** 95 * Status code indicating a resolution error 96 */ 97 public static final int REPORT_RESOLUTION_ERRORS = 122; 98 99 /** 100 * Status code indicating that a baseline is disposed 101 */ 102 public static final int REPORT_BASELINE_IS_DISPOSED = 123; 103 104 /** 105 * Constant representing severity levels for error/warning preferences Value 106 * is: <code>0</code> 107 */ 108 public static final int SEVERITY_IGNORE = 0; 109 /** 110 * Constant representing severity levels for error/warning preferences Value 111 * is: <code>1</code> 112 */ 113 public static final int SEVERITY_WARNING = 1; 114 /** 115 * Constant representing severity levels for error/warning preferences Value 116 * is: <code>2</code> 117 */ 118 public static final int SEVERITY_ERROR = 2; 119 120 /** 121 * Constant representing the preference value 'ignore'. Value is: 122 * <code>Ignore</code> 123 */ 124 public static final String VALUE_IGNORE = "Ignore"; //$NON-NLS-1$ 125 /** 126 * Constant representing the preference value 'warning'. Value is: 127 * <code>Warning</code> 128 */ 129 public static final String VALUE_WARNING = "Warning"; //$NON-NLS-1$ 130 /** 131 * Constant representing the preference value 'error'. Value is: 132 * <code>Error</code> 133 */ 134 public static final String VALUE_ERROR = "Error"; //$NON-NLS-1$ 135 /** 136 * Constant representing the preference value 'disabled'. Value is: 137 * <code>Disabled</code> 138 */ 139 public static final String VALUE_DISABLED = "Disabled"; //$NON-NLS-1$ 140 /** 141 * Constant representing the preference value 'enabled'. Value is: 142 * <code>Enabled</code> 143 */ 144 public static final String VALUE_ENABLED = "Enabled"; //$NON-NLS-1$ 145 /** 146 * The identifier for the API builder Value is: 147 * <code>"org.eclipse.pde.api.tools.apiAnalysisBuilder"</code> 148 */ 149 public static final String BUILDER_ID = PLUGIN_ID + ".apiAnalysisBuilder"; //$NON-NLS-1$ 150 151 /** 152 * Preference ID for a knownEEFragments of EE fragments that were previously 153 * installed, the stored preference string must be a list of name followed 154 * by version separated by semicolons ';'. 155 * <p> 156 * ex: "org.eclipse.one;1.0.0;org.eclipse.two;2.0.0;" 157 * </p> 158 * <p> 159 * Value is: <code>knownEEFragments</code> 160 * </p> 161 */ 162 public static final String KNOWN_EE_FRAGMENTS = "knownEEFragments"; //$NON-NLS-1$ 163 /** 164 * Singleton instance of the plugin 165 */ 166 private static ApiPlugin fgDefault = null; 167 /** 168 * Singleton instance of the {@link JavadocTagManager} 169 */ 170 private static JavadocTagManager fgTagManager = null; 171 /** 172 * Singleton instance of the {@link ISessionManager} 173 */ 174 private static ISessionManager fgSessionManager = null; 175 /** 176 * This bundle's OSGi context 177 */ 178 private BundleContext fBundleContext = null; 179 180 private static boolean DEBUG = false; 181 182 /** 183 * Private debug options 184 */ 185 private static final String DEBUG_FLAG = PLUGIN_ID + "/debug"; //$NON-NLS-1$ 186 private static final String BUILDER_DEBUG = PLUGIN_ID + "/debug/builder"; //$NON-NLS-1$ 187 private static final String DELTA_DEBUG = PLUGIN_ID + "/debug/delta"; //$NON-NLS-1$ 188 private static final String SEARCH_DEBUG = PLUGIN_ID + "/debug/search"; //$NON-NLS-1$ 189 private static final String CLASSFILE_VISITOR_DEBUG = PLUGIN_ID + "/debug/classfilevisitor"; //$NON-NLS-1$ 190 private static final String DESCRIPTOR_FRAMEWORK_DEBUG = PLUGIN_ID + "/debug/descriptor/framework"; //$NON-NLS-1$ 191 private static final String TAG_SCANNER_DEBUG = PLUGIN_ID + "/debug/tagscanner"; //$NON-NLS-1$ 192 private static final String PLUGIN_WORKSPACE_COMPONENT_DEBUG = PLUGIN_ID + "/debug/pluginworkspacecomponent"; //$NON-NLS-1$ 193 private static final String API_PROFILE_MANAGER_DEBUG = PLUGIN_ID + "/debug/profilemanager"; //$NON-NLS-1$ 194 private static final String API_FILTER_STORE_DEBUG = PLUGIN_ID + "/debug/apifilterstore"; //$NON-NLS-1$ 195 private static final String API_REFERENCE_ANALYZER_DEBUG = PLUGIN_ID + "/debug/refanalyzer"; //$NON-NLS-1$ 196 private static final String PROBLEM_DETECTOR_DEBUG = PLUGIN_ID + "/debug/problemdetector"; //$NON-NLS-1$ 197 private static final String REFERENCE_RESOLVER_DEBUG = PLUGIN_ID + "/debug/refresolver"; //$NON-NLS-1$ 198 private static final String API_DESCRIPTION = PLUGIN_ID + "/debug/apidescription"; //$NON-NLS-1$ 199 private static final String WORKSPACE_DELTA_PROCESSOR = PLUGIN_ID + "/debug/workspacedeltaprocessor"; //$NON-NLS-1$ 200 private static final String API_ANALYZER_DEBUG = PLUGIN_ID + "/debug/apianalyzer"; //$NON-NLS-1$ 201 private static final String USE_REPORT_CONVERTER_DEBUG = PLUGIN_ID + "/debug/usereportconverter"; //$NON-NLS-1$ 202 203 /** 204 * Constant used for controlling tracing in the report converter 205 */ 206 public static boolean DEBUG_USE_REPORT_CONVERTER = false; 207 /** 208 * Constant used for controlling tracing in the search engine 209 */ 210 public static boolean DEBUG_SEARCH_ENGINE = false; 211 /** 212 * Constant used for controlling tracing in the scanner 213 */ 214 public static boolean DEBUG_TAG_SCANNER = false; 215 /** 216 * Constant used for controlling tracing in the API comparator 217 */ 218 public static boolean DEBUG_API_COMPARATOR = false; 219 /** 220 * Constant used for controlling tracing in the plug-in workspace component 221 */ 222 public static boolean DEBUG_PROJECT_COMPONENT = false; 223 /** 224 * Constant used for controlling tracing in the descriptor framework 225 */ 226 public static boolean DEBUG_ELEMENT_DESCRIPTOR_FRAMEWORK = false; 227 /** 228 * Constant used for controlling tracing in the class file comparator 229 */ 230 public static boolean DEBUG_CLASSFILE_COMPARATOR = false; 231 /** 232 * Constant used for controlling tracing in the search engine 233 */ 234 public static boolean DEBUG_REFERENCE_RESOLVER = false; 235 /** 236 * Constant used for controlling tracing in the visitor 237 */ 238 public static boolean DEBUG_REFERENCE_EXTRACTOR = false; 239 /** 240 * Constant used for controlling tracing in the search engine 241 */ 242 public static boolean DEBUG_REFERENCE_ANALYZER = false; 243 /** 244 * Constant used for controlling tracing in the API tool builder 245 */ 246 public static boolean DEBUG_API_ANALYZER = false; 247 /** 248 * Constant used for controlling tracing in the problem detectors 249 */ 250 public static boolean DEBUG_PROBLEM_DETECTOR = false; 251 /** 252 * Constant used for controlling tracing in the API tool builder 253 */ 254 public static boolean DEBUG_WORKSPACE_DELTA_PROCESSOR = false; 255 /** 256 * Constant used for controlling tracing in the plug-in workspace component 257 */ 258 public static boolean DEBUG_FILTER_STORE = false; 259 /** 260 * Constant used for controlling tracing in the API descriptions 261 */ 262 public static boolean DEBUG_API_DESCRIPTION = false; 263 /** 264 * Constant used for controlling tracing in the API tool builder 265 */ 266 public static boolean DEBUG_BASELINE_MANAGER = false; 267 /** 268 * Constant used for controlling tracing in the API tool builder 269 */ 270 public static boolean DEBUG_BUILDER = false; 271 272 public static String[] AllCompatibilityKeys = new String[] { 273 IApiProblemTypes.API_COMPONENT_REMOVED_TYPE, 274 IApiProblemTypes.API_COMPONENT_REMOVED_API_TYPE, 275 IApiProblemTypes.API_COMPONENT_REMOVED_REEXPORTED_TYPE, 276 IApiProblemTypes.API_COMPONENT_REMOVED_REEXPORTED_API_TYPE, 277 IApiProblemTypes.ANNOTATION_ADDED_FIELD, 278 IApiProblemTypes.ANNOTATION_REMOVED_FIELD, 279 IApiProblemTypes.ANNOTATION_REMOVED_METHOD, 280 IApiProblemTypes.ANNOTATION_REMOVED_TYPE_MEMBER, 281 IApiProblemTypes.ANNOTATION_CHANGED_TYPE_CONVERSION, 282 IApiProblemTypes.ANNOTATION_ADDED_METHOD_NO_DEFAULT_VALUE, 283 IApiProblemTypes.INTERFACE_ADDED_FIELD, 284 IApiProblemTypes.INTERFACE_ADDED_METHOD, 285 IApiProblemTypes.INTERFACE_ADDED_DEFAULT_METHOD, 286 IApiProblemTypes.INTERFACE_ADDED_RESTRICTIONS, 287 IApiProblemTypes.INTERFACE_ADDED_SUPER_INTERFACE_WITH_METHODS, 288 IApiProblemTypes.INTERFACE_ADDED_TYPE_PARAMETER, 289 IApiProblemTypes.INTERFACE_REMOVED_TYPE_PARAMETER, 290 IApiProblemTypes.INTERFACE_REMOVED_FIELD, 291 IApiProblemTypes.INTERFACE_REMOVED_METHOD, 292 IApiProblemTypes.INTERFACE_REMOVED_TYPE_MEMBER, 293 IApiProblemTypes.INTERFACE_CHANGED_TYPE_CONVERSION, 294 IApiProblemTypes.INTERFACE_CHANGED_CONTRACTED_SUPERINTERFACES_SET, 295 IApiProblemTypes.ENUM_CHANGED_CONTRACTED_SUPERINTERFACES_SET, 296 IApiProblemTypes.ENUM_CHANGED_TYPE_CONVERSION, 297 IApiProblemTypes.ENUM_REMOVED_FIELD, 298 IApiProblemTypes.ENUM_REMOVED_ENUM_CONSTANT, 299 IApiProblemTypes.ENUM_REMOVED_METHOD, 300 IApiProblemTypes.ENUM_REMOVED_TYPE_MEMBER, 301 IApiProblemTypes.CLASS_ADDED_FIELD, 302 IApiProblemTypes.CLASS_ADDED_METHOD, 303 IApiProblemTypes.CLASS_ADDED_RESTRICTIONS, 304 IApiProblemTypes.CLASS_ADDED_TYPE_PARAMETER, 305 IApiProblemTypes.CLASS_CHANGED_CONTRACTED_SUPERINTERFACES_SET, 306 IApiProblemTypes.CLASS_CHANGED_NON_ABSTRACT_TO_ABSTRACT, 307 IApiProblemTypes.CLASS_CHANGED_NON_FINAL_TO_FINAL, 308 IApiProblemTypes.CLASS_CHANGED_TYPE_CONVERSION, 309 IApiProblemTypes.CLASS_CHANGED_DECREASE_ACCESS, 310 IApiProblemTypes.CLASS_REMOVED_FIELD, 311 IApiProblemTypes.CLASS_REMOVED_METHOD, 312 IApiProblemTypes.CLASS_REMOVED_CONSTRUCTOR, 313 IApiProblemTypes.CLASS_REMOVED_SUPERCLASS, 314 IApiProblemTypes.CLASS_REMOVED_TYPE_MEMBER, 315 IApiProblemTypes.CLASS_REMOVED_TYPE_PARAMETER, 316 IApiProblemTypes.FIELD_ADDED_VALUE, 317 IApiProblemTypes.FIELD_CHANGED_TYPE, 318 IApiProblemTypes.FIELD_CHANGED_VALUE, 319 IApiProblemTypes.FIELD_CHANGED_DECREASE_ACCESS, 320 IApiProblemTypes.FIELD_CHANGED_FINAL_TO_NON_FINAL_STATIC_CONSTANT, 321 IApiProblemTypes.FIELD_CHANGED_NON_FINAL_TO_FINAL, 322 IApiProblemTypes.FIELD_CHANGED_STATIC_TO_NON_STATIC, 323 IApiProblemTypes.FIELD_CHANGED_NON_STATIC_TO_STATIC, 324 IApiProblemTypes.FIELD_REMOVED_VALUE, 325 IApiProblemTypes.FIELD_REMOVED_TYPE_ARGUMENT, 326 IApiProblemTypes.METHOD_ADDED_RESTRICTIONS, 327 IApiProblemTypes.METHOD_ADDED_TYPE_PARAMETER, 328 IApiProblemTypes.METHOD_CHANGED_VARARGS_TO_ARRAY, 329 IApiProblemTypes.METHOD_CHANGED_DECREASE_ACCESS, 330 IApiProblemTypes.METHOD_CHANGED_NON_ABSTRACT_TO_ABSTRACT, 331 IApiProblemTypes.METHOD_CHANGED_NON_STATIC_TO_STATIC, 332 IApiProblemTypes.METHOD_CHANGED_STATIC_TO_NON_STATIC, 333 IApiProblemTypes.METHOD_CHANGED_NON_FINAL_TO_FINAL, 334 IApiProblemTypes.METHOD_REMOVED_ANNOTATION_DEFAULT_VALUE, 335 IApiProblemTypes.METHOD_REMOVED_TYPE_PARAMETER, 336 IApiProblemTypes.CONSTRUCTOR_ADDED_TYPE_PARAMETER, 337 IApiProblemTypes.CONSTRUCTOR_CHANGED_VARARGS_TO_ARRAY, 338 IApiProblemTypes.CONSTRUCTOR_CHANGED_DECREASE_ACCESS, 339 IApiProblemTypes.CONSTRUCTOR_REMOVED_TYPE_PARAMETER, 340 IApiProblemTypes.TYPE_PARAMETER_ADDED_CLASS_BOUND, 341 IApiProblemTypes.TYPE_PARAMETER_CHANGED_CLASS_BOUND, 342 IApiProblemTypes.TYPE_PARAMETER_REMOVED_CLASS_BOUND, 343 IApiProblemTypes.TYPE_PARAMETER_ADDED_INTERFACE_BOUND, 344 IApiProblemTypes.TYPE_PARAMETER_CHANGED_INTERFACE_BOUND, 345 IApiProblemTypes.TYPE_PARAMETER_REMOVED_INTERFACE_BOUND, 346 IApiProblemTypes.TYPE_PARAMETER_REMOVED_INTERFACE_BOUND, 347 IApiProblemTypes.REPORT_API_BREAKAGE_WHEN_MAJOR_VERSION_INCREMENTED, 348 // IApiProblemTypes.REPORT_API_CHANGE_WHEN_MINOR_VERSION_INCREMENTED, 349 }; 350 /** 351 * A set of listeners that want to participate in the saving life-cycle of 352 * the workbench via this plug-in 353 */ 354 private HashSet<ISaveParticipant> savelisteners = new HashSet<>(); 355 356 /** 357 * This is used to log resolution errors only once per session 358 */ 359 private int logBits = 0; 360 361 /** 362 * This is used to log resolution errors only once per session. This is used 363 * outside the workbench. 364 */ 365 private static int LogBits = 0; 366 367 /** 368 * Standard delta processor for Java element changes 369 */ 370 private WorkspaceDeltaProcessor deltaProcessor = null; 371 372 private static final int RESOLUTION_LOG_BIT = 1; 373 private static final int BASELINE_DISPOSED_LOG_BIT = 2; 374 375 /** 376 * Constructor 377 */ ApiPlugin()378 public ApiPlugin() { 379 super(); 380 fgDefault = this; 381 } 382 383 /** 384 * @return The singleton instance of the plugin 385 */ getDefault()386 public static ApiPlugin getDefault() { 387 return fgDefault; 388 } 389 390 /** 391 * Logs the specified status with this plug-in's log. 392 * 393 * @param status status to log 394 */ log(IStatus status)395 public static void log(IStatus status) { 396 ApiPlugin getDefault = getDefault(); 397 if (getDefault == null) { 398 switch (status.getCode()) { 399 case REPORT_RESOLUTION_ERRORS: 400 if ((LogBits & RESOLUTION_LOG_BIT) == 0) { 401 Throwable exception = status.getException(); 402 if (exception != null) { 403 exception.printStackTrace(); 404 } 405 LogBits |= RESOLUTION_LOG_BIT; 406 } 407 break; 408 case REPORT_BASELINE_IS_DISPOSED: 409 if ((LogBits & BASELINE_DISPOSED_LOG_BIT) == 0) { 410 Throwable exception = status.getException(); 411 if (exception != null) { 412 exception.printStackTrace(); 413 } 414 LogBits |= BASELINE_DISPOSED_LOG_BIT; 415 } 416 break; 417 default: 418 Throwable exception = status.getException(); 419 if (exception != null) { 420 exception.printStackTrace(); 421 } 422 } 423 } else { 424 switch (status.getCode()) { 425 case REPORT_RESOLUTION_ERRORS: 426 if ((getDefault.logBits & RESOLUTION_LOG_BIT) == 0) { 427 getDefault.getLog().log(status); 428 getDefault.logBits |= RESOLUTION_LOG_BIT; 429 } 430 break; 431 case REPORT_BASELINE_IS_DISPOSED: 432 if ((getDefault.logBits & BASELINE_DISPOSED_LOG_BIT) == 0) { 433 getDefault.getLog().log(status); 434 getDefault.logBits |= BASELINE_DISPOSED_LOG_BIT; 435 } 436 break; 437 default: 438 getDefault.getLog().log(status); 439 } 440 } 441 } 442 443 /** 444 * Logs the specified message and throwable with this plug-in's log. 445 * 446 * @param t throwable to log 447 */ log(String message, Throwable t)448 public static void log(String message, Throwable t) { 449 log(newErrorStatus(message, t)); 450 } 451 452 /** 453 * Logs the specified throwable with this plug-in's log. 454 * 455 * @param t throwable to log 456 */ log(Throwable t)457 public static void log(Throwable t) { 458 log("Error logged from API Tools Core: ", t); //$NON-NLS-1$ 459 } 460 461 /** 462 * Logs an internal error with the specified message. 463 * 464 * @param message the error message to log 465 */ logErrorMessage(String message)466 public static void logErrorMessage(String message) { 467 // this message is intentionally not internationalized, as an exception 468 // may 469 // be due to the resource bundle itself 470 log("Internal message logged from API Tools Core: " + message, null); //$NON-NLS-1$ 471 } 472 473 /** 474 * @noreference This method is not intended to be referenced by clients. 475 * @param message 476 */ logInfoMessage(String message)477 public static void logInfoMessage(String message) { 478 // this message is intentionally not internationalized, as an exception 479 // may 480 // be due to the resource bundle itself 481 log(newInfoStatus("Internal message logged from API Tools Core: " + message, null)); //$NON-NLS-1$ 482 } 483 484 /** 485 * Returns a new error status for this plug-in with the given message 486 * 487 * @param message the message to be included in the status 488 * @param exception the exception to be included in the status or 489 * <code>null</code> if none 490 * @return a new error status 491 */ newErrorStatus(String message, Throwable exception)492 public static IStatus newErrorStatus(String message, Throwable exception) { 493 return new Status(IStatus.ERROR, PLUGIN_ID, INTERNAL_ERROR, message, exception); 494 } 495 newInfoStatus(String message, Throwable exception)496 private static IStatus newInfoStatus(String message, Throwable exception) { 497 return new Status(IStatus.INFO, PLUGIN_ID, INTERNAL_ERROR, message, exception); 498 } 499 500 /** 501 * Returns whether the API tools bundle is running inside an OSGi framework. 502 * 503 * @return whether the API tools bundle is running inside an OSGi framework 504 */ isRunningInFramework()505 public static boolean isRunningInFramework() { 506 return fgDefault != null; 507 } 508 509 /** 510 * Returns the {@link IApiBaselineManager}, allowing clients to add/remove 511 * and search for 512 * {@link org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline} 513 * s stored in the manager. 514 * 515 * @return the singleton instance of the {@link IApiProfileManager} 516 */ getApiBaselineManager()517 public IApiBaselineManager getApiBaselineManager() { 518 return ApiBaselineManager.getManager(); 519 } 520 521 /** 522 * @return The singleton instance of the {@link JavadocTagManager} 523 */ getJavadocTagManager()524 public static JavadocTagManager getJavadocTagManager() { 525 if (fgTagManager == null) { 526 fgTagManager = new JavadocTagManager(); 527 } 528 return fgTagManager; 529 } 530 531 /** 532 * Adds the given save participant to the listing of participants to be 533 * notified when the workbench saving life-cycle occurs. If the specified 534 * participant is <code>null</code> no changes are made. 535 * 536 * @param participant 537 */ addSaveParticipant(ISaveParticipant participant)538 public void addSaveParticipant(ISaveParticipant participant) { 539 if (participant != null) { 540 savelisteners.add(participant); 541 } 542 } 543 544 /** 545 * Removes the given save participant from the current listing. If the 546 * specified participant is <code>null</code> no changes are made. 547 * 548 * @param participant 549 */ removeSaveParticipant(ISaveParticipant participant)550 public void removeSaveParticipant(ISaveParticipant participant) { 551 if (participant != null) { 552 savelisteners.remove(participant); 553 } 554 } 555 556 @Override doneSaving(ISaveContext context)557 public void doneSaving(ISaveContext context) { 558 for (ISaveParticipant sp : savelisteners) { 559 sp.doneSaving(context); 560 } 561 } 562 563 @Override prepareToSave(ISaveContext context)564 public void prepareToSave(ISaveContext context) throws CoreException { 565 for (ISaveParticipant sp : savelisteners) { 566 sp.prepareToSave(context); 567 } 568 } 569 570 @Override rollback(ISaveContext context)571 public void rollback(ISaveContext context) { 572 for (ISaveParticipant sp : savelisteners) { 573 sp.rollback(context); 574 } 575 } 576 577 @Override saving(ISaveContext context)578 public void saving(ISaveContext context) throws CoreException { 579 for (ISaveParticipant sp : savelisteners) { 580 sp.saving(context); 581 } 582 IEclipsePreferences node = InstanceScope.INSTANCE.getNode(PLUGIN_ID); 583 if (node != null) { 584 try { 585 node.flush(); 586 } catch (BackingStoreException e) { 587 log(e); 588 } 589 } 590 } 591 592 @Override start(BundleContext context)593 public void start(BundleContext context) throws Exception { 594 try { 595 super.start(context); 596 Hashtable<String, String> props = new Hashtable<>(2); 597 props.put(org.eclipse.osgi.service.debug.DebugOptions.LISTENER_SYMBOLICNAME, PLUGIN_ID); 598 context.registerService(DebugOptionsListener.class.getName(), this, props); 599 } finally { 600 ResourcesPlugin.getWorkspace().addSaveParticipant(PLUGIN_ID, this); 601 fBundleContext = context; 602 deltaProcessor = new WorkspaceDeltaProcessor(); 603 JavaCore.addElementChangedListener(deltaProcessor, ElementChangedEvent.POST_CHANGE); 604 ResourcesPlugin.getWorkspace().addResourceChangeListener(deltaProcessor, IResourceChangeEvent.PRE_DELETE | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_BUILD); 605 checkForEEDescriptionChanges(); 606 } 607 } 608 609 /** 610 * Checks if the current set of installed execution environment description 611 * fragments differs from the last time this workspace was started. If so, a 612 * full api analysis build is run. 613 */ checkForEEDescriptionChanges()614 private void checkForEEDescriptionChanges() { 615 IEclipsePreferences node = InstanceScope.INSTANCE.getNode(PLUGIN_ID); 616 if (node == null) { 617 return; 618 } 619 String knownFragmentsList = node.get(KNOWN_EE_FRAGMENTS, null); 620 Bundle[] allFragments = Platform.getFragments(fBundleContext.getBundle()); 621 if (allFragments == null) { 622 allFragments = new Bundle[0]; 623 } 624 625 // No preference stored yet, set the preference for future startup 626 if (knownFragmentsList == null) { 627 String list = getListOfEEFragments(allFragments); 628 node.put(KNOWN_EE_FRAGMENTS, list); 629 try { 630 node.flush(); 631 } catch (BackingStoreException e) { 632 log(e); 633 } 634 return; 635 } 636 637 // Break the list into a set we can search 638 Set<NameVersionDescriptor> knownFragments = new HashSet<>(); 639 StringTokenizer tokenizer = new StringTokenizer(knownFragmentsList, ";"); //$NON-NLS-1$ 640 String name = null; 641 while (tokenizer.hasMoreTokens()) { 642 name = tokenizer.nextToken().trim(); 643 if (name.length() > 0 && tokenizer.hasMoreTokens()) { 644 knownFragments.add(new NameVersionDescriptor(name, tokenizer.nextToken())); 645 } 646 } 647 648 // Figure out if we need to rebuild (fragments added or removed 649 boolean mustRebuild = false; 650 for (Bundle allFragment : allFragments) { 651 // We only care about 652 if (allFragment.getSymbolicName().contains(EE_DESCRIPTION_PREFIX)) { 653 NameVersionDescriptor current = new NameVersionDescriptor(allFragment.getSymbolicName(), allFragment.getVersion().toString()); 654 if (knownFragments.contains(current)) { 655 knownFragments.remove(current); 656 } else { 657 // New EE fragment installed 658 mustRebuild = true; 659 break; 660 } 661 } 662 } 663 664 if (knownFragments.size() > 0) { 665 // EE fragment removed 666 mustRebuild = true; 667 } 668 669 // Run a full api analysis build and update the preference 670 if (mustRebuild) { 671 IProject[] projects = Util.getApiProjects(); 672 if (projects != null) { 673 for (IProject project : projects) { 674 try { 675 project.build(IncrementalProjectBuilder.FULL_BUILD, ApiPlugin.BUILDER_ID, null, null); 676 } catch (CoreException e) { 677 log(e.getStatus()); 678 } 679 } 680 } 681 682 // Write out the preferences so we don't rebuild on next startup 683 String list = getListOfEEFragments(allFragments); 684 node.put(KNOWN_EE_FRAGMENTS, list); 685 try { 686 node.flush(); 687 } catch (BackingStoreException e) { 688 log(e); 689 } 690 } 691 } 692 693 /** 694 * @return a list of fragments that consider this bundle their host and 695 * symbolic names contain {@link #EE_DESCRIPTION_PREFIX} 696 */ getListOfEEFragments(Bundle[] allFragments)697 private String getListOfEEFragments(Bundle[] allFragments) { 698 StringBuilder result = new StringBuilder(); 699 for (Bundle allFragment : allFragments) { 700 if (allFragment.getSymbolicName().contains(EE_DESCRIPTION_PREFIX)) { 701 result.append(allFragment.getSymbolicName()); 702 result.append(';'); 703 result.append(allFragment.getVersion().toString()); 704 result.append(';'); 705 } 706 } 707 return result.toString(); 708 } 709 710 @Override stop(BundleContext context)711 public void stop(BundleContext context) throws Exception { 712 try { 713 ApiDescriptionManager.shutdown(); 714 ApiBaselineManager.getManager().stop(); 715 ResourcesPlugin.getWorkspace().removeSaveParticipant(PLUGIN_ID); 716 FileManager.getManager().deleteFiles(); 717 fBundleContext = null; 718 if (deltaProcessor != null) { 719 JavaCore.removeElementChangedListener(deltaProcessor); 720 ResourcesPlugin.getWorkspace().removeResourceChangeListener(deltaProcessor); 721 } 722 } finally { 723 super.stop(context); 724 } 725 } 726 727 /** 728 * Returns the severity for the specific key from the given {@link IProject} 729 * . If the project does not have project specific settings, the workspace 730 * preference is returned. If <code>null</code> is passed in as the project 731 * the workspace preferences are consulted. 732 * 733 * @param prefkey the given preference key 734 * @param project the given project or <code>null</code> 735 * @return the severity level for the given pref key 736 */ getSeverityLevel(String prefkey, IProject project)737 public int getSeverityLevel(String prefkey, IProject project) { 738 IPreferencesService service = Platform.getPreferencesService(); 739 IScopeContext[] context = null; 740 if (hasProjectSettings(project)) { 741 context = new IScopeContext[] { 742 new ProjectScope(project), InstanceScope.INSTANCE, 743 DefaultScope.INSTANCE }; 744 } else { 745 context = new IScopeContext[] { 746 InstanceScope.INSTANCE, DefaultScope.INSTANCE }; 747 } 748 String value = service.get(prefkey, null, getPreferences(context)); 749 if (VALUE_ERROR.equals(value)) { 750 return SEVERITY_ERROR; 751 } 752 if (VALUE_WARNING.equals(value)) { 753 return SEVERITY_WARNING; 754 } 755 return SEVERITY_IGNORE; 756 } 757 758 /** 759 * Returns the array of {@link IEclipsePreferences} nodes to look in to 760 * determine the value of a given preference. This method will return 761 * <code>null</code> iff: 762 * <ul> 763 * <li>the given array of contexts are <code>null</code></li> 764 * <li>if no nodes could be determined from the given contexts</li> 765 * </ul> 766 * 767 * @param context 768 * @return the array of {@link IEclipsePreferences} to look in or 769 * <code>null</code>. 770 * @since 1.1 771 */ getPreferences(IScopeContext[] context)772 IEclipsePreferences[] getPreferences(IScopeContext[] context) { 773 if (context != null) { 774 ArrayList<IEclipsePreferences> nodes = new ArrayList<>(context.length); 775 IEclipsePreferences node = null; 776 for (IScopeContext element : context) { 777 node = element.getNode(PLUGIN_ID); 778 if (node != null) { 779 nodes.add(node); 780 } 781 } 782 if (nodes.size() > 0) { 783 return nodes.toArray(new IEclipsePreferences[nodes.size()]); 784 } 785 } 786 return null; 787 } 788 789 /** 790 * Returns if the given project has project-specific settings. 791 * 792 * @param project 793 * @return true if the project has specific settings, false otherwise 794 * @since 1.1 795 */ hasProjectSettings(IProject project)796 boolean hasProjectSettings(IProject project) { 797 if (project != null) { 798 ProjectScope scope = new ProjectScope(project); 799 IEclipsePreferences node = scope.getNode(PLUGIN_ID); 800 try { 801 return node != null && node.keys().length > 0; 802 } catch (BackingStoreException bse) { 803 log(bse); 804 } 805 } 806 return false; 807 } 808 getSessionManager()809 public ISessionManager getSessionManager() { 810 if (fgSessionManager == null) { 811 fgSessionManager = new SessionManager(); 812 } 813 return fgSessionManager; 814 } 815 816 /** 817 * Returns the enable state for the specific key from the given 818 * {@link IProject}. If the project does not have project specific settings, 819 * the workspace preference is returned. If <code>null</code> is passed in 820 * as the project the workspace preferences are consulted. 821 * 822 * @param prefkey the given preference key 823 * @param project the given project or <code>null</code> 824 * @return the enable state 825 */ getEnableState(String prefkey, IProject project)826 public boolean getEnableState(String prefkey, IProject project) { 827 IPreferencesService service = Platform.getPreferencesService(); 828 IScopeContext[] context = null; 829 if (hasProjectSettings(project)) { 830 context = new IScopeContext[] { 831 new ProjectScope(project), InstanceScope.INSTANCE, 832 DefaultScope.INSTANCE }; 833 } else { 834 context = new IScopeContext[] { 835 InstanceScope.INSTANCE, DefaultScope.INSTANCE }; 836 } 837 String value = service.get(prefkey, null, getPreferences(context)); 838 return VALUE_ENABLED.equals(value); 839 } 840 841 /** 842 * Returns a service with the specified class or <code>null</code> if none. 843 * 844 * @param serviceClass name of service 845 * @return service service or <code>null</code> if none 846 */ acquireService(Class<T> serviceClass)847 public <T> T acquireService(Class<T> serviceClass) { 848 ServiceReference<T> reference = fBundleContext.getServiceReference(serviceClass); 849 if (reference == null) { 850 return null; 851 } 852 return fBundleContext.getService(reference); 853 } 854 855 @Override optionsChanged(DebugOptions options)856 public void optionsChanged(DebugOptions options) { 857 DEBUG = options.getBooleanOption(DEBUG_FLAG, false); 858 boolean option = options.getBooleanOption(DELTA_DEBUG, false); 859 DEBUG_CLASSFILE_COMPARATOR = DEBUG && option; 860 DEBUG_API_COMPARATOR = DEBUG_CLASSFILE_COMPARATOR; 861 DEBUG_BUILDER = DEBUG && options.getBooleanOption(BUILDER_DEBUG, false); 862 DEBUG_SEARCH_ENGINE = DEBUG && options.getBooleanOption(SEARCH_DEBUG, false); 863 DEBUG_REFERENCE_EXTRACTOR = DEBUG && options.getBooleanOption(CLASSFILE_VISITOR_DEBUG, false); 864 DEBUG_ELEMENT_DESCRIPTOR_FRAMEWORK = DEBUG && options.getBooleanOption(DESCRIPTOR_FRAMEWORK_DEBUG, false); 865 DEBUG_TAG_SCANNER = DEBUG && options.getBooleanOption(TAG_SCANNER_DEBUG, false); 866 DEBUG_PROJECT_COMPONENT = DEBUG && options.getBooleanOption(PLUGIN_WORKSPACE_COMPONENT_DEBUG, false); 867 DEBUG_BASELINE_MANAGER = DEBUG && options.getBooleanOption(API_PROFILE_MANAGER_DEBUG, false); 868 DEBUG_FILTER_STORE = DEBUG && options.getBooleanOption(API_FILTER_STORE_DEBUG, false); 869 DEBUG_REFERENCE_ANALYZER = DEBUG && options.getBooleanOption(API_REFERENCE_ANALYZER_DEBUG, false); 870 DEBUG_REFERENCE_RESOLVER = DEBUG && options.getBooleanOption(REFERENCE_RESOLVER_DEBUG, false); 871 DEBUG_PROBLEM_DETECTOR = DEBUG && options.getBooleanOption(PROBLEM_DETECTOR_DEBUG, false); 872 DEBUG_API_DESCRIPTION = DEBUG && options.getBooleanOption(API_DESCRIPTION, false); 873 DEBUG_WORKSPACE_DELTA_PROCESSOR = DEBUG && options.getBooleanOption(WORKSPACE_DELTA_PROCESSOR, false); 874 DEBUG_API_ANALYZER = DEBUG && options.getBooleanOption(API_ANALYZER_DEBUG, false); 875 DEBUG_USE_REPORT_CONVERTER = DEBUG && options.getBooleanOption(USE_REPORT_CONVERTER_DEBUG, false); 876 } 877 } 878