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.comparator; |
12 | |
13 | import java.util.ArrayList; |
14 | import java.util.HashSet; |
15 | import java.util.Iterator; |
16 | import java.util.List; |
17 | import java.util.Set; |
18 | |
19 | import org.eclipse.core.runtime.CoreException; |
20 | import org.eclipse.core.runtime.IProgressMonitor; |
21 | import org.eclipse.core.runtime.IStatus; |
22 | import org.eclipse.core.runtime.MultiStatus; |
23 | import org.eclipse.core.runtime.Status; |
24 | import org.eclipse.core.runtime.SubMonitor; |
25 | import org.eclipse.jdt.core.Flags; |
26 | import org.eclipse.jdt.core.Signature; |
27 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
28 | import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations; |
29 | import org.eclipse.pde.api.tools.internal.provisional.IApiDescription; |
30 | import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers; |
31 | import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers; |
32 | import org.eclipse.pde.api.tools.internal.provisional.comparator.ApiComparator; |
33 | import org.eclipse.pde.api.tools.internal.provisional.comparator.IDelta; |
34 | import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor; |
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.model.IApiType; |
42 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot; |
43 | import org.eclipse.pde.api.tools.internal.util.Signatures; |
44 | import org.eclipse.pde.api.tools.internal.util.Util; |
45 | import org.objectweb.asm.signature.SignatureReader; |
46 | |
47 | import com.ibm.icu.text.MessageFormat; |
48 | |
49 | /** |
50 | * Compares class files from the workspace to those in the default {@link IApiProfile} |
51 | * |
52 | * @since 1.0.0 |
53 | */ |
54 | public class ClassFileComparator { |
55 | |
56 | /** |
57 | * Constant used for controlling tracing in the class file comparator |
58 | */ |
59 | private static boolean Debug = Util.DEBUG; |
60 | |
61 | /** |
62 | * Method used for initializing tracing in the class file comparator |
63 | */ |
64 | public static void setDebug(boolean debugValue) { |
65 | Debug = debugValue || Util.DEBUG; |
66 | } |
67 | |
68 | private boolean isCheckedException(IApiBaseline profile, IApiComponent apiComponent, String exceptionName) { |
69 | if (profile == null) { |
70 | return true; |
71 | } |
72 | try { |
73 | if (Util.isJavaLangRuntimeException(exceptionName)) { |
74 | return false; |
75 | } |
76 | String packageName = Signatures.getPackageName(exceptionName); |
77 | IApiTypeRoot result = Util.getClassFile( |
78 | profile.resolvePackage(apiComponent, packageName), |
79 | exceptionName); |
80 | if (result != null) { |
81 | // TODO should this be reported as a checked exception |
82 | IApiType exception = result.getStructure(); |
83 | if(exception == null) { |
84 | return false; |
85 | } |
86 | while (!Util.isJavaLangObject(exception.getName())) { |
87 | String superName = exception.getSuperclassName(); |
88 | packageName = Signatures.getPackageName(superName); |
89 | result = Util.getClassFile( |
90 | profile.resolvePackage(apiComponent, packageName), |
91 | superName); |
92 | if (result == null) { |
93 | // TODO should we report this failure ? |
94 | if (Debug) { |
95 | System.err.println("CHECKED EXCEPTION LOOKUP: Could not find " + superName + " in profile " + profile.getName() + " from component " + apiComponent.getId()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ |
96 | } |
97 | break; |
98 | } |
99 | exception = result.getStructure(); |
100 | if (Util.isJavaLangRuntimeException(exception.getName())) { |
101 | return false; |
102 | } |
103 | } |
104 | } |
105 | } catch (CoreException e) { |
106 | // by default exception are considered as checked exception |
107 | reportStatus(e); |
108 | } |
109 | return true; |
110 | } |
111 | |
112 | private IApiBaseline apiBaseline1 = null; |
113 | private IApiBaseline apiBaseline2 = null; |
114 | |
115 | private IApiComponent component = null; |
116 | private IApiComponent component2 = null; |
117 | |
118 | private Delta delta = null; |
119 | private IApiType type1 = null; |
120 | |
121 | private IApiType type2 = null; |
122 | |
123 | private int visibilityModifiers; |
124 | private int currentDescriptorRestrictions; |
125 | private int initialDescriptorRestrictions; |
126 | private MultiStatus status = null; |
127 | |
128 | /** |
129 | * Constructor |
130 | * @param classFile the class file from the workspace to compare |
131 | * @param classFile2 the class file from the baseline to compare to |
132 | * @param component the API component from the workspace |
133 | * @param component2 the API component from the baseline |
134 | * @param apiState the workspace API profile |
135 | * @param apiState2 the baseline API profile |
136 | * @param visibilityModifiers any modifiers from the class file |
137 | * @throws CoreException if the contents of the specified class files cannot be acquired |
138 | */ |
139 | public ClassFileComparator(IApiTypeRoot classFile, IApiTypeRoot classFile2, IApiComponent component, IApiComponent component2, IApiBaseline apiState, IApiBaseline apiState2, int visibilityModifiers) throws CoreException { |
140 | this.component = component; |
141 | this.component2 = component2; |
142 | this.type1 = classFile.getStructure(); |
143 | this.type2 = classFile2.getStructure(); |
144 | this.apiBaseline1 = apiState; |
145 | this.apiBaseline2 = apiState2; |
146 | this.visibilityModifiers = visibilityModifiers; |
147 | } |
148 | |
149 | /** |
150 | * Constructor |
151 | * @param type the {@link IApiType} from the workspace to compare |
152 | * @param classFile2 the class file from the baseline to compare to |
153 | * @param component the API component from the workspace |
154 | * @param component2 the API component from the baseline |
155 | * @param apiState the workspace API profile |
156 | * @param apiState2 the baseline API profile |
157 | * @param visibilityModifiers any modifiers from the class file |
158 | * @throws CoreException if the contents of the specified class file cannot be acquired |
159 | */ |
160 | public ClassFileComparator(IApiType type, IApiTypeRoot classFile2, IApiComponent component, IApiComponent component2, IApiBaseline apiState, IApiBaseline apiState2, int visibilityModifiers) throws CoreException { |
161 | this.component = component; |
162 | this.component2 = component2; |
163 | this.type1 = type; |
164 | this.type2 = classFile2.getStructure(); |
165 | this.apiBaseline1 = apiState; |
166 | this.apiBaseline2 = apiState2; |
167 | this.visibilityModifiers = visibilityModifiers; |
168 | } |
169 | private void addDelta(IDelta delta) { |
170 | this.delta.add(delta); |
171 | } |
172 | private void addDelta(int elementType, int kind, int flags, int restrictions, int oldModifiers, int newModifiers, IApiType type, String key, String data) { |
173 | this.addDelta(new Delta(Util.getDeltaComponentVersionsId(this.component2), elementType, kind, flags, restrictions, oldModifiers, newModifiers, type.getName(), key, data)); |
174 | } |
175 | private void addDelta(int elementType, int kind, int flags, int restrictions, int oldModifiers, int newModifiers, IApiType type, String key, String[] datas) { |
176 | this.addDelta(new Delta(Util.getDeltaComponentVersionsId(this.component2), elementType, kind, flags, restrictions, oldModifiers, newModifiers, type.getName(), key, datas)); |
177 | } |
178 | /** |
179 | * Checks if the super-class set has been change in any way compared to the baseline (grown or reduced or types changed) |
180 | */ |
181 | private void checkSuperclass() throws CoreException { |
182 | // check superclass set |
183 | List superclassList1 = getSuperclassList(this.type1); |
184 | if(!isStatusOk()) { |
185 | return; |
186 | } |
187 | Set superclassNames2 = null; |
188 | List superclassList2 = getSuperclassList(this.type2); |
189 | if(!isStatusOk()) { |
190 | return; |
191 | } |
192 | if (superclassList2 != null) { |
193 | superclassNames2 = new HashSet(); |
194 | Iterator iterator = superclassList2.iterator(); |
195 | while (iterator.hasNext()) { |
196 | superclassNames2.add(((IApiType) iterator.next()).getName()); |
197 | } |
198 | } |
199 | if (superclassList1 == null) { |
200 | if (superclassList2 != null) { |
201 | // this means the direct super class of descriptor1 is java.lang.Object |
202 | this.addDelta( |
203 | getElementType(this.type1), |
204 | IDelta.ADDED, |
205 | IDelta.SUPERCLASS, |
206 | this.currentDescriptorRestrictions, |
207 | this.type1.getModifiers(), |
208 | this.type2.getModifiers(), |
209 | this.type1, |
210 | this.type1.getName(), |
211 | Util.getDescriptorName(type1)); |
212 | } |
213 | } else if (superclassList2 == null) { |
214 | // this means the direct super class of descriptor2 is java.lang.Object |
215 | this.addDelta( |
216 | getElementType(this.type1), |
217 | IDelta.REMOVED, |
218 | IDelta.SUPERCLASS, |
219 | this.currentDescriptorRestrictions, |
220 | this.type1.getModifiers(), |
221 | this.type2.getModifiers(), |
222 | this.type1, |
223 | this.type1.getName(), |
224 | Util.getDescriptorName(type1)); |
225 | } |
226 | // get superclass of descriptor2 |
227 | if (superclassList1 != null && superclassList2 != null) { |
228 | IApiType superclassType2 = (IApiType) superclassList2.get(0); |
229 | IApiType superclassType = (IApiType) superclassList1.get(0); |
230 | if (!superclassType.getName().equals(superclassType2.getName())) { |
231 | if (!superclassNames2.contains(superclassType.getName())) { |
232 | this.addDelta( |
233 | getElementType(this.type1), |
234 | IDelta.REMOVED, |
235 | IDelta.SUPERCLASS, |
236 | this.currentDescriptorRestrictions, |
237 | this.type1.getModifiers(), |
238 | this.type2.getModifiers(), |
239 | this.type1, |
240 | this.type1.getName(), |
241 | Util.getDescriptorName(type1)); |
242 | } else { |
243 | this.addDelta( |
244 | getElementType(this.type1), |
245 | IDelta.ADDED, |
246 | IDelta.SUPERCLASS, |
247 | this.currentDescriptorRestrictions, |
248 | this.type1.getModifiers(), |
249 | this.type2.getModifiers(), |
250 | this.type1, |
251 | this.type1.getName(), |
252 | Util.getDescriptorName(type1)); |
253 | } |
254 | } |
255 | } |
256 | } |
257 | /** |
258 | * reports problem status to the comparators' complete status |
259 | * @param newstatus |
260 | */ |
261 | protected void reportStatus(IStatus newstatus) { |
262 | if(this.status == null) { |
263 | String msg = MessageFormat.format(ComparatorMessages.ClassFileComparator_0, new String[] {this.type1.getName()}); |
264 | this.status = new MultiStatus(ApiPlugin.PLUGIN_ID, IStatus.ERROR, msg, null); |
265 | } |
266 | this.status.add(newstatus); |
267 | } |
268 | |
269 | /** |
270 | * Report problem to the comparators' status |
271 | * @param e |
272 | */ |
273 | private void reportStatus(CoreException e) { |
274 | reportStatus(e.getStatus()); |
275 | } |
276 | |
277 | /** |
278 | * @return if the status of the compare is ok |
279 | */ |
280 | private boolean isStatusOk() { |
281 | return this.status == null; |
282 | } |
283 | |
284 | /** |
285 | * @return the status of the compare and delta creation |
286 | */ |
287 | public IStatus getStatus() { |
288 | return this.status; |
289 | } |
290 | /** |
291 | * Checks if there are any changes to the super-interface set for the current type descriptor context. |
292 | * A change is one of: |
293 | * <ul> |
294 | * <li>An interface has been added to the current super-interface set compared to the current baseline</li> |
295 | * <li>An interface has been removed from the current super-interface set compared to the current baseline</li> |
296 | * <li>An interface has changed (same number of interfaces, but different types) compared to the current baseline</li> |
297 | * </ul> |
298 | */ |
299 | private void checkSuperInterfaces() { |
300 | Set superinterfacesSet1 = getInterfacesSet(this.type1); |
301 | if(!isStatusOk()) { |
302 | return; |
303 | } |
304 | Set superinterfacesSet2 = getInterfacesSet(this.type2); |
305 | if(!isStatusOk()) { |
306 | return; |
307 | } |
308 | if (superinterfacesSet1 == null) { |
309 | if (superinterfacesSet2 != null) { |
310 | this.addDelta( |
311 | getElementType(this.type1), |
312 | IDelta.CHANGED, |
313 | IDelta.EXPANDED_SUPERINTERFACES_SET, |
314 | this.currentDescriptorRestrictions, |
315 | this.type1.getModifiers(), |
316 | this.type2.getModifiers(), |
317 | this.type1, |
318 | this.type1.getName(), |
319 | Util.getDescriptorName(type1)); |
320 | if (this.type1.isInterface()) { |
321 | for (Iterator iterator = superinterfacesSet2.iterator(); iterator.hasNext();) { |
322 | IApiType type = (IApiType)iterator.next(); |
323 | IApiMethod[] methods = type.getMethods(); |
324 | int length = methods.length; |
325 | if (length != 0) { |
326 | // we should check if every method defined in the new interface exists in the old hierarchy |
327 | // could be methods moved up in the hierarchy |
328 | methodLoop: for (int j = 0; j < length; j++) { |
329 | IApiMethod method = methods[j]; |
330 | IApiMethod method3 = this.type1.getMethod(method.getName(), method.getSignature()); |
331 | if (method3 == null) { |
332 | this.addDelta( |
333 | getElementType(this.type1), |
334 | IDelta.ADDED, |
335 | IDelta.SUPER_INTERFACE_WITH_METHODS, |
336 | this.currentDescriptorRestrictions, |
337 | this.type1.getModifiers(), |
338 | this.type2.getModifiers(), |
339 | this.type1, |
340 | this.type1.getName(), |
341 | new String[] { |
342 | Util.getDescriptorName(type1), |
343 | type.getName(), |
344 | getMethodDisplayName(method, type) |
345 | } |
346 | ); |
347 | break methodLoop; |
348 | } |
349 | } |
350 | } |
351 | } |
352 | } |
353 | } |
354 | } else if (superinterfacesSet2 == null) { |
355 | this.addDelta( |
356 | getElementType(this.type1), |
357 | IDelta.CHANGED, |
358 | IDelta.CONTRACTED_SUPERINTERFACES_SET, |
359 | this.currentDescriptorRestrictions, |
360 | this.type1.getModifiers(), |
361 | this.type2.getModifiers(), |
362 | this.type1, |
363 | this.type1.getName(), |
364 | Util.getDescriptorName(type1)); |
365 | } else { |
366 | Set names2 = new HashSet(); |
367 | for (Iterator iterator = superinterfacesSet2.iterator(); iterator.hasNext();) { |
368 | names2.add(((IApiType)iterator.next()).getName()); |
369 | } |
370 | boolean contracted = false; |
371 | for (Iterator iterator = superinterfacesSet1.iterator(); iterator.hasNext();) { |
372 | IApiType superInterfaceType = (IApiType) iterator.next(); |
373 | if (!names2.remove(superInterfaceType.getName())) { |
374 | contracted = true; |
375 | } |
376 | } |
377 | if (contracted) { |
378 | this.addDelta( |
379 | getElementType(this.type1), |
380 | IDelta.CHANGED, |
381 | IDelta.CONTRACTED_SUPERINTERFACES_SET, |
382 | this.currentDescriptorRestrictions, |
383 | this.type1.getModifiers(), |
384 | this.type2.getModifiers(), |
385 | this.type1, |
386 | this.type1.getName(), |
387 | Util.getDescriptorName(type1)); |
388 | return; |
389 | } |
390 | if (names2.size() > 0) { |
391 | this.addDelta( |
392 | getElementType(this.type1), |
393 | IDelta.CHANGED, |
394 | IDelta.EXPANDED_SUPERINTERFACES_SET, |
395 | this.currentDescriptorRestrictions, |
396 | this.type1.getModifiers(), |
397 | this.type2.getModifiers(), |
398 | this.type1, |
399 | this.type1.getName(), |
400 | Util.getDescriptorName(type1)); |
401 | if (this.type1.isInterface()) { |
402 | for (Iterator iterator = names2.iterator(); iterator.hasNext();) { |
403 | String interfaceName = (String) iterator.next(); |
404 | try { |
405 | IApiTypeRoot interfaceClassFile = getType(interfaceName, this.component2, this.apiBaseline2); |
406 | if (interfaceClassFile == null) continue; |
407 | IApiType type = interfaceClassFile.getStructure(); |
408 | if(type == null) { |
409 | continue; |
410 | } |
411 | IApiMethod[] methods = type.getMethods(); |
412 | int length = methods.length; |
413 | if (length > 0) { |
414 | // we should check if every method defined in the new interface exists in the old hierarchy |
415 | // could be methods moved up in the hierarchy |
416 | methodLoop: for (int j = 0; j < length; j++) { |
417 | IApiMethod method = methods[j]; |
418 | boolean found = false; |
419 | interfaceLoop: for (Iterator iterator2 = superinterfacesSet1.iterator(); iterator2.hasNext();) { |
420 | IApiType superTypeDescriptor = (IApiType) iterator2.next(); |
421 | IApiMethod method3 = superTypeDescriptor.getMethod(method.getName(), method.getSignature()); |
422 | if (method3 == null) { |
423 | continue interfaceLoop; |
424 | } else { |
425 | found = true; |
426 | break interfaceLoop; |
427 | } |
428 | } |
429 | if (!found) { |
430 | this.addDelta( |
431 | getElementType(this.type1), |
432 | IDelta.ADDED, |
433 | IDelta.SUPER_INTERFACE_WITH_METHODS, |
434 | this.currentDescriptorRestrictions, |
435 | this.type1.getModifiers(), |
436 | this.type2.getModifiers(), |
437 | this.type1, |
438 | this.type1.getName(), |
439 | new String[] { |
440 | Util.getDescriptorName(type1), |
441 | type.getName(), |
442 | getMethodDisplayName(method, type) |
443 | } |
444 | ); |
445 | break methodLoop; |
446 | } |
447 | } |
448 | } |
449 | } catch (CoreException e) { |
450 | ApiPlugin.log(e); |
451 | } |
452 | } |
453 | } |
454 | } |
455 | } |
456 | } |
457 | |
458 | private void checkTypeMembers() throws CoreException { |
459 | IApiType[] typeMembers = this.type1.getMemberTypes(); |
460 | IApiType[] typeMembers2 = this.type2.getMemberTypes(); |
461 | List added = new ArrayList(typeMembers2.length); |
462 | for (int i = 0; i < typeMembers2.length; i++) { |
463 | added.add(typeMembers2[i].getName()); |
464 | } |
465 | if (typeMembers.length > 0) { |
466 | if (typeMembers2.length == 0) { |
467 | loop: for (int i = 0; i < typeMembers.length; i++) { |
468 | try { |
469 | IApiType typeMember = typeMembers[i]; |
470 | // check visibility |
471 | IApiDescription apiDescription = this.component.getApiDescription(); |
472 | IApiAnnotations memberTypeElementDescription = apiDescription.resolveAnnotations(typeMember.getHandle()); |
473 | int memberTypeVisibility = 0; |
474 | if (memberTypeElementDescription != null) { |
475 | memberTypeVisibility = memberTypeElementDescription.getVisibility(); |
476 | } |
477 | if ((memberTypeVisibility & visibilityModifiers) == 0) { |
478 | // we skip the class file according to their visibility |
479 | continue loop; |
480 | } |
481 | if (visibilityModifiers == VisibilityModifiers.API) { |
482 | // if the visibility is API, we only consider public and protected types |
483 | if (Util.isDefault(typeMember.getModifiers()) |
484 | || Flags.isPrivate(typeMember.getModifiers())) { |
485 | continue loop; |
486 | } |
487 | } |
488 | this.addDelta( |
489 | getElementType(this.type1), |
490 | IDelta.REMOVED, |
491 | IDelta.TYPE_MEMBER, |
492 | this.currentDescriptorRestrictions, |
493 | typeMember.getModifiers(), |
494 | 0, |
495 | this.type1, |
496 | typeMember.getName(), |
497 | new String[] { typeMember.getName().replace('$', '.'), Util.getComponentVersionsId(component2)}); |
498 | } catch (CoreException e) { |
499 | reportStatus(e); |
500 | } |
501 | } |
502 | return; |
503 | } |
504 | // check removed or added type members |
505 | List removedTypeMembers = new ArrayList(); |
506 | loop: for (int i = 0; i < typeMembers.length; i++) { |
507 | IApiType typeMember = typeMembers[i]; |
508 | IApiType typeMember2 = this.type2.getMemberType(typeMember.getSimpleName()); |
509 | if (typeMember2 == null) { |
510 | removedTypeMembers.add(typeMember); |
511 | } else { |
512 | added.remove(typeMember2.getName()); |
513 | // check deltas inside the type member |
514 | try { |
515 | // check visibility of member types |
516 | IApiDescription apiDescription = this.component.getApiDescription(); |
517 | IApiAnnotations memberTypeElementDescription = apiDescription.resolveAnnotations(typeMember.getHandle()); |
518 | int memberTypeVisibility = 0; |
519 | if (memberTypeElementDescription != null) { |
520 | memberTypeVisibility = memberTypeElementDescription.getVisibility(); |
521 | } |
522 | if ((memberTypeVisibility & visibilityModifiers) == 0) { |
523 | // we skip the class file according to their visibility |
524 | continue loop; |
525 | } |
526 | IApiDescription apiDescription2 = this.component2.getApiDescription(); |
527 | IApiAnnotations memberTypeElementDescription2 = apiDescription2.resolveAnnotations(typeMember2.getHandle()); |
528 | int memberTypeVisibility2 = 0; |
529 | if (memberTypeElementDescription2 != null) { |
530 | memberTypeVisibility2 = memberTypeElementDescription2.getVisibility(); |
531 | } |
532 | String deltaComponentID = Util.getDeltaComponentVersionsId(component2); |
533 | int restrictions = memberTypeElementDescription2 != null ? memberTypeElementDescription2.getRestrictions() : RestrictionModifiers.NO_RESTRICTIONS; |
534 | if (Flags.isFinal(this.type2.getModifiers())) { |
535 | restrictions |= RestrictionModifiers.NO_EXTEND; |
536 | } |
537 | if (isAPI(memberTypeVisibility, typeMember) && !isAPI(memberTypeVisibility2, typeMember2)) { |
538 | this.addDelta( |
539 | new Delta( |
540 | deltaComponentID, |
541 | getElementType(typeMember), |
542 | IDelta.CHANGED, |
543 | IDelta.DECREASE_ACCESS, |
544 | restrictions | this.currentDescriptorRestrictions, |
545 | typeMember.getModifiers(), |
546 | typeMember2.getModifiers(), |
547 | typeMember.getName(), |
548 | typeMember.getName(), |
549 | new String[] { typeMember.getName().replace('$', '.') })); |
550 | continue; |
551 | } |
552 | if ((memberTypeVisibility2 & visibilityModifiers) == 0) { |
553 | // we simply report a changed visibility |
554 | this.addDelta( |
555 | new Delta( |
556 | deltaComponentID, |
557 | getElementType(typeMember), |
558 | IDelta.CHANGED, |
559 | IDelta.TYPE_VISIBILITY, |
560 | restrictions | this.currentDescriptorRestrictions, |
561 | typeMember.getModifiers(), |
562 | typeMember2.getModifiers(), |
563 | typeMember.getName(), |
564 | typeMember.getName(), |
565 | new String[] { typeMember.getName().replace('$', '.') })); |
566 | } |
567 | if (this.visibilityModifiers == VisibilityModifiers.API) { |
568 | // if the visibility is API, we only consider public and protected types |
569 | if (Util.isDefault(typeMember2.getModifiers()) |
570 | || Flags.isPrivate(typeMember2.getModifiers())) { |
571 | continue loop; |
572 | } |
573 | } |
574 | IApiTypeRoot memberType2 = this.component2.findTypeRoot(typeMember.getName()); |
575 | ClassFileComparator comparator = new ClassFileComparator(typeMember, memberType2, this.component, this.component2, this.apiBaseline1, this.apiBaseline2, this.visibilityModifiers); |
576 | IDelta delta2 = comparator.getDelta(null); |
577 | if (delta2 != null && delta2 != ApiComparator.NO_DELTA) { |
578 | this.addDelta(delta2); |
579 | } |
580 | } catch (CoreException e) { |
581 | reportStatus(e); |
582 | } |
583 | } |
584 | } |
585 | loop: for (Iterator iterator = removedTypeMembers.iterator(); iterator.hasNext();) { |
586 | try { |
587 | IApiType typeMember = (IApiType) iterator.next(); |
588 | // check visibility |
589 | IApiDescription apiDescription = this.component.getApiDescription(); |
590 | IApiAnnotations memberTypeElementDescription = apiDescription.resolveAnnotations(typeMember.getHandle()); |
591 | int memberTypeVisibility = 0; |
592 | if (memberTypeElementDescription != null) { |
593 | memberTypeVisibility = memberTypeElementDescription.getVisibility(); |
594 | } |
595 | if ((memberTypeVisibility & visibilityModifiers) == 0) { |
596 | // we skip the class file according to their visibility |
597 | continue loop; |
598 | } |
599 | if (this.visibilityModifiers == VisibilityModifiers.API) { |
600 | // if the visibility is API, we only consider public and protected types |
601 | if (Util.isDefault(typeMember.getModifiers()) |
602 | || Flags.isPrivate(typeMember.getModifiers())) { |
603 | continue loop; |
604 | } |
605 | } |
606 | this.addDelta( |
607 | getElementType(this.type1), |
608 | IDelta.REMOVED, |
609 | IDelta.TYPE_MEMBER, |
610 | memberTypeElementDescription != null ? memberTypeElementDescription.getRestrictions() : RestrictionModifiers.NO_RESTRICTIONS, |
611 | typeMember.getModifiers(), |
612 | 0, |
613 | this.type1, |
614 | typeMember.getName(), |
615 | new String[] { typeMember.getName().replace('$', '.'), Util.getComponentVersionsId(component2)}); |
616 | } catch (CoreException e) { |
617 | reportStatus(e); |
618 | } |
619 | } |
620 | } |
621 | // report remaining types as addition |
622 | // Report delta as a breakage |
623 | loop: for (Iterator iterator = added.iterator(); iterator.hasNext();) { |
624 | try { |
625 | String name = (String) iterator.next(); |
626 | int index = name.lastIndexOf('$'); |
627 | IApiType typeMember = this.type2.getMemberType(name.substring(index+1)); |
628 | // check visibility |
629 | IApiDescription apiDescription2 = this.component2.getApiDescription(); |
630 | IApiAnnotations memberTypeElementDescription2 = apiDescription2.resolveAnnotations(typeMember.getHandle()); |
631 | int memberTypeVisibility2 = 0; |
632 | if (memberTypeElementDescription2 != null) { |
633 | memberTypeVisibility2 = memberTypeElementDescription2.getVisibility(); |
634 | } |
635 | if ((memberTypeVisibility2 & visibilityModifiers) == 0) { |
636 | // we skip the class file according to their visibility |
637 | continue loop; |
638 | } |
639 | if (this.visibilityModifiers == VisibilityModifiers.API) { |
640 | // if the visibility is API, we only consider public and protected types |
641 | if (Util.isDefault(typeMember.getModifiers()) |
642 | || Flags.isPrivate(typeMember.getModifiers())) { |
643 | continue loop; |
644 | } |
645 | } |
646 | this.addDelta( |
647 | getElementType(this.type1), |
648 | IDelta.ADDED, |
649 | IDelta.TYPE_MEMBER, |
650 | this.currentDescriptorRestrictions, |
651 | 0, |
652 | typeMember.getModifiers(), |
653 | this.type1, |
654 | typeMember.getSimpleName(), |
655 | typeMember.getSimpleName()); |
656 | } catch (CoreException e) { |
657 | reportStatus(e); |
658 | } |
659 | } |
660 | } |
661 | |
662 | private void checkGenericSignature(String signature1, String signature2, IApiMember element1, IApiMember element2) { |
663 | if (signature1 == null) { |
664 | if (signature2 != null) { |
665 | // added type parameter from scratch (none before) |
666 | // report delta as compatible |
667 | SignatureDescriptor signatureDescriptor2 = getSignatureDescriptor(signature2); |
668 | TypeParameterDescriptor[] typeParameterDescriptors = signatureDescriptor2.getTypeParameterDescriptors(); |
669 | if (typeParameterDescriptors.length != 0) { |
670 | this.addDelta( |
671 | getElementType(element1), |
672 | IDelta.ADDED, |
673 | IDelta.TYPE_PARAMETERS, |
674 | this.currentDescriptorRestrictions, |
675 | element1.getModifiers(), |
676 | element2.getModifiers(), |
677 | this.type1, |
678 | element1.getName(), |
679 | new String[] {getDataFor(element1, this.type1)}); |
680 | } else if (signatureDescriptor2.getTypeArguments().length != 0) { |
681 | this.addDelta( |
682 | getElementType(element1), |
683 | IDelta.ADDED, |
684 | IDelta.TYPE_ARGUMENTS, |
685 | this.currentDescriptorRestrictions, |
686 | element1.getModifiers(), |
687 | element2.getModifiers(), |
688 | this.type1, |
689 | element1.getName(), |
690 | new String[] {getDataFor(element1, this.type1)}); |
691 | } |
692 | } |
693 | } else if (signature2 == null) { |
694 | // removed type parameters |
695 | SignatureDescriptor signatureDescriptor = getSignatureDescriptor(signature1); |
696 | TypeParameterDescriptor[] typeParameterDescriptors = signatureDescriptor.getTypeParameterDescriptors(); |
697 | int length = typeParameterDescriptors.length; |
698 | if (length != 0) { |
699 | for (int i = 0; i < length; i++) { |
700 | TypeParameterDescriptor typeParameterDescriptor = typeParameterDescriptors[i]; |
701 | this.addDelta( |
702 | getElementType(element1), |
703 | IDelta.REMOVED, |
704 | IDelta.TYPE_PARAMETER, |
705 | this.currentDescriptorRestrictions, |
706 | element1.getModifiers(), |
707 | element2.getModifiers(), |
708 | this.type1, |
709 | element1.getName(), |
710 | new String[] {getDataFor(element1, type1), typeParameterDescriptor.name}); |
711 | } |
712 | } else { |
713 | String[] typeArguments = signatureDescriptor.getTypeArguments(); |
714 | length = typeArguments.length; |
715 | if (length != 0) { |
716 | for (int i = 0; i < length; i++) { |
717 | String typeArgument = typeArguments[i]; |
718 | this.addDelta( |
719 | getElementType(element1), |
720 | IDelta.REMOVED, |
721 | IDelta.TYPE_ARGUMENT, |
722 | this.currentDescriptorRestrictions, |
723 | element1.getModifiers(), |
724 | element2.getModifiers(), |
725 | this.type1, |
726 | element1.getName(), |
727 | new String[] {getDataFor(element1, type1), typeArgument}); |
728 | } |
729 | } |
730 | } |
731 | } else { |
732 | // both types have generic signature |
733 | // need to check delta for type parameter one by one |
734 | SignatureDescriptor signatureDescriptor = getSignatureDescriptor(signature1); |
735 | SignatureDescriptor signatureDescriptor2 = getSignatureDescriptor(signature2); |
736 | |
737 | TypeParameterDescriptor[] typeParameterDescriptors1 = signatureDescriptor.getTypeParameterDescriptors(); |
738 | TypeParameterDescriptor[] typeParameterDescriptors2 = signatureDescriptor2.getTypeParameterDescriptors(); |
739 | int length = typeParameterDescriptors1.length; |
740 | int length2 = typeParameterDescriptors2.length; |
741 | int min = length; |
742 | int max = length2; |
743 | if (length > length2) { |
744 | min = length2; |
745 | max = length; |
746 | } |
747 | int i = 0; |
748 | for (;i < min; i++) { |
749 | TypeParameterDescriptor parameterDescriptor1 = typeParameterDescriptors1[i]; |
750 | TypeParameterDescriptor parameterDescriptor2 = typeParameterDescriptors2[i]; |
751 | String name = parameterDescriptor1.name; |
752 | if (!name.equals(parameterDescriptor2.name)) { |
753 | this.addDelta( |
754 | IDelta.TYPE_PARAMETER_ELEMENT_TYPE, |
755 | IDelta.CHANGED, |
756 | IDelta.TYPE_PARAMETER_NAME, |
757 | this.currentDescriptorRestrictions, |
758 | element1.getModifiers(), |
759 | element2.getModifiers(), |
760 | this.type1, |
761 | name, |
762 | new String[] {getDataFor(element1, type1), name }); |
763 | } |
764 | if (parameterDescriptor1.classBound == null) { |
765 | if (parameterDescriptor2.classBound != null) { |
766 | // report delta added class bound of a type parameter |
767 | this.addDelta( |
768 | IDelta.TYPE_PARAMETER_ELEMENT_TYPE, |
769 | IDelta.ADDED, |
770 | IDelta.CLASS_BOUND, |
771 | this.currentDescriptorRestrictions, |
772 | element1.getModifiers(), |
773 | element2.getModifiers(), |
774 | this.type1, |
775 | name, |
776 | new String[] {getDataFor(element1, type1), name }); |
777 | } |
778 | } else if (parameterDescriptor2.classBound == null) { |
779 | // report delta removed class bound of a type parameter |
780 | this.addDelta( |
781 | IDelta.TYPE_PARAMETER_ELEMENT_TYPE, |
782 | IDelta.REMOVED, |
783 | IDelta.CLASS_BOUND, |
784 | this.currentDescriptorRestrictions, |
785 | element1.getModifiers(), |
786 | element2.getModifiers(), |
787 | this.type1, |
788 | name, |
789 | new String[] {getDataFor(element1, type1), name}); |
790 | } else if (!parameterDescriptor1.classBound.equals(parameterDescriptor2.classBound)) { |
791 | // report delta changed class bound of a type parameter |
792 | this.addDelta( |
793 | IDelta.TYPE_PARAMETER_ELEMENT_TYPE, |
794 | IDelta.CHANGED, |
795 | IDelta.CLASS_BOUND, |
796 | this.currentDescriptorRestrictions, |
797 | element1.getModifiers(), |
798 | element2.getModifiers(), |
799 | this.type1, |
800 | name, |
801 | new String[] {getDataFor(element1, type1), name}); |
802 | } |
803 | List interfaceBounds1 = parameterDescriptor1.interfaceBounds; |
804 | List interfaceBounds2 = parameterDescriptor2.interfaceBounds; |
805 | if (interfaceBounds1 == null) { |
806 | if (interfaceBounds2 != null) { |
807 | for (Iterator iterator = interfaceBounds2.iterator(); iterator.hasNext(); ) { |
808 | // report delta added interface bounds |
809 | this.addDelta( |
810 | IDelta.TYPE_PARAMETER_ELEMENT_TYPE, |
811 | IDelta.ADDED, |
812 | IDelta.INTERFACE_BOUND, |
813 | this.currentDescriptorRestrictions, |
814 | element1.getModifiers(), |
815 | element2.getModifiers(), |
816 | this.type1, |
817 | name, |
818 | new String[] {getDataFor(element1, type1), name, (String) iterator.next()}); |
819 | } |
820 | } |
821 | } else if (interfaceBounds2 == null) { |
822 | // report delta removed interface bounds |
823 | for (Iterator iterator = interfaceBounds1.iterator(); iterator.hasNext(); ) { |
824 | // report delta added interface bounds |
825 | this.addDelta( |
826 | IDelta.TYPE_PARAMETER_ELEMENT_TYPE, |
827 | IDelta.REMOVED, |
828 | IDelta.INTERFACE_BOUND, |
829 | this.currentDescriptorRestrictions, |
830 | element1.getModifiers(), |
831 | element2.getModifiers(), |
832 | this.type1, |
833 | name, |
834 | new String[] {getDataFor(element1, type1), name, (String) iterator.next()}); |
835 | } |
836 | } else { |
837 | int size1 = interfaceBounds1.size(); |
838 | int size2 = interfaceBounds2.size(); |
839 | int boundsMin = size1; |
840 | int boundsMax = size2; |
841 | if (size1 > size2) { |
842 | boundsMin = size2; |
843 | boundsMax = size1; |
844 | } |
845 | int index = 0; |
846 | for (;index < boundsMin; index++) { |
847 | String currentInterfaceBound = (String) interfaceBounds1.get(index); |
848 | if (!currentInterfaceBound.equals(interfaceBounds2.get(index))) { |
849 | // report delta: different interface bounds (or reordered interface bound) |
850 | this.addDelta( |
851 | IDelta.TYPE_PARAMETER_ELEMENT_TYPE, |
852 | IDelta.CHANGED, |
853 | IDelta.INTERFACE_BOUND, |
854 | this.currentDescriptorRestrictions, |
855 | element1.getModifiers(), |
856 | element2.getModifiers(), |
857 | this.type1, |
858 | name, |
859 | new String[] {getDataFor(element1, type1), name, currentInterfaceBound}); |
860 | } |
861 | } |
862 | if (boundsMin != boundsMax) { |
863 | // if max = length2 => addition of type parameter descriptor |
864 | // if max = length => removal of type parameter descriptor |
865 | boolean added = boundsMax == size2; |
866 | for (; index < boundsMax; index++) { |
867 | String currentInterfaceBound = added ? (String) interfaceBounds2.get(index) : (String) interfaceBounds1.get(index); |
868 | this.addDelta( |
869 | IDelta.TYPE_PARAMETER_ELEMENT_TYPE, |
870 | added ? IDelta.ADDED : IDelta.REMOVED, |
871 | IDelta.INTERFACE_BOUND, |
872 | this.currentDescriptorRestrictions, |
873 | element1.getModifiers(), |
874 | element2.getModifiers(), |
875 | this.type1, |
876 | element1.getName(), |
877 | new String[] {getDataFor(element1, type1), name, currentInterfaceBound}); |
878 | } |
879 | } |
880 | } |
881 | } |
882 | if (min != max) { |
883 | // if max = length2 => addition of type parameter descriptor |
884 | // if max = length => removal of type parameter descriptor |
885 | boolean added = max == length2; |
886 | for (; i < max; i++) { |
887 | TypeParameterDescriptor currentTypeParameter = added ? typeParameterDescriptors2[i] : typeParameterDescriptors1[i]; |
888 | this.addDelta( |
889 | getElementType(element1), |
890 | added ? IDelta.ADDED : IDelta.REMOVED, |
891 | IDelta.TYPE_PARAMETER, |
892 | this.currentDescriptorRestrictions, |
893 | element1.getModifiers(), |
894 | element2.getModifiers(), |
895 | this.type1, |
896 | element1.getName(), |
897 | new String[] {getDataFor(element1, type1), currentTypeParameter.name}); |
898 | } |
899 | } |
900 | if (length2 > 0 || length > 0) return; |
901 | String[] typeArguments = signatureDescriptor.getTypeArguments(); |
902 | String[] typeArguments2 = signatureDescriptor2.getTypeArguments(); |
903 | length = typeArguments.length; |
904 | length2 = typeArguments2.length; |
905 | min = length; |
906 | max = length2; |
907 | if (length > length2) { |
908 | min = length2; |
909 | max = length; |
910 | } |
911 | i = 0; |
912 | for (;i < min; i++) { |
913 | String currentTypeArgument = typeArguments[i]; |
914 | String newTypeArgument = typeArguments2[i]; |
915 | if (!currentTypeArgument.equals(newTypeArgument)) { |
916 | this.addDelta( |
917 | getElementType(element1), |
918 | IDelta.CHANGED, |
919 | IDelta.TYPE_ARGUMENT, |
920 | this.currentDescriptorRestrictions, |
921 | element1.getModifiers(), |
922 | element2.getModifiers(), |
923 | this.type1, |
924 | element1.getName(), |
925 | new String[] {getDataFor(element1, type1), currentTypeArgument, newTypeArgument}); |
926 | } |
927 | } |
928 | if (min != max) { |
929 | // if max = length2 => addition of type arguments |
930 | // if max = length => removal of type arguments |
931 | boolean added = max == length2; |
932 | for (; i < max; i++) { |
933 | String currentTypeArgument = added ? typeArguments2[i] : typeArguments[i]; |
934 | this.addDelta( |
935 | getElementType(element1), |
936 | added ? IDelta.ADDED : IDelta.REMOVED, |
937 | IDelta.TYPE_ARGUMENT, |
938 | this.currentDescriptorRestrictions, |
939 | element1.getModifiers(), |
940 | element2.getModifiers(), |
941 | this.type1, |
942 | element1.getName(), |
943 | new String[] {getDataFor(element1, type1), currentTypeArgument}); |
944 | } |
945 | } |
946 | } |
947 | } |
948 | |
949 | /** |
950 | * Recursively collects all of the super-interfaces of the given type descriptor within the scope of |
951 | * the given API component |
952 | * @param type |
953 | * @param apiComponent |
954 | * @param profile |
955 | * @param set |
956 | */ |
957 | private void collectAllInterfaces(IApiType type, Set set) { |
958 | try { |
959 | IApiType[] interfaces = type.getSuperInterfaces(); |
960 | if (interfaces != null) { |
961 | for (int i = 0; i < interfaces.length; i++) { |
962 | IApiType anInterface = interfaces[i]; |
963 | int visibility = VisibilityModifiers.PRIVATE; |
964 | IApiComponent ifaceComponent = anInterface.getApiComponent(); |
965 | IApiDescription apiDescription = ifaceComponent.getApiDescription(); |
966 | IApiAnnotations elementDescription = apiDescription.resolveAnnotations(anInterface.getHandle()); |
967 | if (elementDescription != null) { |
968 | visibility = elementDescription.getVisibility(); |
969 | } |
970 | if ((visibility & visibilityModifiers) != 0) { |
971 | set.add(anInterface); |
972 | } |
973 | collectAllInterfaces(anInterface, set); |
974 | } |
975 | } |
976 | String superclassName = type.getSuperclassName(); |
977 | if (superclassName != null && !Util.isJavaLangObject(superclassName)) { |
978 | IApiType superclass = type.getSuperclass(); |
979 | collectAllInterfaces(superclass, set); |
980 | } |
981 | } |
982 | catch(CoreException e) { |
983 | reportStatus(e); |
984 | } |
985 | } |
986 | |
987 | private String getDataFor(IApiMember member, IApiType type) { |
988 | switch(member.getType()) { |
989 | case IApiElement.TYPE: |
990 | if (((IApiType)member).isMemberType()) { |
991 | return member.getName().replace('$', '.'); |
992 | } |
993 | return member.getName(); |
994 | case IApiElement.METHOD: |
995 | StringBuffer buffer = new StringBuffer(); |
996 | buffer.append(type.getName()).append('.').append(getMethodDisplayName((IApiMethod) member, type)); |
997 | return String.valueOf(buffer); |
998 | case IApiElement.FIELD: |
999 | buffer = new StringBuffer(); |
1000 | buffer.append(type.getName()).append('.').append(member.getName()); |
1001 | return String.valueOf(buffer); |
1002 | } |
1003 | return null; |
1004 | } |
1005 | |
1006 | /** |
1007 | * Returns a new {@link Delta} to use, and resets the status of creating a delta |
1008 | * @return |
1009 | */ |
1010 | private Delta createDelta() { |
1011 | return new Delta(); |
1012 | } |
1013 | |
1014 | /** |
1015 | * Returns the change(s) between the type descriptor and its equivalent in the current baseline. |
1016 | * @return the changes in the type descriptor or <code>null</code> |
1017 | */ |
1018 | public IDelta getDelta(IProgressMonitor monitor) { |
1019 | SubMonitor localmonitor = SubMonitor.convert(monitor, 10); |
1020 | try { |
1021 | this.delta = createDelta(); |
1022 | // check visibility |
1023 | int typeAccess = this.type1.getModifiers(); |
1024 | int typeAccess2 = this.type2.getModifiers(); |
1025 | final IApiDescription component2ApiDescription = component2.getApiDescription(); |
1026 | IApiAnnotations elementDescription2 = component2ApiDescription.resolveAnnotations(this.type2.getHandle()); |
1027 | this.initialDescriptorRestrictions = RestrictionModifiers.NO_RESTRICTIONS; |
1028 | this.currentDescriptorRestrictions = RestrictionModifiers.NO_RESTRICTIONS; |
1029 | if (elementDescription2 != null) { |
1030 | int restrictions2 = elementDescription2.getRestrictions(); |
1031 | IApiDescription apiDescription = this.component.getApiDescription(); |
1032 | if (this.component.hasApiDescription()) { |
1033 | int restrictions = RestrictionModifiers.NO_RESTRICTIONS; |
1034 | IApiAnnotations componentApiDescription = apiDescription.resolveAnnotations(this.type1.getHandle()); |
1035 | if (componentApiDescription != null) { |
1036 | restrictions = componentApiDescription.getRestrictions(); |
1037 | this.initialDescriptorRestrictions = restrictions; |
1038 | } |
1039 | if (restrictions2 != restrictions) { |
1040 | // report different restrictions |
1041 | // adding/removing no extend on a final class is ok |
1042 | // adding/removing no instantiate on an abstract class is ok |
1043 | if (this.type1.isInterface()) { |
1044 | if ((RestrictionModifiers.isImplementRestriction(restrictions2) && !RestrictionModifiers.isImplementRestriction(restrictions)) || |
1045 | (RestrictionModifiers.isExtendRestriction(restrictions2) && !RestrictionModifiers.isExtendRestriction(restrictions))) { |
1046 | this.addDelta( |
1047 | getElementType(this.type1), |
1048 | IDelta.ADDED, |
1049 | IDelta.RESTRICTIONS, |
1050 | restrictions2, |
1051 | typeAccess, |
1052 | typeAccess2, |
1053 | this.type2, |
1054 | this.type2.getName(), |
1055 | Util.getDescriptorName(type1)); |
1056 | } |
1057 | } else { |
1058 | boolean reportChangedRestrictions = false; |
1059 | if (!Flags.isFinal(typeAccess2) && !Flags.isFinal(typeAccess)) { |
1060 | if (RestrictionModifiers.isExtendRestriction(restrictions2) |
1061 | && !RestrictionModifiers.isExtendRestriction(restrictions)) { |
1062 | reportChangedRestrictions = true; |
1063 | this.addDelta( |
1064 | getElementType(this.type1), |
1065 | IDelta.ADDED, |
1066 | IDelta.RESTRICTIONS, |
1067 | restrictions2, |
1068 | typeAccess, |
1069 | typeAccess2, |
1070 | this.type2, |
1071 | this.type2.getName(), |
1072 | Util.getDescriptorName(type1)); |
1073 | } |
1074 | } |
1075 | if (!reportChangedRestrictions |
1076 | && !Flags.isAbstract(typeAccess2) |
1077 | && !Flags.isAbstract(typeAccess)) { |
1078 | if (RestrictionModifiers.isInstantiateRestriction(restrictions2) |
1079 | && !RestrictionModifiers.isInstantiateRestriction(restrictions)) { |
1080 | this.addDelta( |
1081 | getElementType(this.type1), |
1082 | IDelta.ADDED, |
1083 | IDelta.RESTRICTIONS, |
1084 | restrictions2, |
1085 | typeAccess, |
1086 | typeAccess2, |
1087 | this.type2, |
1088 | this.type2.getName(), |
1089 | Util.getDescriptorName(type1)); |
1090 | } |
1091 | } |
1092 | } |
1093 | } |
1094 | } |
1095 | this.currentDescriptorRestrictions = restrictions2; |
1096 | } |
1097 | // first make sure that we compare interface with interface, class with class, |
1098 | // annotation with annotation and enum with enums |
1099 | if (Flags.isFinal(typeAccess2)) { |
1100 | this.currentDescriptorRestrictions |= RestrictionModifiers.NO_EXTEND; |
1101 | } |
1102 | if (Flags.isFinal(typeAccess)) { |
1103 | this.initialDescriptorRestrictions |= RestrictionModifiers.NO_EXTEND; |
1104 | } |
1105 | |
1106 | if (Flags.isDeprecated(typeAccess)) { |
1107 | if (!Flags.isDeprecated(typeAccess2)) { |
1108 | this.addDelta( |
1109 | getElementType(this.type1), |
1110 | IDelta.REMOVED, |
1111 | IDelta.DEPRECATION, |
1112 | this.currentDescriptorRestrictions, |
1113 | typeAccess, |
1114 | typeAccess2, |
1115 | this.type1, |
1116 | this.type1.getName(), |
1117 | Util.getDescriptorName(type1)); |
1118 | } |
1119 | } else if (Flags.isDeprecated(typeAccess2)) { |
1120 | this.addDelta( |
1121 | getElementType(this.type1), |
1122 | IDelta.ADDED, |
1123 | IDelta.DEPRECATION, |
1124 | this.currentDescriptorRestrictions, |
1125 | typeAccess, |
1126 | typeAccess2, |
1127 | this.type1, |
1128 | this.type1.getName(), |
1129 | Util.getDescriptorName(type1)); |
1130 | } |
1131 | if (Flags.isProtected(typeAccess)) { |
1132 | if (Flags.isPrivate(typeAccess2) || Util.isDefault(typeAccess2)) { |
1133 | // report delta - decrease access: protected to default or private |
1134 | this.addDelta( |
1135 | getElementType(this.type1), |
1136 | IDelta.CHANGED, |
1137 | IDelta.DECREASE_ACCESS, |
1138 | this.currentDescriptorRestrictions, |
1139 | typeAccess, |
1140 | typeAccess2, |
1141 | this.type1, |
1142 | this.type1.getName(), |
1143 | Util.getDescriptorName(type1)); |
1144 | return this.delta; |
1145 | } else if (Flags.isPublic(typeAccess2)) { |
1146 | // report delta - increase access: protected to public |
1147 | this.addDelta( |
1148 | getElementType(this.type1), |
1149 | IDelta.CHANGED, |
1150 | IDelta.INCREASE_ACCESS, |
1151 | this.currentDescriptorRestrictions, |
1152 | typeAccess, |
1153 | typeAccess2, |
1154 | this.type1, |
1155 | this.type1.getName(), |
1156 | Util.getDescriptorName(type1)); |
1157 | return this.delta; |
1158 | } |
1159 | } else if (Flags.isPublic(typeAccess) |
1160 | && (Flags.isProtected(typeAccess2) |
1161 | || Flags.isPrivate(typeAccess2) |
1162 | || Util.isDefault(typeAccess2))) { |
1163 | // report delta - decrease access: public to protected, default or private |
1164 | this.addDelta( |
1165 | getElementType(this.type1), |
1166 | IDelta.CHANGED, |
1167 | IDelta.DECREASE_ACCESS, |
1168 | this.currentDescriptorRestrictions, |
1169 | typeAccess, |
1170 | typeAccess2, |
1171 | this.type1, |
1172 | this.type1.getName(), |
1173 | Util.getDescriptorName(type1)); |
1174 | return this.delta; |
1175 | } else if (Util.isDefault(typeAccess) |
1176 | && (Flags.isPublic(typeAccess2) |
1177 | || Flags.isProtected(typeAccess2))) { |
1178 | this.addDelta( |
1179 | getElementType(this.type1), |
1180 | IDelta.CHANGED, |
1181 | IDelta.INCREASE_ACCESS, |
1182 | this.currentDescriptorRestrictions, |
1183 | typeAccess, |
1184 | typeAccess2, |
1185 | this.type1, |
1186 | this.type1.getName(), |
1187 | Util.getDescriptorName(type1)); |
1188 | return this.delta; |
1189 | } else if (Flags.isPrivate(typeAccess) |
1190 | && (Util.isDefault(typeAccess2) |
1191 | || Flags.isPublic(typeAccess2) |
1192 | || Flags.isProtected(typeAccess2))) { |
1193 | this.addDelta( |
1194 | getElementType(this.type1), |
1195 | IDelta.CHANGED, |
1196 | IDelta.INCREASE_ACCESS, |
1197 | this.currentDescriptorRestrictions, |
1198 | typeAccess, |
1199 | typeAccess2, |
1200 | this.type1, |
1201 | this.type1.getName(), |
1202 | Util.getDescriptorName(type1)); |
1203 | return this.delta; |
1204 | } |
1205 | |
1206 | if (Flags.isAnnotation(typeAccess)) { |
1207 | if (!Flags.isAnnotation(typeAccess2)) { |
1208 | if (Flags.isInterface(typeAccess2)) { |
1209 | // report conversion from annotation to interface |
1210 | this.addDelta( |
1211 | IDelta.ANNOTATION_ELEMENT_TYPE, |
1212 | IDelta.CHANGED, |
1213 | IDelta.TYPE_CONVERSION, |
1214 | this.currentDescriptorRestrictions, |
1215 | typeAccess, |
1216 | typeAccess2, |
1217 | this.type1, |
1218 | this.type1.getName(), |
1219 | new String[] { |
1220 | Util.getDescriptorName(type1), |
1221 | Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE), |
1222 | Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE) |
1223 | }); |
1224 | } else if (Flags.isEnum(typeAccess2)) { |
1225 | // report conversion from annotation to enum |
1226 | this.addDelta( |
1227 | IDelta.ANNOTATION_ELEMENT_TYPE, |
1228 | IDelta.CHANGED, |
1229 | IDelta.TYPE_CONVERSION, |
1230 | this.currentDescriptorRestrictions, |
1231 | typeAccess, |
1232 | typeAccess2, |
1233 | this.type1, |
1234 | this.type1.getName(), |
1235 | new String[] { |
1236 | Util.getDescriptorName(type1), |
1237 | Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE), |
1238 | Integer.toString(IDelta.ENUM_ELEMENT_TYPE) |
1239 | }); |
1240 | } else { |
1241 | // report conversion from annotation to class |
1242 | this.addDelta( |
1243 | IDelta.ANNOTATION_ELEMENT_TYPE, |
1244 | IDelta.CHANGED, |
1245 | IDelta.TYPE_CONVERSION, |
1246 | this.currentDescriptorRestrictions, |
1247 | typeAccess, |
1248 | typeAccess2, |
1249 | this.type1, |
1250 | this.type1.getName(), |
1251 | new String[] { |
1252 | Util.getDescriptorName(type1), |
1253 | Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE), |
1254 | Integer.toString(IDelta.CLASS_ELEMENT_TYPE) |
1255 | }); |
1256 | } |
1257 | return this.delta; |
1258 | } |
1259 | } else if (Flags.isInterface(typeAccess)) { |
1260 | if (Flags.isAnnotation(typeAccess2)) { |
1261 | // conversion from interface to annotation |
1262 | this.addDelta( |
1263 | IDelta.INTERFACE_ELEMENT_TYPE, |
1264 | IDelta.CHANGED, |
1265 | IDelta.TYPE_CONVERSION, |
1266 | this.currentDescriptorRestrictions, |
1267 | typeAccess, |
1268 | typeAccess2, |
1269 | this.type1, |
1270 | this.type1.getName(), |
1271 | new String[] { |
1272 | Util.getDescriptorName(type1), |
1273 | Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE), |
1274 | Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE) |
1275 | }); |
1276 | return this.delta; |
1277 | } else if (!Flags.isInterface(typeAccess2)) { |
1278 | if (Flags.isEnum(typeAccess2)) { |
1279 | // conversion from interface to enum |
1280 | this.addDelta( |
1281 | IDelta.INTERFACE_ELEMENT_TYPE, |
1282 | IDelta.CHANGED, |
1283 | IDelta.TYPE_CONVERSION, |
1284 | this.currentDescriptorRestrictions, |
1285 | typeAccess, |
1286 | typeAccess2, |
1287 | this.type1, |
1288 | this.type1.getName(), |
1289 | new String[] { |
1290 | Util.getDescriptorName(type1), |
1291 | Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE), |
1292 | Integer.toString(IDelta.ENUM_ELEMENT_TYPE) |
1293 | }); |
1294 | } else { |
1295 | // conversion from interface to class |
1296 | this.addDelta( |
1297 | IDelta.INTERFACE_ELEMENT_TYPE, |
1298 | IDelta.CHANGED, |
1299 | IDelta.TYPE_CONVERSION, |
1300 | this.currentDescriptorRestrictions, |
1301 | typeAccess, |
1302 | typeAccess2, |
1303 | this.type1, |
1304 | this.type1.getName(), |
1305 | new String[] { |
1306 | Util.getDescriptorName(type1), |
1307 | Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE), |
1308 | Integer.toString(IDelta.CLASS_ELEMENT_TYPE) |
1309 | }); |
1310 | } |
1311 | return this.delta; |
1312 | } |
1313 | } else if (Flags.isEnum(typeAccess)) { |
1314 | if (!Flags.isEnum(typeAccess2)) { |
1315 | if (Flags.isAnnotation(typeAccess2)) { |
1316 | // report conversion from enum to annotation |
1317 | this.addDelta( |
1318 | IDelta.ENUM_ELEMENT_TYPE, |
1319 | IDelta.CHANGED, |
1320 | IDelta.TYPE_CONVERSION, |
1321 | this.currentDescriptorRestrictions, |
1322 | typeAccess, |
1323 | typeAccess2, |
1324 | this.type1, |
1325 | this.type1.getName(), |
1326 | new String[] { |
1327 | Util.getDescriptorName(type1), |
1328 | Integer.toString(IDelta.ENUM_ELEMENT_TYPE), |
1329 | Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE) |
1330 | }); |
1331 | } else if (Flags.isInterface(typeAccess2)) { |
1332 | // report conversion from enum to interface |
1333 | this.addDelta( |
1334 | IDelta.ENUM_ELEMENT_TYPE, |
1335 | IDelta.CHANGED, |
1336 | IDelta.TYPE_CONVERSION, |
1337 | this.currentDescriptorRestrictions, |
1338 | typeAccess, |
1339 | typeAccess2, |
1340 | this.type1, |
1341 | this.type1.getName(), |
1342 | new String[] { |
1343 | Util.getDescriptorName(type1), |
1344 | Integer.toString(IDelta.ENUM_ELEMENT_TYPE), |
1345 | Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE) |
1346 | }); |
1347 | } else { |
1348 | // report conversion from enum to class |
1349 | this.addDelta( |
1350 | IDelta.ENUM_ELEMENT_TYPE, |
1351 | IDelta.CHANGED, |
1352 | IDelta.TYPE_CONVERSION, |
1353 | this.currentDescriptorRestrictions, |
1354 | typeAccess, |
1355 | typeAccess2, |
1356 | this.type1, |
1357 | this.type1.getName(), |
1358 | new String[] { |
1359 | Util.getDescriptorName(type1), |
1360 | Integer.toString(IDelta.ENUM_ELEMENT_TYPE), |
1361 | Integer.toString(IDelta.CLASS_ELEMENT_TYPE) |
1362 | }); |
1363 | } |
1364 | return this.delta; |
1365 | } |
1366 | } else if (!Util.isClass(typeAccess2)) { |
1367 | if (Flags.isAnnotation(typeAccess2)) { |
1368 | // report conversion from class to annotation |
1369 | this.addDelta( |
1370 | IDelta.CLASS_ELEMENT_TYPE, |
1371 | IDelta.CHANGED, |
1372 | IDelta.TYPE_CONVERSION, |
1373 | this.currentDescriptorRestrictions, |
1374 | typeAccess, |
1375 | typeAccess2, |
1376 | this.type1, |
1377 | this.type1.getName(), |
1378 | new String[] { |
1379 | Util.getDescriptorName(type1), |
1380 | Integer.toString(IDelta.CLASS_ELEMENT_TYPE), |
1381 | Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE) |
1382 | }); |
1383 | } else if (Flags.isInterface(typeAccess2)) { |
1384 | // report conversion from class to interface |
1385 | this.addDelta( |
1386 | IDelta.CLASS_ELEMENT_TYPE, |
1387 | IDelta.CHANGED, |
1388 | IDelta.TYPE_CONVERSION, |
1389 | this.currentDescriptorRestrictions, |
1390 | typeAccess, |
1391 | typeAccess2, |
1392 | this.type1, |
1393 | this.type1.getName(), |
1394 | new String[] { |
1395 | Util.getDescriptorName(type1), |
1396 | Integer.toString(IDelta.CLASS_ELEMENT_TYPE), |
1397 | Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE) |
1398 | }); |
1399 | } else { |
1400 | // report conversion from class to enum |
1401 | this.addDelta( |
1402 | IDelta.CLASS_ELEMENT_TYPE, |
1403 | IDelta.CHANGED, |
1404 | IDelta.TYPE_CONVERSION, |
1405 | this.currentDescriptorRestrictions, |
1406 | typeAccess, |
1407 | typeAccess2, |
1408 | this.type1, |
1409 | this.type1.getName(), |
1410 | new String[] { |
1411 | Util.getDescriptorName(type1), |
1412 | Integer.toString(IDelta.CLASS_ELEMENT_TYPE), |
1413 | Integer.toString(IDelta.ENUM_ELEMENT_TYPE) |
1414 | }); |
1415 | } |
1416 | return this.delta; |
1417 | } |
1418 | |
1419 | if (Flags.isStatic(typeAccess)) { |
1420 | if (!Flags.isStatic(typeAccess2)) { |
1421 | this.addDelta( |
1422 | getElementType(this.type1), |
1423 | IDelta.CHANGED, |
1424 | IDelta.STATIC_TO_NON_STATIC, |
1425 | this.currentDescriptorRestrictions, |
1426 | typeAccess, |
1427 | typeAccess2, |
1428 | this.type1, |
1429 | this.type1.getName(), |
1430 | Util.getDescriptorName(type1)); |
1431 | } |
1432 | } else if (Flags.isStatic(typeAccess2)) { |
1433 | this.addDelta( |
1434 | getElementType(this.type1), |
1435 | IDelta.CHANGED, |
1436 | IDelta.NON_STATIC_TO_STATIC, |
1437 | this.currentDescriptorRestrictions, |
1438 | typeAccess, |
1439 | typeAccess2, |
1440 | this.type1, |
1441 | this.type1.getName(), |
1442 | Util.getDescriptorName(type1)); |
1443 | } |
1444 | // check super class set |
1445 | checkSuperclass(); |
1446 | // check super interfaces set |
1447 | checkSuperInterfaces(); |
1448 | |
1449 | // checks fields |
1450 | IApiField[] fields1 = this.type1.getFields(); |
1451 | IApiField[] fields2 = this.type2.getFields(); |
1452 | Set addedFields = new HashSet(fields2.length); |
1453 | for (int i = 0; i < fields2.length; i++) { |
1454 | addedFields.add(fields2[i].getName()); |
1455 | } |
1456 | for (int i = 0; i < fields1.length; i++) { |
1457 | addedFields.remove(fields1[i].getName()); |
1458 | getDeltaForField(fields1[i]); |
1459 | } |
1460 | // checks remaining fields (added fields) |
1461 | for (Iterator iterator = addedFields.iterator(); iterator.hasNext();) { |
1462 | IApiField field = this.type2.getField((String) iterator.next()); |
1463 | reportFieldAddition(field, this.type2); |
1464 | } |
1465 | |
1466 | // checks methods |
1467 | IApiMethod[] methods1 = this.type1.getMethods(); |
1468 | IApiMethod[] methods2 = this.type2.getMethods(); |
1469 | Set addedMethods = new HashSet(methods2.length); |
1470 | for (int i = 0; i < methods2.length; i++) { |
1471 | if(!methods2[i].isSynthetic()) { |
1472 | addedMethods.add(methods2[i].getHandle()); |
1473 | } |
1474 | } |
1475 | for (int i = 0; i < methods1.length; i++) { |
1476 | addedMethods.remove(methods1[i].getHandle()); |
1477 | getDeltaForMethod(methods1[i]); |
1478 | } |
1479 | // checks remaining methods (added methods) |
1480 | for (Iterator iterator = addedMethods.iterator(); iterator.hasNext();) { |
1481 | IMethodDescriptor md = (IMethodDescriptor) iterator.next(); |
1482 | IApiMethod method = this.type2.getMethod(md.getName(), md.getSignature()); |
1483 | reportMethodAddition(method, this.type2); |
1484 | } |
1485 | if (Flags.isAbstract(typeAccess)) { |
1486 | if (!Flags.isAbstract(typeAccess2)) { |
1487 | // report delta - changed from abstract to non-abstract |
1488 | this.addDelta( |
1489 | getElementType(this.type1), |
1490 | IDelta.CHANGED, |
1491 | IDelta.ABSTRACT_TO_NON_ABSTRACT, |
1492 | this.currentDescriptorRestrictions, |
1493 | typeAccess, |
1494 | typeAccess2, |
1495 | this.type1, |
1496 | this.type1.getName(), |
1497 | Util.getDescriptorName(type1)); |
1498 | } |
1499 | } else if (Flags.isAbstract(typeAccess2)){ |
1500 | // report delta - changed from non-abstract to abstract |
1501 | if(!RestrictionModifiers.isInstantiateRestriction(initialDescriptorRestrictions)) { |
1502 | this.addDelta( |
1503 | getElementType(this.type1), |
1504 | IDelta.CHANGED, |
1505 | IDelta.NON_ABSTRACT_TO_ABSTRACT, |
1506 | this.currentDescriptorRestrictions, |
1507 | typeAccess, |
1508 | typeAccess2, |
1509 | this.type1, |
1510 | this.type1.getName(), |
1511 | Util.getDescriptorName(type1)); |
1512 | } |
1513 | } |
1514 | |
1515 | if (Flags.isFinal(typeAccess)) { |
1516 | if (!Flags.isFinal(typeAccess2)) { |
1517 | // report delta - changed from final to non-final |
1518 | this.addDelta( |
1519 | getElementType(this.type1), |
1520 | IDelta.CHANGED, |
1521 | IDelta.FINAL_TO_NON_FINAL, |
1522 | this.currentDescriptorRestrictions, |
1523 | typeAccess, |
1524 | typeAccess2, |
1525 | this.type1, |
1526 | this.type1.getName(), |
1527 | Util.getDescriptorName(type1)); |
1528 | } |
1529 | } else if (Flags.isFinal(typeAccess2)){ |
1530 | // report delta - changed from non-final to final |
1531 | this.addDelta( |
1532 | getElementType(this.type1), |
1533 | IDelta.CHANGED, |
1534 | IDelta.NON_FINAL_TO_FINAL, |
1535 | this.initialDescriptorRestrictions, |
1536 | typeAccess, |
1537 | typeAccess2, |
1538 | this.type1, |
1539 | this.type1.getName(), |
1540 | Util.getDescriptorName(type1)); |
1541 | } |
1542 | // check type parameters |
1543 | String signature1 = this.type1.getGenericSignature(); |
1544 | String signature2 = this.type2.getGenericSignature(); |
1545 | checkGenericSignature(signature1, signature2, this.type1, this.type2); |
1546 | |
1547 | // check type members |
1548 | checkTypeMembers(); |
1549 | return this.delta.isEmpty() ? ApiComparator.NO_DELTA : this.delta; |
1550 | } catch (CoreException e) { |
1551 | reportStatus(e); |
1552 | return null; |
1553 | } |
1554 | finally { |
1555 | localmonitor.done(); |
1556 | } |
1557 | } |
1558 | |
1559 | private void getDeltaForField(IApiField field) { |
1560 | int access = field.getModifiers(); |
1561 | if (Flags.isSynthetic(access)) { |
1562 | // we ignore synthetic fields |
1563 | return; |
1564 | } |
1565 | String name = field.getName(); |
1566 | IApiField field2 = this.type2.getField(name); |
1567 | if (field2 == null) { |
1568 | if (Flags.isPrivate(access) |
1569 | || Util.isDefault(access)) { |
1570 | this.addDelta( |
1571 | getElementType(this.type1), |
1572 | IDelta.REMOVED, |
1573 | IDelta.FIELD, |
1574 | this.currentDescriptorRestrictions, |
1575 | access, |
1576 | 0, |
1577 | this.type1, |
1578 | name, |
1579 | new String[] {Util.getDescriptorName(this.type1), name}); |
1580 | } else { |
1581 | boolean found = false; |
1582 | if (this.component2 != null) { |
1583 | if (this.type1.isInterface()) { |
1584 | Set interfacesSet = getInterfacesSet(this.type2); |
1585 | if (interfacesSet != null) { |
1586 | for (Iterator iterator = interfacesSet.iterator(); iterator.hasNext();) { |
1587 | IApiType superTypeDescriptor = (IApiType) iterator.next(); |
1588 | IApiField field3 = superTypeDescriptor.getField(name); |
1589 | if (field3 == null) { |
1590 | continue; |
1591 | } else { |
1592 | // interface method can only be public |
1593 | // method has been move up in the hierarchy - report the delta and abort loop |
1594 | this.addDelta( |
1595 | getElementType(this.type1), |
1596 | IDelta.REMOVED, |
1597 | IDelta.FIELD_MOVED_UP, |
1598 | this.currentDescriptorRestrictions, |
1599 | access, |
1600 | field3.getModifiers(), |
1601 | this.type1, |
1602 | name, |
1603 | new String[] {Util.getDescriptorName(this.type1), name}); |
1604 | found = true; |
1605 | break; |
1606 | } |
1607 | } |
1608 | } |
1609 | } else { |
1610 | List superclassList = getSuperclassList(this.type2); |
1611 | if (superclassList != null && isStatusOk()) { |
1612 | loop: for (Iterator iterator = superclassList.iterator(); iterator.hasNext();) { |
1613 | IApiType superTypeDescriptor = (IApiType) iterator.next(); |
1614 | IApiField field3 = superTypeDescriptor.getField(name); |
1615 | if (field3 == null) { |
1616 | continue; |
1617 | } else { |
1618 | int access3 = field3.getModifiers(); |
1619 | if (Flags.isPublic(access3) |
1620 | || Flags.isProtected(access3)) { |
1621 | // method has been move up in the hierarchy - report the delta and abort loop |
1622 | this.addDelta( |
1623 | getElementType(this.type1), |
1624 | IDelta.REMOVED, |
1625 | IDelta.FIELD_MOVED_UP, |
1626 | this.currentDescriptorRestrictions, |
1627 | access, |
1628 | field3.getModifiers(), |
1629 | this.type1, |
1630 | name, |
1631 | new String[] {Util.getDescriptorName(this.type1), name}); |
1632 | found = true; |
1633 | break loop; |
1634 | } |
1635 | } |
1636 | } |
1637 | } |
1638 | } |
1639 | } |
1640 | if (!found) { |
1641 | if ((this.visibilityModifiers == VisibilityModifiers.API) && component.hasApiDescription()) { |
1642 | // check if this field should be removed because it is tagged as @noreference |
1643 | IApiDescription apiDescription = null; |
1644 | try { |
1645 | apiDescription = this.component.getApiDescription(); |
1646 | } catch (CoreException e) { |
1647 | reportStatus(e); |
1648 | } |
1649 | if (apiDescription != null) { |
1650 | IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(field.getHandle()); |
1651 | if (apiAnnotations != null) { |
1652 | int restrictions = apiAnnotations.getRestrictions(); |
1653 | if (RestrictionModifiers.isReferenceRestriction(restrictions)) { |
1654 | // if not found, but tagged as @noreference in reference we don't need to report |
1655 | // a removed field |
1656 | return; |
1657 | } |
1658 | } |
1659 | } |
1660 | } |
1661 | if (field.isEnumConstant()) { |
1662 | // report delta (removal of an enum constant - not compatible) |
1663 | this.addDelta( |
1664 | getElementType(this.type1), |
1665 | IDelta.REMOVED, |
1666 | IDelta.ENUM_CONSTANT, |
1667 | this.currentDescriptorRestrictions, |
1668 | this.type1.getModifiers(), |
1669 | this.type2.getModifiers(), |
1670 | this.type1, |
1671 | name, |
1672 | new String[] {Util.getDescriptorName(this.type1), name}); |
1673 | return; |
1674 | } |
1675 | // removing a public field is a breakage |
1676 | this.addDelta( |
1677 | getElementType(this.type1), |
1678 | IDelta.REMOVED, |
1679 | IDelta.FIELD, |
1680 | this.currentDescriptorRestrictions, |
1681 | access, |
1682 | 0, |
1683 | this.type1, |
1684 | name, |
1685 | new String[] {Util.getDescriptorName(this.type1), name}); |
1686 | } |
1687 | } |
1688 | return; |
1689 | } |
1690 | int restrictions = RestrictionModifiers.NO_RESTRICTIONS; |
1691 | int referenceRestrictions = RestrictionModifiers.NO_RESTRICTIONS; |
1692 | int access2 = field2.getModifiers(); |
1693 | if (this.component2.hasApiDescription()) { |
1694 | try { |
1695 | IApiDescription apiDescription = this.component2.getApiDescription(); |
1696 | IApiAnnotations resolvedAPIDescription = apiDescription.resolveAnnotations(field2.getHandle()); |
1697 | if (resolvedAPIDescription != null) { |
1698 | restrictions = resolvedAPIDescription.getRestrictions(); |
1699 | } |
1700 | } catch (CoreException e) { |
1701 | // ignore |
1702 | } |
1703 | } |
1704 | if ((this.visibilityModifiers == VisibilityModifiers.API) && component.hasApiDescription()) { |
1705 | // check if this field should be removed because it is tagged as @noreference |
1706 | IApiDescription apiDescription = null; |
1707 | try { |
1708 | apiDescription = this.component.getApiDescription(); |
1709 | } catch (CoreException e) { |
1710 | reportStatus(e); |
1711 | } |
1712 | if (apiDescription != null) { |
1713 | IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(field.getHandle()); |
1714 | if (apiAnnotations != null) { |
1715 | referenceRestrictions = apiAnnotations.getRestrictions(); |
1716 | } |
1717 | } |
1718 | if (RestrictionModifiers.isReferenceRestriction(referenceRestrictions)) { |
1719 | // tagged as @noreference in the reference component |
1720 | if (!RestrictionModifiers.isReferenceRestriction(restrictions)) { |
1721 | // no longer tagged as @noreference |
1722 | // report a field addition |
1723 | if (field2.isEnumConstant()) { |
1724 | // report delta (addition of an enum constant - compatible |
1725 | this.addDelta( |
1726 | getElementType(this.type2), |
1727 | IDelta.ADDED, |
1728 | IDelta.ENUM_CONSTANT, |
1729 | this.currentDescriptorRestrictions, |
1730 | access, |
1731 | access2, |
1732 | this.type1, |
1733 | name, |
1734 | new String[] {Util.getDescriptorName(this.type2), name}); |
1735 | } else { |
1736 | this.addDelta( |
1737 | getElementType(this.type2), |
1738 | IDelta.ADDED, |
1739 | IDelta.FIELD, |
1740 | this.currentDescriptorRestrictions, |
1741 | access, |
1742 | access2, |
1743 | this.type1, |
1744 | name, |
1745 | new String[] {Util.getDescriptorName(this.type2), name}); |
1746 | } |
1747 | return; |
1748 | } |
1749 | } else if (RestrictionModifiers.isReferenceRestriction(restrictions)) { |
1750 | if (((Flags.isPublic(access2) || Flags.isProtected(access2)) && (Flags.isPublic(access) || Flags.isProtected(access))) |
1751 | && (this.visibilityModifiers == VisibilityModifiers.API)) { |
1752 | // report that it is no longer an API field |
1753 | this.addDelta( |
1754 | getElementType(this.type2), |
1755 | IDelta.REMOVED, |
1756 | field2.isEnumConstant() ? IDelta.API_ENUM_CONSTANT : IDelta.API_FIELD, |
1757 | restrictions, |
1758 | access, |
1759 | access2, |
1760 | this.type1, |
1761 | name, |
1762 | new String[] {Util.getDescriptorName(this.type2), name}); |
1763 | } |
1764 | return; |
1765 | } |
1766 | } |
1767 | |
1768 | restrictions |= this.currentDescriptorRestrictions; |
1769 | |
1770 | if (!field.getSignature().equals(field2.getSignature())) { |
1771 | // report delta |
1772 | this.addDelta( |
1773 | IDelta.FIELD_ELEMENT_TYPE, |
1774 | IDelta.CHANGED, |
1775 | IDelta.TYPE, |
1776 | restrictions, |
1777 | access, |
1778 | access2, |
1779 | this.type1, |
1780 | name, |
1781 | new String[] {Util.getDescriptorName(this.type1), name}); |
1782 | } else { |
1783 | // check type parameters |
1784 | String signature1 = field.getGenericSignature(); |
1785 | String signature2 = field2.getGenericSignature(); |
1786 | checkGenericSignature(signature1, signature2, field, field2); |
1787 | } |
1788 | boolean changeFinalToNonFinal = false; |
1789 | if (Flags.isProtected(access)) { |
1790 | if (Flags.isPrivate(access2) || Util.isDefault(access2)) { |
1791 | // report delta - decrease access: protected to default or private |
1792 | this.addDelta( |
1793 | IDelta.FIELD_ELEMENT_TYPE, |
1794 | IDelta.CHANGED, |
1795 | IDelta.DECREASE_ACCESS, |
1796 | restrictions, |
1797 | access, |
1798 | access2, |
1799 | this.type1, |
1800 | name, |
1801 | new String[] {Util.getDescriptorName(this.type1), name}); |
1802 | } else if (Flags.isPublic(access2)) { |
1803 | // report delta - increase access: protected to public |
1804 | this.addDelta( |
1805 | IDelta.FIELD_ELEMENT_TYPE, |
1806 | IDelta.CHANGED, |
1807 | IDelta.INCREASE_ACCESS, |
1808 | restrictions, |
1809 | access, |
1810 | access2, |
1811 | this.type1, |
1812 | name, |
1813 | new String[] {Util.getDescriptorName(this.type1), name}); |
1814 | } |
1815 | } else if (Flags.isPublic(access) |
1816 | && (Flags.isProtected(access2) |
1817 | || Flags.isPrivate(access2) |
1818 | || Util.isDefault(access2))) { |
1819 | // report delta - decrease access: public to protected, default or private |
1820 | this.addDelta( |
1821 | IDelta.FIELD_ELEMENT_TYPE, |
1822 | IDelta.CHANGED, |
1823 | IDelta.DECREASE_ACCESS, |
1824 | restrictions, |
1825 | access, |
1826 | access2, |
1827 | this.type1, |
1828 | name, |
1829 | new String[] {Util.getDescriptorName(this.type1), name}); |
1830 | } else if (Flags.isPrivate(access) |
1831 | && (Flags.isProtected(access2) |
1832 | || Util.isDefault(access2) |
1833 | || Flags.isPublic(access2))) { |
1834 | this.addDelta( |
1835 | IDelta.FIELD_ELEMENT_TYPE, |
1836 | IDelta.CHANGED, |
1837 | IDelta.INCREASE_ACCESS, |
1838 | restrictions, |
1839 | access, |
1840 | access2, |
1841 | this.type1, |
1842 | name, |
1843 | new String[] {Util.getDescriptorName(this.type1), name}); |
1844 | } else if (Util.isDefault(access) |
1845 | && (Flags.isProtected(access2) |
1846 | || Flags.isPublic(access2))) { |
1847 | this.addDelta( |
1848 | IDelta.FIELD_ELEMENT_TYPE, |
1849 | IDelta.CHANGED, |
1850 | IDelta.INCREASE_ACCESS, |
1851 | restrictions, |
1852 | access, |
1853 | access2, |
1854 | this.type1, |
1855 | name, |
1856 | new String[] {Util.getDescriptorName(this.type1), name}); |
1857 | } |
1858 | if (Flags.isFinal(access)) { |
1859 | if (!Flags.isFinal(access2)) { |
1860 | if (!Flags.isStatic(access2)) { |
1861 | // report delta - final to non-final for a non static field |
1862 | this.addDelta( |
1863 | IDelta.FIELD_ELEMENT_TYPE, |
1864 | IDelta.CHANGED, |
1865 | IDelta.FINAL_TO_NON_FINAL_NON_STATIC, |
1866 | restrictions, |
1867 | access, |
1868 | access2, |
1869 | this.type1, |
1870 | name, |
1871 | new String[] {Util.getDescriptorName(this.type1), name}); |
1872 | } else if (field.getConstantValue() != null) { |
1873 | // report delta - final to non-final for a static field with a compile time constant |
1874 | changeFinalToNonFinal = true; |
1875 | this.addDelta( |
1876 | IDelta.FIELD_ELEMENT_TYPE, |
1877 | IDelta.CHANGED, |
1878 | IDelta.FINAL_TO_NON_FINAL_STATIC_CONSTANT, |
1879 | restrictions, |
1880 | access, |
1881 | access2, |
1882 | this.type1, |
1883 | name, |
1884 | new String[] {Util.getDescriptorName(this.type1), name}); |
1885 | } else { |
1886 | // report delta - final to non-final for a static field with no compile time constant |
1887 | this.addDelta( |
1888 | IDelta.FIELD_ELEMENT_TYPE, |
1889 | IDelta.CHANGED, |
1890 | IDelta.FINAL_TO_NON_FINAL_STATIC_NON_CONSTANT, |
1891 | restrictions, |
1892 | access, |
1893 | access2, |
1894 | this.type1, |
1895 | name, |
1896 | new String[] {Util.getDescriptorName(this.type1), name}); |
1897 | } |
1898 | } |
1899 | } else if (Flags.isFinal(access2)) { |
1900 | // report delta - non-final to final |
1901 | this.addDelta( |
1902 | IDelta.FIELD_ELEMENT_TYPE, |
1903 | IDelta.CHANGED, |
1904 | IDelta.NON_FINAL_TO_FINAL, |
1905 | restrictions, |
1906 | access, |
1907 | access2, |
1908 | this.type1, |
1909 | name, |
1910 | new String[] {Util.getDescriptorName(this.type1), name}); |
1911 | } |
1912 | if (Flags.isStatic(access)) { |
1913 | if (!Flags.isStatic(access2)) { |
1914 | // report delta - static to non-static |
1915 | this.addDelta( |
1916 | IDelta.FIELD_ELEMENT_TYPE, |
1917 | IDelta.CHANGED, |
1918 | IDelta.STATIC_TO_NON_STATIC, |
1919 | restrictions, |
1920 | access, |
1921 | access2, |
1922 | this.type1, |
1923 | name, |
1924 | new String[] {Util.getDescriptorName(this.type1), name}); |
1925 | } |
1926 | } else if (Flags.isStatic(access2)) { |
1927 | // report delta - non-static to static |
1928 | this.addDelta( |
1929 | IDelta.FIELD_ELEMENT_TYPE, |
1930 | IDelta.CHANGED, |
1931 | IDelta.NON_STATIC_TO_STATIC, |
1932 | restrictions, |
1933 | access, |
1934 | access2, |
1935 | this.type1, |
1936 | name, |
1937 | new String[] {Util.getDescriptorName(this.type1), name}); |
1938 | } |
1939 | if (Flags.isTransient(access)) { |
1940 | if (!Flags.isTransient(access2)) { |
1941 | // report delta - transient to non-transient |
1942 | this.addDelta( |
1943 | IDelta.FIELD_ELEMENT_TYPE, |
1944 | IDelta.CHANGED, |
1945 | IDelta.TRANSIENT_TO_NON_TRANSIENT, |
1946 | restrictions, |
1947 | access, |
1948 | access2, |
1949 | this.type1, |
1950 | name, |
1951 | new String[] {Util.getDescriptorName(this.type1), name}); |
1952 | } |
1953 | } else if (Flags.isTransient(access2)) { |
1954 | // report delta - non-transient to transient |
1955 | this.addDelta( |
1956 | IDelta.FIELD_ELEMENT_TYPE, |
1957 | IDelta.CHANGED, |
1958 | IDelta.NON_TRANSIENT_TO_TRANSIENT, |
1959 | restrictions, |
1960 | access, |
1961 | access2, |
1962 | this.type1, |
1963 | name, |
1964 | new String[] {Util.getDescriptorName(this.type1), name}); |
1965 | } |
1966 | if (Flags.isVolatile(access)) { |
1967 | if (!Flags.isVolatile(access2)) { |
1968 | // report delta - volatile to non-volatile |
1969 | this.addDelta( |
1970 | IDelta.FIELD_ELEMENT_TYPE, |
1971 | IDelta.CHANGED, |
1972 | IDelta.VOLATILE_TO_NON_VOLATILE, |
1973 | restrictions, |
1974 | access, |
1975 | access2, |
1976 | this.type1, |
1977 | name, |
1978 | new String[] {Util.getDescriptorName(this.type1), name}); |
1979 | } |
1980 | } else if (Flags.isVolatile(access2)) { |
1981 | // report delta - non-volatile to volatile |
1982 | this.addDelta( |
1983 | IDelta.FIELD_ELEMENT_TYPE, |
1984 | IDelta.CHANGED, |
1985 | IDelta.NON_VOLATILE_TO_VOLATILE, |
1986 | restrictions, |
1987 | access, |
1988 | access2, |
1989 | this.type1, |
1990 | name, |
1991 | new String[] {Util.getDescriptorName(this.type1), name}); |
1992 | } |
1993 | if (Flags.isDeprecated(access)) { |
1994 | if (!Flags.isDeprecated(access2)) { |
1995 | this.addDelta( |
1996 | IDelta.FIELD_ELEMENT_TYPE, |
1997 | IDelta.REMOVED, |
1998 | IDelta.DEPRECATION, |
1999 | restrictions, |
2000 | access, |
2001 | access2, |
2002 | this.type1, |
2003 | name, |
2004 | new String[] {Util.getDescriptorName(this.type1), name}); |
2005 | } |
2006 | } else if (Flags.isDeprecated(access2)) { |
2007 | // report delta - non-volatile to volatile |
2008 | this.addDelta( |
2009 | IDelta.FIELD_ELEMENT_TYPE, |
2010 | IDelta.ADDED, |
2011 | IDelta.DEPRECATION, |
2012 | restrictions, |
2013 | access, |
2014 | access2, |
2015 | this.type1, |
2016 | name, |
2017 | new String[] {Util.getDescriptorName(this.type1), name}); |
2018 | } |
2019 | if (field.getConstantValue() != null) { |
2020 | if (field2.getConstantValue() == null) { |
2021 | if (!changeFinalToNonFinal) { |
2022 | // report delta - removal of constant value |
2023 | this.addDelta( |
2024 | IDelta.FIELD_ELEMENT_TYPE, |
2025 | IDelta.REMOVED, |
2026 | IDelta.VALUE, |
2027 | restrictions, |
2028 | access, |
2029 | access2, |
2030 | this.type1, |
2031 | name, |
2032 | new String[] {Util.getDescriptorName(this.type1), name, String.valueOf(field.getConstantValue())}); |
2033 | } |
2034 | } else if (!field.getConstantValue().equals(field2.getConstantValue())) { |
2035 | // report delta - modified constant value |
2036 | this.addDelta( |
2037 | IDelta.FIELD_ELEMENT_TYPE, |
2038 | IDelta.CHANGED, |
2039 | IDelta.VALUE, |
2040 | restrictions, |
2041 | access, |
2042 | access2, |
2043 | this.type1, |
2044 | name, |
2045 | new String[] {Util.getDescriptorName(this.type1), name, String.valueOf(field.getConstantValue())}); |
2046 | } |
2047 | } else if (field2.getConstantValue() != null) { |
2048 | // report delta |
2049 | this.addDelta( |
2050 | IDelta.FIELD_ELEMENT_TYPE, |
2051 | IDelta.ADDED, |
2052 | IDelta.VALUE, |
2053 | restrictions, |
2054 | access, |
2055 | access2, |
2056 | this.type1, |
2057 | name, |
2058 | new String[] {Util.getDescriptorName(this.type1), name, String.valueOf(field2.getConstantValue())}); |
2059 | } |
2060 | } |
2061 | private void getDeltaForMethod(IApiMethod method) { |
2062 | int access = method.getModifiers(); |
2063 | if (Flags.isSynthetic(access)) { |
2064 | // we ignore synthetic methods |
2065 | return; |
2066 | } |
2067 | String name = method.getName(); |
2068 | String descriptor = method.getSignature(); |
2069 | String key = getKeyForMethod(method, this.type1); |
2070 | IApiMethod method2 = this.type2.getMethod(name, descriptor); |
2071 | String methodDisplayName = getMethodDisplayName(method, this.type1); |
2072 | if (method2 == null) { |
2073 | if (method.isClassInitializer()) { |
2074 | // report delta: removal of a clinit method |
2075 | this.addDelta( |
2076 | getElementType(this.type1), |
2077 | IDelta.REMOVED, |
2078 | IDelta.CLINIT, |
2079 | this.currentDescriptorRestrictions, |
2080 | access, |
2081 | 0, |
2082 | this.type1, |
2083 | this.type1.getName(), |
2084 | Util.getDescriptorName(type1)); |
2085 | return; |
2086 | } else if (Flags.isPrivate(access) |
2087 | || Util.isDefault(access)) { |
2088 | this.addDelta( |
2089 | getElementType(this.type1), |
2090 | IDelta.REMOVED, |
2091 | getTargetType(method), |
2092 | Flags.isAbstract(this.type2.getModifiers()) ? this.currentDescriptorRestrictions | RestrictionModifiers.NO_INSTANTIATE : this.currentDescriptorRestrictions, |
2093 | access, |
2094 | 0, |
2095 | this.type1, |
2096 | getKeyForMethod(method, this.type1), |
2097 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2098 | return; |
2099 | } |
2100 | // if null we need to walk the hierarchy of descriptor2 |
2101 | boolean found = false; |
2102 | if (this.component2 != null && !method.isConstructor()) { |
2103 | if (this.type1.isInterface()) { |
2104 | Set interfacesSet = getInterfacesSet(this.type2); |
2105 | if (interfacesSet != null && isStatusOk()) { |
2106 | for (Iterator iterator = interfacesSet.iterator(); iterator.hasNext();) { |
2107 | IApiType superTypeDescriptor = (IApiType) iterator.next(); |
2108 | IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor); |
2109 | if (method3 == null) { |
2110 | continue; |
2111 | } else { |
2112 | // interface method can only be public |
2113 | // method has been move up in the hierarchy - report the delta and abort loop |
2114 | this.addDelta( |
2115 | getElementType(this.type1), |
2116 | IDelta.REMOVED, |
2117 | IDelta.METHOD_MOVED_UP, |
2118 | this.currentDescriptorRestrictions, |
2119 | access, |
2120 | method3.getModifiers(), |
2121 | this.type1, |
2122 | getKeyForMethod(method3, this.type1), |
2123 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2124 | found = true; |
2125 | break; |
2126 | } |
2127 | } |
2128 | } |
2129 | } else { |
2130 | List superclassList = getSuperclassList(this.type2, true); |
2131 | if (superclassList != null && isStatusOk()) { |
2132 | loop: for (Iterator iterator = superclassList.iterator(); iterator.hasNext();) { |
2133 | IApiType superTypeDescriptor = (IApiType) iterator.next(); |
2134 | IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor); |
2135 | if (method3 == null) { |
2136 | continue; |
2137 | } else { |
2138 | int access3 = method3.getModifiers(); |
2139 | if (Flags.isPublic(access3) |
2140 | || Flags.isProtected(access3)) { |
2141 | // method has been move up in the hierarchy - report the delta and abort loop |
2142 | // TODO need to make the distinction between methods that need to be re-implemented and methods that don't |
2143 | this.addDelta( |
2144 | getElementType(this.type1), |
2145 | IDelta.REMOVED, |
2146 | IDelta.METHOD_MOVED_UP, |
2147 | this.currentDescriptorRestrictions, |
2148 | access, |
2149 | access3, |
2150 | this.type1, |
2151 | getKeyForMethod(method3, this.type1), |
2152 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2153 | found = true; |
2154 | break loop; |
2155 | } |
2156 | } |
2157 | } |
2158 | } |
2159 | } |
2160 | } |
2161 | if (!found) { |
2162 | if ((this.visibilityModifiers == VisibilityModifiers.API) && component.hasApiDescription()) { |
2163 | // check if this method should be removed because it is tagged as @noreference |
2164 | IApiDescription apiDescription = null; |
2165 | try { |
2166 | apiDescription = this.component.getApiDescription(); |
2167 | } catch (CoreException e) { |
2168 | reportStatus(e); |
2169 | } |
2170 | if (apiDescription != null) { |
2171 | IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(method.getHandle()); |
2172 | if (apiAnnotations != null) { |
2173 | int restrictions = apiAnnotations.getRestrictions(); |
2174 | if (RestrictionModifiers.isReferenceRestriction(restrictions)) { |
2175 | // if not found, but tagged as @noreference in reference we don't need to report |
2176 | // a removed method |
2177 | return; |
2178 | } |
2179 | } |
2180 | } |
2181 | } |
2182 | if (this.type1.isAnnotation()) { |
2183 | this.addDelta( |
2184 | getElementType(this.type1), |
2185 | IDelta.REMOVED, |
2186 | method.getDefaultValue() != null ? IDelta.METHOD_WITH_DEFAULT_VALUE : IDelta.METHOD_WITHOUT_DEFAULT_VALUE, |
2187 | this.currentDescriptorRestrictions, |
2188 | access, |
2189 | 0, |
2190 | this.type1, |
2191 | getKeyForMethod(method, this.type1), |
2192 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2193 | } else { |
2194 | int restrictions = this.currentDescriptorRestrictions; |
2195 | if (RestrictionModifiers.isExtendRestriction(this.initialDescriptorRestrictions) |
2196 | && !RestrictionModifiers.isExtendRestriction(this.currentDescriptorRestrictions)) { |
2197 | restrictions = this.initialDescriptorRestrictions; |
2198 | } |
2199 | this.addDelta( |
2200 | getElementType(this.type1), |
2201 | IDelta.REMOVED, |
2202 | getTargetType(method), |
2203 | Flags.isAbstract(this.type2.getModifiers()) ? restrictions | RestrictionModifiers.NO_INSTANTIATE : restrictions, |
2204 | access, |
2205 | 0, |
2206 | this.type1, |
2207 | getKeyForMethod(method, this.type1), |
2208 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2209 | } |
2210 | } |
2211 | return; |
2212 | } |
2213 | int restrictions = this.currentDescriptorRestrictions; |
2214 | if (component2.hasApiDescription()) { |
2215 | try { |
2216 | IApiDescription apiDescription = this.component2.getApiDescription(); |
2217 | IApiAnnotations resolvedAPIDescription = apiDescription.resolveAnnotations(method2.getHandle()); |
2218 | if (resolvedAPIDescription != null) { |
2219 | restrictions |= resolvedAPIDescription.getRestrictions(); |
2220 | } |
2221 | } catch (CoreException e) { |
2222 | // ignore |
2223 | } |
2224 | } |
2225 | int referenceRestrictions = this.initialDescriptorRestrictions; |
2226 | int access2 = method2.getModifiers(); |
2227 | if (this.component.hasApiDescription()) { |
2228 | // check if this method should be removed because it is tagged as @noreference |
2229 | IApiDescription apiDescription = null; |
2230 | try { |
2231 | apiDescription = this.component.getApiDescription(); |
2232 | } catch (CoreException e) { |
2233 | reportStatus(e); |
2234 | } |
2235 | if (apiDescription != null) { |
2236 | IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(method.getHandle()); |
2237 | if (apiAnnotations != null) { |
2238 | referenceRestrictions |= apiAnnotations.getRestrictions(); |
2239 | } |
2240 | } |
2241 | } |
2242 | if ((this.visibilityModifiers == VisibilityModifiers.API) && this.component.hasApiDescription()) { |
2243 | if (RestrictionModifiers.isReferenceRestriction(referenceRestrictions)) { |
2244 | // tagged as @noreference in the reference component |
2245 | if (!RestrictionModifiers.isReferenceRestriction(restrictions)) { |
2246 | // no longer tagged as @noreference |
2247 | // report a method addition |
2248 | if (method.isConstructor()) { |
2249 | this.addDelta( |
2250 | getElementType(this.type2), |
2251 | IDelta.ADDED, |
2252 | IDelta.CONSTRUCTOR, |
2253 | this.currentDescriptorRestrictions, |
2254 | access, |
2255 | access2, |
2256 | this.type1, |
2257 | getKeyForMethod(method, this.type2), |
2258 | new String[] {Util.getDescriptorName(this.type2), methodDisplayName}); |
2259 | } else if (this.type2.isAnnotation()) { |
2260 | if (method.getDefaultValue() != null) { |
2261 | this.addDelta( |
2262 | getElementType(this.type2), |
2263 | IDelta.ADDED, |
2264 | IDelta.METHOD_WITH_DEFAULT_VALUE, |
2265 | this.currentDescriptorRestrictions, |
2266 | access, |
2267 | access2, |
2268 | this.type1, |
2269 | getKeyForMethod(method, this.type2), |
2270 | new String[] {Util.getDescriptorName(this.type2), methodDisplayName }); |
2271 | } else { |
2272 | this.addDelta( |
2273 | getElementType(this.type2), |
2274 | IDelta.ADDED, |
2275 | IDelta.METHOD_WITHOUT_DEFAULT_VALUE, |
2276 | this.currentDescriptorRestrictions, |
2277 | access, |
2278 | access2, |
2279 | this.type1, |
2280 | getKeyForMethod(method, this.type2), |
2281 | new String[] {Util.getDescriptorName(this.type2), methodDisplayName }); |
2282 | } |
2283 | } else { |
2284 | // check superclass |
2285 | // if null we need to walk the hierarchy of descriptor2 |
2286 | boolean found = false; |
2287 | if (this.component2 != null) { |
2288 | if (this.type1.isInterface()) { |
2289 | Set interfacesSet = getInterfacesSet(this.type2); |
2290 | if (interfacesSet != null && isStatusOk()) { |
2291 | for (Iterator iterator = interfacesSet.iterator(); iterator.hasNext();) { |
2292 | IApiType superTypeDescriptor = (IApiType) iterator.next(); |
2293 | IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor); |
2294 | if (method3 == null) { |
2295 | continue; |
2296 | } else { |
2297 | // interface method can only be public |
2298 | // method has been move up in the hierarchy - report the delta and abort loop |
2299 | found = true; |
2300 | break; |
2301 | } |
2302 | } |
2303 | } |
2304 | } else { |
2305 | List superclassList = getSuperclassList(this.type2, true); |
2306 | if (superclassList != null) { |
2307 | loop: for (Iterator iterator = superclassList.iterator(); iterator.hasNext();) { |
2308 | IApiType superTypeDescriptor = (IApiType) iterator.next(); |
2309 | IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor); |
2310 | if (method3 == null) { |
2311 | continue; |
2312 | } else { |
2313 | int access3 = method3.getModifiers(); |
2314 | if (Flags.isPublic(access3) |
2315 | || Flags.isProtected(access3)) { |
2316 | // method has been move up in the hierarchy - report the delta and abort loop |
2317 | // TODO need to make the distinction between methods that need to be re-implemented and methods that don't |
2318 | found = true; |
2319 | break loop; |
2320 | } |
2321 | } |
2322 | } |
2323 | } |
2324 | } |
2325 | } |
2326 | this.addDelta( |
2327 | getElementType(this.type2), |
2328 | IDelta.ADDED, |
2329 | found ? IDelta.OVERRIDEN_METHOD : IDelta.METHOD, |
2330 | this.currentDescriptorRestrictions, |
2331 | access, |
2332 | access2, |
2333 | this.type1, |
2334 | getKeyForMethod(method, this.type2), |
2335 | new String[] {Util.getDescriptorName(this.type2), methodDisplayName }); |
2336 | } |
2337 | return; |
2338 | } |
2339 | } else if (RestrictionModifiers.isReferenceRestriction(restrictions)) { |
2340 | if (Flags.isPublic(access2) || Flags.isProtected(access2)) { |
2341 | // report that it is no longer an API method |
2342 | if (this.type2.isAnnotation()) { |
2343 | this.addDelta( |
2344 | getElementType(this.type2), |
2345 | IDelta.REMOVED, |
2346 | method.getDefaultValue() != null ? IDelta.API_METHOD_WITH_DEFAULT_VALUE : IDelta.API_METHOD_WITHOUT_DEFAULT_VALUE, |
2347 | this.currentDescriptorRestrictions, |
2348 | access, |
2349 | access2, |
2350 | this.type1, |
2351 | getKeyForMethod(method2, this.type2), |
2352 | new String[] {Util.getDescriptorName(this.type2), methodDisplayName}); |
2353 | } else if (Flags.isPublic(access) || Flags.isProtected(access)) { |
2354 | this.addDelta( |
2355 | getElementType(this.type2), |
2356 | IDelta.REMOVED, |
2357 | method.isConstructor() ? IDelta.API_CONSTRUCTOR : IDelta.API_METHOD, |
2358 | Flags.isAbstract(this.type2.getModifiers()) ? this.currentDescriptorRestrictions | RestrictionModifiers.NO_INSTANTIATE : this.currentDescriptorRestrictions, |
2359 | access, |
2360 | access2, |
2361 | this.type1, |
2362 | getKeyForMethod(method2, this.type2), |
2363 | new String[] {Util.getDescriptorName(this.type2), methodDisplayName}); |
2364 | } |
2365 | return; |
2366 | } |
2367 | } |
2368 | } |
2369 | if (this.component.hasApiDescription() && !method.isConstructor() && !method.isClassInitializer() |
2370 | && !(type1.isInterface() || type1.isAnnotation())) { |
2371 | if (restrictions != referenceRestrictions) { |
2372 | if (!Flags.isFinal(access2)) { |
2373 | if (RestrictionModifiers.isOverrideRestriction(restrictions) |
2374 | && !RestrictionModifiers.isOverrideRestriction(referenceRestrictions)) { |
2375 | this.addDelta( |
2376 | getElementType(method), |
2377 | IDelta.ADDED, |
2378 | IDelta.RESTRICTIONS, |
2379 | restrictions, |
2380 | access, |
2381 | access2, |
2382 | this.type1, |
2383 | getKeyForMethod(method2, this.type2), |
2384 | new String[] {Util.getDescriptorName(this.type2), methodDisplayName}); |
2385 | } |
2386 | } |
2387 | } |
2388 | } |
2389 | String[] names1 = method.getExceptionNames(); |
2390 | List list1 = null; |
2391 | if (names1 != null) { |
2392 | list1 = new ArrayList(names1.length); |
2393 | for (int i = 0; i < names1.length; i++) { |
2394 | list1.add(names1[i]); |
2395 | } |
2396 | } |
2397 | String[] names2 = method2.getExceptionNames(); |
2398 | List list2 = null; |
2399 | if (names2 != null) { |
2400 | list2 = new ArrayList(names2.length); |
2401 | for (int i = 0; i < names2.length; i++) { |
2402 | list2.add(names2[i]); |
2403 | } |
2404 | } |
2405 | if (names1 != null) { |
2406 | if (names2 == null) { |
2407 | // check all exception in method descriptor to see if they are checked or unchecked exceptions |
2408 | loop: for (Iterator iterator = list1.iterator(); iterator.hasNext(); ) { |
2409 | String exceptionName = ((String) iterator.next()).replace('/', '.'); |
2410 | if (isCheckedException(this.apiBaseline1, this.component, exceptionName)) { |
2411 | // report delta - removal of checked exception |
2412 | // TODO should we continue the loop for all remaining exceptions |
2413 | this.addDelta( |
2414 | getElementType(method), |
2415 | IDelta.REMOVED, |
2416 | IDelta.CHECKED_EXCEPTION, |
2417 | restrictions, |
2418 | access, |
2419 | access2, |
2420 | this.type1, |
2421 | key, |
2422 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName, exceptionName}); |
2423 | break loop; |
2424 | } else { |
2425 | // report delta - removal of unchecked exception |
2426 | this.addDelta( |
2427 | getElementType(method), |
2428 | IDelta.REMOVED, |
2429 | IDelta.UNCHECKED_EXCEPTION, |
2430 | restrictions, |
2431 | access, |
2432 | access2, |
2433 | this.type1, |
2434 | key, |
2435 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName, exceptionName}); |
2436 | } |
2437 | } |
2438 | } else { |
2439 | // check if the exceptions are consistent for both descriptors |
2440 | List removedExceptions = new ArrayList(); |
2441 | for (Iterator iterator = list1.iterator(); iterator.hasNext(); ) { |
2442 | String exceptionName = ((String) iterator.next()).replace('/', '.'); |
2443 | if (!list2.remove(exceptionName)) { |
2444 | // this means that the exceptionName was not found inside the new set of exceptions |
2445 | // so it has been removed |
2446 | removedExceptions.add(exceptionName); |
2447 | } |
2448 | } |
2449 | if (removedExceptions.size() != 0) { |
2450 | loop: for (Iterator iterator = removedExceptions.iterator(); iterator.hasNext(); ) { |
2451 | String exceptionName = ((String) iterator.next()).replace('/', '.'); |
2452 | if (isCheckedException(this.apiBaseline1, this.component, exceptionName)) { |
2453 | // report delta - removal of checked exception |
2454 | // TODO should we continue the loop for all remaining exceptions |
2455 | this.addDelta( |
2456 | getElementType(method), |
2457 | IDelta.REMOVED, |
2458 | IDelta.CHECKED_EXCEPTION, |
2459 | restrictions, |
2460 | access, |
2461 | access2, |
2462 | this.type1, |
2463 | key, |
2464 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName, exceptionName}); |
2465 | break loop; |
2466 | } else { |
2467 | // report delta - removal of unchecked exception |
2468 | this.addDelta( |
2469 | getElementType(method), |
2470 | IDelta.REMOVED, |
2471 | IDelta.UNCHECKED_EXCEPTION, |
2472 | restrictions, |
2473 | access, |
2474 | access2, |
2475 | this.type1, |
2476 | key, |
2477 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName, exceptionName}); |
2478 | } |
2479 | } |
2480 | } |
2481 | loop: for (Iterator iterator = list2.iterator(); iterator.hasNext(); ) { |
2482 | String exceptionName = ((String) iterator.next()).replace('/', '.'); |
2483 | if (isCheckedException(this.apiBaseline2, this.component2, exceptionName)) { |
2484 | // report delta - addition of checked exception |
2485 | // TODO should we continue the loop for all remaining exceptions |
2486 | this.addDelta( |
2487 | getElementType(method), |
2488 | IDelta.ADDED, |
2489 | IDelta.CHECKED_EXCEPTION, |
2490 | restrictions, |
2491 | access, |
2492 | access2, |
2493 | this.type1, |
2494 | key, |
2495 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName, exceptionName}); |
2496 | break loop; |
2497 | } else { |
2498 | // report delta - addition of unchecked exception |
2499 | this.addDelta( |
2500 | getElementType(method), |
2501 | IDelta.ADDED, |
2502 | IDelta.UNCHECKED_EXCEPTION, |
2503 | restrictions, |
2504 | access, |
2505 | access2, |
2506 | this.type1, |
2507 | key, |
2508 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName, exceptionName}); |
2509 | } |
2510 | } |
2511 | } |
2512 | } else if (names2 != null) { |
2513 | // check all exception in method descriptor to see if they are checked or unchecked exceptions |
2514 | loop: for (Iterator iterator = list2.iterator(); iterator.hasNext(); ) { |
2515 | String exceptionName = ((String) iterator.next()).replace('/', '.'); |
2516 | if (isCheckedException(this.apiBaseline2, this.component2, exceptionName)) { |
2517 | // report delta - addition of checked exception |
2518 | this.addDelta( |
2519 | getElementType(method), |
2520 | IDelta.ADDED, |
2521 | IDelta.CHECKED_EXCEPTION, |
2522 | restrictions, |
2523 | access, |
2524 | access2, |
2525 | this.type1, |
2526 | key, |
2527 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName, exceptionName}); |
2528 | // TODO should we continue the loop for all remaining exceptions |
2529 | break loop; |
2530 | } else { |
2531 | // report delta - addition of unchecked exception |
2532 | this.addDelta( |
2533 | getElementType(method), |
2534 | IDelta.ADDED, |
2535 | IDelta.UNCHECKED_EXCEPTION, |
2536 | restrictions, |
2537 | access, |
2538 | access2, |
2539 | this.type1, |
2540 | key, |
2541 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName, exceptionName}); |
2542 | } |
2543 | } |
2544 | } |
2545 | if (Flags.isVarargs(access)) { |
2546 | if (!Flags.isVarargs(access2)) { |
2547 | // report delta: conversion from T... to T[] - break compatibility |
2548 | this.addDelta( |
2549 | getElementType(method), |
2550 | IDelta.CHANGED, |
2551 | IDelta.VARARGS_TO_ARRAY, |
2552 | restrictions, |
2553 | access, |
2554 | access2, |
2555 | this.type1, |
2556 | key, |
2557 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2558 | } |
2559 | } else if (Flags.isVarargs(access2)) { |
2560 | // report delta: conversion from T[] to T... compatible |
2561 | this.addDelta( |
2562 | getElementType(method), |
2563 | IDelta.CHANGED, |
2564 | IDelta.ARRAY_TO_VARARGS, |
2565 | restrictions, |
2566 | access, |
2567 | access2, |
2568 | this.type1, |
2569 | key, |
2570 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2571 | } |
2572 | if (Flags.isProtected(access)) { |
2573 | if (Flags.isPrivate(access2) || Util.isDefault(access2)) { |
2574 | // report delta - decrease access: protected to default or private |
2575 | this.addDelta( |
2576 | getElementType(method), |
2577 | IDelta.CHANGED, |
2578 | IDelta.DECREASE_ACCESS, |
2579 | restrictions, |
2580 | access, |
2581 | access2, |
2582 | this.type1, |
2583 | key, |
2584 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2585 | } else if (Flags.isPublic(access2)) { |
2586 | // report delta - increase access: protected to public |
2587 | this.addDelta( |
2588 | getElementType(method), |
2589 | IDelta.CHANGED, |
2590 | IDelta.INCREASE_ACCESS, |
2591 | restrictions, |
2592 | access, |
2593 | access2, |
2594 | this.type1, |
2595 | key, |
2596 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2597 | } |
2598 | } else if (Flags.isPublic(access) |
2599 | && (Flags.isProtected(access2) |
2600 | || Flags.isPrivate(access2) |
2601 | || Util.isDefault(access2))) { |
2602 | // report delta - decrease access: public to protected, default or private |
2603 | this.addDelta( |
2604 | getElementType(method), |
2605 | IDelta.CHANGED, |
2606 | IDelta.DECREASE_ACCESS, |
2607 | restrictions, |
2608 | access, |
2609 | access2, |
2610 | this.type1, |
2611 | key, |
2612 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2613 | } else if (Util.isDefault(access) |
2614 | && (Flags.isPublic(access2) |
2615 | || Flags.isProtected(access2))) { |
2616 | this.addDelta( |
2617 | getElementType(method), |
2618 | IDelta.CHANGED, |
2619 | IDelta.INCREASE_ACCESS, |
2620 | restrictions, |
2621 | access, |
2622 | access2, |
2623 | this.type1, |
2624 | key, |
2625 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2626 | } else if (Flags.isPrivate(access) |
2627 | && (Util.isDefault(access2) |
2628 | || Flags.isPublic(access2) |
2629 | || Flags.isProtected(access2))) { |
2630 | this.addDelta( |
2631 | getElementType(method), |
2632 | IDelta.CHANGED, |
2633 | IDelta.INCREASE_ACCESS, |
2634 | restrictions, |
2635 | access, |
2636 | access2, |
2637 | this.type1, |
2638 | key, |
2639 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2640 | } |
2641 | if (Flags.isAbstract(access)) { |
2642 | if (!Flags.isAbstract(access2)) { |
2643 | // report delta - changed from abstract to non-abstract |
2644 | this.addDelta( |
2645 | getElementType(method), |
2646 | IDelta.CHANGED, |
2647 | IDelta.ABSTRACT_TO_NON_ABSTRACT, |
2648 | restrictions, |
2649 | access, |
2650 | access2, |
2651 | this.type1, |
2652 | key, |
2653 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2654 | } |
2655 | } else if (Flags.isAbstract(access2)){ |
2656 | // report delta - changed from non-abstract to abstract |
2657 | this.addDelta( |
2658 | getElementType(method), |
2659 | IDelta.CHANGED, |
2660 | IDelta.NON_ABSTRACT_TO_ABSTRACT, |
2661 | restrictions, |
2662 | access, |
2663 | access2, |
2664 | this.type1, |
2665 | key, |
2666 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2667 | } |
2668 | if (Flags.isFinal(access)) { |
2669 | if (!Flags.isFinal(access2)) { |
2670 | // report delta - changed from final to non-final |
2671 | this.addDelta( |
2672 | getElementType(method), |
2673 | IDelta.CHANGED, |
2674 | IDelta.FINAL_TO_NON_FINAL, |
2675 | restrictions, |
2676 | access, |
2677 | access2, |
2678 | this.type1, |
2679 | key, |
2680 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2681 | } |
2682 | } else if (Flags.isFinal(access2)) { |
2683 | int res = restrictions; |
2684 | if (!RestrictionModifiers.isOverrideRestriction(res)) { |
2685 | if (RestrictionModifiers.isExtendRestriction(this.currentDescriptorRestrictions)) { |
2686 | res = this.currentDescriptorRestrictions; |
2687 | } else if (RestrictionModifiers.isExtendRestriction(this.initialDescriptorRestrictions)) { |
2688 | res = this.initialDescriptorRestrictions; |
2689 | } |
2690 | if (RestrictionModifiers.isOverrideRestriction(referenceRestrictions)) { |
2691 | // it is ok to remove @nooverride and add final at the same time |
2692 | res = referenceRestrictions; |
2693 | } |
2694 | } |
2695 | // only report this delta is the method was visible |
2696 | this.addDelta( |
2697 | getElementType(method2), |
2698 | IDelta.CHANGED, |
2699 | IDelta.NON_FINAL_TO_FINAL, |
2700 | res, |
2701 | access, |
2702 | access2, |
2703 | this.type1, |
2704 | key, |
2705 | new String[] {Util.getDescriptorName(this.type2), getMethodDisplayName(method2, this.type2)}); |
2706 | } |
2707 | if (Flags.isStatic(access)) { |
2708 | if (!Flags.isStatic(access2)) { |
2709 | // report delta: change from static to non-static |
2710 | this.addDelta( |
2711 | getElementType(method), |
2712 | IDelta.CHANGED, |
2713 | IDelta.STATIC_TO_NON_STATIC, |
2714 | restrictions, |
2715 | access, |
2716 | access2, |
2717 | this.type1, |
2718 | key, |
2719 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2720 | } |
2721 | } else if (Flags.isStatic(access2)){ |
2722 | // report delta: change from non-static to static |
2723 | this.addDelta( |
2724 | getElementType(method), |
2725 | IDelta.CHANGED, |
2726 | IDelta.NON_STATIC_TO_STATIC, |
2727 | restrictions, |
2728 | access, |
2729 | access2, |
2730 | this.type1, |
2731 | key, |
2732 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2733 | } |
2734 | if (Flags.isNative(access)) { |
2735 | if (!Flags.isNative(access2)) { |
2736 | // report delta: change from native to non-native |
2737 | this.addDelta( |
2738 | getElementType(method), |
2739 | IDelta.CHANGED, |
2740 | IDelta.NATIVE_TO_NON_NATIVE, |
2741 | restrictions, |
2742 | access, |
2743 | access2, |
2744 | this.type1, |
2745 | key, |
2746 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2747 | } |
2748 | } else if (Flags.isNative(access2)){ |
2749 | // report delta: change from non-native to native |
2750 | this.addDelta( |
2751 | getElementType(method), |
2752 | IDelta.CHANGED, |
2753 | IDelta.NON_NATIVE_TO_NATIVE, |
2754 | restrictions, |
2755 | access, |
2756 | access2, |
2757 | this.type1, |
2758 | key, |
2759 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2760 | } |
2761 | if (Flags.isSynchronized(access)) { |
2762 | if (!Flags.isSynchronized(access2)) { |
2763 | // report delta: change from synchronized to non-synchronized |
2764 | this.addDelta( |
2765 | getElementType(method), |
2766 | IDelta.CHANGED, |
2767 | IDelta.SYNCHRONIZED_TO_NON_SYNCHRONIZED, |
2768 | restrictions, |
2769 | access, |
2770 | access2, |
2771 | this.type1, |
2772 | key, |
2773 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2774 | } |
2775 | } else if (Flags.isSynchronized(access2)){ |
2776 | // report delta: change from non-synchronized to synchronized |
2777 | this.addDelta( |
2778 | getElementType(method), |
2779 | IDelta.CHANGED, |
2780 | IDelta.NON_SYNCHRONIZED_TO_SYNCHRONIZED, |
2781 | restrictions, |
2782 | access, |
2783 | access2, |
2784 | this.type1, |
2785 | key, |
2786 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2787 | } |
2788 | if (Flags.isDeprecated(access)) { |
2789 | if (!Flags.isDeprecated(access2)) { |
2790 | this.addDelta( |
2791 | getElementType(method), |
2792 | IDelta.REMOVED, |
2793 | IDelta.DEPRECATION, |
2794 | restrictions, |
2795 | access, |
2796 | access2, |
2797 | this.type1, |
2798 | key, |
2799 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2800 | } |
2801 | } else if (Flags.isDeprecated(access2)) { |
2802 | // report delta - non-volatile to volatile |
2803 | this.addDelta( |
2804 | getElementType(method), |
2805 | IDelta.ADDED, |
2806 | IDelta.DEPRECATION, |
2807 | restrictions, |
2808 | access, |
2809 | access2, |
2810 | this.type1, |
2811 | key, |
2812 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2813 | } |
2814 | // check type parameters |
2815 | String signature1 = method.getGenericSignature(); |
2816 | String signature2 = method2.getGenericSignature(); |
2817 | checkGenericSignature(signature1, signature2, method, method2); |
2818 | |
2819 | if (method.getDefaultValue() == null) { |
2820 | if (method2.getDefaultValue() != null) { |
2821 | // report delta : default value has been added - compatible |
2822 | this.addDelta( |
2823 | getElementType(method), |
2824 | IDelta.ADDED, |
2825 | IDelta.ANNOTATION_DEFAULT_VALUE, |
2826 | restrictions, |
2827 | access, |
2828 | access2, |
2829 | this.type1, |
2830 | key, |
2831 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2832 | } |
2833 | } else if (method2.getDefaultValue() == null) { |
2834 | // report delta : default value has been removed - incompatible |
2835 | this.addDelta( |
2836 | getElementType(method), |
2837 | IDelta.REMOVED, |
2838 | IDelta.ANNOTATION_DEFAULT_VALUE, |
2839 | restrictions, |
2840 | access, |
2841 | access2, |
2842 | this.type1, |
2843 | key, |
2844 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2845 | } else if (!method.getDefaultValue().equals(method2.getDefaultValue())) { |
2846 | // report delta: default value has changed |
2847 | this.addDelta( |
2848 | getElementType(method), |
2849 | IDelta.CHANGED, |
2850 | IDelta.ANNOTATION_DEFAULT_VALUE, |
2851 | restrictions, |
2852 | access, |
2853 | access2, |
2854 | this.type1, |
2855 | key, |
2856 | new String[] {Util.getDescriptorName(this.type1), methodDisplayName}); |
2857 | } |
2858 | } |
2859 | |
2860 | /** |
2861 | * Returns the complete super-interface set for the given type descriptor or null, if it could not be |
2862 | * computed |
2863 | * @param type |
2864 | * @param apiComponent |
2865 | * @param profile |
2866 | * @return the complete super-interface set for the given descriptor, or <code>null</code> |
2867 | */ |
2868 | private Set getInterfacesSet(IApiType type) { |
2869 | HashSet set = new HashSet(); |
2870 | this.status = null; |
2871 | collectAllInterfaces(type, set); |
2872 | if (set.isEmpty()) { |
2873 | return null; |
2874 | } |
2875 | return set; |
2876 | } |
2877 | |
2878 | private String getMethodDisplayName(IApiMethod method, IApiType type) { |
2879 | String methodName = null; |
2880 | if (method.isConstructor()) { |
2881 | methodName = type.getSimpleName(); |
2882 | } else { |
2883 | methodName = method.getName(); |
2884 | } |
2885 | String signature = null; |
2886 | String genericSignature = method.getGenericSignature(); |
2887 | if (genericSignature != null) { |
2888 | signature = genericSignature; |
2889 | } else { |
2890 | signature = method.getSignature(); |
2891 | } |
2892 | return Signature.toString(signature, methodName, null, false, false); |
2893 | } |
2894 | |
2895 | private SignatureDescriptor getSignatureDescriptor(String signature) { |
2896 | SignatureDescriptor signatureDescriptor = new SignatureDescriptor(); |
2897 | SignatureReader signatureReader = new SignatureReader(signature); |
2898 | signatureReader.accept(new SignatureDecoder(signatureDescriptor)); |
2899 | return signatureDescriptor; |
2900 | } |
2901 | private List getSuperclassList(IApiType type) { |
2902 | return getSuperclassList(type, false); |
2903 | } |
2904 | private List getSuperclassList(IApiType type, boolean includeObject) { |
2905 | return getSuperclassList(type, includeObject, false); |
2906 | } |
2907 | private List getSuperclassList(IApiType type, boolean includeObject, boolean includePrivate) { |
2908 | IApiType superClass = type; |
2909 | this.status = null; |
2910 | String superName = superClass.getSuperclassName(); |
2911 | if (Util.isJavaLangObject(superName) && !includeObject) { |
2912 | return null; |
2913 | } |
2914 | List list = new ArrayList(); |
2915 | try { |
2916 | while (superName != null && (!Util.isJavaLangObject(superName) || includeObject)) { |
2917 | superClass = superClass.getSuperclass(); |
2918 | int visibility = VisibilityModifiers.PRIVATE; |
2919 | IApiComponent superComponent = superClass.getApiComponent(); |
2920 | IApiDescription apiDescription = superComponent.getApiDescription(); |
2921 | IApiAnnotations elementDescription = apiDescription.resolveAnnotations(superClass.getHandle()); |
2922 | if (elementDescription != null) { |
2923 | visibility = elementDescription.getVisibility(); |
2924 | } |
2925 | if (includePrivate || ((visibility & visibilityModifiers) != 0)) { |
2926 | list.add(superClass); |
2927 | } |
2928 | superName = superClass.getSuperclassName(); |
2929 | } |
2930 | } catch (CoreException e) { |
2931 | reportStatus(e); |
2932 | } |
2933 | if (list.isEmpty()) return null; |
2934 | return list; |
2935 | } |
2936 | |
2937 | private void reportFieldAddition(IApiField field, IApiType type) { |
2938 | int access = field.getModifiers(); |
2939 | String name = field.getName(); |
2940 | |
2941 | if (Flags.isSynthetic(access)) { |
2942 | // we ignore synthetic fields |
2943 | return; |
2944 | } |
2945 | if ((this.visibilityModifiers == VisibilityModifiers.API) && component2.hasApiDescription()) { |
2946 | // check if this method should be removed because it is tagged as @noreference |
2947 | IApiDescription apiDescription = null; |
2948 | try { |
2949 | apiDescription = this.component2.getApiDescription(); |
2950 | } catch (CoreException e) { |
2951 | reportStatus(e); |
2952 | } |
2953 | if (apiDescription != null) { |
2954 | IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(field.getHandle()); |
2955 | if (apiAnnotations != null) { |
2956 | int restrictions = apiAnnotations.getRestrictions(); |
2957 | if (RestrictionModifiers.isReferenceRestriction(restrictions)) { |
2958 | // such a method is not seen as an API method |
2959 | return; |
2960 | } |
2961 | } |
2962 | } |
2963 | } |
2964 | if (field.isEnumConstant()) { |
2965 | // report delta (addition of an enum constant - compatible |
2966 | this.addDelta( |
2967 | getElementType(type), |
2968 | IDelta.ADDED, |
2969 | IDelta.ENUM_CONSTANT, |
2970 | this.currentDescriptorRestrictions, |
2971 | 0, |
2972 | access, |
2973 | this.type1, |
2974 | name, |
2975 | new String[] {Util.getDescriptorName(type), name}); |
2976 | } else { |
2977 | this.addDelta( |
2978 | getElementType(type), |
2979 | IDelta.ADDED, |
2980 | IDelta.FIELD, |
2981 | this.currentDescriptorRestrictions, |
2982 | 0, |
2983 | access, |
2984 | this.type1, |
2985 | name, |
2986 | new String[] {Util.getDescriptorName(type), name}); |
2987 | } |
2988 | } |
2989 | private void reportMethodAddition(IApiMethod method, IApiType type) { |
2990 | int access = method.getModifiers(); |
2991 | if (method.isClassInitializer()) { |
2992 | // report delta: addition of clinit method |
2993 | this.addDelta( |
2994 | getElementType(type), |
2995 | IDelta.ADDED, |
2996 | IDelta.CLINIT, |
2997 | this.currentDescriptorRestrictions, |
2998 | 0, |
2999 | access, |
3000 | this.type1, |
3001 | type.getName(), |
3002 | Util.getDescriptorName(type1)); |
3003 | return; |
3004 | } |
3005 | if (Flags.isSynthetic(access)) { |
3006 | // we ignore synthetic method |
3007 | return; |
3008 | } |
3009 | if ((this.visibilityModifiers == VisibilityModifiers.API) && component2.hasApiDescription()) { |
3010 | // check if this method should be removed because it is tagged as @noreference |
3011 | IApiDescription apiDescription = null; |
3012 | int restrictions = RestrictionModifiers.NO_RESTRICTIONS; |
3013 | try { |
3014 | apiDescription = this.component2.getApiDescription(); |
3015 | } catch (CoreException e) { |
3016 | reportStatus(e); |
3017 | } |
3018 | if (apiDescription != null) { |
3019 | IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(method.getHandle()); |
3020 | if (apiAnnotations != null) { |
3021 | restrictions = apiAnnotations.getRestrictions(); |
3022 | } |
3023 | } |
3024 | // check if this method should be removed because it is tagged as @noreference |
3025 | if (RestrictionModifiers.isReferenceRestriction(restrictions)) { |
3026 | // such a method is not seen as an API method |
3027 | return; |
3028 | } |
3029 | } |
3030 | String methodDisplayName = getMethodDisplayName(method, type); |
3031 | int restrictionsForMethodAddition = this.currentDescriptorRestrictions; |
3032 | if (Flags.isFinal(this.type2.getModifiers())) { |
3033 | restrictionsForMethodAddition |= RestrictionModifiers.NO_EXTEND; |
3034 | } |
3035 | if (Flags.isPublic(access) || Flags.isProtected(access)) { |
3036 | if (method.isConstructor()) { |
3037 | this.addDelta( |
3038 | getElementType(type), |
3039 | IDelta.ADDED, |
3040 | IDelta.CONSTRUCTOR, |
3041 | restrictionsForMethodAddition, |
3042 | 0, |
3043 | access, |
3044 | this.type1, |
3045 | getKeyForMethod(method, type), |
3046 | new String[] {Util.getDescriptorName(type), methodDisplayName}); |
3047 | } else if (type.isAnnotation()) { |
3048 | if (method.getDefaultValue() != null) { |
3049 | this.addDelta( |
3050 | getElementType(type), |
3051 | IDelta.ADDED, |
3052 | IDelta.METHOD_WITH_DEFAULT_VALUE, |
3053 | restrictionsForMethodAddition, |
3054 | 0, |
3055 | access, |
3056 | this.type1, |
3057 | getKeyForMethod(method, type), |
3058 | new String[] {Util.getDescriptorName(type), methodDisplayName }); |
3059 | } else { |
3060 | this.addDelta( |
3061 | getElementType(type), |
3062 | IDelta.ADDED, |
3063 | IDelta.METHOD_WITHOUT_DEFAULT_VALUE, |
3064 | restrictionsForMethodAddition, |
3065 | 0, |
3066 | access, |
3067 | this.type1, |
3068 | getKeyForMethod(method, type), |
3069 | new String[] {Util.getDescriptorName(type), methodDisplayName }); |
3070 | } |
3071 | } else { |
3072 | // check superclass |
3073 | // if null we need to walk the hierarchy of descriptor2 |
3074 | boolean found = false; |
3075 | if (this.component2 != null) { |
3076 | String name = method.getName(); |
3077 | String descriptor = method.getSignature(); |
3078 | if (this.type1.isInterface()) { |
3079 | Set interfacesSet = getInterfacesSet(this.type2); |
3080 | if (interfacesSet != null && isStatusOk()) { |
3081 | for (Iterator iterator = interfacesSet.iterator(); iterator.hasNext();) { |
3082 | IApiType superTypeDescriptor = (IApiType) iterator.next(); |
3083 | IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor); |
3084 | if (method3 == null) { |
3085 | continue; |
3086 | } else { |
3087 | // interface method can only be public |
3088 | // method has been move up in the hierarchy - report the delta and abort loop |
3089 | found = true; |
3090 | break; |
3091 | } |
3092 | } |
3093 | } |
3094 | } else { |
3095 | List superclassList = getSuperclassList(this.type2, true); |
3096 | if (superclassList != null && isStatusOk()) { |
3097 | loop: for (Iterator iterator = superclassList.iterator(); iterator.hasNext();) { |
3098 | IApiType superTypeDescriptor = (IApiType) iterator.next(); |
3099 | IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor); |
3100 | if (method3 == null) { |
3101 | continue; |
3102 | } else { |
3103 | int access3 = method3.getModifiers(); |
3104 | if (Flags.isPublic(access3) |
3105 | || Flags.isProtected(access3)) { |
3106 | // method has been move up in the hierarchy - report the delta and abort loop |
3107 | // TODO need to make the distinction between methods that need to be re-implemented and methods that don't |
3108 | found = true; |
3109 | break loop; |
3110 | } |
3111 | } |
3112 | } |
3113 | } |
3114 | } |
3115 | } |
3116 | if (!found) { |
3117 | // check if the method has been pushed down |
3118 | // if null we need to walk the hierarchy of descriptor |
3119 | if (this.component != null) { |
3120 | String name = method.getName(); |
3121 | String descriptor = method.getSignature(); |
3122 | if (this.type1.isInterface()) { |
3123 | Set interfacesSet = getInterfacesSet(this.type1); |
3124 | if (interfacesSet != null && isStatusOk()) { |
3125 | for (Iterator iterator = interfacesSet.iterator(); iterator.hasNext();) { |
3126 | IApiType superTypeDescriptor = (IApiType) iterator.next(); |
3127 | IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor); |
3128 | if (method3 == null) { |
3129 | continue; |
3130 | } else { |
3131 | // interface method can only be public |
3132 | // method has been move up in the hierarchy - report the delta and abort loop |
3133 | found = true; |
3134 | break; |
3135 | } |
3136 | } |
3137 | } |
3138 | } else { |
3139 | List superclassList = getSuperclassList(this.type1, true); |
3140 | if (superclassList != null && isStatusOk()) { |
3141 | loop: for (Iterator iterator = superclassList.iterator(); iterator.hasNext();) { |
3142 | IApiType superTypeDescriptor = (IApiType) iterator.next(); |
3143 | IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor); |
3144 | if (method3 == null) { |
3145 | continue; |
3146 | } else { |
3147 | int access3 = method3.getModifiers(); |
3148 | if (Flags.isPublic(access3) |
3149 | || Flags.isProtected(access3)) { |
3150 | // method has been pushed down in the hierarchy - report the delta and abort loop |
3151 | // TODO need to make the distinction between methods that need to be re-implemented and methods that don't |
3152 | found = true; |
3153 | break loop; |
3154 | } |
3155 | } |
3156 | } |
3157 | } |
3158 | } |
3159 | } |
3160 | this.addDelta( |
3161 | getElementType(type), |
3162 | IDelta.ADDED, |
3163 | found ? IDelta.METHOD_MOVED_DOWN : IDelta.METHOD, |
3164 | restrictionsForMethodAddition, |
3165 | 0, |
3166 | method.getModifiers(), |
3167 | this.type1, |
3168 | getKeyForMethod(method, type), |
3169 | new String[] {Util.getDescriptorName(type), methodDisplayName }); |
3170 | } else { |
3171 | this.addDelta( |
3172 | getElementType(type), |
3173 | IDelta.ADDED, |
3174 | found ? IDelta.OVERRIDEN_METHOD : IDelta.METHOD, |
3175 | restrictionsForMethodAddition, |
3176 | 0, |
3177 | method.getModifiers(), |
3178 | this.type1, |
3179 | getKeyForMethod(method, type), |
3180 | new String[] {Util.getDescriptorName(type), methodDisplayName }); |
3181 | } |
3182 | } |
3183 | } else { |
3184 | this.addDelta( |
3185 | getElementType(type), |
3186 | IDelta.ADDED, |
3187 | method.isConstructor() ? IDelta.CONSTRUCTOR : IDelta.METHOD, |
3188 | restrictionsForMethodAddition, |
3189 | 0, |
3190 | method.getModifiers(), |
3191 | this.type1, |
3192 | getKeyForMethod(method, type), |
3193 | new String[] {Util.getDescriptorName(type), methodDisplayName }); |
3194 | } |
3195 | } |
3196 | |
3197 | private String getKeyForMethod(IApiMethod method, IApiType type) { |
3198 | StringBuffer buffer = new StringBuffer(); |
3199 | if (method.isConstructor()) { |
3200 | String name = type.getName(); |
3201 | int index = name.lastIndexOf('.'); |
3202 | int dollarIndex = name.lastIndexOf('$'); |
3203 | if (dollarIndex != -1 && type.isMemberType()) { |
3204 | buffer.append(type.getName().substring(dollarIndex + 1)); |
3205 | } else { |
3206 | buffer.append(type.getName().substring(index + 1)); |
3207 | } |
3208 | } else { |
3209 | buffer.append(method.getName()); |
3210 | } |
3211 | String genericSignature = method.getGenericSignature(); |
3212 | if (genericSignature != null) { |
3213 | buffer.append(genericSignature); |
3214 | } else { |
3215 | buffer.append(method.getSignature()); |
3216 | } |
3217 | return String.valueOf(buffer); |
3218 | } |
3219 | |
3220 | private static boolean isAPI(int visibility, |
3221 | IApiType memberTypeDescriptor) { |
3222 | int access = memberTypeDescriptor.getModifiers(); |
3223 | return VisibilityModifiers.isAPI(visibility) |
3224 | && (Flags.isPublic(access) || Flags.isProtected(access)); |
3225 | } |
3226 | |
3227 | private IApiTypeRoot getType(String typeName, IApiComponent component, IApiBaseline profile) throws CoreException { |
3228 | String packageName = Signatures.getPackageName(typeName); |
3229 | IApiComponent[] components = profile.resolvePackage(component, packageName); |
3230 | if (components == null) { |
3231 | String msg = MessageFormat.format(ComparatorMessages.ClassFileComparator_1, new String[] {packageName, profile.getName(), component.getId()}); |
3232 | if (Debug) { |
3233 | System.err.println("TYPE LOOKUP: "+msg); //$NON-NLS-1$ |
3234 | } |
3235 | reportStatus(new Status(IStatus.ERROR, component.getId(), msg)); |
3236 | return null; |
3237 | } |
3238 | IApiTypeRoot result = Util.getClassFile(components, typeName); |
3239 | if (result == null) { |
3240 | String msg = MessageFormat.format(ComparatorMessages.ClassFileComparator_2, new String[] {typeName, profile.getName(), component.getId()}); |
3241 | if (Debug) { |
3242 | System.err.println("TYPE LOOKUP: "+msg); //$NON-NLS-1$ |
3243 | } |
3244 | reportStatus(new Status(IStatus.ERROR, component.getId(), msg)); |
3245 | return null; |
3246 | } |
3247 | return result; |
3248 | } |
3249 | |
3250 | /** |
3251 | * Returns the delta element type code for the given type. |
3252 | * Translates a type to interface, class, emum or annotation. |
3253 | * |
3254 | * @param type |
3255 | * @return delta element type |
3256 | */ |
3257 | private int getElementType(IApiType type) { |
3258 | if (type.isAnnotation()) { |
3259 | return IDelta.ANNOTATION_ELEMENT_TYPE; |
3260 | } |
3261 | if (type.isEnum()) { |
3262 | return IDelta.ENUM_ELEMENT_TYPE; |
3263 | } |
3264 | if (type.isInterface()) { |
3265 | return IDelta.INTERFACE_ELEMENT_TYPE; |
3266 | } |
3267 | return IDelta.CLASS_ELEMENT_TYPE; |
3268 | } |
3269 | |
3270 | /** |
3271 | * Returns the delta element type code for the given method. |
3272 | * Translates a method to constructor or method. |
3273 | * |
3274 | * @param method |
3275 | * @return delta element type |
3276 | */ |
3277 | private int getElementType(IApiMethod method) { |
3278 | if (method.isConstructor()) { |
3279 | return IDelta.CONSTRUCTOR_ELEMENT_TYPE; |
3280 | } |
3281 | return IDelta.METHOD_ELEMENT_TYPE; |
3282 | } |
3283 | |
3284 | /** |
3285 | * Returns the delta type code for the given method when it is the target of a remove/add. |
3286 | * Translates a method to constructor or method. |
3287 | * |
3288 | * @param method |
3289 | * @return delta type |
3290 | */ |
3291 | private int getTargetType(IApiMethod method) { |
3292 | if (method.isConstructor()) { |
3293 | return IDelta.CONSTRUCTOR; |
3294 | } |
3295 | return IDelta.METHOD; |
3296 | } |
3297 | |
3298 | /** |
3299 | * Translates a member to its delta element type code. |
3300 | * |
3301 | * @param member |
3302 | * @return delta element type code |
3303 | */ |
3304 | private int getElementType(IApiMember member) { |
3305 | switch (member.getType()) { |
3306 | case IApiElement.TYPE: |
3307 | return getElementType((IApiType)member); |
3308 | case IApiElement.METHOD: |
3309 | return getElementType((IApiMethod)member); |
3310 | } |
3311 | return IDelta.FIELD_ELEMENT_TYPE; |
3312 | } |
3313 | } |