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