1 /*******************************************************************************
2  * Copyright (c) 2008, 2013 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.builder;
15 
16 import java.util.ArrayList;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Set;
20 
21 import org.eclipse.core.resources.IProject;
22 import org.eclipse.pde.api.tools.internal.model.ProjectComponent;
23 import org.eclipse.pde.api.tools.internal.model.StubApiComponent;
24 import org.eclipse.pde.api.tools.internal.provisional.ApiDescriptionVisitor;
25 import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
26 import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations;
27 import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers;
28 import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers;
29 import org.eclipse.pde.api.tools.internal.provisional.builder.IApiProblemDetector;
30 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor;
31 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IFieldDescriptor;
32 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor;
33 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IPackageDescriptor;
34 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor;
35 import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
36 import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes;
37 
38 /**
39  * Builds problem detectors for reference analysis.
40  *
41  * @since 1.1
42  */
43 public class ProblemDetectorBuilder extends ApiDescriptionVisitor {
44 
45 	/**
46 	 * Kind mask bits to allow only certain detectors to be built regardless of
47 	 * the IDE being available
48 	 */
49 	/**
50 	 * All detectors will be added
51 	 */
52 	public static int K_ALL = 0xFFFFFFFF;
53 	/**
54 	 * Illegal use detectors will be added
55 	 */
56 	public static int K_USE = 1;
57 	/**
58 	 * Leak detectors will be added
59 	 */
60 	public static int K_LEAK = 1 << 1;
61 	/**
62 	 * The system detector will be added
63 	 */
64 	public static int K_SYSTEM = 1 << 2;
65 
66 	/**
67 	 * Problem detectors
68 	 */
69 	private IllegalExtendsProblemDetector fIllegalExtends = null;
70 	private IllegalImplementsProblemDetector fIllegalImplements = null;
71 	private IllegalInstantiateProblemDetector fIllegalInstantiate = null;
72 	private IllegalOverrideProblemDetector fIllegalOverride = null;
73 	private IllegalMethodReferenceDetector fIllegalMethodRef = null;
74 	private IllegalFieldReferenceDetector fIllegalFieldRef = null;
75 	private IllegalAnnotationReferenceDetector fIllegalAnnotationRef = null;
76 	private SystemApiDetector fSystemApiDetector = null;
77 
78 	/**
79 	 * Cache of non-API package names visited
80 	 */
81 	private Set<String> fNonApiPackageNames = new HashSet<>();
82 
83 	/**
84 	 * The owning {@link IApiComponent} of this builder
85 	 */
86 	private IApiComponent fComponent = null;
87 
88 	/**
89 	 * Problem detectors used for a component's analysis
90 	 */
91 	private List<IApiProblemDetector> fDetectors;
92 
93 	/**
94 	 * The mask of kinds of detectors to build
95 	 */
96 	private int fKindMask = 0;
97 
98 	/**
99 	 * Stack containing all of the types tagged &#64;noreference
100 	 *
101 	 * @since 1.0.400
102 	 */
103 	Set<IElementDescriptor> fRestrictedTypes = new HashSet<>();
104 
105 	/**
106 	 * Build problem detectors for a component.
107 	 *
108 	 * @param component
109 	 * @param kinds the integer mask of the kinds of detectors to create
110 	 */
ProblemDetectorBuilder(IApiComponent component, int kinds)111 	public ProblemDetectorBuilder(IApiComponent component, int kinds) {
112 		fKindMask = kinds;
113 		initializeDetectors(component);
114 	}
115 
116 	@Override
visitElement(IElementDescriptor element, IApiAnnotations description)117 	public boolean visitElement(IElementDescriptor element, IApiAnnotations description) {
118 		int mask = description.getRestrictions();
119 		switch (element.getElementType()) {
120 			case IElementDescriptor.PACKAGE: {
121 				if (VisibilityModifiers.isPrivate(description.getVisibility())) {
122 					fNonApiPackageNames.add(((IPackageDescriptor) element).getName());
123 					return false; // no need to visit types in non-API package
124 				}
125 				break;
126 			}
127 			case IElementDescriptor.TYPE: {
128 				if ((fKindMask & K_USE) > 0) {
129 					String symbolicname = fComponent.getSymbolicName();
130 					IReferenceTypeDescriptor type = (IReferenceTypeDescriptor) element;
131 					if (RestrictionModifiers.isExtendRestriction(mask) && fIllegalExtends != null) {
132 						fIllegalExtends.addIllegalType(type, symbolicname);
133 					}
134 					if (RestrictionModifiers.isImplementRestriction(mask) && fIllegalImplements != null) {
135 						fIllegalImplements.addIllegalType(type, symbolicname);
136 					}
137 					if (RestrictionModifiers.isInstantiateRestriction(mask) && fIllegalInstantiate != null) {
138 						fIllegalInstantiate.addIllegalType(type, symbolicname);
139 					}
140 					if (RestrictionModifiers.isReferenceRestriction(mask)) {
141 						fRestrictedTypes.add(element);
142 						if (fIllegalFieldRef != null) {
143 							fIllegalFieldRef.addIllegalType(type, symbolicname);
144 						}
145 						if (fIllegalMethodRef != null) {
146 							fIllegalMethodRef.addIllegalType(type, symbolicname);
147 						}
148 						if (fIllegalAnnotationRef != null) {
149 							fIllegalAnnotationRef.addIllegalType(type, symbolicname);
150 						}
151 					}
152 				}
153 				break;
154 			}
155 			case IElementDescriptor.METHOD: {
156 				if ((fKindMask & K_USE) > 0) {
157 					String symbolicname = fComponent.getSymbolicName();
158 					IMethodDescriptor method = (IMethodDescriptor) element;
159 					if (RestrictionModifiers.isOverrideRestriction(mask) && fIllegalOverride != null) {
160 						fIllegalOverride.addIllegalMethod(method, symbolicname);
161 					}
162 					if (fIllegalMethodRef != null) {
163 						if (RestrictionModifiers.isReferenceRestriction(mask)) {
164 							fIllegalMethodRef.addIllegalMethod(method, symbolicname);
165 						}
166 					}
167 				}
168 				break;
169 			}
170 			case IElementDescriptor.FIELD: {
171 				if ((fKindMask & K_USE) > 0 && fIllegalFieldRef != null) {
172 					if (RestrictionModifiers.isReferenceRestriction(mask)) {
173 						fIllegalFieldRef.addIllegalField((IFieldDescriptor) element, fComponent.getSymbolicName());
174 					}
175 				}
176 				break;
177 			}
178 			default: {
179 				break;
180 			}
181 		}
182 		return true;
183 	}
184 
185 	/**
186 	 * Sets the owning component of this builder
187 	 *
188 	 * @param component
189 	 */
setOwningComponent(IApiComponent component)190 	public void setOwningComponent(IApiComponent component) {
191 		fComponent = component;
192 	}
193 
194 	/**
195 	 * @return the {@link IProject} associated with the set
196 	 *         {@link IApiComponent} or <code>null</code> if the component is
197 	 *         not a {@link PluginProjectApiComponent}
198 	 */
getProject(IApiComponent component)199 	private IProject getProject(IApiComponent component) {
200 		if (component instanceof ProjectComponent) {
201 			ProjectComponent comp = (ProjectComponent) component;
202 			return comp.getJavaProject().getProject();
203 		}
204 		return null;
205 	}
206 
207 	/**
208 	 * Initializes the detectors for this builder. This method is only called
209 	 * when an owning component is set
210 	 */
initializeDetectors(IApiComponent component)211 	private void initializeDetectors(IApiComponent component) {
212 		fDetectors = new ArrayList<>();
213 		IProject project = getProject(component);
214 		if ((fKindMask & K_USE) > 0) {
215 			addUseDetectors(fDetectors, project);
216 		}
217 		if ((fKindMask & K_SYSTEM) > 0) {
218 			addSystemDetector(fDetectors, project);
219 		}
220 		if ((fKindMask & K_LEAK) > 0) {
221 			addLeakDetectors(fDetectors, project);
222 		}
223 	}
224 
225 	/**
226 	 * Returns whether the given problem kind should be ignored.
227 	 *
228 	 * @param problemKey
229 	 * @return whether the given problem kind should be ignored
230 	 */
isIgnore(String problemKey, IProject project)231 	private boolean isIgnore(String problemKey, IProject project) {
232 		int severity = ApiPlugin.getDefault().getSeverityLevel(problemKey, project);
233 		return severity == ApiPlugin.SEVERITY_IGNORE;
234 	}
235 
236 	/**
237 	 * Returns a set of all non-API package names that are in prerequisite
238 	 * components.
239 	 *
240 	 * @return
241 	 */
getNonApiPackageNames()242 	Set<String> getNonApiPackageNames() {
243 		return fNonApiPackageNames;
244 	}
245 
246 	/**
247 	 * Adds additional non-API package descriptors to the detector builder.
248 	 *
249 	 * @param packagee
250 	 * @return true if the descriptor did not exist in the current collection
251 	 *         and was added, false otherwise
252 	 */
addNonApiPackageName(String packagee)253 	public boolean addNonApiPackageName(String packagee) {
254 		if (packagee != null) {
255 			return fNonApiPackageNames.add(packagee);
256 		}
257 		return false;
258 	}
259 
260 	/**
261 	 * Returns a list of problem detectors to be used.
262 	 *
263 	 * @return problem detectors
264 	 */
getProblemDetectors()265 	public List<IApiProblemDetector> getProblemDetectors() {
266 		return fDetectors;
267 	}
268 
269 	/**
270 	 * Adds the system detector to the given listing
271 	 *
272 	 * @param detectors
273 	 * @param project
274 	 */
addSystemDetector(List<IApiProblemDetector> detectors, IProject project)275 	private void addSystemDetector(List<IApiProblemDetector> detectors, IProject project) {
276 		if (project != null) {
277 			// do not add the detector even if the setting is not ignore if
278 			// there are no EE descriptions installed
279 			if (!isIgnore(IApiProblemTypes.INVALID_REFERENCE_IN_SYSTEM_LIBRARIES, project) && fSystemApiDetector == null && StubApiComponent.getInstalledMetadata().length > 0) {
280 				fSystemApiDetector = new SystemApiDetector();
281 				fDetectors.add(fSystemApiDetector);
282 			}
283 		} else {
284 			// add detector by default only outside of the IDE if we have no
285 			// preference context
286 			fSystemApiDetector = new SystemApiDetector();
287 			fDetectors.add(fSystemApiDetector);
288 		}
289 	}
290 
291 	/**
292 	 * Adds the use detectors to the listing
293 	 *
294 	 * @param detectors
295 	 * @param project
296 	 */
addUseDetectors(List<IApiProblemDetector> detectors, IProject project)297 	private void addUseDetectors(List<IApiProblemDetector> detectors, IProject project) {
298 		if (project != null) {
299 			if (!isIgnore(IApiProblemTypes.ILLEGAL_EXTEND, project) && fIllegalExtends == null) {
300 				fIllegalExtends = new IllegalExtendsProblemDetector();
301 				detectors.add(fIllegalExtends);
302 			}
303 			if (!isIgnore(IApiProblemTypes.ILLEGAL_IMPLEMENT, project) && fIllegalImplements == null) {
304 				fIllegalImplements = new IllegalImplementsProblemDetector();
305 				detectors.add(fIllegalImplements);
306 			}
307 			if (!isIgnore(IApiProblemTypes.ILLEGAL_INSTANTIATE, project) && fIllegalInstantiate == null) {
308 				fIllegalInstantiate = new IllegalInstantiateProblemDetector();
309 				detectors.add(fIllegalInstantiate);
310 			}
311 			if (!isIgnore(IApiProblemTypes.ILLEGAL_OVERRIDE, project) && fIllegalOverride == null) {
312 				fIllegalOverride = new IllegalOverrideProblemDetector();
313 				detectors.add(fIllegalOverride);
314 			}
315 			if (!isIgnore(IApiProblemTypes.ILLEGAL_REFERENCE, project) && fIllegalMethodRef == null) {
316 				fIllegalMethodRef = new IllegalMethodReferenceDetector();
317 				detectors.add(fIllegalMethodRef);
318 				fIllegalFieldRef = new IllegalFieldReferenceDetector();
319 				detectors.add(fIllegalFieldRef);
320 				fIllegalAnnotationRef = new IllegalAnnotationReferenceDetector();
321 				detectors.add(fIllegalAnnotationRef);
322 			}
323 		} else {
324 			// add all detectors by default if we have no preference context
325 			fIllegalExtends = new IllegalExtendsProblemDetector();
326 			detectors.add(fIllegalExtends);
327 			fIllegalImplements = new IllegalImplementsProblemDetector();
328 			detectors.add(fIllegalImplements);
329 			fIllegalInstantiate = new IllegalInstantiateProblemDetector();
330 			detectors.add(fIllegalInstantiate);
331 			fIllegalOverride = new IllegalOverrideProblemDetector();
332 			detectors.add(fIllegalOverride);
333 			fIllegalMethodRef = new IllegalMethodReferenceDetector();
334 			detectors.add(fIllegalMethodRef);
335 			fIllegalFieldRef = new IllegalFieldReferenceDetector();
336 			detectors.add(fIllegalFieldRef);
337 			fIllegalAnnotationRef = new IllegalAnnotationReferenceDetector();
338 			detectors.add(fIllegalAnnotationRef);
339 		}
340 	}
341 
342 	/**
343 	 * Adds any leak detectors to the listing. If a project context is available
344 	 * we filter out disabled detectors based on project / workspace preference
345 	 * settings
346 	 *
347 	 * @param detectors
348 	 * @param project
349 	 */
addLeakDetectors(List<IApiProblemDetector> detectors, IProject project)350 	private void addLeakDetectors(List<IApiProblemDetector> detectors, IProject project) {
351 		if (project != null) {
352 			if (!isIgnore(IApiProblemTypes.LEAK_EXTEND, project)) {
353 				detectors.add(new LeakExtendsProblemDetector(fNonApiPackageNames));
354 			}
355 			if (!isIgnore(IApiProblemTypes.LEAK_IMPLEMENT, project)) {
356 				detectors.add(new LeakImplementsProblemDetector(fNonApiPackageNames));
357 			}
358 			if (!isIgnore(IApiProblemTypes.LEAK_FIELD_DECL, project)) {
359 				detectors.add(new LeakFieldProblemDetector(fNonApiPackageNames));
360 			}
361 			if (!isIgnore(IApiProblemTypes.LEAK_METHOD_PARAM, project)) {
362 				detectors.add(new LeakParameterTypeDetector(fNonApiPackageNames));
363 			}
364 			if (!isIgnore(IApiProblemTypes.LEAK_METHOD_RETURN_TYPE, project)) {
365 				detectors.add(new LeakReturnTypeDetector(fNonApiPackageNames));
366 			}
367 		} else {
368 			// add all leak detectors by default if we have no preference
369 			// context
370 			detectors.add(new LeakExtendsProblemDetector(fNonApiPackageNames));
371 			detectors.add(new LeakImplementsProblemDetector(fNonApiPackageNames));
372 			detectors.add(new LeakFieldProblemDetector(fNonApiPackageNames));
373 			detectors.add(new LeakReturnTypeDetector(fNonApiPackageNames));
374 			detectors.add(new LeakParameterTypeDetector(fNonApiPackageNames));
375 		}
376 	}
377 }
378