1 /*******************************************************************************
2  * Copyright (c) 2008, 2019 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *******************************************************************************/
14 package org.eclipse.pde.api.tools.internal.comparator;
15 
16 import java.text.MessageFormat;
17 import java.util.ArrayList;
18 import java.util.Collections;
19 import java.util.HashSet;
20 import java.util.Iterator;
21 import java.util.List;
22 import java.util.Set;
23 
24 import org.eclipse.core.runtime.CoreException;
25 import org.eclipse.core.runtime.IProgressMonitor;
26 import org.eclipse.core.runtime.IStatus;
27 import org.eclipse.core.runtime.MultiStatus;
28 import org.eclipse.core.runtime.Status;
29 import org.eclipse.core.runtime.SubMonitor;
30 import org.eclipse.jdt.core.Flags;
31 import org.eclipse.jdt.core.Signature;
32 import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
33 import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations;
34 import org.eclipse.pde.api.tools.internal.provisional.IApiDescription;
35 import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers;
36 import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers;
37 import org.eclipse.pde.api.tools.internal.provisional.comparator.ApiComparator;
38 import org.eclipse.pde.api.tools.internal.provisional.comparator.IDelta;
39 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMemberDescriptor;
40 import org.eclipse.pde.api.tools.internal.provisional.descriptors.IMethodDescriptor;
41 import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
42 import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
43 import org.eclipse.pde.api.tools.internal.provisional.model.IApiElement;
44 import org.eclipse.pde.api.tools.internal.provisional.model.IApiField;
45 import org.eclipse.pde.api.tools.internal.provisional.model.IApiMember;
46 import org.eclipse.pde.api.tools.internal.provisional.model.IApiMethod;
47 import org.eclipse.pde.api.tools.internal.provisional.model.IApiType;
48 import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot;
49 import org.eclipse.pde.api.tools.internal.util.Signatures;
50 import org.eclipse.pde.api.tools.internal.util.Util;
51 import org.objectweb.asm.signature.SignatureReader;
52 
53 /**
54  * Compares class files from the workspace to those in the default
55  * {@link IApiBaseline}
56  *
57  * @since 1.0.0
58  */
59 public class ClassFileComparator {
60 
isCheckedException(IApiBaseline baseline, IApiComponent apiComponent, String exceptionName)61 	private boolean isCheckedException(IApiBaseline baseline, IApiComponent apiComponent, String exceptionName) {
62 		if (baseline == null) {
63 			return true;
64 		}
65 		try {
66 			if (Util.isJavaLangRuntimeException(exceptionName)) {
67 				return false;
68 			}
69 			String packageName = Signatures.getPackageName(exceptionName);
70 			IApiTypeRoot result = Util.getClassFile(baseline.resolvePackage(apiComponent, packageName), exceptionName);
71 			if (result != null) {
72 				// TODO should this be reported as a checked exception
73 				IApiType exception = result.getStructure();
74 				if (exception == null) {
75 					return false;
76 				}
77 				while (!Util.isJavaLangObject(exception.getName())) {
78 					String superName = exception.getSuperclassName();
79 					packageName = Signatures.getPackageName(superName);
80 					result = Util.getClassFile(baseline.resolvePackage(apiComponent, packageName), superName);
81 					if (result == null) {
82 						// TODO should we report this failure ?
83 						if (ApiPlugin.DEBUG_CLASSFILE_COMPARATOR) {
84 							System.err.println("CHECKED EXCEPTION LOOKUP: Could not find " + superName + " in baseline " + baseline.getName() + " from component " + apiComponent.getSymbolicName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
85 						}
86 						break;
87 					}
88 					exception = result.getStructure();
89 					if (Util.isJavaLangRuntimeException(exception.getName())) {
90 						return false;
91 					}
92 				}
93 			}
94 		} catch (CoreException e) {
95 			// by default exception are considered as checked exception
96 			reportStatus(e);
97 		}
98 		return true;
99 	}
100 
101 	private IApiBaseline apiBaseline1 = null;
102 	private IApiBaseline apiBaseline2 = null;
103 
104 	private IApiComponent component = null;
105 	private IApiComponent component2 = null;
106 
107 	private Delta delta = null;
108 	private IApiType type1 = null;
109 
110 	private IApiType type2 = null;
111 
112 	private int visibilityModifiers;
113 	private int currentDescriptorRestrictions;
114 	private int initialDescriptorRestrictions;
115 	private MultiStatus status = null;
116 
117 	/**
118 	 * Constructor
119 	 *
120 	 * @param classFile the class file from the workspace to compare
121 	 * @param classFile2 the class file from the baseline to compare to
122 	 * @param component the API component from the workspace
123 	 * @param component2 the API component from the baseline
124 	 * @param apiState the workspace API baseline
125 	 * @param apiState2 the baseline API baseline
126 	 * @param visibilityModifiers any modifiers from the class file
127 	 * @throws CoreException if the contents of the specified class files cannot
128 	 *             be acquired
129 	 */
ClassFileComparator(IApiTypeRoot classFile, IApiTypeRoot classFile2, IApiComponent component, IApiComponent component2, IApiBaseline apiState, IApiBaseline apiState2, int visibilityModifiers)130 	public ClassFileComparator(IApiTypeRoot classFile, IApiTypeRoot classFile2, IApiComponent component, IApiComponent component2, IApiBaseline apiState, IApiBaseline apiState2, int visibilityModifiers) throws CoreException {
131 		this.component = component;
132 		this.component2 = component2;
133 		this.type1 = classFile.getStructure();
134 		this.type2 = classFile2.getStructure();
135 		this.apiBaseline1 = apiState;
136 		this.apiBaseline2 = apiState2;
137 		this.visibilityModifiers = visibilityModifiers;
138 	}
139 
140 	/**
141 	 * Constructor
142 	 *
143 	 * @param type the {@link IApiType} from the workspace to compare
144 	 * @param classFile2 the class file from the baseline to compare to
145 	 * @param component the API component from the workspace
146 	 * @param component2 the API component from the baseline
147 	 * @param apiState the workspace API baseline
148 	 * @param apiState2 the baseline API baseline
149 	 * @param visibilityModifiers any modifiers from the class file
150 	 * @throws CoreException if the contents of the specified class file cannot
151 	 *             be acquired
152 	 */
ClassFileComparator(IApiType type, IApiTypeRoot classFile2, IApiComponent component, IApiComponent component2, IApiBaseline apiState, IApiBaseline apiState2, int visibilityModifiers)153 	public ClassFileComparator(IApiType type, IApiTypeRoot classFile2, IApiComponent component, IApiComponent component2, IApiBaseline apiState, IApiBaseline apiState2, int visibilityModifiers) throws CoreException {
154 		this.component = component;
155 		this.component2 = component2;
156 		this.type1 = type;
157 		this.type2 = classFile2.getStructure();
158 		this.apiBaseline1 = apiState;
159 		this.apiBaseline2 = apiState2;
160 		this.visibilityModifiers = visibilityModifiers;
161 	}
162 
addDelta(IDelta delta)163 	private void addDelta(IDelta delta) {
164 		this.delta.add(delta);
165 	}
166 
addDelta(int elementType, int kind, int flags, int restrictions, int oldModifiers, int newModifiers, IApiType type, String key, String data)167 	private void addDelta(int elementType, int kind, int flags, int restrictions, int oldModifiers, int newModifiers, IApiType type, String key, String data) {
168 		this.addDelta(new Delta(Util.getDeltaComponentVersionsId(this.component2), elementType, kind, flags, restrictions, oldModifiers, newModifiers, type.getName(), key, data));
169 	}
170 
addDelta(int elementType, int kind, int flags, int restrictions, int oldModifiers, int newModifiers, IApiType type, String key, String[] datas)171 	private void addDelta(int elementType, int kind, int flags, int restrictions, int oldModifiers, int newModifiers, IApiType type, String key, String[] datas) {
172 		this.addDelta(new Delta(Util.getDeltaComponentVersionsId(this.component2), elementType, kind, flags, restrictions, 0, oldModifiers, newModifiers, type.getName(), key, datas));
173 	}
174 
addDelta(int elementType, int kind, int flags, int currentRestrictions, int previousRestrictions, int oldModifiers, int newModifiers, IApiType type, String key, String[] datas)175 	private void addDelta(int elementType, int kind, int flags, int currentRestrictions, int previousRestrictions, int oldModifiers, int newModifiers, IApiType type, String key, String[] datas) {
176 		this.addDelta(new Delta(Util.getDeltaComponentVersionsId(this.component2), elementType, kind, flags, currentRestrictions, previousRestrictions, oldModifiers, newModifiers, type.getName(), key, datas));
177 	}
178 
179 	/**
180 	 * Checks if the super-class set has been change in any way compared to the
181 	 * baseline (grown or reduced or types changed)
182 	 */
checkSuperclass()183 	private void checkSuperclass() {
184 		// check superclass set
185 		List<?> superclassList1 = getSuperclassList(this.type1);
186 		if (!isStatusOk()) {
187 			return;
188 		}
189 		Set<String> superclassNames2 = null;
190 		List<IApiType> superclassList2 = getSuperclassList(this.type2);
191 		if (!isStatusOk()) {
192 			return;
193 		}
194 		if (superclassList2 != null) {
195 			superclassNames2 = new HashSet<>();
196 			Iterator<IApiType> iterator = superclassList2.iterator();
197 			while (iterator.hasNext()) {
198 				superclassNames2.add(iterator.next().getName());
199 			}
200 		}
201 		if (superclassList1 == null) {
202 			if (superclassList2 != null) {
203 				// If superclassList2 has 1 abstract method and current class don't have method
204 				// implemented, then breaking change
205 				for (IApiType iApiType : superclassList2) {
206 					IApiMethod[] methods = iApiType.getMethods();
207 					for (IApiMethod iMethod : methods) {
208 						boolean isAbstractMethod = Flags.isAbstract(iMethod.getModifiers());
209 						if (isAbstractMethod) {
210 							boolean isBreakingChange = false;
211 							IApiMethod meth = this.type2.getMethod(iMethod.getName(), iMethod.getSignature());
212 							if(meth == null) {
213 								isBreakingChange = true;
214 							}
215 							if(meth !=null) {
216 								isBreakingChange= Flags.isSynthetic(meth.getModifiers());
217 							}
218 							if (isBreakingChange) {
219 								this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.SUPERCLASS_BREAKING,
220 										this.currentDescriptorRestrictions, this.type1.getModifiers(),
221 										this.type2.getModifiers(), this.type1, this.type1.getName(),
222 										Util.getDescriptorName(type1));
223 								return;
224 							}
225 						}
226 					}
227 				}
228 				// this means the direct super class of descriptor1 is
229 				// java.lang.Object
230 				this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.SUPERCLASS, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
231 			}
232 		} else if (superclassList2 == null) {
233 			// this means the direct super class of descriptor2 is
234 			// java.lang.Object
235 			this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.SUPERCLASS, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
236 		}
237 		// get superclass of descriptor2
238 		if (superclassList1 != null && superclassList2 != null) {
239 			IApiType superclassType2 = superclassList2.get(0);
240 			IApiType superclassType = (IApiType) superclassList1.get(0);
241 			if (!superclassType.getName().equals(superclassType2.getName())) {
242 				if (!superclassNames2.contains(superclassType.getName())) {
243 					this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.SUPERCLASS, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
244 				} else {
245 					this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.SUPERCLASS, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, this.type1.getName(), Util.getDescriptorName(type1));
246 				}
247 			}
248 		}
249 	}
250 
251 	/**
252 	 * reports problem status to the comparators' complete status
253 	 *
254 	 * @param newstatus
255 	 */
reportStatus(IStatus newstatus)256 	protected void reportStatus(IStatus newstatus) {
257 		if (this.status == null) {
258 			String msg = MessageFormat.format(ComparatorMessages.ClassFileComparator_0, this.type1.getName());
259 			this.status = new MultiStatus(ApiPlugin.PLUGIN_ID, IStatus.ERROR, msg, null);
260 		}
261 		this.status.add(newstatus);
262 	}
263 
264 	/**
265 	 * Report problem to the comparators' status
266 	 *
267 	 * @param e
268 	 */
reportStatus(CoreException e)269 	private void reportStatus(CoreException e) {
270 		reportStatus(e.getStatus());
271 	}
272 
273 	/**
274 	 * @return if the status of the compare is ok
275 	 */
isStatusOk()276 	private boolean isStatusOk() {
277 		return this.status == null;
278 	}
279 
280 	/**
281 	 * @return the status of the compare and delta creation
282 	 */
getStatus()283 	public IStatus getStatus() {
284 		return this.status;
285 	}
286 
287 	/**
288 	 * Checks if there are any changes to the super-interface set for the
289 	 * current type descriptor context. A change is one of:
290 	 * <ul>
291 	 * <li>An interface has been added to the current super-interface set
292 	 * compared to the current baseline</li>
293 	 * <li>An interface has been removed from the current super-interface set
294 	 * compared to the current baseline</li>
295 	 * <li>An interface has changed (same number of interfaces, but different
296 	 * types) compared to the current baseline</li>
297 	 * </ul>
298 	 */
checkSuperInterfaces()299 	private void checkSuperInterfaces() {
300 		Set<IApiType> superinterfacesSet1 = getInterfacesSet(this.type1);
301 		if (!isStatusOk()) {
302 			return;
303 		}
304 		Set<IApiType> superinterfacesSet2 = getInterfacesSet(this.type2);
305 		if (!isStatusOk()) {
306 			return;
307 		}
308 		if (superinterfacesSet1 == null) {
309 			if (superinterfacesSet2 != null) {
310 				if (this.type1.isClass()) {
311 					for (IApiType iApiType : superinterfacesSet2) {
312 						IApiMethod[] methods = iApiType.getMethods();
313 						for (IApiMethod iMethod : methods) {
314 
315 							boolean defMethod = iMethod.isDefaultMethod();
316 							if (defMethod == false) {
317 								boolean isBreakingChange = false;
318 								IApiMethod meth = this.type2.getMethod(iMethod.getName(), iMethod.getSignature());
319 								if (meth == null) {
320 									// check in superclasses
321 									List<IApiType> superclassList = getSuperclassList(this.type2);
322 									if (superclassList != null) {
323 										for (IApiType apiType : superclassList) {
324 											meth = apiType.getMethod(iMethod.getName(),
325 													iMethod.getSignature());
326 											if (meth != null) {
327 												break;
328 											}
329 										}
330 									}
331 								}
332 								if (meth == null) {
333 									isBreakingChange = true;
334 								}
335 								if(meth !=null) {
336 									isBreakingChange = Flags.isSynthetic(meth.getModifiers());
337 								}
338 								if (isBreakingChange) {
339 									this.addDelta(getElementType(this.type1), IDelta.ADDED,
340 											IDelta.EXPANDED_SUPERINTERFACES_SET_BREAKING,
341 											this.currentDescriptorRestrictions, this.type1.getModifiers(),
342 											this.type2.getModifiers(), this.type1, this.type1.getName(),
343 											Util.getDescriptorName(type1));
344 									return;
345 								}
346 							}
347 						}
348 					}
349 				}
350 
351 				this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.EXPANDED_SUPERINTERFACES_SET,
352 						this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(),
353 						this.type1, this.type1.getName(), new String[] { Util.getDescriptorName(type1),
354 								computeDiff(superinterfacesSet1, superinterfacesSet2, true) });
355 				if (this.type1.isInterface()) {
356 					for (Iterator<IApiType> iterator = superinterfacesSet2.iterator(); iterator.hasNext();) {
357 						IApiType type = iterator.next();
358 						IApiMethod[] methods = type.getMethods();
359 						int length = methods.length;
360 						if (length != 0) {
361 							// we should check if every method defined in the
362 							// new interface exists in the old hierarchy
363 							// could be methods moved up in the hierarchy
364 							boolean isSuperInterfaceWithMethodDeltaAdded = false;
365 							for (int j = 0; j < length; j++) {
366 								IApiMethod method = methods[j];
367 								IApiMethod method3 = this.type1.getMethod(method.getName(), method.getSignature());
368 								if (method3 == null) {
369 									String key = this.type1.getName();
370 									boolean isDefaultMethod = false;
371 									if (this.type2.getMethod(method.getName(), method.getSignature()) != null) {
372 										isDefaultMethod = this.type2.getMethod(method.getName(), method.getSignature()).isDefaultMethod();
373 										if(isDefaultMethod) {
374 											key = getKeyForMethod(this.type2.getMethod(method.getName(), method.getSignature()), this.type2);
375 										}
376 									}
377 									if (!isDefaultMethod && isSuperInterfaceWithMethodDeltaAdded == false) {
378 										this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.SUPER_INTERFACE_WITH_METHODS, this.currentDescriptorRestrictions, this.initialDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, key, new String[] {
379 												Util.getDescriptorName(type1),
380 												type.getName(),
381 												getMethodDisplayName(method, type) });
382 										isSuperInterfaceWithMethodDeltaAdded = true;
383 									}
384 									if (isDefaultMethod) {
385 										this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.SUPER_INTERFACE_DEFAULT_METHOD, this.currentDescriptorRestrictions, this.initialDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, key, new String[] {
386 												Util.getDescriptorName(type1),
387 												type.getName(),
388 												getMethodDisplayName(method, type) });
389 									}
390 								}
391 							}
392 						}
393 					}
394 				}
395 			}
396 		} else if (superinterfacesSet2 == null) {
397 			this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.CONTRACTED_SUPERINTERFACES_SET,
398 					this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(),
399 					this.type1, this.type1.getName(), new String[] { Util.getDescriptorName(type1),
400 							computeDiff(superinterfacesSet1, superinterfacesSet2, false) });
401 		} else {
402 			Set<String> names2 = new HashSet<>();
403 			for (Iterator<IApiType> iterator = superinterfacesSet2.iterator(); iterator.hasNext();) {
404 				names2.add(iterator.next().getName());
405 			}
406 			Set<String> names1 = new HashSet<>();
407 			for (Iterator<IApiType> iterator = superinterfacesSet1.iterator(); iterator.hasNext();) {
408 				names1.add(iterator.next().getName());
409 			}
410 			boolean contracted = false;
411 			for (String name : names1) {
412 				if (!names2.remove(name)) {
413 					contracted = true;
414 				}
415 			}
416 			if (contracted) {
417 				this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.CONTRACTED_SUPERINTERFACES_SET,
418 						this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(),
419 						this.type1, this.type1.getName(), new String[] { Util.getDescriptorName(type1),
420 								computeDiff(superinterfacesSet1, superinterfacesSet2, false) });
421 				return;
422 			}
423 			if (names2.size() > 0) {
424 				if (this.type1.isClass()) {
425 					for (IApiType apiType : superinterfacesSet2) {
426 						String name = apiType.getName();
427 						if (names2.contains(name)) {
428 							IApiMethod[] methods = apiType.getMethods();
429 							for (IApiMethod iMethod : methods) {
430 								boolean defMethod = iMethod.isDefaultMethod();
431 								if (defMethod == false) {
432 									boolean isBreakingChange = false;
433 									IApiMethod meth = this.type2.getMethod(iMethod.getName(), iMethod.getSignature());
434 									if (meth == null) {
435 										// check in superclasses
436 										List<IApiType> superclassList = getSuperclassList(this.type2);
437 										if (superclassList != null) {
438 											for (IApiType type : superclassList) {
439 												meth = type.getMethod(iMethod.getName(), iMethod.getSignature());
440 												if (meth != null) {
441 													break;
442 												}
443 											}
444 										}
445 									}
446 									if (meth == null) {
447 										isBreakingChange = true;
448 									}
449 									if(meth !=null) {
450 										isBreakingChange = Flags.isSynthetic(meth.getModifiers());
451 									}
452 									if (isBreakingChange) {
453 										this.addDelta(getElementType(this.type1), IDelta.CHANGED,
454 												IDelta.EXPANDED_SUPERINTERFACES_SET_BREAKING,
455 												this.currentDescriptorRestrictions, this.type1.getModifiers(),
456 												this.type2.getModifiers(), this.type1, this.type1.getName(),
457 												Util.getDescriptorName(type1));
458 										return;
459 									}
460 								}
461 							}
462 
463 						}
464 					}
465 				}
466 
467 				this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.EXPANDED_SUPERINTERFACES_SET,
468 						this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(),
469 						this.type1, this.type1.getName(), new String[] { Util.getDescriptorName(type1),
470 								computeDiff(superinterfacesSet1, superinterfacesSet2, true) });
471 				if (this.type1.isInterface()) {
472 					for (Iterator<String> iterator = names2.iterator(); iterator.hasNext();) {
473 						String interfaceName = iterator.next();
474 						try {
475 							IApiTypeRoot interfaceClassFile = getType(interfaceName, this.component2, this.apiBaseline2);
476 							if (interfaceClassFile == null) {
477 								continue;
478 							}
479 							IApiType type = interfaceClassFile.getStructure();
480 							if (type == null) {
481 								continue;
482 							}
483 							IApiMethod[] methods = type.getMethods();
484 							int length = methods.length;
485 							if (length > 0) {
486 								// we should check if every method defined in
487 								// the new interface exists in the old hierarchy
488 								// could be methods moved up in the hierarchy
489 								methodLoop: for (int j = 0; j < length; j++) {
490 									IApiMethod method = methods[j];
491 									boolean found = false;
492 									interfaceLoop: for (Iterator<IApiType> iterator2 = superinterfacesSet1.iterator(); iterator2.hasNext();) {
493 										IApiType superTypeDescriptor = iterator2.next();
494 										IApiMethod method3 = superTypeDescriptor.getMethod(method.getName(), method.getSignature());
495 										if (method3 == null) {
496 											continue interfaceLoop;
497 										} else {
498 											found = true;
499 											break interfaceLoop;
500 										}
501 									}
502 									if (!found) {
503 										String key = this.type1.getName();
504 										boolean isDefaultMethod = false;
505 										if(this.type2.getMethod(method.getName(), method.getSignature())!=null) {
506 											isDefaultMethod = this.type2.getMethod(method.getName(), method.getSignature()).isDefaultMethod();
507 											if (isDefaultMethod) {
508 												key = getKeyForMethod(this.type2.getMethod(method.getName(), method.getSignature()), this.type2);
509 											}
510 										}
511 										this.addDelta(getElementType(this.type1), IDelta.ADDED, isDefaultMethod ? IDelta.DEFAULT_METHOD : IDelta.SUPER_INTERFACE_WITH_METHODS, this.currentDescriptorRestrictions, this.initialDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, key, new String[] {
512 												Util.getDescriptorName(type1),
513 												type.getName(),
514 												getMethodDisplayName(method, type) });
515 										break methodLoop;
516 									}
517 								}
518 							}
519 						} catch (CoreException e) {
520 							ApiPlugin.log(e);
521 						}
522 					}
523 				}
524 			}
525 		}
526 	}
527 
computeDiff(Set<IApiType> superinterfacesSet1, Set<IApiType> superinterfacesSet2, boolean expand)528 	private String computeDiff(Set<IApiType> superinterfacesSet1, Set<IApiType> superinterfacesSet2, boolean expand) {
529 		Set<String> namesToReturn = new HashSet<>();
530 		if (superinterfacesSet1 == null) {
531 			for (Iterator<IApiType> iterator = superinterfacesSet2.iterator(); iterator.hasNext();) {
532 				namesToReturn.add(iterator.next().getName());
533 			}
534 			return processNames(namesToReturn);
535 
536 		}
537 		if (superinterfacesSet2 == null) {
538 			for (Iterator<IApiType> iterator = superinterfacesSet1.iterator(); iterator.hasNext();) {
539 				namesToReturn.add(iterator.next().getName());
540 			}
541 			return processNames(namesToReturn);
542 
543 		}
544 		for (Iterator<IApiType> iterator = expand ? superinterfacesSet2.iterator()
545 				: superinterfacesSet1.iterator(); iterator.hasNext();) {
546 			namesToReturn.add(iterator.next().getName());
547 		}
548 		Set<String> names1 = new HashSet<>();
549 		for (Iterator<IApiType> iterator = expand ? superinterfacesSet1.iterator()
550 				: superinterfacesSet2.iterator(); iterator.hasNext();) {
551 			names1.add(iterator.next().getName());
552 		}
553 
554 		for (String name : names1) {
555 			namesToReturn.remove(name);
556 
557 		}
558 		return processNames(namesToReturn);
559 
560 	}
561 
processNames(Set<String> namesToReturn)562 	private String processNames(Set<String> namesToReturn) {
563 		StringBuilder str = new StringBuilder();
564 		for (String string : namesToReturn) {
565 			str.append(string);
566 			str.append(',');
567 		}
568 		return str.substring(0, str.length() - 1);
569 	}
570 
checkTypeMembers()571 	private void checkTypeMembers() throws CoreException {
572 		IApiType[] typeMembers = this.type1.getMemberTypes();
573 		IApiType[] typeMembers2 = this.type2.getMemberTypes();
574 		List<String> added = new ArrayList<>(typeMembers2.length);
575 		for (int i = 0; i < typeMembers2.length; i++) {
576 			added.add(typeMembers2[i].getName());
577 		}
578 		if (typeMembers.length > 0) {
579 			if (typeMembers2.length == 0) {
580 				loop: for (int i = 0; i < typeMembers.length; i++) {
581 					try {
582 						IApiType typeMember = typeMembers[i];
583 						// check visibility
584 						IApiDescription apiDescription = this.component.getApiDescription();
585 						IApiAnnotations memberTypeElementDescription = apiDescription.resolveAnnotations(typeMember.getHandle());
586 						int memberTypeVisibility = 0;
587 						if (memberTypeElementDescription != null) {
588 							memberTypeVisibility = memberTypeElementDescription.getVisibility();
589 						}
590 						if ((memberTypeVisibility & visibilityModifiers) == 0) {
591 							// we skip the class file according to their
592 							// visibility
593 							continue loop;
594 						}
595 						if (visibilityModifiers == VisibilityModifiers.API) {
596 							// if the visibility is API, we only consider public
597 							// and protected types
598 							if (Util.isDefault(typeMember.getModifiers()) || Flags.isPrivate(typeMember.getModifiers())) {
599 								continue loop;
600 							}
601 						}
602 						this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.TYPE_MEMBER, this.currentDescriptorRestrictions, typeMember.getModifiers(), 0, this.type1, typeMember.getName(), new String[] {
603 								typeMember.getName().replace('$', '.'),
604 								Util.getComponentVersionsId(component2) });
605 					} catch (CoreException e) {
606 						reportStatus(e);
607 					}
608 				}
609 				return;
610 			}
611 			// check removed or added type members
612 			List<IApiType> removedTypeMembers = new ArrayList<>();
613 			loop: for (int i = 0; i < typeMembers.length; i++) {
614 				IApiType typeMember = typeMembers[i];
615 				IApiType typeMember2 = this.type2.getMemberType(typeMember.getSimpleName());
616 				if (typeMember2 == null) {
617 					removedTypeMembers.add(typeMember);
618 				} else {
619 					added.remove(typeMember2.getName());
620 					// check deltas inside the type member
621 					try {
622 						// check visibility of member types
623 						IApiDescription apiDescription = this.component.getApiDescription();
624 						IApiAnnotations memberTypeElementDescription = apiDescription.resolveAnnotations(typeMember.getHandle());
625 						int memberTypeVisibility = 0;
626 						if (memberTypeElementDescription != null) {
627 							memberTypeVisibility = memberTypeElementDescription.getVisibility();
628 						}
629 						if ((memberTypeVisibility & visibilityModifiers) == 0) {
630 							// we skip the class file according to their
631 							// visibility
632 							continue loop;
633 						}
634 						IApiDescription apiDescription2 = this.component2.getApiDescription();
635 						IApiAnnotations memberTypeElementDescription2 = apiDescription2.resolveAnnotations(typeMember2.getHandle());
636 						int memberTypeVisibility2 = 0;
637 						if (memberTypeElementDescription2 != null) {
638 							memberTypeVisibility2 = memberTypeElementDescription2.getVisibility();
639 						}
640 						String deltaComponentID = Util.getDeltaComponentVersionsId(component2);
641 						int restrictions = memberTypeElementDescription2 != null ? memberTypeElementDescription2.getRestrictions() : RestrictionModifiers.NO_RESTRICTIONS;
642 						if (Flags.isFinal(this.type2.getModifiers())) {
643 							restrictions |= RestrictionModifiers.NO_EXTEND;
644 						}
645 						if (isAPI(memberTypeVisibility, typeMember) && !isAPI(memberTypeVisibility2, typeMember2)) {
646 							this.addDelta(new Delta(deltaComponentID, getElementType(typeMember), IDelta.CHANGED, IDelta.DECREASE_ACCESS, restrictions | this.currentDescriptorRestrictions, 0, typeMember.getModifiers(), typeMember2.getModifiers(), typeMember.getName(), typeMember.getName(), new String[] { typeMember.getName().replace('$', '.') }));
647 							continue;
648 						}
649 						if ((memberTypeVisibility2 & visibilityModifiers) == 0) {
650 							// we simply report a changed visibility
651 							this.addDelta(new Delta(deltaComponentID, getElementType(typeMember), IDelta.CHANGED, IDelta.TYPE_VISIBILITY, restrictions | this.currentDescriptorRestrictions, 0, typeMember.getModifiers(), typeMember2.getModifiers(), typeMember.getName(), typeMember.getName(), new String[] { typeMember.getName().replace('$', '.') }));
652 						}
653 						if (this.visibilityModifiers == VisibilityModifiers.API) {
654 							// if the visibility is API, we only consider public
655 							// and protected types
656 							if (Util.isDefault(typeMember2.getModifiers()) || Flags.isPrivate(typeMember2.getModifiers())) {
657 								continue loop;
658 							}
659 						}
660 						IApiTypeRoot memberType2 = this.component2.findTypeRoot(typeMember.getName());
661 						ClassFileComparator comparator = new ClassFileComparator(typeMember, memberType2, this.component, this.component2, this.apiBaseline1, this.apiBaseline2, this.visibilityModifiers);
662 						IDelta delta2 = comparator.getDelta(null);
663 						if (delta2 != null && delta2 != ApiComparator.NO_DELTA) {
664 							this.addDelta(delta2);
665 						}
666 					} catch (CoreException e) {
667 						reportStatus(e);
668 					}
669 				}
670 			}
671 			loop: for (Iterator<IApiType> iterator = removedTypeMembers.iterator(); iterator.hasNext();) {
672 				try {
673 					IApiType typeMember = iterator.next();
674 					// check visibility
675 					IApiDescription apiDescription = this.component.getApiDescription();
676 					IApiAnnotations memberTypeElementDescription = apiDescription.resolveAnnotations(typeMember.getHandle());
677 					int memberTypeVisibility = 0;
678 					if (memberTypeElementDescription != null) {
679 						memberTypeVisibility = memberTypeElementDescription.getVisibility();
680 					}
681 					if ((memberTypeVisibility & visibilityModifiers) == 0) {
682 						// we skip the class file according to their visibility
683 						continue loop;
684 					}
685 					if (this.visibilityModifiers == VisibilityModifiers.API) {
686 						// if the visibility is API, we only consider public and
687 						// protected types
688 						if (Util.isDefault(typeMember.getModifiers()) || Flags.isPrivate(typeMember.getModifiers())) {
689 							continue loop;
690 						}
691 					}
692 					this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.TYPE_MEMBER, memberTypeElementDescription != null ? memberTypeElementDescription.getRestrictions() : RestrictionModifiers.NO_RESTRICTIONS, typeMember.getModifiers(), 0, this.type1, typeMember.getName(), new String[] {
693 							typeMember.getName().replace('$', '.'),
694 							Util.getComponentVersionsId(component2) });
695 				} catch (CoreException e) {
696 					reportStatus(e);
697 				}
698 			}
699 		}
700 		// report remaining types as addition
701 		// Report delta as a breakage
702 		loop: for (Iterator<String> iterator = added.iterator(); iterator.hasNext();) {
703 			try {
704 				String name = iterator.next();
705 				int index = name.lastIndexOf('$');
706 				IApiType typeMember = this.type2.getMemberType(name.substring(index + 1));
707 				// check visibility
708 				IApiDescription apiDescription2 = this.component2.getApiDescription();
709 				IApiAnnotations memberTypeElementDescription2 = apiDescription2.resolveAnnotations(typeMember.getHandle());
710 				int memberTypeVisibility2 = 0;
711 				if (memberTypeElementDescription2 != null) {
712 					memberTypeVisibility2 = memberTypeElementDescription2.getVisibility();
713 				}
714 				if ((memberTypeVisibility2 & visibilityModifiers) == 0) {
715 					// we skip the class file according to their visibility
716 					continue loop;
717 				}
718 				if (this.visibilityModifiers == VisibilityModifiers.API) {
719 					// if the visibility is API, we only consider public and
720 					// protected types
721 					if (Util.isDefault(typeMember.getModifiers()) || Flags.isPrivate(typeMember.getModifiers())) {
722 						continue loop;
723 					}
724 				}
725 				this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.TYPE_MEMBER, this.currentDescriptorRestrictions, 0, typeMember.getModifiers(), this.type1, typeMember.getSimpleName(), typeMember.getSimpleName());
726 			} catch (CoreException e) {
727 				reportStatus(e);
728 			}
729 		}
730 	}
731 
checkGenericSignature(String signature1, String signature2, IApiMember element1, IApiMember element2)732 	private void checkGenericSignature(String signature1, String signature2, IApiMember element1, IApiMember element2) {
733 		if (signature1 == null) {
734 			if (signature2 != null) {
735 				// added type parameter from scratch (none before)
736 				// report delta as compatible
737 				SignatureDescriptor signatureDescriptor2 = getSignatureDescriptor(signature2);
738 				TypeParameterDescriptor[] typeParameterDescriptors = signatureDescriptor2.getTypeParameterDescriptors();
739 				if (typeParameterDescriptors.length != 0) {
740 					this.addDelta(getElementType(element1), IDelta.ADDED, IDelta.TYPE_PARAMETERS, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
741 							getDataFor(element1, this.type1) });
742 				} else if (signatureDescriptor2.getTypeArguments().length != 0) {
743 					this.addDelta(getElementType(element1), IDelta.ADDED, IDelta.TYPE_ARGUMENTS, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
744 							getDataFor(element1, this.type1) });
745 				}
746 			}
747 		} else if (signature2 == null) {
748 			// removed type parameters
749 			SignatureDescriptor signatureDescriptor = getSignatureDescriptor(signature1);
750 			TypeParameterDescriptor[] typeParameterDescriptors = signatureDescriptor.getTypeParameterDescriptors();
751 			int length = typeParameterDescriptors.length;
752 			if (length != 0) {
753 				for (int i = 0; i < length; i++) {
754 					TypeParameterDescriptor typeParameterDescriptor = typeParameterDescriptors[i];
755 					this.addDelta(getElementType(element1), IDelta.REMOVED, IDelta.TYPE_PARAMETER, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
756 							getDataFor(element1, type1),
757 							typeParameterDescriptor.name });
758 				}
759 			} else {
760 				String[] typeArguments = signatureDescriptor.getTypeArguments();
761 				length = typeArguments.length;
762 				if (length != 0) {
763 					for (int i = 0; i < length; i++) {
764 						String typeArgument = typeArguments[i];
765 						this.addDelta(getElementType(element1), IDelta.REMOVED, IDelta.TYPE_ARGUMENT, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
766 								getDataFor(element1, type1), typeArgument });
767 					}
768 				}
769 			}
770 		} else {
771 			// both types have generic signature
772 			// need to check delta for type parameter one by one
773 			SignatureDescriptor signatureDescriptor = getSignatureDescriptor(signature1);
774 			SignatureDescriptor signatureDescriptor2 = getSignatureDescriptor(signature2);
775 
776 			TypeParameterDescriptor[] typeParameterDescriptors1 = signatureDescriptor.getTypeParameterDescriptors();
777 			TypeParameterDescriptor[] typeParameterDescriptors2 = signatureDescriptor2.getTypeParameterDescriptors();
778 			int length = typeParameterDescriptors1.length;
779 			int length2 = typeParameterDescriptors2.length;
780 			int min = length;
781 			int max = length2;
782 			if (length > length2) {
783 				min = length2;
784 				max = length;
785 			}
786 			int i = 0;
787 			for (; i < min; i++) {
788 				TypeParameterDescriptor parameterDescriptor1 = typeParameterDescriptors1[i];
789 				TypeParameterDescriptor parameterDescriptor2 = typeParameterDescriptors2[i];
790 				String name = parameterDescriptor1.name;
791 				if (!name.equals(parameterDescriptor2.name)) {
792 					this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_PARAMETER_NAME, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
793 							getDataFor(element1, type1), name });
794 				}
795 				if (parameterDescriptor1.classBound == null) {
796 					if (parameterDescriptor2.classBound != null) {
797 						// report delta added class bound of a type parameter
798 						this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.ADDED, IDelta.CLASS_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
799 								getDataFor(element1, type1), name });
800 					}
801 				} else if (parameterDescriptor2.classBound == null) {
802 					// report delta removed class bound of a type parameter
803 					this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.REMOVED, IDelta.CLASS_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
804 							getDataFor(element1, type1), name });
805 				} else if (!parameterDescriptor1.classBound.equals(parameterDescriptor2.classBound)) {
806 					// report delta changed class bound of a type parameter
807 					this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.CHANGED, IDelta.CLASS_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
808 							getDataFor(element1, type1), name });
809 				}
810 				List<String> interfaceBounds1 = parameterDescriptor1.interfaceBounds;
811 				List<String> interfaceBounds2 = parameterDescriptor2.interfaceBounds;
812 				if (interfaceBounds1 == null) {
813 					if (interfaceBounds2 != null) {
814 						for (Iterator<String> iterator = interfaceBounds2.iterator(); iterator.hasNext();) {
815 							// report delta added interface bounds
816 							this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.ADDED, IDelta.INTERFACE_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
817 									getDataFor(element1, type1), name,
818 									iterator.next() });
819 						}
820 					}
821 				} else if (interfaceBounds2 == null) {
822 					// report delta removed interface bounds
823 					for (Iterator<String> iterator = interfaceBounds1.iterator(); iterator.hasNext();) {
824 						// report delta added interface bounds
825 						this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.REMOVED, IDelta.INTERFACE_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
826 								getDataFor(element1, type1), name,
827 								iterator.next() });
828 					}
829 				} else {
830 					int size1 = interfaceBounds1.size();
831 					int size2 = interfaceBounds2.size();
832 					int boundsMin = size1;
833 					int boundsMax = size2;
834 					if (size1 > size2) {
835 						boundsMin = size2;
836 						boundsMax = size1;
837 					}
838 					int index = 0;
839 					for (; index < boundsMin; index++) {
840 						String currentInterfaceBound = interfaceBounds1.get(index);
841 						if (!currentInterfaceBound.equals(interfaceBounds2.get(index))) {
842 							// report delta: different interface bounds (or
843 							// reordered interface bound)
844 							this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, IDelta.CHANGED, IDelta.INTERFACE_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, name, new String[] {
845 									getDataFor(element1, type1), name,
846 									currentInterfaceBound });
847 						}
848 					}
849 					if (boundsMin != boundsMax) {
850 						// if max = length2 => addition of type parameter
851 						// descriptor
852 						// if max = length => removal of type parameter
853 						// descriptor
854 						boolean added = boundsMax == size2;
855 						for (; index < boundsMax; index++) {
856 							String currentInterfaceBound = added ? (String) interfaceBounds2.get(index) : (String) interfaceBounds1.get(index);
857 							this.addDelta(IDelta.TYPE_PARAMETER_ELEMENT_TYPE, added ? IDelta.ADDED : IDelta.REMOVED, IDelta.INTERFACE_BOUND, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
858 									getDataFor(element1, type1), name,
859 									currentInterfaceBound });
860 						}
861 					}
862 				}
863 			}
864 			if (min != max) {
865 				// if max = length2 => addition of type parameter descriptor
866 				// if max = length => removal of type parameter descriptor
867 				boolean added = max == length2;
868 				for (; i < max; i++) {
869 					TypeParameterDescriptor currentTypeParameter = added ? typeParameterDescriptors2[i] : typeParameterDescriptors1[i];
870 					int kind = added ? IDelta.ADDED : IDelta.REMOVED;
871 					int flags = added && length == 0 ? IDelta.TYPE_PARAMETERS : IDelta.TYPE_PARAMETER;
872 					this.addDelta(getElementType(element1), kind, flags, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
873 							getDataFor(element1, type1),
874 							currentTypeParameter.name });
875 				}
876 			}
877 			if (length2 > 0 || length > 0) {
878 				return;
879 			}
880 			String[] typeArguments = signatureDescriptor.getTypeArguments();
881 			String[] typeArguments2 = signatureDescriptor2.getTypeArguments();
882 			length = typeArguments.length;
883 			length2 = typeArguments2.length;
884 			min = length;
885 			max = length2;
886 			if (length > length2) {
887 				min = length2;
888 				max = length;
889 			}
890 			i = 0;
891 			for (; i < min; i++) {
892 				String currentTypeArgument = typeArguments[i];
893 				String newTypeArgument = typeArguments2[i];
894 				if (!currentTypeArgument.equals(newTypeArgument)) {
895 					this.addDelta(getElementType(element1), IDelta.CHANGED, IDelta.TYPE_ARGUMENT, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
896 							getDataFor(element1, type1), currentTypeArgument,
897 							newTypeArgument });
898 				}
899 			}
900 			if (min != max) {
901 				// if max = length2 => addition of type arguments
902 				// if max = length => removal of type arguments
903 				boolean added = max == length2;
904 				for (; i < max; i++) {
905 					String currentTypeArgument = added ? typeArguments2[i] : typeArguments[i];
906 					this.addDelta(getElementType(element1), added ? IDelta.ADDED : IDelta.REMOVED, IDelta.TYPE_ARGUMENT, this.currentDescriptorRestrictions, element1.getModifiers(), element2.getModifiers(), this.type1, getKeyFor(element2, this.type1), new String[] {
907 							getDataFor(element1, type1), currentTypeArgument });
908 				}
909 			}
910 		}
911 	}
912 
913 	/**
914 	 * Recursively collects all of the super-interfaces of the given type
915 	 * descriptor within the scope of the given API component
916 	 *
917 	 * @param type
918 	 * @param set
919 	 */
collectAllInterfaces(IApiType type, Set<IApiType> set)920 	private void collectAllInterfaces(IApiType type, Set<IApiType> set) {
921 		try {
922 			IApiType[] interfaces = type.getSuperInterfaces();
923 			if (interfaces != null) {
924 				for (int i = 0; i < interfaces.length; i++) {
925 					IApiType anInterface = interfaces[i];
926 					int visibility = VisibilityModifiers.PRIVATE;
927 					IApiComponent ifaceComponent = anInterface.getApiComponent();
928 					IApiDescription apiDescription = ifaceComponent.getApiDescription();
929 					IApiAnnotations elementDescription = apiDescription.resolveAnnotations(anInterface.getHandle());
930 					if (elementDescription != null) {
931 						visibility = elementDescription.getVisibility();
932 					}
933 					if ((visibility & visibilityModifiers) != 0) {
934 						set.add(anInterface);
935 					}
936 					collectAllInterfaces(anInterface, set);
937 				}
938 			}
939 			String superclassName = type.getSuperclassName();
940 			if (superclassName != null && !Util.isJavaLangObject(superclassName)) {
941 				IApiType superclass = type.getSuperclass();
942 				collectAllInterfaces(superclass, set);
943 			}
944 		} catch (CoreException e) {
945 			if (ApiPlugin.DEBUG_API_COMPARATOR) {
946 				ApiPlugin.log(e);
947 			}
948 			reportStatus(e);
949 		}
950 	}
951 
getDataFor(IApiMember member, IApiType type)952 	private String getDataFor(IApiMember member, IApiType type) {
953 		switch (member.getType()) {
954 			case IApiElement.TYPE:
955 				if (((IApiType) member).isMemberType()) {
956 					return member.getName().replace('$', '.');
957 				}
958 				return member.getName();
959 			case IApiElement.METHOD:
960 				StringBuilder buffer = new StringBuilder();
961 				buffer.append(type.getName()).append('.').append(getMethodDisplayName((IApiMethod) member, type));
962 				return String.valueOf(buffer);
963 			case IApiElement.FIELD:
964 				buffer = new StringBuilder();
965 				buffer.append(type.getName()).append('.').append(member.getName());
966 				return String.valueOf(buffer);
967 			default:
968 				break;
969 		}
970 		return null;
971 	}
972 
getKeyFor(IApiMember member, IApiType type)973 	private String getKeyFor(IApiMember member, IApiType type) {
974 		switch (member.getType()) {
975 			case IApiElement.TYPE:
976 				return member.getName();
977 			case IApiElement.METHOD:
978 				return getKeyForMethod((IApiMethod) member, type);
979 			case IApiElement.FIELD:
980 				return member.getName();
981 			default:
982 				break;
983 		}
984 		return null;
985 	}
986 
987 	/**
988 	 * Returns a new {@link Delta} to use, and resets the status of creating a
989 	 * delta
990 	 *
991 	 * @return
992 	 */
createDelta()993 	private Delta createDelta() {
994 		return new Delta();
995 	}
996 
997 	/**
998 	 * Returns the change(s) between the type descriptor and its equivalent in
999 	 * the current baseline.
1000 	 *
1001 	 * @return the changes in the type descriptor or <code>null</code>
1002 	 */
getDelta(IProgressMonitor monitor)1003 	public IDelta getDelta(IProgressMonitor monitor) {
1004 		SubMonitor localmonitor = SubMonitor.convert(monitor, 10);
1005 		try {
1006 			this.delta = createDelta();
1007 			// check visibility
1008 			int typeAccess = this.type1.getModifiers();
1009 			int typeAccess2 = this.type2.getModifiers();
1010 			final IApiDescription component2ApiDescription = component2.getApiDescription();
1011 			IApiAnnotations elementDescription2 = component2ApiDescription.resolveAnnotations(this.type2.getHandle());
1012 			this.initialDescriptorRestrictions = RestrictionModifiers.NO_RESTRICTIONS;
1013 			this.currentDescriptorRestrictions = RestrictionModifiers.NO_RESTRICTIONS;
1014 			if (elementDescription2 != null) {
1015 				int restrictions2 = elementDescription2.getRestrictions();
1016 				IApiDescription apiDescription = this.component.getApiDescription();
1017 				if (this.component.hasApiDescription()) {
1018 					int restrictions = RestrictionModifiers.NO_RESTRICTIONS;
1019 					IApiAnnotations componentApiDescription = apiDescription.resolveAnnotations(this.type1.getHandle());
1020 					if (componentApiDescription != null) {
1021 						restrictions = componentApiDescription.getRestrictions();
1022 						this.initialDescriptorRestrictions = restrictions;
1023 					}
1024 					if (restrictions2 != restrictions) {
1025 						// report different restrictions
1026 						// adding/removing no extend on a final class is ok
1027 						// adding/removing no instantiate on an abstract class
1028 						// is ok
1029 						String NO_EXTEND = "@noextend"; //$NON-NLS-1$
1030 						String NO_IMPLEMENT = "@noimplement"; //$NON-NLS-1$
1031 						String NO_INSTANSTIATE = "@noinstantiate"; //$NON-NLS-1$
1032 						if (this.type1.isInterface()) {
1033 							boolean noImplementAdded = (RestrictionModifiers.isImplementRestriction(restrictions2) && !RestrictionModifiers.isImplementRestriction(restrictions));
1034 							if (noImplementAdded || (RestrictionModifiers.isExtendRestriction(restrictions2) && !RestrictionModifiers.isExtendRestriction(restrictions))) {
1035 								this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.RESTRICTIONS, restrictions2, typeAccess, typeAccess2, this.type2, this.type2.getName(), new String[] {noImplementAdded? NO_IMPLEMENT:NO_EXTEND, Util.getDescriptorName(type1) });
1036 							}
1037 							boolean noImplementRemoved = (!RestrictionModifiers.isImplementRestriction(restrictions2) && RestrictionModifiers.isImplementRestriction(restrictions)) ;
1038 							if (noImplementRemoved || (!RestrictionModifiers.isExtendRestriction(restrictions2) && RestrictionModifiers.isExtendRestriction(restrictions))) {
1039 								this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.RESTRICTIONS, restrictions2, typeAccess, typeAccess2, this.type2, this.type2.getName(), new String[] {noImplementRemoved?  NO_IMPLEMENT:NO_EXTEND, Util.getDescriptorName(type1) });
1040 							}
1041 						} else {
1042 							boolean reportChangedRestrictions = false;
1043 							if (!Flags.isFinal(typeAccess2) && !Flags.isFinal(typeAccess)) {
1044 								if (RestrictionModifiers.isExtendRestriction(restrictions2) && !RestrictionModifiers.isExtendRestriction(restrictions)) {
1045 									reportChangedRestrictions = true;
1046 									this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.RESTRICTIONS,restrictions2, typeAccess, typeAccess2, this.type2, this.type2.getName(), new String[] { NO_EXTEND, Util.getDescriptorName(type1) });
1047 								}
1048 								if (!RestrictionModifiers.isExtendRestriction(restrictions2) && RestrictionModifiers.isExtendRestriction(restrictions)) {
1049 									reportChangedRestrictions = true;
1050 									this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.RESTRICTIONS,restrictions2, typeAccess, typeAccess2, this.type2, this.type2.getName(),new String[] { NO_EXTEND, Util.getDescriptorName(type1) });
1051 								}
1052 							}
1053 							if (!reportChangedRestrictions && !Flags.isAbstract(typeAccess2) && !Flags.isAbstract(typeAccess)) {
1054 								if (RestrictionModifiers.isInstantiateRestriction(restrictions2) && !RestrictionModifiers.isInstantiateRestriction(restrictions)) {
1055 									this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.RESTRICTIONS, restrictions2, typeAccess, typeAccess2, this.type2, this.type2.getName(), new String[] { NO_INSTANSTIATE, Util.getDescriptorName(type1) });
1056 								}
1057 							}
1058 							if (!reportChangedRestrictions && !Flags.isAbstract(typeAccess2)
1059 									&& !Flags.isAbstract(typeAccess)) {
1060 								if (!RestrictionModifiers.isInstantiateRestriction(restrictions2) && RestrictionModifiers.isInstantiateRestriction(restrictions)) {
1061 									this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.RESTRICTIONS,restrictions2, typeAccess, typeAccess2, this.type2, this.type2.getName(),new String[] {  NO_INSTANSTIATE, Util.getDescriptorName(type1) });
1062 								}
1063 							}
1064 						}
1065 					}
1066 				}
1067 				this.currentDescriptorRestrictions = restrictions2;
1068 			}
1069 			// first make sure that we compare interface with interface, class
1070 			// with class,
1071 			// annotation with annotation and enum with enums
1072 			if (Flags.isFinal(typeAccess2)) {
1073 				this.currentDescriptorRestrictions |= RestrictionModifiers.NO_EXTEND;
1074 			}
1075 			if (Flags.isFinal(typeAccess)) {
1076 				this.initialDescriptorRestrictions |= RestrictionModifiers.NO_EXTEND;
1077 			}
1078 
1079 			if (Flags.isDeprecated(typeAccess)) {
1080 				if (!Flags.isDeprecated(typeAccess2)) {
1081 					this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.DEPRECATION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1082 				}
1083 			} else if (Flags.isDeprecated(typeAccess2)) {
1084 				this.addDelta(getElementType(this.type1), IDelta.ADDED, IDelta.DEPRECATION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1085 			}
1086 			if (Flags.isProtected(typeAccess)) {
1087 				if (Flags.isPrivate(typeAccess2) || Util.isDefault(typeAccess2)) {
1088 					// report delta - decrease access: protected to default or
1089 					// private
1090 					this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.DECREASE_ACCESS, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1091 					return this.delta;
1092 				} else if (Flags.isPublic(typeAccess2)) {
1093 					// report delta - increase access: protected to public
1094 					this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.INCREASE_ACCESS, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1095 					return this.delta;
1096 				}
1097 			} else if (Flags.isPublic(typeAccess) && (Flags.isProtected(typeAccess2) || Flags.isPrivate(typeAccess2) || Util.isDefault(typeAccess2))) {
1098 				// report delta - decrease access: public to protected, default
1099 				// or private
1100 				this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.DECREASE_ACCESS, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1101 				return this.delta;
1102 			} else if (Util.isDefault(typeAccess) && (Flags.isPublic(typeAccess2) || Flags.isProtected(typeAccess2))) {
1103 				this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.INCREASE_ACCESS, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1104 				return this.delta;
1105 			} else if (Flags.isPrivate(typeAccess) && (Util.isDefault(typeAccess2) || Flags.isPublic(typeAccess2) || Flags.isProtected(typeAccess2))) {
1106 				this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.INCREASE_ACCESS, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1107 				return this.delta;
1108 			}
1109 
1110 			if (Flags.isAnnotation(typeAccess)) {
1111 				if (!Flags.isAnnotation(typeAccess2)) {
1112 					if (Flags.isInterface(typeAccess2)) {
1113 						// report conversion from annotation to interface
1114 						this.addDelta(IDelta.ANNOTATION_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1115 								Util.getDescriptorName(type1),
1116 								Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE),
1117 								Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE) });
1118 					} else if (Flags.isEnum(typeAccess2)) {
1119 						// report conversion from annotation to enum
1120 						this.addDelta(IDelta.ANNOTATION_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1121 								Util.getDescriptorName(type1),
1122 								Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE),
1123 								Integer.toString(IDelta.ENUM_ELEMENT_TYPE) });
1124 					} else {
1125 						// report conversion from annotation to class
1126 						this.addDelta(IDelta.ANNOTATION_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1127 								Util.getDescriptorName(type1),
1128 								Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE),
1129 								Integer.toString(IDelta.CLASS_ELEMENT_TYPE) });
1130 					}
1131 					return this.delta;
1132 				}
1133 			} else if (Flags.isInterface(typeAccess)) {
1134 				if (Flags.isAnnotation(typeAccess2)) {
1135 					// conversion from interface to annotation
1136 					this.addDelta(IDelta.INTERFACE_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1137 							Util.getDescriptorName(type1),
1138 							Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE),
1139 							Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE) });
1140 					return this.delta;
1141 				} else if (!Flags.isInterface(typeAccess2)) {
1142 					if (Flags.isEnum(typeAccess2)) {
1143 						// conversion from interface to enum
1144 						this.addDelta(IDelta.INTERFACE_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1145 								Util.getDescriptorName(type1),
1146 								Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE),
1147 								Integer.toString(IDelta.ENUM_ELEMENT_TYPE) });
1148 					} else {
1149 						// conversion from interface to class
1150 						this.addDelta(IDelta.INTERFACE_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1151 								Util.getDescriptorName(type1),
1152 								Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE),
1153 								Integer.toString(IDelta.CLASS_ELEMENT_TYPE) });
1154 					}
1155 					return this.delta;
1156 				}
1157 			} else if (Flags.isEnum(typeAccess)) {
1158 				if (!Flags.isEnum(typeAccess2)) {
1159 					if (Flags.isAnnotation(typeAccess2)) {
1160 						// report conversion from enum to annotation
1161 						this.addDelta(IDelta.ENUM_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1162 								Util.getDescriptorName(type1),
1163 								Integer.toString(IDelta.ENUM_ELEMENT_TYPE),
1164 								Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE) });
1165 					} else if (Flags.isInterface(typeAccess2)) {
1166 						// report conversion from enum to interface
1167 						this.addDelta(IDelta.ENUM_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1168 								Util.getDescriptorName(type1),
1169 								Integer.toString(IDelta.ENUM_ELEMENT_TYPE),
1170 								Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE) });
1171 					} else {
1172 						// report conversion from enum to class
1173 						this.addDelta(IDelta.ENUM_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1174 								Util.getDescriptorName(type1),
1175 								Integer.toString(IDelta.ENUM_ELEMENT_TYPE),
1176 								Integer.toString(IDelta.CLASS_ELEMENT_TYPE) });
1177 					}
1178 					return this.delta;
1179 				}
1180 			} else if (!Util.isClass(typeAccess2)) {
1181 				if (Flags.isAnnotation(typeAccess2)) {
1182 					// report conversion from class to annotation
1183 					this.addDelta(IDelta.CLASS_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1184 							Util.getDescriptorName(type1),
1185 							Integer.toString(IDelta.CLASS_ELEMENT_TYPE),
1186 							Integer.toString(IDelta.ANNOTATION_ELEMENT_TYPE) });
1187 				} else if (Flags.isInterface(typeAccess2)) {
1188 					// report conversion from class to interface
1189 					this.addDelta(IDelta.CLASS_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1190 							Util.getDescriptorName(type1),
1191 							Integer.toString(IDelta.CLASS_ELEMENT_TYPE),
1192 							Integer.toString(IDelta.INTERFACE_ELEMENT_TYPE) });
1193 				} else {
1194 					// report conversion from class to enum
1195 					this.addDelta(IDelta.CLASS_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE_CONVERSION, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), new String[] {
1196 							Util.getDescriptorName(type1),
1197 							Integer.toString(IDelta.CLASS_ELEMENT_TYPE),
1198 							Integer.toString(IDelta.ENUM_ELEMENT_TYPE) });
1199 				}
1200 				return this.delta;
1201 			}
1202 
1203 			if (Flags.isStatic(typeAccess)) {
1204 				if (!Flags.isStatic(typeAccess2)) {
1205 					this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.STATIC_TO_NON_STATIC, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1206 				}
1207 			} else if (Flags.isStatic(typeAccess2)) {
1208 				this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.NON_STATIC_TO_STATIC, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1209 			}
1210 			// check super class set
1211 			checkSuperclass();
1212 			// check super interfaces set
1213 			checkSuperInterfaces();
1214 
1215 			// checks fields
1216 			IApiField[] fields1 = this.type1.getFields();
1217 			IApiField[] fields2 = this.type2.getFields();
1218 			Set<String> addedFields = new HashSet<>(fields2.length);
1219 			for (int i = 0; i < fields2.length; i++) {
1220 				addedFields.add(fields2[i].getName());
1221 			}
1222 			for (int i = 0; i < fields1.length; i++) {
1223 				addedFields.remove(fields1[i].getName());
1224 				getDeltaForField(fields1[i]);
1225 			}
1226 			// checks remaining fields (added fields)
1227 			for (Iterator<String> iterator = addedFields.iterator(); iterator.hasNext();) {
1228 				IApiField field = this.type2.getField(iterator.next());
1229 				reportFieldAddition(field, this.type2);
1230 			}
1231 
1232 			// checks methods
1233 			IApiMethod[] methods1 = this.type1.getMethods();
1234 			IApiMethod[] methods2 = this.type2.getMethods();
1235 			Set<IMemberDescriptor> addedMethods = new HashSet<>(methods2.length);
1236 			for (int i = 0; i < methods2.length; i++) {
1237 				if (!methods2[i].isSynthetic()) {
1238 					addedMethods.add(methods2[i].getHandle());
1239 				}
1240 			}
1241 			for (int i = 0; i < methods1.length; i++) {
1242 				addedMethods.remove(methods1[i].getHandle());
1243 				getDeltaForMethod(methods1[i]);
1244 			}
1245 			// checks remaining methods (added methods)
1246 			for (Iterator<IMemberDescriptor> iterator = addedMethods.iterator(); iterator.hasNext();) {
1247 				IMethodDescriptor md = (IMethodDescriptor) iterator.next();
1248 				IApiMethod method = this.type2.getMethod(md.getName(), md.getSignature());
1249 				reportMethodAddition(method, this.type2);
1250 			}
1251 			if (Flags.isAbstract(typeAccess)) {
1252 				if (!Flags.isAbstract(typeAccess2)) {
1253 					// report delta - changed from abstract to non-abstract
1254 					this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.ABSTRACT_TO_NON_ABSTRACT, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1255 				}
1256 			} else if (Flags.isAbstract(typeAccess2)) {
1257 				// report delta - changed from non-abstract to abstract
1258 				if (!RestrictionModifiers.isInstantiateRestriction(initialDescriptorRestrictions)) {
1259 					this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.NON_ABSTRACT_TO_ABSTRACT, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1260 				}
1261 			}
1262 
1263 			if (Flags.isFinal(typeAccess)) {
1264 				if (!Flags.isFinal(typeAccess2)) {
1265 					// report delta - changed from final to non-final
1266 					this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.FINAL_TO_NON_FINAL, this.currentDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1267 				}
1268 			} else if (Flags.isFinal(typeAccess2)) {
1269 				// report delta - changed from non-final to final
1270 				this.addDelta(getElementType(this.type1), IDelta.CHANGED, IDelta.NON_FINAL_TO_FINAL, this.initialDescriptorRestrictions, typeAccess, typeAccess2, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1271 			}
1272 			// check type parameters
1273 			String signature1 = this.type1.getGenericSignature();
1274 			String signature2 = this.type2.getGenericSignature();
1275 			checkGenericSignature(signature1, signature2, this.type1, this.type2);
1276 
1277 			// check type members
1278 			checkTypeMembers();
1279 			return this.delta.isEmpty() ? ApiComparator.NO_DELTA : this.delta;
1280 		} catch (CoreException e) {
1281 			reportStatus(e);
1282 			return null;
1283 		} finally {
1284 			localmonitor.done();
1285 		}
1286 	}
1287 
getDeltaForField(IApiField field)1288 	private void getDeltaForField(IApiField field) {
1289 		int access = field.getModifiers();
1290 		if (Flags.isSynthetic(access)) {
1291 			// we ignore synthetic fields
1292 			return;
1293 		}
1294 		String name = field.getName();
1295 		IApiField field2 = this.type2.getField(name);
1296 		if (field2 == null) {
1297 			if (Flags.isPrivate(access) || Util.isDefault(access)) {
1298 				if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription())) {
1299 					// report non-API delta:
1300 					this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.FIELD, this.currentDescriptorRestrictions, access, 0, this.type1, name, new String[] {
1301 							Util.getDescriptorName(this.type1), name });
1302 				}
1303 			} else {
1304 				boolean found = false;
1305 				if (this.component2 != null) {
1306 					if (this.type1.isInterface()) {
1307 						Set<?> interfacesSet = getInterfacesSet(this.type2);
1308 						if (interfacesSet != null) {
1309 							for (Iterator<?> iterator = interfacesSet.iterator(); iterator.hasNext();) {
1310 								IApiType superTypeDescriptor = (IApiType) iterator.next();
1311 								IApiField field3 = superTypeDescriptor.getField(name);
1312 								if (field3 == null) {
1313 									continue;
1314 								} else {
1315 									// interface method can only be public
1316 									// method has been move up in the hierarchy
1317 									// - report the delta and abort loop
1318 									this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.FIELD_MOVED_UP, this.currentDescriptorRestrictions, access, field3.getModifiers(), this.type1, name, new String[] {
1319 											Util.getDescriptorName(this.type1),
1320 											name });
1321 									found = true;
1322 									break;
1323 								}
1324 							}
1325 						}
1326 					} else {
1327 						List<IApiType> superclassList = getSuperclassList(this.type2);
1328 						if (superclassList != null && isStatusOk()) {
1329 							loop: for (Iterator<IApiType> iterator = superclassList.iterator(); iterator.hasNext();) {
1330 								IApiType superTypeDescriptor = iterator.next();
1331 								IApiField field3 = superTypeDescriptor.getField(name);
1332 								if (field3 == null) {
1333 									continue;
1334 								} else {
1335 									int access3 = field3.getModifiers();
1336 									if (Flags.isPublic(access3) || Flags.isProtected(access3)) {
1337 										// method has been move up in the
1338 										// hierarchy - report the delta and
1339 										// abort loop
1340 										this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.FIELD_MOVED_UP, this.currentDescriptorRestrictions, access, field3.getModifiers(), this.type1, name, new String[] {
1341 												Util.getDescriptorName(this.type1),
1342 												name });
1343 										found = true;
1344 										break loop;
1345 									}
1346 								}
1347 							}
1348 						}
1349 					}
1350 				}
1351 				if (!found) {
1352 					if ((this.visibilityModifiers == VisibilityModifiers.API) && component.hasApiDescription()) {
1353 						// check if this field should be removed because it is
1354 						// tagged as @noreference
1355 						IApiDescription apiDescription = null;
1356 						try {
1357 							apiDescription = this.component.getApiDescription();
1358 						} catch (CoreException e) {
1359 							reportStatus(e);
1360 						}
1361 						if (apiDescription != null) {
1362 							IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(field.getHandle());
1363 							if (apiAnnotations != null) {
1364 								int restrictions = apiAnnotations.getRestrictions();
1365 								if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
1366 									// if not found, but tagged as @noreference
1367 									// in reference we don't need to report
1368 									// a removed field
1369 									return;
1370 								}
1371 							}
1372 						}
1373 					}
1374 					if (field.isEnumConstant()) {
1375 						// report delta (removal of an enum constant - not
1376 						// compatible)
1377 						this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.ENUM_CONSTANT, this.currentDescriptorRestrictions, this.type1.getModifiers(), this.type2.getModifiers(), this.type1, name, new String[] {
1378 								Util.getDescriptorName(this.type1), name });
1379 						return;
1380 					}
1381 					// removing a public field is a breakage
1382 					this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.FIELD, this.currentDescriptorRestrictions, access, 0, this.type1, name, new String[] {
1383 							Util.getDescriptorName(this.type1), name });
1384 				}
1385 			}
1386 			return;
1387 		}
1388 		int restrictions = RestrictionModifiers.NO_RESTRICTIONS;
1389 		int referenceRestrictions = RestrictionModifiers.NO_RESTRICTIONS;
1390 		int access2 = field2.getModifiers();
1391 		if (this.component2.hasApiDescription()) {
1392 			try {
1393 				IApiDescription apiDescription = this.component2.getApiDescription();
1394 				IApiAnnotations resolvedAPIDescription = apiDescription.resolveAnnotations(field2.getHandle());
1395 				if (resolvedAPIDescription != null) {
1396 					restrictions = resolvedAPIDescription.getRestrictions();
1397 				}
1398 			} catch (CoreException e) {
1399 				// ignore
1400 			}
1401 		}
1402 		if ((this.visibilityModifiers == VisibilityModifiers.API) && component.hasApiDescription()) {
1403 			// check if this field should be removed because it is tagged as
1404 			// @noreference
1405 			IApiDescription apiDescription = null;
1406 			try {
1407 				apiDescription = this.component.getApiDescription();
1408 			} catch (CoreException e) {
1409 				reportStatus(e);
1410 			}
1411 			if (apiDescription != null) {
1412 				IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(field.getHandle());
1413 				if (apiAnnotations != null) {
1414 					referenceRestrictions = apiAnnotations.getRestrictions();
1415 				}
1416 			}
1417 			if (RestrictionModifiers.isReferenceRestriction(referenceRestrictions)) {
1418 				// tagged as @noreference in the reference component
1419 				if (!RestrictionModifiers.isReferenceRestriction(restrictions)) {
1420 					// no longer tagged as @noreference
1421 					// report a field addition
1422 					if (field2.isEnumConstant()) {
1423 						// report delta (addition of an enum constant -
1424 						// compatible
1425 						this.addDelta(getElementType(this.type2), IDelta.ADDED, IDelta.ENUM_CONSTANT, this.currentDescriptorRestrictions, access, access2, this.type1, name, new String[] {
1426 								Util.getDescriptorName(this.type2), name });
1427 					} else {
1428 						this.addDelta(getElementType(this.type2), IDelta.ADDED, IDelta.FIELD, this.currentDescriptorRestrictions, access, access2, this.type1, name, new String[] {
1429 								Util.getDescriptorName(this.type2), name });
1430 					}
1431 					return;
1432 				}
1433 			} else if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
1434 				if (((Flags.isPublic(access2) || Flags.isProtected(access2)) && (Flags.isPublic(access) || Flags.isProtected(access))) && (this.visibilityModifiers == VisibilityModifiers.API)) {
1435 					// report that it is no longer an API field
1436 					this.addDelta(getElementType(this.type2), IDelta.REMOVED, field2.isEnumConstant() ? IDelta.API_ENUM_CONSTANT : IDelta.API_FIELD, restrictions, access, access2, this.type1, name, new String[] {
1437 							Util.getDescriptorName(this.type2), name });
1438 				}
1439 				return;
1440 			}
1441 			if ((Flags.isPrivate(access) || Util.isDefault(access) || (Flags.isProtected(access) && RestrictionModifiers.isExtendRestriction(this.initialDescriptorRestrictions)))
1442 					&& (Flags.isPrivate(access2) || Util.isDefault(access2) || (Flags.isProtected(access2) && RestrictionModifiers.isExtendRestriction(this.currentDescriptorRestrictions)))) {
1443 				// don't report non-API deltas
1444 				return;
1445 			}
1446 		}
1447 
1448 		restrictions |= this.currentDescriptorRestrictions;
1449 
1450 		if (!field.getSignature().equals(field2.getSignature())) {
1451 			// report delta
1452 			this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TYPE, restrictions, access, access2, this.type1, name, new String[] {
1453 					Util.getDescriptorName(this.type1), name });
1454 		} else {
1455 			// check type parameters
1456 			String signature1 = field.getGenericSignature();
1457 			String signature2 = field2.getGenericSignature();
1458 			checkGenericSignature(signature1, signature2, field, field2);
1459 		}
1460 		boolean changeFinalToNonFinal = false;
1461 		if (Flags.isProtected(access)) {
1462 			if (Flags.isPrivate(access2) || Util.isDefault(access2)) {
1463 				// report delta - decrease access: protected to default or
1464 				// private
1465 				this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.DECREASE_ACCESS, restrictions, access, access2, this.type1, name, new String[] {
1466 						Util.getDescriptorName(this.type1), name });
1467 			} else if (Flags.isPublic(access2)) {
1468 				// report delta - increase access: protected to public
1469 				this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, name, new String[] {
1470 						Util.getDescriptorName(this.type1), name });
1471 			}
1472 		} else if (Flags.isPublic(access) && (Flags.isProtected(access2) || Flags.isPrivate(access2) || Util.isDefault(access2))) {
1473 			// report delta - decrease access: public to protected, default or
1474 			// private
1475 			this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.DECREASE_ACCESS, restrictions, access, access2, this.type1, name, new String[] {
1476 					Util.getDescriptorName(this.type1), name });
1477 		} else if (Flags.isPrivate(access) && (Flags.isProtected(access2) || Util.isDefault(access2) || Flags.isPublic(access2))) {
1478 			this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, name, new String[] {
1479 					Util.getDescriptorName(this.type1), name });
1480 		} else if (Util.isDefault(access) && (Flags.isProtected(access2) || Flags.isPublic(access2))) {
1481 			this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, name, new String[] {
1482 					Util.getDescriptorName(this.type1), name });
1483 		}
1484 		if (Flags.isFinal(access)) {
1485 			if (!Flags.isFinal(access2)) {
1486 				if (!Flags.isStatic(access2)) {
1487 					// report delta - final to non-final for a non static field
1488 					this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.FINAL_TO_NON_FINAL_NON_STATIC, restrictions, access, access2, this.type1, name, new String[] {
1489 							Util.getDescriptorName(this.type1), name });
1490 				} else if (field.getConstantValue() != null) {
1491 					// report delta - final to non-final for a static field with
1492 					// a compile time constant
1493 					changeFinalToNonFinal = true;
1494 					this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.FINAL_TO_NON_FINAL_STATIC_CONSTANT, restrictions, access, access2, this.type1, name, new String[] {
1495 							Util.getDescriptorName(this.type1), name });
1496 				} else {
1497 					// report delta - final to non-final for a static field with
1498 					// no compile time constant
1499 					this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.FINAL_TO_NON_FINAL_STATIC_NON_CONSTANT, restrictions, access, access2, this.type1, name, new String[] {
1500 							Util.getDescriptorName(this.type1), name });
1501 				}
1502 			}
1503 		} else if (Flags.isFinal(access2)) {
1504 			// report delta - non-final to final
1505 			this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.NON_FINAL_TO_FINAL, restrictions, access, access2, this.type1, name, new String[] {
1506 					Util.getDescriptorName(this.type1), name });
1507 		}
1508 		if (Flags.isStatic(access)) {
1509 			if (!Flags.isStatic(access2)) {
1510 				// report delta - static to non-static
1511 				this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.STATIC_TO_NON_STATIC, restrictions, access, access2, this.type1, name, new String[] {
1512 						Util.getDescriptorName(this.type1), name });
1513 			}
1514 		} else if (Flags.isStatic(access2)) {
1515 			// report delta - non-static to static
1516 			this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.NON_STATIC_TO_STATIC, restrictions, access, access2, this.type1, name, new String[] {
1517 					Util.getDescriptorName(this.type1), name });
1518 		}
1519 		if (Flags.isTransient(access)) {
1520 			if (!Flags.isTransient(access2)) {
1521 				// report delta - transient to non-transient
1522 				this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.TRANSIENT_TO_NON_TRANSIENT, restrictions, access, access2, this.type1, name, new String[] {
1523 						Util.getDescriptorName(this.type1), name });
1524 			}
1525 		} else if (Flags.isTransient(access2)) {
1526 			// report delta - non-transient to transient
1527 			this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.NON_TRANSIENT_TO_TRANSIENT, restrictions, access, access2, this.type1, name, new String[] {
1528 					Util.getDescriptorName(this.type1), name });
1529 		}
1530 		if (Flags.isVolatile(access)) {
1531 			if (!Flags.isVolatile(access2)) {
1532 				// report delta - volatile to non-volatile
1533 				this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.VOLATILE_TO_NON_VOLATILE, restrictions, access, access2, this.type1, name, new String[] {
1534 						Util.getDescriptorName(this.type1), name });
1535 			}
1536 		} else if (Flags.isVolatile(access2)) {
1537 			// report delta - non-volatile to volatile
1538 			this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.NON_VOLATILE_TO_VOLATILE, restrictions, access, access2, this.type1, name, new String[] {
1539 					Util.getDescriptorName(this.type1), name });
1540 		}
1541 		if (Flags.isDeprecated(access)) {
1542 			if (!Flags.isDeprecated(access2)) {
1543 				this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.REMOVED, IDelta.DEPRECATION, restrictions, access, access2, this.type1, name, new String[] {
1544 						Util.getDescriptorName(this.type1), name });
1545 			}
1546 		} else if (Flags.isDeprecated(access2)) {
1547 			// report delta - non-volatile to volatile
1548 			this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.ADDED, IDelta.DEPRECATION, restrictions, access, access2, this.type1, name, new String[] {
1549 					Util.getDescriptorName(this.type1), name });
1550 		}
1551 		if (field.getConstantValue() != null) {
1552 			if (field2.getConstantValue() == null) {
1553 				if (!changeFinalToNonFinal) {
1554 					// report delta - removal of constant value
1555 					this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.REMOVED, IDelta.VALUE, restrictions, access, access2, this.type1, name, new String[] {
1556 							Util.getDescriptorName(this.type1), name,
1557 							String.valueOf(field.getConstantValue()) });
1558 				}
1559 			} else if (!field.getConstantValue().equals(field2.getConstantValue())) {
1560 				// report delta - modified constant value
1561 				this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.CHANGED, IDelta.VALUE, restrictions, access, access2, this.type1, name, new String[] {
1562 						Util.getDescriptorName(this.type1), name,
1563 						String.valueOf(field.getConstantValue()) });
1564 			}
1565 		} else if (field2.getConstantValue() != null) {
1566 			// report delta
1567 			this.addDelta(IDelta.FIELD_ELEMENT_TYPE, IDelta.ADDED, IDelta.VALUE, restrictions, access, access2, this.type1, name, new String[] {
1568 					Util.getDescriptorName(this.type1), name,
1569 					String.valueOf(field2.getConstantValue()) });
1570 		}
1571 	}
1572 
getDeltaForMethod(IApiMethod method)1573 	private void getDeltaForMethod(IApiMethod method) {
1574 		int access = method.getModifiers();
1575 		if (Flags.isSynthetic(access)) {
1576 			// we ignore synthetic methods
1577 			return;
1578 		}
1579 		String name = method.getName();
1580 		String descriptor = method.getSignature();
1581 		String key = getKeyForMethod(method, this.type1);
1582 		IApiMethod method2 = this.type2.getMethod(name, descriptor);
1583 		String methodDisplayName = getMethodDisplayName(method, this.type1);
1584 		if (method2 == null) {
1585 			if (method.isClassInitializer()) {
1586 				if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription())) {
1587 					// report non-API delta: removal of a clinit method
1588 					this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.CLINIT, this.currentDescriptorRestrictions, access, 0, this.type1, this.type1.getName(), Util.getDescriptorName(type1));
1589 				}
1590 				return;
1591 			} else if (Flags.isPrivate(access) || Util.isDefault(access)) {
1592 				if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription())) {
1593 					// report non-API delta:
1594 					this.addDelta(getElementType(this.type1), IDelta.REMOVED, getTargetType(method), Flags.isAbstract(this.type2.getModifiers()) ? this.currentDescriptorRestrictions | RestrictionModifiers.NO_INSTANTIATE : this.currentDescriptorRestrictions, access, 0, this.type1, getKeyForMethod(method, this.type1), new String[] {
1595 							Util.getDescriptorName(this.type1), methodDisplayName });
1596 				}
1597 				return;
1598 			}
1599 			// if null we need to walk the hierarchy of descriptor2
1600 			boolean found = false;
1601 			if (this.component2 != null && !method.isConstructor()) {
1602 				if (this.type1.isInterface()) {
1603 					Set<?> interfacesSet = getInterfacesSet(this.type2);
1604 					if (interfacesSet != null && isStatusOk()) {
1605 						for (Iterator<?> iterator = interfacesSet.iterator(); iterator.hasNext();) {
1606 							IApiType superTypeDescriptor = (IApiType) iterator.next();
1607 							IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
1608 							if (method3 == null) {
1609 								continue;
1610 							} else {
1611 								// interface method can only be public
1612 								// method has been move up in the hierarchy -
1613 								// report the delta and abort loop
1614 								this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.METHOD_MOVED_UP, this.currentDescriptorRestrictions, access, method3.getModifiers(), this.type1, getKeyForMethod(method3, this.type1), new String[] {
1615 										Util.getDescriptorName(this.type1),
1616 										methodDisplayName });
1617 								found = true;
1618 								break;
1619 							}
1620 						}
1621 					}
1622 				} else {
1623 					List<?> superclassList = getSuperclassList(this.type2, true);
1624 					if (superclassList != null && isStatusOk()) {
1625 						loop: for (Iterator<?> iterator = superclassList.iterator(); iterator.hasNext();) {
1626 							IApiType superTypeDescriptor = (IApiType) iterator.next();
1627 							IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
1628 							if (method3 == null) {
1629 								continue;
1630 							} else {
1631 								int access3 = method3.getModifiers();
1632 								if (Flags.isPublic(access3) || Flags.isProtected(access3)) {
1633 									// method has been move up in the hierarchy
1634 									// - report the delta and abort loop
1635 									// TODO need to make the distinction between
1636 									// methods that need to be re-implemented
1637 									// and methods that don't
1638 									this.addDelta(getElementType(this.type1), IDelta.REMOVED, IDelta.METHOD_MOVED_UP, this.currentDescriptorRestrictions, access, access3, this.type1, getKeyForMethod(method3, this.type1), new String[] {
1639 											Util.getDescriptorName(this.type1),
1640 											methodDisplayName });
1641 									found = true;
1642 									break loop;
1643 								}
1644 							}
1645 						}
1646 					}
1647 				}
1648 			}
1649 			if (!found) {
1650 				if ((this.visibilityModifiers == VisibilityModifiers.API) && component.hasApiDescription()) {
1651 					// check if this method should be removed because it is
1652 					// tagged as @noreference
1653 					IApiDescription apiDescription = null;
1654 					try {
1655 						apiDescription = this.component.getApiDescription();
1656 					} catch (CoreException e) {
1657 						reportStatus(e);
1658 					}
1659 					if (apiDescription != null) {
1660 						IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(method.getHandle());
1661 						if (apiAnnotations != null) {
1662 							int restrictions = apiAnnotations.getRestrictions();
1663 							if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
1664 								// if not found, but tagged as @noreference in
1665 								// reference we don't need to report
1666 								// a removed method
1667 								return;
1668 							}
1669 						}
1670 					}
1671 				}
1672 				if (this.type1.isAnnotation()) {
1673 					this.addDelta(getElementType(this.type1), IDelta.REMOVED, method.getDefaultValue() != null ? IDelta.METHOD_WITH_DEFAULT_VALUE : IDelta.METHOD_WITHOUT_DEFAULT_VALUE, this.currentDescriptorRestrictions, access, 0, this.type1, getKeyForMethod(method, this.type1), new String[] {
1674 							Util.getDescriptorName(this.type1),
1675 							methodDisplayName });
1676 				} else {
1677 					int restrictions = this.currentDescriptorRestrictions;
1678 					if (RestrictionModifiers.isExtendRestriction(this.initialDescriptorRestrictions) && !RestrictionModifiers.isExtendRestriction(this.currentDescriptorRestrictions)) {
1679 						restrictions = this.initialDescriptorRestrictions;
1680 					}
1681 					this.addDelta(getElementType(this.type1), IDelta.REMOVED, getTargetType(method), Flags.isAbstract(this.type2.getModifiers()) ? restrictions | RestrictionModifiers.NO_INSTANTIATE : restrictions, access, 0, this.type1, getKeyForMethod(method, this.type1), new String[] {
1682 							Util.getDescriptorName(this.type1),
1683 							methodDisplayName });
1684 				}
1685 			}
1686 			return;
1687 		}
1688 		int restrictions = this.currentDescriptorRestrictions;
1689 		if (component2.hasApiDescription()) {
1690 			try {
1691 				IApiDescription apiDescription = this.component2.getApiDescription();
1692 				IApiAnnotations resolvedAPIDescription = apiDescription.resolveAnnotations(method2.getHandle());
1693 				if (resolvedAPIDescription != null) {
1694 					restrictions |= resolvedAPIDescription.getRestrictions();
1695 				}
1696 			} catch (CoreException e) {
1697 				// ignore
1698 			}
1699 		}
1700 		int referenceRestrictions = this.initialDescriptorRestrictions;
1701 		int access2 = method2.getModifiers();
1702 		if (this.component.hasApiDescription()) {
1703 			// check if this method should be removed because it is tagged as
1704 			// @noreference
1705 			IApiDescription apiDescription = null;
1706 			try {
1707 				apiDescription = this.component.getApiDescription();
1708 			} catch (CoreException e) {
1709 				reportStatus(e);
1710 			}
1711 			if (apiDescription != null) {
1712 				IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(method.getHandle());
1713 				if (apiAnnotations != null) {
1714 					referenceRestrictions |= apiAnnotations.getRestrictions();
1715 				}
1716 			}
1717 		}
1718 		if ((this.visibilityModifiers == VisibilityModifiers.API) && this.component.hasApiDescription()) {
1719 			if (RestrictionModifiers.isReferenceRestriction(referenceRestrictions)) {
1720 				// tagged as @noreference in the reference component
1721 				if (!RestrictionModifiers.isReferenceRestriction(restrictions)) {
1722 					// no longer tagged as @noreference
1723 					// report a method addition
1724 					if (method.isConstructor()) {
1725 						this.addDelta(getElementType(this.type2), IDelta.ADDED, IDelta.CONSTRUCTOR, this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method, this.type2), new String[] {
1726 								Util.getDescriptorName(this.type2),
1727 								methodDisplayName });
1728 					} else if (this.type2.isAnnotation()) {
1729 						if (method.getDefaultValue() != null) {
1730 							this.addDelta(getElementType(this.type2), IDelta.ADDED, IDelta.METHOD_WITH_DEFAULT_VALUE, this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method, this.type2), new String[] {
1731 									Util.getDescriptorName(this.type2),
1732 									methodDisplayName });
1733 						} else {
1734 							this.addDelta(getElementType(this.type2), IDelta.ADDED, IDelta.METHOD_WITHOUT_DEFAULT_VALUE, this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method, this.type2), new String[] {
1735 									Util.getDescriptorName(this.type2),
1736 									methodDisplayName });
1737 						}
1738 					} else {
1739 						// check superclass
1740 						// if null we need to walk the hierarchy of descriptor2
1741 						boolean found = false;
1742 						if (this.component2 != null) {
1743 							if (this.type1.isInterface()) {
1744 								Set<?> interfacesSet = getInterfacesSet(this.type2);
1745 								if (interfacesSet != null && isStatusOk()) {
1746 									for (Iterator<?> iterator = interfacesSet.iterator(); iterator.hasNext();) {
1747 										IApiType superTypeDescriptor = (IApiType) iterator.next();
1748 										IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
1749 										if (method3 == null) {
1750 											continue;
1751 										} else {
1752 											// interface method can only be
1753 											// public
1754 											// method has been move up in the
1755 											// hierarchy - report the delta and
1756 											// abort loop
1757 											found = true;
1758 											break;
1759 										}
1760 									}
1761 								}
1762 							} else {
1763 								List<IApiType> superclassList = getSuperclassList(this.type2, true);
1764 								if (superclassList != null) {
1765 									loop: for (Iterator<IApiType> iterator = superclassList.iterator(); iterator.hasNext();) {
1766 										IApiType superTypeDescriptor = iterator.next();
1767 										IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
1768 										if (method3 == null) {
1769 											continue;
1770 										} else {
1771 											int access3 = method3.getModifiers();
1772 											if (Flags.isPublic(access3) || Flags.isProtected(access3)) {
1773 												// method has been move up in
1774 												// the hierarchy - report the
1775 												// delta and abort loop
1776 												// TODO need to make the
1777 												// distinction between methods
1778 												// that need to be
1779 												// re-implemented and methods
1780 												// that don't
1781 												found = true;
1782 												break loop;
1783 											}
1784 										}
1785 									}
1786 								}
1787 							}
1788 						}
1789 						this.addDelta(getElementType(this.type2), IDelta.ADDED, found ? IDelta.OVERRIDEN_METHOD : IDelta.METHOD, this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method, this.type2), new String[] {
1790 								Util.getDescriptorName(this.type2),
1791 								methodDisplayName });
1792 					}
1793 					return;
1794 				}
1795 			} else if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
1796 				if (Flags.isPublic(access2) || Flags.isProtected(access2)) {
1797 					// report that it is no longer an API method
1798 					if (this.type2.isAnnotation()) {
1799 						this.addDelta(getElementType(this.type2), IDelta.REMOVED, method.getDefaultValue() != null ? IDelta.API_METHOD_WITH_DEFAULT_VALUE : IDelta.API_METHOD_WITHOUT_DEFAULT_VALUE, this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method2, this.type2), new String[] {
1800 								Util.getDescriptorName(this.type2),
1801 								methodDisplayName });
1802 					} else if (Flags.isPublic(access) || Flags.isProtected(access)) {
1803 						this.addDelta(getElementType(this.type2), IDelta.REMOVED, method.isConstructor() ? IDelta.API_CONSTRUCTOR : IDelta.API_METHOD, Flags.isAbstract(this.type2.getModifiers()) ? this.currentDescriptorRestrictions | RestrictionModifiers.NO_INSTANTIATE : this.currentDescriptorRestrictions, access, access2, this.type1, getKeyForMethod(method2, this.type2), new String[] {
1804 								Util.getDescriptorName(this.type2),
1805 								methodDisplayName });
1806 					}
1807 					return;
1808 				}
1809 			}
1810 			if ((Flags.isPrivate(access) || Util.isDefault(access) || (Flags.isProtected(access) && RestrictionModifiers.isExtendRestriction(this.initialDescriptorRestrictions)))
1811 					&& (Flags.isPrivate(access2) || Util.isDefault(access2) || (Flags.isProtected(access2) && RestrictionModifiers.isExtendRestriction(this.currentDescriptorRestrictions)))) {
1812 				// don't report non-API deltas
1813 				return;
1814 			}
1815 		}
1816 		if (this.component.hasApiDescription() && !method.isConstructor() && !method.isClassInitializer() && !(type1.isInterface() || type1.isAnnotation())) {
1817 			if (restrictions != referenceRestrictions) {
1818 				if (!Flags.isFinal(access2)) {
1819 					if (RestrictionModifiers.isOverrideRestriction(restrictions) && !RestrictionModifiers.isOverrideRestriction(referenceRestrictions)) {
1820 						this.addDelta(getElementType(method), IDelta.ADDED, IDelta.RESTRICTIONS, restrictions, referenceRestrictions, access, access2, this.type1, getKeyForMethod(method2, this.type2), new String[] {
1821 								Util.getDescriptorName(this.type2),
1822 								methodDisplayName });
1823 					}
1824 				}
1825 			}
1826 		}
1827 		String[] names1 = method.getExceptionNames();
1828 		List<String> list1 = null;
1829 		if (names1 != null) {
1830 			list1 = new ArrayList<>(names1.length);
1831 			Collections.addAll(list1, names1);
1832 		}
1833 		String[] names2 = method2.getExceptionNames();
1834 		List<String> list2 = null;
1835 		if (names2 != null) {
1836 			list2 = new ArrayList<>(names2.length);
1837 			Collections.addAll(list2, names2);
1838 		}
1839 		if (names1 != null) {
1840 			if (names2 == null) {
1841 				// check all exception in method descriptor to see if they are
1842 				// checked or unchecked exceptions
1843 				loop: for (Iterator<String> iterator = list1.iterator(); iterator.hasNext();) {
1844 					String exceptionName = iterator.next().replace('/', '.');
1845 					if (isCheckedException(this.apiBaseline1, this.component, exceptionName)) {
1846 						// report delta - removal of checked exception
1847 						// TODO should we continue the loop for all remaining
1848 						// exceptions
1849 						this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.CHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
1850 								Util.getDescriptorName(this.type1),
1851 								methodDisplayName, exceptionName });
1852 						break loop;
1853 					} else {
1854 						// report delta - removal of unchecked exception
1855 						this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.UNCHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
1856 								Util.getDescriptorName(this.type1),
1857 								methodDisplayName, exceptionName });
1858 					}
1859 				}
1860 			} else {
1861 				// check if the exceptions are consistent for both descriptors
1862 				List<String> removedExceptions = new ArrayList<>();
1863 				for (Iterator<String> iterator = list1.iterator(); iterator.hasNext();) {
1864 					String exceptionName = iterator.next().replace('/', '.');
1865 					if (!list2.remove(exceptionName)) {
1866 						// this means that the exceptionName was not found
1867 						// inside the new set of exceptions
1868 						// so it has been removed
1869 						removedExceptions.add(exceptionName);
1870 					}
1871 				}
1872 				if (removedExceptions.size() != 0) {
1873 					loop: for (Iterator<String> iterator = removedExceptions.iterator(); iterator.hasNext();) {
1874 						String exceptionName = iterator.next().replace('/', '.');
1875 						if (isCheckedException(this.apiBaseline1, this.component, exceptionName)) {
1876 							// report delta - removal of checked exception
1877 							// TODO should we continue the loop for all
1878 							// remaining exceptions
1879 							this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.CHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
1880 									Util.getDescriptorName(this.type1),
1881 									methodDisplayName, exceptionName });
1882 							break loop;
1883 						} else {
1884 							// report delta - removal of unchecked exception
1885 							this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.UNCHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
1886 									Util.getDescriptorName(this.type1),
1887 									methodDisplayName, exceptionName });
1888 						}
1889 					}
1890 				}
1891 				loop: for (Iterator<String> iterator = list2.iterator(); iterator.hasNext();) {
1892 					String exceptionName = iterator.next().replace('/', '.');
1893 					if (isCheckedException(this.apiBaseline2, this.component2, exceptionName)) {
1894 						// report delta - addition of checked exception
1895 						// TODO should we continue the loop for all remaining
1896 						// exceptions
1897 						this.addDelta(getElementType(method), IDelta.ADDED, IDelta.CHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
1898 								Util.getDescriptorName(this.type1),
1899 								methodDisplayName, exceptionName });
1900 						break loop;
1901 					} else {
1902 						// report delta - addition of unchecked exception
1903 						this.addDelta(getElementType(method), IDelta.ADDED, IDelta.UNCHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
1904 								Util.getDescriptorName(this.type1),
1905 								methodDisplayName, exceptionName });
1906 					}
1907 				}
1908 			}
1909 		} else if (names2 != null) {
1910 			// check all exception in method descriptor to see if they are
1911 			// checked or unchecked exceptions
1912 			loop: for (Iterator<String> iterator = list2.iterator(); iterator.hasNext();) {
1913 				String exceptionName = iterator.next().replace('/', '.');
1914 				if (isCheckedException(this.apiBaseline2, this.component2, exceptionName)) {
1915 					// report delta - addition of checked exception
1916 					this.addDelta(getElementType(method), IDelta.ADDED, IDelta.CHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
1917 							Util.getDescriptorName(this.type1),
1918 							methodDisplayName, exceptionName });
1919 					// TODO should we continue the loop for all remaining
1920 					// exceptions
1921 					break loop;
1922 				} else {
1923 					// report delta - addition of unchecked exception
1924 					this.addDelta(getElementType(method), IDelta.ADDED, IDelta.UNCHECKED_EXCEPTION, restrictions, access, access2, this.type1, key, new String[] {
1925 							Util.getDescriptorName(this.type1),
1926 							methodDisplayName, exceptionName });
1927 				}
1928 			}
1929 		}
1930 		if (Flags.isVarargs(access)) {
1931 			if (!Flags.isVarargs(access2)) {
1932 				// report delta: conversion from T... to T[] - break
1933 				// compatibility
1934 				this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.VARARGS_TO_ARRAY, restrictions, access, access2, this.type1, key, new String[] {
1935 						Util.getDescriptorName(this.type1), methodDisplayName });
1936 			}
1937 		} else if (Flags.isVarargs(access2)) {
1938 			// report delta: conversion from T[] to T... compatible
1939 			this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.ARRAY_TO_VARARGS, restrictions, access, access2, this.type1, key, new String[] {
1940 					Util.getDescriptorName(this.type1), methodDisplayName });
1941 		}
1942 		if (Flags.isProtected(access)) {
1943 			if (Flags.isPrivate(access2) || Util.isDefault(access2)) {
1944 				// report delta - decrease access: protected to default or
1945 				// private
1946 				this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.DECREASE_ACCESS, restrictions, access, access2, this.type1, key, new String[] {
1947 						Util.getDescriptorName(this.type1), methodDisplayName });
1948 			} else if (Flags.isPublic(access2)) {
1949 				// report delta - increase access: protected to public
1950 				this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, key, new String[] {
1951 						Util.getDescriptorName(this.type1), methodDisplayName });
1952 			}
1953 		} else if (Flags.isPublic(access) && (Flags.isProtected(access2) || Flags.isPrivate(access2) || Util.isDefault(access2))) {
1954 			// report delta - decrease access: public to protected, default or
1955 			// private
1956 			this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.DECREASE_ACCESS, restrictions, access, access2, this.type1, key, new String[] {
1957 					Util.getDescriptorName(this.type1), methodDisplayName });
1958 		} else if (Util.isDefault(access) && (Flags.isPublic(access2) || Flags.isProtected(access2))) {
1959 			this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, key, new String[] {
1960 					Util.getDescriptorName(this.type1), methodDisplayName });
1961 		} else if (Flags.isPrivate(access) && (Util.isDefault(access2) || Flags.isPublic(access2) || Flags.isProtected(access2))) {
1962 			this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.INCREASE_ACCESS, restrictions, access, access2, this.type1, key, new String[] {
1963 					Util.getDescriptorName(this.type1), methodDisplayName });
1964 		}
1965 		if (Flags.isAbstract(access)) {
1966 			if (!Flags.isAbstract(access2)) {
1967 				// report delta - changed from abstract to non-abstract
1968 				this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.ABSTRACT_TO_NON_ABSTRACT, restrictions, access, access2, this.type1, key, new String[] {
1969 						Util.getDescriptorName(this.type1), methodDisplayName });
1970 			}
1971 		} else if (Flags.isAbstract(access2)) {
1972 			// report delta - changed from non-abstract to abstract
1973 			this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.NON_ABSTRACT_TO_ABSTRACT, restrictions, access, access2, this.type1, key, new String[] {
1974 					Util.getDescriptorName(this.type1), methodDisplayName });
1975 		}
1976 		if (Flags.isFinal(access)) {
1977 			if (!Flags.isFinal(access2)) {
1978 				// report delta - changed from final to non-final
1979 				this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.FINAL_TO_NON_FINAL, restrictions, access, access2, this.type1, key, new String[] {
1980 						Util.getDescriptorName(this.type1), methodDisplayName });
1981 			}
1982 		} else if (Flags.isFinal(access2)) {
1983 			int res = restrictions;
1984 			if (!RestrictionModifiers.isOverrideRestriction(res)) {
1985 				if (RestrictionModifiers.isExtendRestriction(this.currentDescriptorRestrictions)) {
1986 					res = this.currentDescriptorRestrictions;
1987 				} else if (RestrictionModifiers.isExtendRestriction(this.initialDescriptorRestrictions)) {
1988 					res = this.initialDescriptorRestrictions;
1989 				}
1990 				if (RestrictionModifiers.isOverrideRestriction(referenceRestrictions)) {
1991 					// it is ok to remove @nooverride and add final at the same
1992 					// time
1993 					res = referenceRestrictions;
1994 				}
1995 			}
1996 			// only report this delta is the method was visible
1997 			this.addDelta(getElementType(method2), IDelta.CHANGED, IDelta.NON_FINAL_TO_FINAL, res, access, access2, this.type1, key, new String[] {
1998 					Util.getDescriptorName(this.type2),
1999 					getMethodDisplayName(method2, this.type2) });
2000 		}
2001 		if (Flags.isStatic(access)) {
2002 			if (!Flags.isStatic(access2)) {
2003 				// report delta: change from static to non-static
2004 				this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.STATIC_TO_NON_STATIC, restrictions, access, access2, this.type1, key, new String[] {
2005 						Util.getDescriptorName(this.type1), methodDisplayName });
2006 			}
2007 		} else if (Flags.isStatic(access2)) {
2008 			// report delta: change from non-static to static
2009 			this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.NON_STATIC_TO_STATIC, restrictions, access, access2, this.type1, key, new String[] {
2010 					Util.getDescriptorName(this.type1), methodDisplayName });
2011 		}
2012 		if (Flags.isNative(access)) {
2013 			if (!Flags.isNative(access2)) {
2014 				// report delta: change from native to non-native
2015 				this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.NATIVE_TO_NON_NATIVE, restrictions, access, access2, this.type1, key, new String[] {
2016 						Util.getDescriptorName(this.type1), methodDisplayName });
2017 			}
2018 		} else if (Flags.isNative(access2)) {
2019 			// report delta: change from non-native to native
2020 			this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.NON_NATIVE_TO_NATIVE, restrictions, access, access2, this.type1, key, new String[] {
2021 					Util.getDescriptorName(this.type1), methodDisplayName });
2022 		}
2023 		if (Flags.isSynchronized(access)) {
2024 			if (!Flags.isSynchronized(access2)) {
2025 				// report delta: change from synchronized to non-synchronized
2026 				this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.SYNCHRONIZED_TO_NON_SYNCHRONIZED, restrictions, access, access2, this.type1, key, new String[] {
2027 						Util.getDescriptorName(this.type1), methodDisplayName });
2028 			}
2029 		} else if (Flags.isSynchronized(access2)) {
2030 			// report delta: change from non-synchronized to synchronized
2031 			this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.NON_SYNCHRONIZED_TO_SYNCHRONIZED, restrictions, access, access2, this.type1, key, new String[] {
2032 					Util.getDescriptorName(this.type1), methodDisplayName });
2033 		}
2034 		if (Flags.isDeprecated(access)) {
2035 			if (!Flags.isDeprecated(access2)) {
2036 				this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.DEPRECATION, restrictions, access, access2, this.type1, key, new String[] {
2037 						Util.getDescriptorName(this.type1), methodDisplayName });
2038 			}
2039 		} else if (Flags.isDeprecated(access2)) {
2040 			// report delta - non-volatile to volatile
2041 			this.addDelta(getElementType(method), IDelta.ADDED, IDelta.DEPRECATION, restrictions, access, access2, this.type1, key, new String[] {
2042 					Util.getDescriptorName(this.type1), methodDisplayName });
2043 		}
2044 		// check type parameters
2045 		String signature1 = method.getGenericSignature();
2046 		String signature2 = method2.getGenericSignature();
2047 		checkGenericSignature(signature1, signature2, method, method2);
2048 
2049 		if (method.getDefaultValue() == null) {
2050 			if (method2.getDefaultValue() != null) {
2051 				// report delta : default value has been added - compatible
2052 				this.addDelta(getElementType(method), IDelta.ADDED, IDelta.ANNOTATION_DEFAULT_VALUE, restrictions, access, access2, this.type1, key, new String[] {
2053 						Util.getDescriptorName(this.type1), methodDisplayName });
2054 			}
2055 		} else if (method2.getDefaultValue() == null) {
2056 			// report delta : default value has been removed - incompatible
2057 			this.addDelta(getElementType(method), IDelta.REMOVED, IDelta.ANNOTATION_DEFAULT_VALUE, restrictions, access, access2, this.type1, key, new String[] {
2058 					Util.getDescriptorName(this.type1), methodDisplayName });
2059 		} else if (!method.getDefaultValue().equals(method2.getDefaultValue())) {
2060 			// report delta: default value has changed
2061 			this.addDelta(getElementType(method), IDelta.CHANGED, IDelta.ANNOTATION_DEFAULT_VALUE, restrictions, access, access2, this.type1, key, new String[] {
2062 					Util.getDescriptorName(this.type1), methodDisplayName });
2063 		}
2064 	}
2065 
2066 	/**
2067 	 * Returns the complete super-interface set for the given type descriptor or
2068 	 * null, if it could not be computed
2069 	 *
2070 	 * @param type
2071 	 * @return the complete super-interface set for the given descriptor, or
2072 	 *         <code>null</code>
2073 	 */
getInterfacesSet(IApiType type)2074 	private Set<IApiType> getInterfacesSet(IApiType type) {
2075 		HashSet<IApiType> set = new HashSet<>();
2076 		this.status = null;
2077 		collectAllInterfaces(type, set);
2078 		if (set.isEmpty()) {
2079 			return null;
2080 		}
2081 		return set;
2082 	}
2083 
getMethodDisplayName(IApiMethod method, IApiType type)2084 	private String getMethodDisplayName(IApiMethod method, IApiType type) {
2085 		String methodName = null;
2086 		if (method.isConstructor()) {
2087 			methodName = type.getSimpleName();
2088 		} else {
2089 			methodName = method.getName();
2090 		}
2091 		String signature = null;
2092 		String genericSignature = method.getGenericSignature();
2093 		if (genericSignature != null) {
2094 			signature = genericSignature;
2095 		} else {
2096 			signature = method.getSignature();
2097 		}
2098 		return Signature.toString(signature, methodName, null, false, false);
2099 	}
2100 
getSignatureDescriptor(String signature)2101 	private SignatureDescriptor getSignatureDescriptor(String signature) {
2102 		SignatureDescriptor signatureDescriptor = new SignatureDescriptor();
2103 		SignatureReader signatureReader = new SignatureReader(signature);
2104 		signatureReader.accept(new SignatureDecoder(signatureDescriptor));
2105 		return signatureDescriptor;
2106 	}
2107 
getSuperclassList(IApiType type)2108 	private List<IApiType> getSuperclassList(IApiType type) {
2109 		return getSuperclassList(type, false);
2110 	}
2111 
getSuperclassList(IApiType type, boolean includeObject)2112 	private List<IApiType> getSuperclassList(IApiType type, boolean includeObject) {
2113 		return getSuperclassList(type, includeObject, false);
2114 	}
2115 
getSuperclassList(IApiType type, boolean includeObject, boolean includePrivate)2116 	private List<IApiType> getSuperclassList(IApiType type, boolean includeObject, boolean includePrivate) {
2117 		IApiType superClass = type;
2118 		this.status = null;
2119 		String superName = superClass.getSuperclassName();
2120 		if (Util.isJavaLangObject(superName) && !includeObject) {
2121 			return null;
2122 		}
2123 		List<IApiType> list = new ArrayList<>();
2124 		try {
2125 			while (superName != null && (!Util.isJavaLangObject(superName) || includeObject)) {
2126 				superClass = superClass.getSuperclass();
2127 				int visibility = VisibilityModifiers.PRIVATE;
2128 				IApiComponent superComponent = superClass.getApiComponent();
2129 				IApiDescription apiDescription = superComponent.getApiDescription();
2130 				IApiAnnotations elementDescription = apiDescription.resolveAnnotations(superClass.getHandle());
2131 				if (elementDescription != null) {
2132 					visibility = elementDescription.getVisibility();
2133 				}
2134 				if (Util.isJavaLangObject(superName) && includeObject) {
2135 					list.add(superClass);
2136 					break;
2137 				}
2138 				if (includePrivate || ((visibility & visibilityModifiers) != 0)) {
2139 					list.add(superClass);
2140 				}
2141 				superName = superClass.getSuperclassName();
2142 			}
2143 		} catch (CoreException e) {
2144 			reportStatus(e);
2145 		}
2146 		if (list.isEmpty()) {
2147 			return null;
2148 		}
2149 		return list;
2150 	}
2151 
reportFieldAddition(IApiField field, IApiType type)2152 	private void reportFieldAddition(IApiField field, IApiType type) {
2153 		int access = field.getModifiers();
2154 		String name = field.getName();
2155 
2156 		if (Flags.isSynthetic(access)) {
2157 			// we ignore synthetic fields
2158 			return;
2159 		}
2160 		if ((this.visibilityModifiers == VisibilityModifiers.API) && component2.hasApiDescription()) {
2161 			// check if this method should be removed because it is tagged as
2162 			// @noreference
2163 			IApiDescription apiDescription = null;
2164 			try {
2165 				apiDescription = this.component2.getApiDescription();
2166 			} catch (CoreException e) {
2167 				reportStatus(e);
2168 			}
2169 			if (apiDescription != null) {
2170 				IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(field.getHandle());
2171 				if (apiAnnotations != null) {
2172 					int restrictions = apiAnnotations.getRestrictions();
2173 					if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
2174 						// such a method is not seen as an API method
2175 						return;
2176 					}
2177 				}
2178 			}
2179 		}
2180 		if (field.isEnumConstant()) {
2181 			// report delta (addition of an enum constant - compatible
2182 			this.addDelta(getElementType(type), IDelta.ADDED, IDelta.ENUM_CONSTANT, this.currentDescriptorRestrictions, this.initialDescriptorRestrictions, 0, access, this.type1, name, new String[] {
2183 					Util.getDescriptorName(type), name });
2184 		} else {
2185 			if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription()) || Flags.isPublic(access) || Flags.isProtected(access)) {
2186 				// report non-API delta:
2187 				this.addDelta(getElementType(type), IDelta.ADDED, IDelta.FIELD, this.currentDescriptorRestrictions, this.initialDescriptorRestrictions, 0, access, this.type1, name, new String[] {
2188 						Util.getDescriptorName(type), name });
2189 			}
2190 		}
2191 	}
2192 
reportMethodAddition(IApiMethod method, IApiType type)2193 	private void reportMethodAddition(IApiMethod method, IApiType type) {
2194 		int access = method.getModifiers();
2195 		if (method.isClassInitializer()) {
2196 			if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription())) {
2197 				// report non-API delta: addition of clinit method
2198 				this.addDelta(getElementType(type), IDelta.ADDED, IDelta.CLINIT, this.currentDescriptorRestrictions, 0, access, this.type1, type.getName(), Util.getDescriptorName(type1));
2199 			}
2200 			return;
2201 		}
2202 		if (Flags.isSynthetic(access)) {
2203 			// we ignore synthetic method
2204 			return;
2205 		}
2206 		IApiDescription apiDescription = null;
2207 		if (((this.visibilityModifiers & VisibilityModifiers.API) != 0) && component2.hasApiDescription()) {
2208 			// check if this method should be removed because it is tagged as
2209 			// @noreference
2210 			int restrictions = RestrictionModifiers.NO_RESTRICTIONS;
2211 			try {
2212 				apiDescription = this.component2.getApiDescription();
2213 			} catch (CoreException e) {
2214 				reportStatus(e);
2215 			}
2216 			if (apiDescription != null) {
2217 				IApiAnnotations apiAnnotations = apiDescription.resolveAnnotations(method.getHandle());
2218 				if (apiAnnotations != null) {
2219 					restrictions = apiAnnotations.getRestrictions();
2220 				}
2221 			}
2222 			// check if this method should be removed because it is tagged as
2223 			// @noreference
2224 			if (this.visibilityModifiers == VisibilityModifiers.API && RestrictionModifiers.isReferenceRestriction(restrictions)) {
2225 				// such a method is not seen as an API method
2226 				return;
2227 			}
2228 		}
2229 		String methodDisplayName = getMethodDisplayName(method, type);
2230 		int restrictionsForMethodAddition = this.currentDescriptorRestrictions;
2231 		if (Flags.isFinal(this.type2.getModifiers())) {
2232 			restrictionsForMethodAddition |= RestrictionModifiers.NO_EXTEND;
2233 		}
2234 		if (apiDescription != null) {
2235 			if (this.type2.isMemberType() && Flags.isProtected(this.type2.getModifiers())) {
2236 				// protected member - check restriction on the enclosing type
2237 				IApiType enclosingType = this.type2;
2238 				try {
2239 					do {
2240 						if (enclosingType != null) {
2241 							final IApiAnnotations memberTypeAnnotations = apiDescription.resolveAnnotations(enclosingType.getHandle());
2242 							if (memberTypeAnnotations != null) {
2243 								int restrictions = memberTypeAnnotations.getRestrictions();
2244 								if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
2245 									restrictionsForMethodAddition |= RestrictionModifiers.NO_REFERENCE;
2246 								}
2247 								if (RestrictionModifiers.isExtendRestriction(restrictions)) {
2248 									// @noextend on a class that contains a
2249 									// protected member means that it cannot be
2250 									// referenced
2251 									restrictionsForMethodAddition |= RestrictionModifiers.NO_EXTEND;
2252 									if (this.visibilityModifiers == VisibilityModifiers.API) {
2253 										return;
2254 									}
2255 								}
2256 								if (RestrictionModifiers.isImplementRestriction(restrictions)) {
2257 									restrictionsForMethodAddition |= RestrictionModifiers.NO_IMPLEMENT;
2258 								}
2259 							}
2260 						}
2261 						enclosingType = enclosingType.getEnclosingType();
2262 					} while (enclosingType != null);
2263 				} catch (CoreException e) {
2264 					reportStatus(e);
2265 				}
2266 			}
2267 		}
2268 		if (Flags.isPublic(access) || Flags.isProtected(access)) {
2269 			if (method.isConstructor()) {
2270 				this.addDelta(getElementType(type), IDelta.ADDED, IDelta.CONSTRUCTOR, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, access, this.type1, getKeyForMethod(method, type), new String[] {
2271 						Util.getDescriptorName(type), methodDisplayName });
2272 			} else if (type.isAnnotation()) {
2273 				if (method.getDefaultValue() != null) {
2274 					this.addDelta(getElementType(type), IDelta.ADDED, IDelta.METHOD_WITH_DEFAULT_VALUE, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, access, this.type1, getKeyForMethod(method, type), new String[] {
2275 							Util.getDescriptorName(type), methodDisplayName });
2276 				} else {
2277 					this.addDelta(getElementType(type), IDelta.ADDED, IDelta.METHOD_WITHOUT_DEFAULT_VALUE, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, access, this.type1, getKeyForMethod(method, type), new String[] {
2278 							Util.getDescriptorName(type), methodDisplayName });
2279 				}
2280 			} else {
2281 				// check superclass
2282 				// if null we need to walk the hierarchy of descriptor2
2283 				boolean found = false;
2284 				if (this.component2 != null) {
2285 					String name = method.getName();
2286 					String descriptor = method.getSignature();
2287 					if (this.type1.isInterface()) {
2288 						Set<?> interfacesSet = getInterfacesSet(this.type2);
2289 						if (interfacesSet != null && isStatusOk()) {
2290 							for (Iterator<?> iterator = interfacesSet.iterator(); iterator.hasNext();) {
2291 								IApiType superTypeDescriptor = (IApiType) iterator.next();
2292 								IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
2293 								if (method3 == null) {
2294 									continue;
2295 								} else {
2296 									// interface method can only be public
2297 									// method has been move up in the hierarchy
2298 									// - 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 && isStatusOk()) {
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) || Flags.isProtected(access3)) {
2315 										IApiAnnotations apiAnnotations = null;
2316 										if (apiDescription != null) {
2317 											apiAnnotations = apiDescription.resolveAnnotations(method3.getHandle());
2318 										}
2319 										if (apiAnnotations != null) {
2320 											int restrictions = apiAnnotations.getRestrictions();
2321 											// if overriding no reference method, break the loop and report method addition
2322 											if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
2323 												found = false;
2324 												break loop;
2325 											}
2326 										}
2327 										// method has been move up in the
2328 										// hierarchy - report the delta and
2329 										// abort loop
2330 										// TODO need to make the distinction
2331 										// between methods that need to be
2332 										// re-implemented and methods that don't
2333 										found = true;
2334 										break loop;
2335 									}
2336 								}
2337 							}
2338 						}
2339 					}
2340 				}
2341 				if (!found) {
2342 					// check if the method has been pushed down
2343 					// if null we need to walk the hierarchy of descriptor
2344 					if (this.component != null) {
2345 						String name = method.getName();
2346 						String descriptor = method.getSignature();
2347 						if (this.type1.isInterface()) {
2348 							Set<?> interfacesSet = getInterfacesSet(this.type1);
2349 							if (interfacesSet != null && isStatusOk()) {
2350 								for (Iterator<?> iterator = interfacesSet.iterator(); iterator.hasNext();) {
2351 									IApiType superTypeDescriptor = (IApiType) iterator.next();
2352 									IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
2353 									if (method3 == null) {
2354 										continue;
2355 									} else {
2356 										// interface method can only be public
2357 										// method has been move up in the
2358 										// hierarchy - report the delta and
2359 										// abort loop
2360 										found = true;
2361 										break;
2362 									}
2363 								}
2364 							}
2365 						} else {
2366 							List<?> superclassList = getSuperclassList(this.type1, true);
2367 							if (superclassList != null && isStatusOk()) {
2368 								loop: for (Iterator<?> iterator = superclassList.iterator(); iterator.hasNext();) {
2369 									IApiType superTypeDescriptor = (IApiType) iterator.next();
2370 									IApiMethod method3 = superTypeDescriptor.getMethod(name, descriptor);
2371 									if (method3 == null) {
2372 										continue;
2373 									} else {
2374 										int access3 = method3.getModifiers();
2375 										if (Flags.isPublic(access3) || Flags.isProtected(access3)) {
2376 											IApiAnnotations apiAnnotations = null;
2377 											if (apiDescription != null) {
2378 												apiAnnotations = apiDescription.resolveAnnotations(method3.getHandle());
2379 											}
2380 											if (apiAnnotations != null) {
2381 												int restrictions = apiAnnotations.getRestrictions();
2382 												// if overriding no reference
2383 												// method, break the loop and
2384 												// report method addition
2385 												if (RestrictionModifiers.isReferenceRestriction(restrictions)) {
2386 													found = false;
2387 													break loop;
2388 												}
2389 
2390 											}
2391 											// method has been pushed down in
2392 											// the hierarchy - report the delta
2393 											// and abort loop
2394 											// TODO need to make the distinction
2395 											// between methods that need to be
2396 											// re-implemented and methods that
2397 											// don't
2398 											found = true;
2399 											break loop;
2400 										}
2401 									}
2402 								}
2403 							}
2404 						}
2405 					}
2406 					boolean isOverride = false;
2407 					if (!found) {
2408 						if (this.component != null) {
2409 							String name = method.getName();
2410 							String descriptor = method.getSignature();
2411 							// check method of interfaces.
2412 							HashSet<IApiType> interfaces = new HashSet<>();
2413 							collectAllInterfaces(this.type2, interfaces);
2414 							if (!interfaces.isEmpty()) {
2415 								for (IApiType inter : interfaces) {
2416 									IApiMethod methodInterface = inter.getMethod(name, descriptor);
2417 									if (methodInterface == null) {
2418 										continue;
2419 									} else {
2420 										int access3 = methodInterface.getModifiers();
2421 										if (Flags.isPublic(access3) || Flags.isProtected(access3)) {
2422 											isOverride = true;
2423 											break;
2424 										}
2425 									}
2426 								}
2427 							}
2428 						}
2429 					}
2430 					if (isOverride) {
2431 						this.addDelta(getElementType(type), IDelta.ADDED, isOverride ? IDelta.OVERRIDEN_METHOD : method.isDefaultMethod() ? IDelta.DEFAULT_METHOD : IDelta.METHOD, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, method.isDefaultMethod() ? (method.getModifiers() | Flags.AccDefaultMethod) : method.getModifiers(), this.type1, getKeyForMethod(method, type), new String[] {
2432 								Util.getDescriptorName(type),
2433 								methodDisplayName });
2434 					} else {
2435 						this.addDelta(getElementType(type), IDelta.ADDED, found ? IDelta.METHOD_MOVED_DOWN : method.isDefaultMethod() ? IDelta.DEFAULT_METHOD : IDelta.METHOD, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, method.isDefaultMethod() ? (method.getModifiers() | Flags.AccDefaultMethod) : method.getModifiers(), this.type1, getKeyForMethod(method, type), new String[] {
2436 							Util.getDescriptorName(type), methodDisplayName });
2437 					}
2438 
2439 				} else {
2440 					this.addDelta(getElementType(type), IDelta.ADDED, found ? IDelta.OVERRIDEN_METHOD : IDelta.METHOD, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, method.getModifiers(), this.type1, getKeyForMethod(method, type), new String[] {
2441 							Util.getDescriptorName(type), methodDisplayName });
2442 				}
2443 			}
2444 		} else if (!(this.visibilityModifiers == VisibilityModifiers.API && component.hasApiDescription())) {
2445 			// report non-API deltas for private and package-accessible methods
2446 			// as well:
2447 			this.addDelta(getElementType(type), IDelta.ADDED, method.isConstructor() ? IDelta.CONSTRUCTOR : IDelta.METHOD, restrictionsForMethodAddition, this.initialDescriptorRestrictions, 0, method.getModifiers(), this.type1, getKeyForMethod(method, type), new String[] {
2448 					Util.getDescriptorName(type), methodDisplayName });
2449 		}
2450 	}
2451 
getKeyForMethod(IApiMethod method, IApiType type)2452 	private String getKeyForMethod(IApiMethod method, IApiType type) {
2453 		StringBuilder buffer = new StringBuilder();
2454 		if (method.isConstructor()) {
2455 			String name = type.getName();
2456 			int index = name.lastIndexOf('.');
2457 			int dollarIndex = name.lastIndexOf('$');
2458 			if (dollarIndex != -1 && type.isMemberType()) {
2459 				buffer.append(type.getName().substring(dollarIndex + 1));
2460 			} else {
2461 				buffer.append(type.getName().substring(index + 1));
2462 			}
2463 		} else {
2464 			buffer.append(method.getName());
2465 		}
2466 		String genericSignature = method.getGenericSignature();
2467 		if (genericSignature != null) {
2468 			buffer.append(genericSignature);
2469 		} else {
2470 			buffer.append(method.getSignature());
2471 		}
2472 		return String.valueOf(buffer);
2473 	}
2474 
isAPI(int visibility, IApiType memberTypeDescriptor)2475 	private static boolean isAPI(int visibility, IApiType memberTypeDescriptor) {
2476 		int access = memberTypeDescriptor.getModifiers();
2477 		return VisibilityModifiers.isAPI(visibility) && (Flags.isPublic(access) || Flags.isProtected(access));
2478 	}
2479 
getType(String typeName, IApiComponent component, IApiBaseline baseline)2480 	private IApiTypeRoot getType(String typeName, IApiComponent component, IApiBaseline baseline) throws CoreException {
2481 		String packageName = Signatures.getPackageName(typeName);
2482 		IApiComponent[] components = baseline.resolvePackage(component, packageName);
2483 		if (components == null) {
2484 			String msg = MessageFormat.format(ComparatorMessages.ClassFileComparator_1, packageName, baseline.getName(), component.getSymbolicName());
2485 			if (ApiPlugin.DEBUG_CLASSFILE_COMPARATOR) {
2486 				System.err.println("TYPE LOOKUP: " + msg); //$NON-NLS-1$
2487 			}
2488 			reportStatus(new Status(IStatus.ERROR, component.getSymbolicName(), msg));
2489 			return null;
2490 		}
2491 		IApiTypeRoot result = Util.getClassFile(components, typeName);
2492 		if (result == null) {
2493 			String msg = MessageFormat.format(ComparatorMessages.ClassFileComparator_2, typeName, baseline.getName(), component.getSymbolicName());
2494 			if (ApiPlugin.DEBUG_CLASSFILE_COMPARATOR) {
2495 				System.err.println("TYPE LOOKUP: " + msg); //$NON-NLS-1$
2496 			}
2497 			reportStatus(new Status(IStatus.ERROR, component.getSymbolicName(), msg));
2498 			return null;
2499 		}
2500 		return result;
2501 	}
2502 
2503 	/**
2504 	 * Returns the delta element type code for the given type. Translates a type
2505 	 * to interface, class, emum or annotation.
2506 	 *
2507 	 * @param type
2508 	 * @return delta element type
2509 	 */
getElementType(IApiType type)2510 	private int getElementType(IApiType type) {
2511 		if (type.isAnnotation()) {
2512 			return IDelta.ANNOTATION_ELEMENT_TYPE;
2513 		}
2514 		if (type.isEnum()) {
2515 			return IDelta.ENUM_ELEMENT_TYPE;
2516 		}
2517 		if (type.isInterface()) {
2518 			return IDelta.INTERFACE_ELEMENT_TYPE;
2519 		}
2520 		return IDelta.CLASS_ELEMENT_TYPE;
2521 	}
2522 
2523 	/**
2524 	 * Returns the delta element type code for the given method. Translates a
2525 	 * method to constructor or method.
2526 	 *
2527 	 * @param method
2528 	 * @return delta element type
2529 	 */
getElementType(IApiMethod method)2530 	private int getElementType(IApiMethod method) {
2531 		if (method.isConstructor()) {
2532 			return IDelta.CONSTRUCTOR_ELEMENT_TYPE;
2533 		}
2534 		return IDelta.METHOD_ELEMENT_TYPE;
2535 	}
2536 
2537 	/**
2538 	 * Returns the delta type code for the given method when it is the target of
2539 	 * a remove/add. Translates a method to constructor or method.
2540 	 *
2541 	 * @param method
2542 	 * @return delta type
2543 	 */
getTargetType(IApiMethod method)2544 	private int getTargetType(IApiMethod method) {
2545 		if (method.isConstructor()) {
2546 			return IDelta.CONSTRUCTOR;
2547 		}
2548 		return IDelta.METHOD;
2549 	}
2550 
2551 	/**
2552 	 * Translates a member to its delta element type code.
2553 	 *
2554 	 * @param member
2555 	 * @return delta element type code
2556 	 */
getElementType(IApiMember member)2557 	private int getElementType(IApiMember member) {
2558 		switch (member.getType()) {
2559 			case IApiElement.TYPE:
2560 				return getElementType((IApiType) member);
2561 			case IApiElement.METHOD:
2562 				return getElementType((IApiMethod) member);
2563 			default:
2564 				break;
2565 		}
2566 		return IDelta.FIELD_ELEMENT_TYPE;
2567 	}
2568 }
2569