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