1 | /******************************************************************************* |
2 | * Copyright (c) 2008, 2009 IBM Corporation and others. |
3 | * All rights reserved. This program and the accompanying materials |
4 | * are made available under the terms of the Eclipse Public License v1.0 |
5 | * which accompanies this distribution, and is available at |
6 | * http://www.eclipse.org/legal/epl-v10.html |
7 | * |
8 | * Contributors: |
9 | * IBM Corporation - initial API and implementation |
10 | *******************************************************************************/ |
11 | package org.eclipse.pde.api.tools.internal.builder; |
12 | |
13 | import java.util.Iterator; |
14 | import java.util.LinkedList; |
15 | import java.util.List; |
16 | |
17 | import org.eclipse.core.runtime.CoreException; |
18 | import org.eclipse.core.runtime.IProgressMonitor; |
19 | import org.eclipse.core.runtime.MultiStatus; |
20 | import org.eclipse.core.runtime.SubMonitor; |
21 | import org.eclipse.pde.api.tools.internal.provisional.ApiDescriptionVisitor; |
22 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
23 | import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations; |
24 | import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers; |
25 | import org.eclipse.pde.api.tools.internal.provisional.builder.IApiProblemDetector; |
26 | import org.eclipse.pde.api.tools.internal.provisional.builder.IReference; |
27 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; |
28 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IPackageDescriptor; |
29 | import org.eclipse.pde.api.tools.internal.provisional.model.ApiTypeContainerVisitor; |
30 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; |
31 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiType; |
32 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeContainer; |
33 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot; |
34 | import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; |
35 | import org.eclipse.pde.api.tools.internal.util.Util; |
36 | |
37 | import com.ibm.icu.text.MessageFormat; |
38 | |
39 | /** |
40 | * The reference analyzer |
41 | * |
42 | * @since 1.1 |
43 | */ |
44 | public class ReferenceAnalyzer { |
45 | |
46 | /** |
47 | * Natural log of 2. |
48 | */ |
49 | private static final double LOG2 = Math.log(2); |
50 | |
51 | /** |
52 | * Constant used for controlling tracing in the search engine |
53 | */ |
54 | private static boolean DEBUG = Util.DEBUG; |
55 | |
56 | /** |
57 | * Empty result collection. |
58 | */ |
59 | private static final IApiProblem[] EMPTY_RESULT = new IApiProblem[0]; |
60 | /** |
61 | * No problem detector to use |
62 | */ |
63 | private static final IApiProblemDetector[] NO_PROBLEM_DETECTORS = new IApiProblemDetector[0]; |
64 | |
65 | /** |
66 | * Visits each class file, extracting references. |
67 | */ |
68 | class Visitor extends ApiTypeContainerVisitor { |
69 | |
70 | private IProgressMonitor fMonitor = null; |
71 | |
72 | public Visitor(IProgressMonitor monitor) { |
73 | fMonitor = monitor; |
74 | } |
75 | public boolean visitPackage(String packageName) { |
76 | fMonitor.subTask(MessageFormat.format(BuilderMessages.ReferenceAnalyzer_checking_api_used_by, new String[]{packageName})); |
77 | return true; |
78 | } |
79 | public void endVisitPackage(String packageName) { |
80 | fMonitor.worked(1); |
81 | } |
82 | /* (non-Javadoc) |
83 | * @see org.eclipse.pde.api.tools.model.component.ClassFileContainerVisitor#visit(java.lang.String, org.eclipse.pde.api.tools.model.component.IClassFile) |
84 | */ |
85 | public void visit(String packageName, IApiTypeRoot classFile) { |
86 | if (!fMonitor.isCanceled()) { |
87 | try { |
88 | //don't process inner/anonymous/local types, this is done in the extractor |
89 | if(classFile.getTypeName().indexOf('$') > -1) { |
90 | return; |
91 | } |
92 | IApiType type = classFile.getStructure(); |
93 | if(type == null) { |
94 | //do nothing for bad class files |
95 | return; |
96 | } |
97 | List references = type.extractReferences(fAllReferenceKinds, null); |
98 | // keep potential matches |
99 | Iterator iterator = references.iterator(); |
100 | while (iterator.hasNext()) { |
101 | IReference ref = (IReference) iterator.next(); |
102 | // compute index of interested problem detectors |
103 | int index = getLog2(ref.getReferenceKind()); |
104 | IApiProblemDetector[] detectors = fIndexedDetectors[index]; |
105 | boolean added = false; |
106 | if (detectors != null) { |
107 | for (int i = 0; i < detectors.length; i++) { |
108 | IApiProblemDetector detector = detectors[i]; |
109 | if (detector.considerReference(ref)) { |
110 | if (!added) { |
111 | fReferences.add(ref); |
112 | added = true; |
113 | } |
114 | } |
115 | } |
116 | } |
117 | } |
118 | } catch (CoreException e) { |
119 | fStatus.add(e.getStatus()); |
120 | } |
121 | } |
122 | } |
123 | } |
124 | |
125 | /** |
126 | * Scan status |
127 | */ |
128 | MultiStatus fStatus; |
129 | |
130 | /** |
131 | * Bit mask of reference kinds that problem detectors care about. |
132 | */ |
133 | int fAllReferenceKinds = 0; |
134 | |
135 | /** |
136 | * List of references to consider/resolve. |
137 | */ |
138 | List fReferences = new LinkedList(); |
139 | |
140 | /** |
141 | * Problem detectors indexed by the log base 2 of each reference kind they |
142 | * are interested in. Provides a fast way to hand references off to interested |
143 | * problem detectors. |
144 | */ |
145 | IApiProblemDetector[][] fIndexedDetectors; |
146 | |
147 | /** |
148 | * Method used for initializing tracing |
149 | */ |
150 | public static void setDebug(boolean debugValue) { |
151 | DEBUG = debugValue || Util.DEBUG; |
152 | } |
153 | |
154 | /** |
155 | * Performs the actual reference extraction and analysis. |
156 | * |
157 | * @param scope the scope to extract and analyze references from |
158 | * @param detectors problem detectors to use |
159 | * @param monitor progress monitor |
160 | * @return any problems |
161 | * @throws CoreException |
162 | */ |
163 | private IApiProblem[] analyze(IApiTypeContainer scope, IApiProblemDetector[] detectors, IProgressMonitor monitor) throws CoreException { |
164 | try { |
165 | // 1. index problem detectors |
166 | indexProblemDetectors(detectors); |
167 | // 2. extract references |
168 | SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.ReferenceAnalyzer_analyzing_api, 3); |
169 | localMonitor.subTask(BuilderMessages.ReferenceAnalyzer_analyzing_api_checking_use); |
170 | extractReferences(scope, localMonitor); |
171 | localMonitor.worked(1); |
172 | if (localMonitor.isCanceled()) { |
173 | return EMPTY_RESULT; |
174 | } |
175 | // 3. resolve problematic references |
176 | localMonitor.subTask(BuilderMessages.ReferenceAnalyzer_analyzing_api_checking_use); |
177 | if (fReferences.size() != 0) { |
178 | ReferenceResolver.resolveReferences(fReferences, localMonitor); |
179 | } |
180 | localMonitor.worked(1); |
181 | if (localMonitor.isCanceled()) { |
182 | return EMPTY_RESULT; |
183 | } |
184 | // 4. create problems |
185 | List allProblems = new LinkedList(); |
186 | localMonitor.subTask(BuilderMessages.ReferenceAnalyzer_analyzing_api_checking_use); |
187 | for (int i = 0; i < detectors.length; i++) { |
188 | IApiProblemDetector detector = detectors[i]; |
189 | List problems = detector.createProblems(); |
190 | allProblems.addAll(problems); |
191 | if (localMonitor.isCanceled()) { |
192 | return EMPTY_RESULT; |
193 | } |
194 | } |
195 | IApiProblem[] array = (IApiProblem[]) allProblems.toArray(new IApiProblem[allProblems.size()]); |
196 | localMonitor.worked(1); |
197 | localMonitor.done(); |
198 | return array; |
199 | } finally { |
200 | // clean up |
201 | fIndexedDetectors = null; |
202 | fReferences.clear(); |
203 | } |
204 | } |
205 | |
206 | /** |
207 | * Indexes the problem detectors by the reference kinds they are interested in. |
208 | * For example, a detector interested in a |
209 | * {@link org.eclipse.pde.api.tools.internal.provisional.search.ReferenceModifiers#REF_INSTANTIATE} |
210 | * will be in the 26th index (0x1 << 27, which is 2 ^ 26). |
211 | * Also initializes the bit mask of all interesting reference kinds. |
212 | * |
213 | * @param detectors problem detectors |
214 | */ |
215 | private void indexProblemDetectors(IApiProblemDetector[] detectors) { |
216 | fIndexedDetectors = new IApiProblemDetector[32][]; |
217 | for (int i = 0; i < detectors.length; i++) { |
218 | IApiProblemDetector detector = detectors[i]; |
219 | int kinds = detector.getReferenceKinds(); |
220 | fAllReferenceKinds |= kinds; |
221 | int mask = 0x1; |
222 | for (int bit = 0; bit < 32; bit++) { |
223 | if ((mask & kinds) > 0) { |
224 | IApiProblemDetector[] indexed = fIndexedDetectors[bit]; |
225 | if (indexed == null) { |
226 | fIndexedDetectors[bit] = new IApiProblemDetector[]{detector}; |
227 | } else { |
228 | IApiProblemDetector[] next = new IApiProblemDetector[indexed.length + 1]; |
229 | System.arraycopy(indexed, 0, next, 0, indexed.length); |
230 | next[indexed.length] = detector; |
231 | fIndexedDetectors[bit] = next; |
232 | } |
233 | } |
234 | mask = mask << 1; |
235 | } |
236 | } |
237 | } |
238 | |
239 | /** |
240 | * log 2 (x) = ln(x) / ln(2) |
241 | * |
242 | * @param bitConstant a single bit constant (0x1 << n) |
243 | * @return log base 2 of the constant (the power of 2 the constant is equal to) |
244 | */ |
245 | int getLog2(int bitConstant) { |
246 | double logX = Math.log(bitConstant); |
247 | double pow = logX / LOG2; |
248 | return (int)Math.round(pow); |
249 | } |
250 | |
251 | /** |
252 | * Scans the given scope extracting all reference information. |
253 | * |
254 | * @param scope scope to scan |
255 | * @param monitor progress monitor |
256 | * @exception CoreException if the scan fails |
257 | */ |
258 | private void extractReferences(IApiTypeContainer scope, IProgressMonitor monitor) throws CoreException { |
259 | fStatus = new MultiStatus(ApiPlugin.PLUGIN_ID, 0, BuilderMessages.ReferenceAnalyzer_api_analysis_error, null); |
260 | String[] packageNames = scope.getPackageNames(); |
261 | SubMonitor localMonitor = SubMonitor.convert(monitor, packageNames.length); |
262 | ApiTypeContainerVisitor visitor = new Visitor(localMonitor); |
263 | long start = System.currentTimeMillis(); |
264 | try { |
265 | scope.accept(visitor); |
266 | } catch (CoreException e) { |
267 | fStatus.add(e.getStatus()); |
268 | } |
269 | long end = System.currentTimeMillis(); |
270 | if (!fStatus.isOK()) { |
271 | throw new CoreException(fStatus); |
272 | } |
273 | localMonitor.done(); |
274 | if (DEBUG) { |
275 | System.out.println("Reference Analyzer: extracted " + fReferences.size() + " references in " + (end - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
276 | } |
277 | } |
278 | |
279 | /** |
280 | * Analyzes the given {@link IApiComponent} within the given {@link IApiTypeContainer} (scope) and returns |
281 | * a collection of detected {@link IApiProblem}s or an empty collection, never <code>null</code> |
282 | * @param component |
283 | * @param scope |
284 | * @param monitor |
285 | * @return the collection of detected {@link IApiProblem}s or an empty collection, never <code>null</code> |
286 | * @throws CoreException |
287 | */ |
288 | public IApiProblem[] analyze(IApiComponent component, IApiTypeContainer scope, IProgressMonitor monitor) throws CoreException { |
289 | // build problem detectors |
290 | IApiProblemDetector[] detectors = buildProblemDetectors(component, monitor); |
291 | // analyze |
292 | return analyze(scope, detectors, monitor); |
293 | } |
294 | |
295 | /** |
296 | * Builds problem detectors to use when analyzing the given component. |
297 | * |
298 | * @param component component to be analyzed |
299 | * @param monitor |
300 | * |
301 | * @return problem detectors |
302 | */ |
303 | private IApiProblemDetector[] buildProblemDetectors(IApiComponent component, IProgressMonitor monitor) { |
304 | try { |
305 | long start = System.currentTimeMillis(); |
306 | IApiComponent[] components = component.getBaseline().getPrerequisiteComponents(new IApiComponent[]{component}); |
307 | final ProblemDetectorBuilder visitor = new ProblemDetectorBuilder(component); |
308 | for (int i = 0; i < components.length; i++) { |
309 | Util.updateMonitor(monitor); |
310 | IApiComponent prereq = components[i]; |
311 | if (!prereq.equals(component)) { |
312 | visitor.setOwningComponent(prereq); |
313 | try { |
314 | prereq.getApiDescription().accept(visitor, monitor); |
315 | } catch (CoreException e) { |
316 | ApiPlugin.log(e.getStatus()); |
317 | } |
318 | } |
319 | } |
320 | long end = System.currentTimeMillis(); |
321 | if (DEBUG) { |
322 | System.out.println("Time to build problem detectors: " + (end-start) + "ms"); //$NON-NLS-1$//$NON-NLS-2$ |
323 | } |
324 | // add names from the leak component as well |
325 | ApiDescriptionVisitor nameVisitor = new ApiDescriptionVisitor() { |
326 | /* (non-Javadoc) |
327 | * @see org.eclipse.pde.api.tools.internal.provisional.ApiDescriptionVisitor#visitElement(org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor, org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations) |
328 | */ |
329 | public boolean visitElement(IElementDescriptor element, IApiAnnotations description) { |
330 | if (element.getElementType() == IElementDescriptor.PACKAGE) { |
331 | if (VisibilityModifiers.isPrivate(description.getVisibility())) { |
332 | visitor.addNonApiPackageName(((IPackageDescriptor)element).getName()); |
333 | } |
334 | } |
335 | return false; |
336 | } |
337 | }; |
338 | component.getApiDescription().accept(nameVisitor, null); |
339 | List detectors = visitor.getProblemDetectors(); |
340 | int size = detectors.size(); |
341 | if (size == 0) return NO_PROBLEM_DETECTORS; |
342 | return (IApiProblemDetector[]) detectors.toArray(new IApiProblemDetector[size]); |
343 | } catch (CoreException e) { |
344 | ApiPlugin.log(e); |
345 | } |
346 | return NO_PROBLEM_DETECTORS; |
347 | } |
348 | } |