diff --git a/japicmp/pom.xml b/japicmp/pom.xml index b38a52f4d..71250366a 100644 --- a/japicmp/pom.xml +++ b/japicmp/pom.xml @@ -88,7 +88,7 @@ org.apache.maven.plugins maven-checkstyle-plugin - 2.15 + 2.17 com.github.siom79.japicmp @@ -99,7 +99,7 @@ com.github.siom79.japicmp japicmp - 0.6.0 + 0.7.0 diff --git a/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java b/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java index d10ebc0d5..dac56ab9d 100644 --- a/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java +++ b/japicmp/src/main/java/japicmp/cmp/ClassesComparator.java @@ -35,7 +35,7 @@ private void sortIntoLists(Map oldClassesMap, Mapabsent(), JApiChangeStatus.REMOVED); - JApiClass jApiClass = new JApiClass(this.jarArchiveComparator, oldCtClass.getName(), Optional.of(oldCtClass), Optional.absent(), JApiChangeStatus.REMOVED, classType, options); + JApiClass jApiClass = new JApiClass(this.jarArchiveComparator, oldCtClass.getName(), Optional.of(oldCtClass), Optional.absent(), JApiChangeStatus.REMOVED, classType); if (includeClass(jApiClass)) { classes.add(jApiClass); } @@ -47,7 +47,7 @@ private void sortIntoLists(Map oldClassesMap, Map oldClassesMap, Mapabsent(), Optional.of(newType), JApiChangeStatus.NEW); - JApiClass jApiClass = new JApiClass(this.jarArchiveComparator, newCtClass.getName(), Optional.absent(), Optional.of(newCtClass), JApiChangeStatus.NEW, classType, options); + JApiClass jApiClass = new JApiClass(this.jarArchiveComparator, newCtClass.getName(), Optional.absent(), Optional.of(newCtClass), JApiChangeStatus.NEW, classType); if (includeClass(jApiClass)) { classes.add(jApiClass); } diff --git a/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java b/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java index fbf893a93..d8e72b772 100644 --- a/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java +++ b/japicmp/src/main/java/japicmp/cmp/JarArchiveComparator.java @@ -1,5 +1,6 @@ package japicmp.cmp; +import com.google.common.base.Optional; import japicmp.exception.JApiCmpException; import japicmp.exception.JApiCmpException.Reason; import japicmp.filter.AnnotationFilterBase; @@ -8,6 +9,7 @@ import japicmp.filter.JavadocLikePackageFilter; import japicmp.compat.CompatibilityChanges; import japicmp.model.JApiClass; +import japicmp.model.JApiException; import japicmp.model.JavaObjectSerializationCompatibility; import japicmp.output.OutputFilter; import japicmp.util.AnnotationHelper; @@ -304,4 +306,51 @@ public ClassPool getOldClassPool() { public ClassPool getNewClassPool() { return newClassPool; } + + public enum ArchiveType { + OLD, NEW + } + + /** + * Loads a class either from the old, new or common classpath. + * @param archiveType specify if this class should be loaded from the old or new class path + * @param name the name of the class (FQN) + * @return the loaded class (if options are not set to ignore missing classes) + * @throws japicmp.exception.JApiCmpException if loading the class fails + */ + public Optional loadClass(ArchiveType archiveType, String name) { + Optional loadedClass = Optional.absent(); + if (this.options.getClassPathMode() == JarArchiveComparatorOptions.ClassPathMode.ONE_COMMON_CLASSPATH) { + try { + loadedClass = Optional.of(commonClassPool.get(name)); + } catch (NotFoundException e) { + if (!options.isIgnoreMissingClasses()) { + throw JApiCmpException.forClassLoading(e, name, this); + } + } + } else if (this.options.getClassPathMode() == JarArchiveComparatorOptions.ClassPathMode.TWO_SEPARATE_CLASSPATHS) { + if (archiveType == ArchiveType.OLD) { + try { + loadedClass = Optional.of(oldClassPool.get(name)); + } catch (NotFoundException e) { + if (!options.isIgnoreMissingClasses()) { + throw JApiCmpException.forClassLoading(e, name, this); + } + } + } else if (archiveType == ArchiveType.NEW) { + try { + loadedClass = Optional.of(newClassPool.get(name)); + } catch (NotFoundException e) { + if (!options.isIgnoreMissingClasses()) { + throw JApiCmpException.forClassLoading(e, name, this); + } + } + } else { + throw new JApiCmpException(Reason.IllegalState, "Unknown archive type: " + archiveType); + } + } else { + throw new JApiCmpException(Reason.IllegalState, "Unknown classpath mode: " + this.options.getClassPathMode()); + } + return loadedClass; + } } diff --git a/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java b/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java index 34b122f8e..84f8c214d 100644 --- a/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java +++ b/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java @@ -203,7 +203,7 @@ private void forAllSuperclasses(JApiClass jApiClass, Map clas } else { classType = new JApiClassType(Optional.absent(), Optional.absent(), JApiChangeStatus.UNCHANGED); } - foundClass = new JApiClass(this.jarArchiveComparator, newSuperclassName, oldClassOptional, newClassOptional, changeStatus, classType, this.jarArchiveComparator.getJarArchiveComparatorOptions()); + foundClass = new JApiClass(this.jarArchiveComparator, newSuperclassName, oldClassOptional, newClassOptional, changeStatus, classType); evaluate(Collections.singletonList(foundClass)); int returnValue = onSuperclassCallback.callback(foundClass, classMap); returnValues.add(returnValue); @@ -329,6 +329,15 @@ public int callback(JApiClass superclass, Map classMap) { addCompatibilityChange(method, JApiCompatibilityChange.METHOD_ADDED_TO_INTERFACE); } } + checkIfExceptionIsNowChecked(method); + } + } + + private void checkIfExceptionIsNowChecked(JApiMethod method) { + for (JApiException exception : method.getExceptions()) { + if (exception.getChangeStatus() == JApiChangeStatus.NEW && exception.isCheckedException()) { + addCompatibilityChange(method, JApiCompatibilityChange.METHOD_NOW_THROWS_CHECKED_EXCEPTION); + } } } @@ -439,10 +448,10 @@ public int callback(JApiClass superclass, Map classMap) { } } } - checkIfClassNowCheckedException(jApiClass, classMap); + checkIfClassNowCheckedException(jApiClass); } - private void checkIfClassNowCheckedException(JApiClass jApiClass, Map classMap) { + private void checkIfClassNowCheckedException(JApiClass jApiClass) { JApiSuperclass jApiClassSuperclass = jApiClass.getSuperclass(); if (jApiClassSuperclass.getChangeStatus() == JApiChangeStatus.MODIFIED) { if (jApiClassSuperclass.getNewSuperclassName().isPresent()) { diff --git a/japicmp/src/main/java/japicmp/model/JApiBehavior.java b/japicmp/src/main/java/japicmp/model/JApiBehavior.java index 3feb9ff3b..e5e107d27 100644 --- a/japicmp/src/main/java/japicmp/model/JApiBehavior.java +++ b/japicmp/src/main/java/japicmp/model/JApiBehavior.java @@ -1,15 +1,13 @@ package japicmp.model; import com.google.common.base.Optional; +import japicmp.cmp.JarArchiveComparator; import japicmp.cmp.JarArchiveComparatorOptions; import japicmp.util.AnnotationHelper; import japicmp.util.Constants; import japicmp.util.ModifierHelper; import japicmp.util.OptionalHelper; -import javassist.CtBehavior; -import javassist.CtConstructor; -import javassist.CtMethod; -import javassist.Modifier; +import javassist.*; import javassist.bytecode.AnnotationsAttribute; import javassist.bytecode.ExceptionsAttribute; @@ -23,6 +21,7 @@ public class JApiBehavior implements JApiHasModifiers, JApiHasChangeStatus, JApi JApiHasFinalModifier, JApiHasAbstractModifier, JApiCompatibility, JApiHasAnnotations, JApiHasBridgeModifier, JApiCanBeSynthetic, JApiHasLineNumber { private final String name; + private final JarArchiveComparator jarArchiveComparator; private final List parameters = new LinkedList<>(); private final List annotations = new LinkedList<>(); private final JApiModifier accessModifier; @@ -38,9 +37,10 @@ public class JApiBehavior implements JApiHasModifiers, JApiHasChangeStatus, JApi private final Optional newLineNumber; private final List compatibilityChanges = new ArrayList<>(); - public JApiBehavior(String name, Optional oldBehavior, Optional newBehavior, JApiChangeStatus changeStatus, JarArchiveComparatorOptions options) { + public JApiBehavior(String name, Optional oldBehavior, Optional newBehavior, JApiChangeStatus changeStatus, JarArchiveComparator jarArchiveComparator) { this.name = name; - computeAnnotationChanges(annotations, oldBehavior, newBehavior, options); + this.jarArchiveComparator = jarArchiveComparator; + computeAnnotationChanges(annotations, oldBehavior, newBehavior, jarArchiveComparator.getJarArchiveComparatorOptions()); this.accessModifier = extractAccessModifier(oldBehavior, newBehavior); this.finalModifier = extractFinalModifier(oldBehavior, newBehavior); this.staticModifier = extractStaticModifier(oldBehavior, newBehavior); @@ -61,14 +61,14 @@ private List computeExceptionChanges(Optional newExceptions = extractExceptions(newMethodOptional); for (String oldException : oldExceptions) { if (newExceptions.contains(oldException)) { - exceptionList.add(new JApiException(oldException, JApiChangeStatus.UNCHANGED)); + exceptionList.add(new JApiException(jarArchiveComparator, oldException, jarArchiveComparator.loadClass(JarArchiveComparator.ArchiveType.NEW, oldException), JApiChangeStatus.UNCHANGED)); newExceptions.remove(oldException); } else { - exceptionList.add(new JApiException(oldException, JApiChangeStatus.REMOVED)); + exceptionList.add(new JApiException(jarArchiveComparator, oldException, jarArchiveComparator.loadClass(JarArchiveComparator.ArchiveType.OLD, oldException), JApiChangeStatus.REMOVED)); } } for (String newException : newExceptions) { - exceptionList.add(new JApiException(newException, JApiChangeStatus.NEW)); + exceptionList.add(new JApiException(jarArchiveComparator, newException, jarArchiveComparator.loadClass(JarArchiveComparator.ArchiveType.NEW, newException), JApiChangeStatus.NEW)); } } return exceptionList; @@ -153,6 +153,11 @@ private JApiChangeStatus evaluateChangeStatus(JApiChangeStatus changeStatus) { if (this.syntheticAttribute.getChangeStatus() != JApiChangeStatus.UNCHANGED) { changeStatus = JApiChangeStatus.MODIFIED; } + for (JApiException jApiException : exceptions) { + if (jApiException.getChangeStatus() == JApiChangeStatus.NEW || jApiException.getChangeStatus() == JApiChangeStatus.REMOVED) { + changeStatus = JApiChangeStatus.MODIFIED; + } + } } return changeStatus; } diff --git a/japicmp/src/main/java/japicmp/model/JApiClass.java b/japicmp/src/main/java/japicmp/model/JApiClass.java index af3730d51..e6b90f57c 100644 --- a/japicmp/src/main/java/japicmp/model/JApiClass.java +++ b/japicmp/src/main/java/japicmp/model/JApiClass.java @@ -40,13 +40,13 @@ public class JApiClass implements JApiHasModifiers, JApiHasChangeStatus, JApiHas private JApiJavaObjectSerializationChangeStatus jApiJavaObjectSerializationChangeStatus = JApiJavaObjectSerializationChangeStatus.NOT_SERIALIZABLE; private boolean changeCausedByClassElement = false; - public JApiClass(JarArchiveComparator jarArchiveComparator, String fullyQualifiedName, Optional oldClass, Optional newClass, JApiChangeStatus changeStatus, JApiClassType classType, JarArchiveComparatorOptions options) { + public JApiClass(JarArchiveComparator jarArchiveComparator, String fullyQualifiedName, Optional oldClass, Optional newClass, JApiChangeStatus changeStatus, JApiClassType classType) { this.jarArchiveComparator = jarArchiveComparator; + this.options = this.jarArchiveComparator.getJarArchiveComparatorOptions(); this.fullyQualifiedName = fullyQualifiedName; this.newClass = newClass; this.oldClass = oldClass; this.classType = classType; - this.options = options; this.superclass = extractSuperclass(oldClass, newClass); computeMethodChanges(oldClass, newClass); computeInterfaceChanges(this.interfaces, oldClass, newClass); @@ -304,7 +304,7 @@ private void sortMethodsIntoLists(Map> oldMethodsMap, Map methodDescriptorParser.parse(oldMethod.getSignature()); List newMethodsWithSameName = newMethodsMap.get(methodName); if (newMethodsWithSameName == null) { - JApiMethod jApiMethod = new JApiMethod(oldMethod.getName(), JApiChangeStatus.REMOVED, Optional.of(oldMethod), Optional.absent(), options); + JApiMethod jApiMethod = new JApiMethod(oldMethod.getName(), JApiChangeStatus.REMOVED, Optional.of(oldMethod), Optional.absent(), jarArchiveComparator); addParametersToMethod(methodDescriptorParser, jApiMethod); if (includeMethod(jApiMethod)) { methods.add(jApiMethod); @@ -313,7 +313,7 @@ private void sortMethodsIntoLists(Map> oldMethodsMap, Map Optional matchingMethodOptional = findMatchingMethod(oldMethod, newMethodsWithSameName); if (matchingMethodOptional.isPresent()) { CtMethod matchingMethod = matchingMethodOptional.get(); - JApiMethod jApiMethod = new JApiMethod(oldMethod.getName(), JApiChangeStatus.UNCHANGED, Optional.of(oldMethod), Optional.of(matchingMethod), options); + JApiMethod jApiMethod = new JApiMethod(oldMethod.getName(), JApiChangeStatus.UNCHANGED, Optional.of(oldMethod), Optional.of(matchingMethod), jarArchiveComparator); addParametersToMethod(methodDescriptorParser, jApiMethod); if (includeMethod(jApiMethod)) { methods.add(jApiMethod); @@ -321,7 +321,7 @@ private void sortMethodsIntoLists(Map> oldMethodsMap, Map oldMethodsWithSameNameIterator.remove(); newMethodsWithSameName.remove(matchingMethod); } else { - JApiMethod jApiMethod = new JApiMethod(oldMethod.getName(), JApiChangeStatus.REMOVED, Optional.of(oldMethod), Optional.absent(), options); + JApiMethod jApiMethod = new JApiMethod(oldMethod.getName(), JApiChangeStatus.REMOVED, Optional.of(oldMethod), Optional.absent(), jarArchiveComparator); addParametersToMethod(methodDescriptorParser, jApiMethod); if (includeMethod(jApiMethod)) { methods.add(jApiMethod); @@ -336,7 +336,7 @@ private void sortMethodsIntoLists(Map> oldMethodsMap, Map methodDescriptorParser.parse(ctMethod.getSignature()); List methodsWithSameName = oldMethodsMap.get(ctMethod.getName()); if (methodsWithSameName == null) { - JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.NEW, Optional.absent(), Optional.of(ctMethod), options); + JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.NEW, Optional.absent(), Optional.of(ctMethod), jarArchiveComparator); addParametersToMethod(methodDescriptorParser, jApiMethod); if (includeMethod(jApiMethod)) { methods.add(jApiMethod); @@ -345,13 +345,13 @@ private void sortMethodsIntoLists(Map> oldMethodsMap, Map Optional matchingMethodOptional = findMatchingMethod(ctMethod, methodsWithSameName); if (matchingMethodOptional.isPresent()) { CtMethod matchingMethod = matchingMethodOptional.get(); - JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.UNCHANGED, Optional.of(ctMethod), Optional.of(matchingMethod), options); + JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.UNCHANGED, Optional.of(ctMethod), Optional.of(matchingMethod), jarArchiveComparator); addParametersToMethod(methodDescriptorParser, jApiMethod); if (includeMethod(jApiMethod)) { methods.add(jApiMethod); } } else { - JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.NEW, Optional.absent(), Optional.of(ctMethod), options); + JApiMethod jApiMethod = new JApiMethod(ctMethod.getName(), JApiChangeStatus.NEW, Optional.absent(), Optional.of(ctMethod), jarArchiveComparator); addParametersToMethod(methodDescriptorParser, jApiMethod); if (includeMethod(jApiMethod)) { methods.add(jApiMethod); @@ -422,13 +422,13 @@ private void sortConstructorsIntoLists(Map oldConstructor methodDescriptorParser.parse(ctMethod.getSignature()); CtConstructor foundMethod = newConstructorsMap.get(longName); if (foundMethod == null) { - JApiConstructor jApiConstructor = new JApiConstructor(ctMethod.getName(), JApiChangeStatus.REMOVED, Optional.of(ctMethod), Optional.absent(), options); + JApiConstructor jApiConstructor = new JApiConstructor(ctMethod.getName(), JApiChangeStatus.REMOVED, Optional.of(ctMethod), Optional.absent(), jarArchiveComparator); addParametersToMethod(methodDescriptorParser, jApiConstructor); if (includeConstructor(jApiConstructor)) { constructors.add(jApiConstructor); } } else { - JApiConstructor jApiConstructor = new JApiConstructor(ctMethod.getName(), JApiChangeStatus.UNCHANGED, Optional.of(ctMethod), Optional.of(foundMethod), options); + JApiConstructor jApiConstructor = new JApiConstructor(ctMethod.getName(), JApiChangeStatus.UNCHANGED, Optional.of(ctMethod), Optional.of(foundMethod), jarArchiveComparator); addParametersToMethod(methodDescriptorParser, jApiConstructor); if (includeConstructor(jApiConstructor)) { constructors.add(jApiConstructor); @@ -440,7 +440,7 @@ private void sortConstructorsIntoLists(Map oldConstructor methodDescriptorParser.parse(ctMethod.getSignature()); CtConstructor foundMethod = oldConstructorsMap.get(longName); if (foundMethod == null) { - JApiConstructor jApiConstructor = new JApiConstructor(ctMethod.getName(), JApiChangeStatus.NEW, Optional.absent(), Optional.of(ctMethod), options); + JApiConstructor jApiConstructor = new JApiConstructor(ctMethod.getName(), JApiChangeStatus.NEW, Optional.absent(), Optional.of(ctMethod), jarArchiveComparator); addParametersToMethod(methodDescriptorParser, jApiConstructor); if (includeConstructor(jApiConstructor)) { constructors.add(jApiConstructor); diff --git a/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java b/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java index 4de6de9d0..6e6e41ccd 100644 --- a/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java +++ b/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java @@ -21,6 +21,7 @@ public enum JApiCompatibilityChange { METHOD_NOW_STATIC(false, false), METHOD_NO_LONGER_STATIC(false, false), METHOD_ADDED_TO_INTERFACE(true, false), + METHOD_NOW_THROWS_CHECKED_EXCEPTION(true, false), FIELD_STATIC_AND_OVERRIDES_STATIC(false, false), FIELD_LESS_ACCESSIBLE_THAN_IN_SUPERCLASS(false, false), FIELD_NOW_FINAL(false, false), diff --git a/japicmp/src/main/java/japicmp/model/JApiConstructor.java b/japicmp/src/main/java/japicmp/model/JApiConstructor.java index 4e3d12fee..3d8ac39f1 100644 --- a/japicmp/src/main/java/japicmp/model/JApiConstructor.java +++ b/japicmp/src/main/java/japicmp/model/JApiConstructor.java @@ -1,6 +1,7 @@ package japicmp.model; import com.google.common.base.Optional; +import japicmp.cmp.JarArchiveComparator; import japicmp.cmp.JarArchiveComparatorOptions; import javassist.CtConstructor; @@ -10,8 +11,8 @@ public class JApiConstructor extends JApiBehavior { private final Optional oldConstructor; private final Optional newConstructor; - public JApiConstructor(String name, JApiChangeStatus changeStatus, Optional oldConstructor, Optional newConstructor, JarArchiveComparatorOptions options) { - super(name, oldConstructor, newConstructor, changeStatus, options); + public JApiConstructor(String name, JApiChangeStatus changeStatus, Optional oldConstructor, Optional newConstructor, JarArchiveComparator jarArchiveComparator) { + super(name, oldConstructor, newConstructor, changeStatus, jarArchiveComparator); this.oldConstructor = oldConstructor; this.newConstructor = newConstructor; } diff --git a/japicmp/src/main/java/japicmp/model/JApiException.java b/japicmp/src/main/java/japicmp/model/JApiException.java index a782511ca..00665cba1 100644 --- a/japicmp/src/main/java/japicmp/model/JApiException.java +++ b/japicmp/src/main/java/japicmp/model/JApiException.java @@ -1,14 +1,33 @@ package japicmp.model; +import com.google.common.base.Optional; +import japicmp.cmp.JarArchiveComparator; +import javassist.CtClass; + import javax.xml.bind.annotation.XmlAttribute; +import java.util.List; public class JApiException implements JApiHasChangeStatus { private final String name; private final JApiChangeStatus changeStatus; + private final boolean checkedException; - public JApiException(String name, JApiChangeStatus changeStatus) { + public JApiException(JarArchiveComparator jarArchiveComparator, String name, Optional ctClassOptional, JApiChangeStatus changeStatus) { this.name = name; this.changeStatus = changeStatus; + this.checkedException = isCheckedException(ctClassOptional, jarArchiveComparator); + } + + private boolean isCheckedException(Optional ctClassOptional, JarArchiveComparator jarArchiveComparator) { + boolean checked = false; + Optional exceptionOptional = jarArchiveComparator.loadClass(JarArchiveComparator.ArchiveType.NEW, "java.lang.Exception"); + if (exceptionOptional.isPresent()) { + CtClass ctClass = ctClassOptional.get(); + if (ctClass.subclassOf(exceptionOptional.get())) { + checked = true; + } + } + return checked; } @XmlAttribute(name = "name") @@ -20,4 +39,8 @@ public String getName() { public JApiChangeStatus getChangeStatus() { return changeStatus; } + + public boolean isCheckedException() { + return checkedException; + } } diff --git a/japicmp/src/main/java/japicmp/model/JApiMethod.java b/japicmp/src/main/java/japicmp/model/JApiMethod.java index 1939a2578..c0a9cd35e 100644 --- a/japicmp/src/main/java/japicmp/model/JApiMethod.java +++ b/japicmp/src/main/java/japicmp/model/JApiMethod.java @@ -1,6 +1,7 @@ package japicmp.model; import com.google.common.base.Optional; +import japicmp.cmp.JarArchiveComparator; import japicmp.cmp.JarArchiveComparatorOptions; import japicmp.util.MethodDescriptorParser; import javassist.CtMethod; @@ -18,8 +19,8 @@ public class JApiMethod extends JApiBehavior { private final Optional newMethod; private final JApiReturnType returnType; - public JApiMethod(String name, JApiChangeStatus changeStatus, Optional oldMethod, Optional newMethod, JarArchiveComparatorOptions options) { - super(name, oldMethod, newMethod, changeStatus, options); + public JApiMethod(String name, JApiChangeStatus changeStatus, Optional oldMethod, Optional newMethod, JarArchiveComparator jarArchiveComparator) { + super(name, oldMethod, newMethod, changeStatus, jarArchiveComparator); this.oldMethod = oldMethod; this.newMethod = newMethod; this.returnType = computeReturnTypeChanges(oldMethod, newMethod); diff --git a/japicmp/src/main/java/japicmp/model/JApiSuperclass.java b/japicmp/src/main/java/japicmp/model/JApiSuperclass.java index ee3b2e087..0ffff9f63 100644 --- a/japicmp/src/main/java/japicmp/model/JApiSuperclass.java +++ b/japicmp/src/main/java/japicmp/model/JApiSuperclass.java @@ -42,7 +42,7 @@ public Optional getJApiClass() { String newSuperclassName = newSuperclass.getName(); if (oldSuperclassName.equals(newSuperclassName)) { JApiClassType classType = new JApiClassType(Optional.of(ClassHelper.getType(oldSuperclass)), Optional.of(ClassHelper.getType(newSuperclass)), JApiChangeStatus.UNCHANGED); - JApiClass jApiClass = new JApiClass(jarArchiveComparator, oldSuperclassName, Optional.of(oldSuperclass), Optional.of(newSuperclass), JApiChangeStatus.UNCHANGED, classType, jarArchiveComparator.getJarArchiveComparatorOptions()); + JApiClass jApiClass = new JApiClass(jarArchiveComparator, oldSuperclassName, Optional.of(oldSuperclass), Optional.of(newSuperclass), JApiChangeStatus.UNCHANGED, classType); return Optional.of(jApiClass); } else { return Optional.absent(); @@ -51,13 +51,13 @@ public Optional getJApiClass() { CtClass oldSuperclass = oldSuperclassOptional.get(); String oldSuperclassName = oldSuperclass.getName(); JApiClassType classType = new JApiClassType(Optional.of(ClassHelper.getType(oldSuperclass)), Optional.absent(), JApiChangeStatus.REMOVED); - JApiClass jApiClass = new JApiClass(jarArchiveComparator, oldSuperclassName, Optional.of(oldSuperclass), Optional.absent(), JApiChangeStatus.REMOVED, classType, jarArchiveComparator.getJarArchiveComparatorOptions()); + JApiClass jApiClass = new JApiClass(jarArchiveComparator, oldSuperclassName, Optional.of(oldSuperclass), Optional.absent(), JApiChangeStatus.REMOVED, classType); return Optional.of(jApiClass); } else if (newSuperclassOptional.isPresent()) { CtClass newSuperclass = newSuperclassOptional.get(); String newSuperclassName = newSuperclass.getName(); JApiClassType classType = new JApiClassType(Optional.absent(), Optional.of(ClassHelper.getType(newSuperclass)), JApiChangeStatus.NEW); - JApiClass jApiClass = new JApiClass(jarArchiveComparator, newSuperclassName, Optional.absent(), Optional.of(newSuperclass), JApiChangeStatus.NEW, classType, jarArchiveComparator.getJarArchiveComparatorOptions()); + JApiClass jApiClass = new JApiClass(jarArchiveComparator, newSuperclassName, Optional.absent(), Optional.of(newSuperclass), JApiChangeStatus.NEW, classType); return Optional.of(jApiClass); } return Optional.absent(); diff --git a/japicmp/src/main/resources/html.xslt b/japicmp/src/main/resources/html.xslt index 36c9c653c..316e0770b 100644 --- a/japicmp/src/main/resources/html.xslt +++ b/japicmp/src/main/resources/html.xslt @@ -314,6 +314,7 @@ Status Modifier Constructor + Exceptions Line Number @@ -439,6 +440,9 @@ () + + + @@ -478,29 +482,7 @@
- - - - - - - - - - - - - - - - -
Status:Name:
- - - -
-
- n.a. +
@@ -525,6 +507,32 @@ + + +
+ + + + + + + + + + + + + + +
Status:Name:
+ + + +
+ + n.a. + +
diff --git a/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java b/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java index c684d1a95..8f8232e3b 100644 --- a/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java +++ b/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java @@ -8,6 +8,7 @@ import javassist.CtClass; import javassist.CtMethod; import javassist.Modifier; +import org.hamcrest.core.Is; import org.junit.Test; import java.util.Arrays; @@ -817,4 +818,32 @@ public List createNewClasses(ClassPool classPool) throws Exception { assertThat(jApiClass.isSourceCompatible(), is(false)); assertThat(jApiClass.getCompatibilityChanges(), hasItem(JApiCompatibilityChange.CLASS_NOW_CHECKED_EXCEPTION)); } + + @Test + public void testMethodThrowsNewCheckedException() throws Exception { + JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); + List jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() { + @Override + public List createOldClasses(ClassPool classPool) throws Exception { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtMethodBuilder.create().publicAccess().name("method").addToClass(ctClass); + return Collections.singletonList(ctClass); + } + + @Override + public List createNewClasses(ClassPool classPool) throws Exception { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtMethodBuilder.create().publicAccess().name("method").exceptions(new CtClass[] {classPool.get("java.lang.Exception")}).addToClass(ctClass); + return Collections.singletonList(ctClass); + } + }); + JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test"); + JApiMethod method = getJApiMethod(jApiClass.getMethods(), "method"); + assertThat(method.getExceptions().size(), Is.is(1)); + assertThat(method.getExceptions().get(0).getChangeStatus(), Is.is(JApiChangeStatus.NEW)); + assertThat(method.getExceptions().get(0).isCheckedException(), Is.is(true)); + assertThat(method.isBinaryCompatible(), is(true)); + assertThat(method.isSourceCompatible(), is(false)); + assertThat(method.getCompatibilityChanges(), hasItem(JApiCompatibilityChange.METHOD_NOW_THROWS_CHECKED_EXCEPTION)); + } } diff --git a/japicmp/src/test/java/japicmp/output/xml/XmlOutputGeneratorTest.java b/japicmp/src/test/java/japicmp/output/xml/XmlOutputGeneratorTest.java index 5a333082f..c38b4a264 100644 --- a/japicmp/src/test/java/japicmp/output/xml/XmlOutputGeneratorTest.java +++ b/japicmp/src/test/java/japicmp/output/xml/XmlOutputGeneratorTest.java @@ -75,7 +75,7 @@ private void generateHtmlReport(Options options) { List jApiClasses = new ArrayList<>(); JarArchiveComparatorOptions jarArchiveComparatorOptions = JarArchiveComparatorOptions.of(options); JApiClassType classType = new JApiClassType(Optional.absent(), Optional.absent(), JApiChangeStatus.REMOVED); - jApiClasses.add(new JApiClass(new JarArchiveComparator(jarArchiveComparatorOptions), "japicmp.Test", Optional.absent(), Optional.absent(), JApiChangeStatus.NEW, classType, jarArchiveComparatorOptions)); + jApiClasses.add(new JApiClass(new JarArchiveComparator(jarArchiveComparatorOptions), "japicmp.Test", Optional.absent(), Optional.absent(), JApiChangeStatus.NEW, classType)); XmlOutputGeneratorOptions xmlOutputGeneratorOptions = new XmlOutputGeneratorOptions(); xmlOutputGeneratorOptions.setCreateSchemaFile(true); XmlOutputGenerator generator = new XmlOutputGenerator(jApiClasses, options, xmlOutputGeneratorOptions);