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.HashMap; |
14 | import java.util.Iterator; |
15 | import java.util.LinkedList; |
16 | import java.util.List; |
17 | import java.util.Map; |
18 | |
19 | import org.eclipse.core.runtime.CoreException; |
20 | import org.eclipse.jdt.core.IJavaProject; |
21 | import org.eclipse.jdt.core.ISourceRange; |
22 | import org.eclipse.jdt.core.IType; |
23 | import org.eclipse.jdt.core.Signature; |
24 | import org.eclipse.jface.text.BadLocationException; |
25 | import org.eclipse.jface.text.IDocument; |
26 | import org.eclipse.jface.text.Position; |
27 | import org.eclipse.osgi.service.resolver.BundleDescription; |
28 | import org.eclipse.osgi.service.resolver.ImportPackageSpecification; |
29 | import org.eclipse.pde.api.tools.internal.model.BundleApiComponent; |
30 | import org.eclipse.pde.api.tools.internal.model.PluginProjectApiComponent; |
31 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
32 | import org.eclipse.pde.api.tools.internal.provisional.ProfileModifiers; |
33 | import org.eclipse.pde.api.tools.internal.provisional.builder.IReference; |
34 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; |
35 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline; |
36 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; |
37 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement; |
38 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiField; |
39 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiMember; |
40 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiMethod; |
41 | import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; |
42 | import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes; |
43 | import org.eclipse.pde.api.tools.internal.util.Signatures; |
44 | import org.eclipse.pde.api.tools.internal.util.Util; |
45 | |
46 | /** |
47 | * Detects references to fields, methods and types that are not available for a specific EE. |
48 | * |
49 | * @since 1.1 |
50 | */ |
51 | public class SystemApiDetector extends AbstractProblemDetector { |
52 | |
53 | private Map referenceEEs; |
54 | |
55 | /** |
56 | * Constructor |
57 | */ |
58 | public SystemApiDetector() {} |
59 | |
60 | /* (non-Javadoc) |
61 | * @see org.eclipse.pde.api.tools.internal.builder.AbstractProblemDetector#getElementType(org.eclipse.pde.api.tools.internal.provisional.builder.IReference) |
62 | */ |
63 | protected int getElementType(IReference reference) { |
64 | IApiMember member = reference.getMember(); |
65 | switch(member.getType()) { |
66 | case IApiElement.TYPE : |
67 | return IElementDescriptor.TYPE; |
68 | case IApiElement.METHOD : |
69 | return IElementDescriptor.METHOD; |
70 | case IApiElement.FIELD : |
71 | return IElementDescriptor.FIELD; |
72 | default : |
73 | return 0; |
74 | } |
75 | } |
76 | |
77 | /* (non-Javadoc) |
78 | * @see org.eclipse.pde.api.tools.internal.builder.AbstractProblemDetector#getMessageArgs(org.eclipse.pde.api.tools.internal.provisional.builder.IReference) |
79 | */ |
80 | protected String[] getMessageArgs(IReference reference) |
81 | throws CoreException { |
82 | IApiMember member = reference.getMember(); |
83 | String eeValue = ProfileModifiers.getName(((Integer) this.referenceEEs.get(reference)).intValue()); |
84 | String simpleTypeName = Signatures.getSimpleTypeName(reference.getReferencedTypeName()); |
85 | if (simpleTypeName.indexOf('$') != -1) { |
86 | simpleTypeName = simpleTypeName.replace('$', '.'); |
87 | } |
88 | switch (reference.getReferenceType()) { |
89 | case IReference.T_TYPE_REFERENCE: { |
90 | return new String[] { |
91 | getDisplay(member, false), |
92 | simpleTypeName, |
93 | eeValue, |
94 | }; |
95 | } |
96 | case IReference.T_FIELD_REFERENCE:{ |
97 | return new String[] { |
98 | getDisplay(member, false), |
99 | simpleTypeName, |
100 | reference.getReferencedMemberName(), |
101 | eeValue, |
102 | }; |
103 | } |
104 | case IReference.T_METHOD_REFERENCE:{ |
105 | String referenceMemberName = reference.getReferencedMemberName(); |
106 | if (Util.isConstructor(referenceMemberName)) { |
107 | return new String[] { |
108 | getDisplay(member, false), |
109 | Signature.toString(reference.getReferencedSignature(), simpleTypeName, null, false, false), |
110 | eeValue, |
111 | }; |
112 | } else { |
113 | return new String[] { |
114 | getDisplay(member, false), |
115 | simpleTypeName, |
116 | Signature.toString(reference.getReferencedSignature(), referenceMemberName, null, false, false), |
117 | eeValue, |
118 | }; |
119 | } |
120 | } |
121 | } |
122 | return null; |
123 | } |
124 | |
125 | /** |
126 | * Returns the signature to display for found problems |
127 | * @param member the member to get the signature from |
128 | * @param qualified if the returned signature should be type-qualified or not |
129 | * @return |
130 | * @throws CoreException |
131 | */ |
132 | private String getDisplay(IApiMember member, boolean qualified) throws CoreException { |
133 | String typeName = qualified ? getTypeName(member) : getSimpleTypeName(member); |
134 | if (typeName.indexOf('$') != -1) { |
135 | typeName = typeName.replace('$', '.'); |
136 | } |
137 | switch(member.getType()) { |
138 | case IApiElement.FIELD : { |
139 | StringBuffer buffer = new StringBuffer(); |
140 | IApiField field = (IApiField) member; |
141 | buffer |
142 | .append(typeName) |
143 | .append('.') |
144 | .append(field.getName()); |
145 | return String.valueOf(buffer); |
146 | } |
147 | case IApiElement.METHOD : { |
148 | // reference in a method declaration |
149 | IApiMethod method = (IApiMethod) member; |
150 | if(qualified) { |
151 | return Signatures.getMethodSignature(method); |
152 | } |
153 | return Signatures.getQualifiedMethodSignature(method); |
154 | } |
155 | } |
156 | return typeName; |
157 | } |
158 | |
159 | /* (non-Javadoc) |
160 | * @see org.eclipse.pde.api.tools.internal.builder.AbstractProblemDetector#getProblemFlags(org.eclipse.pde.api.tools.internal.provisional.builder.IReference) |
161 | */ |
162 | protected int getProblemFlags(IReference reference) { |
163 | switch(reference.getReferenceType()) { |
164 | case IReference.T_TYPE_REFERENCE : { |
165 | return IApiProblem.NO_FLAGS; |
166 | } |
167 | case IReference.T_METHOD_REFERENCE : { |
168 | if (Util.isConstructor(reference.getReferencedMemberName())) { |
169 | return IApiProblem.CONSTRUCTOR_METHOD; |
170 | } |
171 | return IApiProblem.METHOD; |
172 | } |
173 | case IReference.T_FIELD_REFERENCE : { |
174 | return IApiProblem.FIELD; |
175 | } |
176 | default : { |
177 | return IApiProblem.NO_FLAGS; |
178 | } |
179 | } |
180 | } |
181 | |
182 | /* (non-Javadoc) |
183 | * @see org.eclipse.pde.api.tools.internal.builder.AbstractProblemDetector#getProblemKind() |
184 | */ |
185 | protected int getProblemKind() { |
186 | return IApiProblem.INVALID_REFERENCE_IN_SYSTEM_LIBRARIES; |
187 | } |
188 | |
189 | /* (non-Javadoc) |
190 | * @see org.eclipse.pde.api.tools.internal.builder.AbstractProblemDetector#getQualifiedMessageArgs(org.eclipse.pde.api.tools.internal.provisional.builder.IReference) |
191 | */ |
192 | protected String[] getQualifiedMessageArgs(IReference reference) throws CoreException { |
193 | IApiMember member = reference.getMember(); |
194 | String eeValue = ProfileModifiers.getName(((Integer) this.referenceEEs.get(reference)).intValue()); |
195 | String simpleTypeName = reference.getReferencedTypeName(); |
196 | if (simpleTypeName.indexOf('$') != -1) { |
197 | simpleTypeName = simpleTypeName.replace('$', '.'); |
198 | } |
199 | switch (reference.getReferenceType()) { |
200 | case IReference.T_TYPE_REFERENCE: { |
201 | return new String[] { |
202 | getDisplay(member, false), |
203 | simpleTypeName, |
204 | eeValue, |
205 | }; |
206 | } |
207 | case IReference.T_FIELD_REFERENCE:{ |
208 | return new String[] { |
209 | getDisplay(member, false), |
210 | simpleTypeName, |
211 | reference.getReferencedMemberName(), |
212 | eeValue, |
213 | }; |
214 | } |
215 | case IReference.T_METHOD_REFERENCE:{ |
216 | String referenceMemberName = reference.getReferencedMemberName(); |
217 | if (Util.isConstructor(referenceMemberName)) { |
218 | return new String[] { |
219 | getDisplay(member, false), |
220 | Signature.toString(reference.getReferencedSignature(), simpleTypeName, null, false, false), |
221 | eeValue, |
222 | }; |
223 | } else { |
224 | return new String[] { |
225 | getDisplay(member, false), |
226 | simpleTypeName, |
227 | Signature.toString(reference.getReferencedSignature(), referenceMemberName, null, false, false), |
228 | eeValue, |
229 | }; |
230 | } |
231 | } |
232 | } |
233 | return null; |
234 | } |
235 | |
236 | /* (non-Javadoc) |
237 | * @see org.eclipse.pde.api.tools.internal.builder.AbstractProblemDetector#getSeverityKey() |
238 | */ |
239 | protected String getSeverityKey() { |
240 | return IApiProblemTypes.INVALID_REFERENCE_IN_SYSTEM_LIBRARIES; |
241 | } |
242 | |
243 | /* (non-Javadoc) |
244 | * @see org.eclipse.pde.api.tools.internal.builder.AbstractProblemDetector#getSourceRange(org.eclipse.jdt.core.IType, org.eclipse.jface.text.IDocument, org.eclipse.pde.api.tools.internal.provisional.builder.IReference) |
245 | */ |
246 | protected Position getSourceRange(IType type, IDocument document, IReference reference) throws CoreException, BadLocationException { |
247 | switch(reference.getReferenceType()) { |
248 | case IReference.T_TYPE_REFERENCE : { |
249 | int linenumber = reference.getLineNumber(); |
250 | if (linenumber > 0) { |
251 | linenumber--; |
252 | } |
253 | if (linenumber > 0) { |
254 | int offset = document.getLineOffset(linenumber); |
255 | String line = document.get(offset, document.getLineLength(linenumber)); |
256 | String qname = reference.getReferencedTypeName().replace('$', '.'); |
257 | int first = line.indexOf(qname); |
258 | if(first < 0) { |
259 | qname = Signatures.getSimpleTypeName(reference.getReferencedTypeName()); |
260 | qname = qname.replace('$', '.'); |
261 | first = line.indexOf(qname); |
262 | } |
263 | Position pos = null; |
264 | if(first > -1) { |
265 | pos = new Position(offset + first, qname.length()); |
266 | } |
267 | else { |
268 | //optimistically select the whole line since we can't find the correct variable name and we can't just select |
269 | //the first occurrence |
270 | pos = new Position(offset, line.length()); |
271 | } |
272 | return pos; |
273 | } else { |
274 | IApiMember apiMember = reference.getMember(); |
275 | switch(apiMember.getType()) { |
276 | case IApiElement.FIELD : { |
277 | IApiField field = (IApiField) reference.getMember(); |
278 | return getSourceRangeForField(type, reference, field); |
279 | } |
280 | case IApiElement.METHOD : { |
281 | // reference in a method declaration |
282 | IApiMethod method = (IApiMethod) reference.getMember(); |
283 | return getSourceRangeForMethod(type, reference, method); |
284 | } |
285 | } |
286 | // reference in a type declaration |
287 | ISourceRange range = type.getNameRange(); |
288 | Position pos = null; |
289 | if(range != null) { |
290 | pos = new Position(range.getOffset(), range.getLength()); |
291 | } |
292 | if(pos == null) { |
293 | return defaultSourcePosition(type, reference); |
294 | } |
295 | return pos; |
296 | } |
297 | } |
298 | case IReference.T_FIELD_REFERENCE : { |
299 | int linenumber = reference.getLineNumber(); |
300 | if (linenumber > 0) { |
301 | return getFieldNameRange(reference.getReferencedTypeName(), reference.getReferencedMemberName(), document, reference); |
302 | } |
303 | // reference in a field declaration |
304 | IApiField field = (IApiField) reference.getMember(); |
305 | return getSourceRangeForField(type, reference, field); |
306 | } |
307 | case IReference.T_METHOD_REFERENCE : { |
308 | if (reference.getLineNumber() >= 0) { |
309 | String referenceMemberName = reference.getReferencedMemberName(); |
310 | String methodName = null; |
311 | boolean isConstructor = Util.isConstructor(referenceMemberName); |
312 | if (isConstructor) { |
313 | methodName = Signatures.getSimpleTypeName(reference.getReferencedTypeName().replace('$', '.')); |
314 | } else { |
315 | methodName = referenceMemberName; |
316 | } |
317 | Position pos = getMethodNameRange(isConstructor, methodName, document, reference); |
318 | if(pos == null) { |
319 | return defaultSourcePosition(type, reference); |
320 | } |
321 | return pos; |
322 | } |
323 | // reference in a method declaration |
324 | IApiMethod method = (IApiMethod) reference.getMember(); |
325 | return getSourceRangeForMethod(type, reference, method); |
326 | } |
327 | default : |
328 | return null; |
329 | } |
330 | } |
331 | |
332 | /* (non-Javadoc) |
333 | * @see org.eclipse.pde.api.tools.internal.search.AbstractProblemDetector#isProblem(org.eclipse.pde.api.tools.internal.provisional.model.IReference) |
334 | */ |
335 | protected boolean isProblem(IReference reference) { |
336 | // the reference must be in the system library |
337 | try { |
338 | IApiMember member = reference.getMember(); |
339 | IApiComponent apiComponent = member.getApiComponent(); |
340 | String[] lowestEEs = apiComponent.getLowestEEs(); |
341 | if (lowestEEs == null) { |
342 | // this should not be true for Eclipse bundle as they should always have a EE set |
343 | return false; |
344 | } |
345 | loop: for (int i = 0, max = lowestEEs.length; i < max; i++) { |
346 | String lowestEE = lowestEEs[i]; |
347 | int eeValue = ProfileModifiers.getValue(lowestEE); |
348 | if (eeValue == ProfileModifiers.NO_PROFILE_VALUE) { |
349 | return false; |
350 | } |
351 | if (!((Reference) reference).resolve(eeValue)) { |
352 | /* |
353 | * Make sure that the resolved reference doesn't below to one of the imported package of |
354 | * the current component |
355 | */ |
356 | if (apiComponent instanceof BundleApiComponent) { |
357 | BundleDescription bundle = ((BundleApiComponent)apiComponent).getBundleDescription(); |
358 | ImportPackageSpecification[] importPackages = bundle.getImportPackages(); |
359 | String referencedTypeName = reference.getReferencedTypeName(); |
360 | int index = referencedTypeName.lastIndexOf('.'); |
361 | String packageName = referencedTypeName.substring(0, index); |
362 | for (int j = 0, max2 = importPackages.length; j < max2; j++) { |
363 | ImportPackageSpecification importPackageSpecification = importPackages[j]; |
364 | // get the IPackageDescriptor for the element descriptor |
365 | String importPackageName = importPackageSpecification.getName(); |
366 | if (importPackageName.equals(packageName)) { |
367 | continue loop; |
368 | } |
369 | } |
370 | } |
371 | if (this.referenceEEs == null) { |
372 | this.referenceEEs = new HashMap(3); |
373 | } |
374 | this.referenceEEs.put(reference, new Integer(eeValue)); |
375 | return true; |
376 | } |
377 | } |
378 | } catch (CoreException e) { |
379 | ApiPlugin.log(e); |
380 | } |
381 | return false; |
382 | } |
383 | |
384 | /* (non-Javadoc) |
385 | * @see org.eclipse.pde.api.tools.internal.provisional.builder.IApiProblemDetector#considerReference(org.eclipse.pde.api.tools.internal.provisional.builder.IReference) |
386 | */ |
387 | public boolean considerReference(IReference reference) { |
388 | try { |
389 | IApiComponent apiComponent = reference.getMember().getApiComponent(); |
390 | IApiBaseline baseline = apiComponent.getBaseline(); |
391 | if (baseline == null) return false; |
392 | String referencedTypeName = reference.getReferencedTypeName(); |
393 | // extract the package name |
394 | int index = referencedTypeName.lastIndexOf('.'); |
395 | if (index == -1) return false; |
396 | String substring = referencedTypeName.substring(0, index); |
397 | IApiComponent[] resolvePackages = baseline.resolvePackage(apiComponent, substring); |
398 | switch(resolvePackages.length) { |
399 | case 1 : { |
400 | if (resolvePackages[0].isSystemComponent()) { |
401 | switch(reference.getReferenceKind()) { |
402 | case IReference.REF_OVERRIDE : |
403 | case IReference.REF_CONSTANTPOOL : |
404 | return false; |
405 | } |
406 | ((Reference) reference).setResolveStatus(false); |
407 | retainReference(reference); |
408 | return true; |
409 | } |
410 | } |
411 | } |
412 | } catch (CoreException e) { |
413 | ApiPlugin.log(e); |
414 | } |
415 | return false; |
416 | } |
417 | |
418 | /* (non-Javadoc) |
419 | * @see org.eclipse.pde.api.tools.internal.provisional.builder.IApiProblemDetector#getReferenceKinds() |
420 | */ |
421 | public int getReferenceKinds() { |
422 | return IReference.MASK_REF_ALL & ~IReference.REF_OVERRIDE; |
423 | } |
424 | /* (non-Javadoc) |
425 | * @see org.eclipse.pde.api.tools.internal.provisional.search.IApiProblemDetector#createProblems() |
426 | */ |
427 | public List createProblems() { |
428 | List references = getRetainedReferences(); |
429 | List problems = new LinkedList(); |
430 | Iterator iterator = references.iterator(); |
431 | while (iterator.hasNext()) { |
432 | IReference reference = (IReference) iterator.next(); |
433 | if (isProblem(reference)) { |
434 | try { |
435 | IApiProblem problem = null; |
436 | IApiComponent component = reference.getMember().getApiComponent(); |
437 | if (component instanceof PluginProjectApiComponent) { |
438 | PluginProjectApiComponent ppac = (PluginProjectApiComponent) component; |
439 | IJavaProject project = ppac.getJavaProject(); |
440 | problem = createProblem(reference, project); |
441 | } else { |
442 | problem = createProblem(reference); |
443 | } |
444 | if (problem != null) { |
445 | problems.add(problem); |
446 | } |
447 | } catch (CoreException e) { |
448 | ApiPlugin.log(e.getStatus()); |
449 | } |
450 | } |
451 | } |
452 | return problems; |
453 | } |
454 | } |