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 @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