From 35f95ed08ee4936356f1276ae560cf3875a1f88e Mon Sep 17 00:00:00 2001 From: Martin Mois Date: Tue, 19 Jan 2016 21:37:49 +0100 Subject: [PATCH] #59: added compatibility change METHOD_ADDED_TO_INTERFACE --- .../japicmp/compat/CompatibilityChanges.java | 11 ++++- .../java/japicmp/model/JApiAnnotation.java | 8 +++- .../japicmp/model/JApiAnnotationElement.java | 7 ++- .../main/java/japicmp/model/JApiBehavior.java | 14 +++++- .../main/java/japicmp/model/JApiClass.java | 47 ++++++++++++++++++- ...patibility.java => JApiCompatibility.java} | 12 ++++- .../model/JApiCompatibilityChange.java | 1 + .../main/java/japicmp/model/JApiField.java | 14 +++++- .../model/JApiImplementedInterface.java | 19 +++++++- .../java/japicmp/model/JApiSuperclass.java | 19 +++++++- .../java/japicmp/output/semver/SemverOut.java | 6 +-- .../output/stdout/StdoutOutputGenerator.java | 4 +- .../compat/CompatibilityChangesTest.java | 33 ++++++++++++- 13 files changed, 178 insertions(+), 17 deletions(-) rename japicmp/src/main/java/japicmp/model/{JApiBinaryCompatibility.java => JApiCompatibility.java} (65%) diff --git a/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java b/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java index 7712c8301..4a84b8f48 100644 --- a/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java +++ b/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java @@ -265,9 +265,18 @@ public int callback(JApiClass superclass, Map classMap) { addCompatibilityChange(method, JApiCompatibilityChange.METHOD_NO_LONGER_STATIC); } } + if (isInterface(jApiClass)) { + if (method.getChangeStatus() == JApiChangeStatus.NEW) { + addCompatibilityChange(method, JApiCompatibilityChange.METHOD_ADDED_TO_INTERFACE); + } + } } } + private boolean isInterface(JApiClass jApiClass) { + return jApiClass.getClassType().getNewTypeOptional().isPresent() && jApiClass.getClassType().getNewTypeOptional().get() == JApiClassType.ClassType.INTERFACE; + } + private void checkIfMethodHasBeenPulledUp(JApiClass jApiClass, Map classMap, final JApiMethod method, List returnValues) { JApiClassType classType = jApiClass.getClassType(); Optional newTypeOptional = classType.getNewTypeOptional(); @@ -373,7 +382,7 @@ public int callback(JApiClass superclass, Map classMap) { } } - private void addCompatibilityChange(JApiBinaryCompatibility binaryCompatibility, JApiCompatibilityChange compatibilityChange) { + private void addCompatibilityChange(JApiCompatibility binaryCompatibility, JApiCompatibilityChange compatibilityChange) { List compatibilityChanges = binaryCompatibility.getCompatibilityChanges(); if (!compatibilityChanges.contains(compatibilityChange)) { compatibilityChanges.add(compatibilityChange); diff --git a/japicmp/src/main/java/japicmp/model/JApiAnnotation.java b/japicmp/src/main/java/japicmp/model/JApiAnnotation.java index 5304c7577..2ccdc3e17 100644 --- a/japicmp/src/main/java/japicmp/model/JApiAnnotation.java +++ b/japicmp/src/main/java/japicmp/model/JApiAnnotation.java @@ -10,7 +10,7 @@ import javax.xml.bind.annotation.XmlTransient; import java.util.*; -public class JApiAnnotation implements JApiHasChangeStatus, JApiBinaryCompatibility { +public class JApiAnnotation implements JApiHasChangeStatus, JApiCompatibility { private final String fullyQualifiedName; private final Optional oldAnnotation; private final Optional newAnnotation; @@ -134,6 +134,12 @@ public boolean isBinaryCompatible() { return true; } + @Override + @XmlAttribute + public boolean isSourceCompatible() { + return true; + } + @XmlElementWrapper(name = "compatibilityChanges") @XmlElement(name = "compatibilityChange") public List getCompatibilityChanges() { diff --git a/japicmp/src/main/java/japicmp/model/JApiAnnotationElement.java b/japicmp/src/main/java/japicmp/model/JApiAnnotationElement.java index 992b03aaf..f4ac1a55f 100644 --- a/japicmp/src/main/java/japicmp/model/JApiAnnotationElement.java +++ b/japicmp/src/main/java/japicmp/model/JApiAnnotationElement.java @@ -11,7 +11,7 @@ import java.util.Collections; import java.util.List; -public class JApiAnnotationElement implements JApiHasChangeStatus, JApiBinaryCompatibility { +public class JApiAnnotationElement implements JApiHasChangeStatus, JApiCompatibility { private final String name; private final Optional oldValue; private final Optional newValue; @@ -150,6 +150,11 @@ public boolean isBinaryCompatible() { return true; } + @XmlAttribute + public boolean isSourceCompatible() { + return true; + } + @XmlElementWrapper(name = "compatibilityChanges") @XmlElement(name = "compatibilityChange") public List getCompatibilityChanges() { diff --git a/japicmp/src/main/java/japicmp/model/JApiBehavior.java b/japicmp/src/main/java/japicmp/model/JApiBehavior.java index 1937009d8..40cca10f6 100644 --- a/japicmp/src/main/java/japicmp/model/JApiBehavior.java +++ b/japicmp/src/main/java/japicmp/model/JApiBehavior.java @@ -22,7 +22,7 @@ import java.util.List; public class JApiBehavior implements JApiHasModifiers, JApiHasChangeStatus, JApiHasAccessModifier, JApiHasStaticModifier, - JApiHasFinalModifier, JApiHasAbstractModifier, JApiBinaryCompatibility, JApiHasAnnotations, JApiHasBridgeModifier, + JApiHasFinalModifier, JApiHasAbstractModifier, JApiCompatibility, JApiHasAnnotations, JApiHasBridgeModifier, JApiCanBeSynthetic, JApiHasLineNumber { private final String name; private final List parameters = new LinkedList<>(); @@ -344,6 +344,18 @@ public boolean isBinaryCompatible() { return binaryCompatible; } + @Override + @XmlAttribute + public boolean isSourceCompatible() { + boolean sourceCompatible = true; + for (JApiCompatibilityChange compatibilityChange : compatibilityChanges) { + if (!compatibilityChange.isSourceCompatible()) { + sourceCompatible = false; + } + } + return sourceCompatible; + } + @XmlElementWrapper(name = "compatibilityChanges") @XmlElement(name = "compatibilityChange") public List getCompatibilityChanges() { diff --git a/japicmp/src/main/java/japicmp/model/JApiClass.java b/japicmp/src/main/java/japicmp/model/JApiClass.java index 3e0cae0ef..af3730d51 100644 --- a/japicmp/src/main/java/japicmp/model/JApiClass.java +++ b/japicmp/src/main/java/japicmp/model/JApiClass.java @@ -15,7 +15,7 @@ import java.util.*; public class JApiClass implements JApiHasModifiers, JApiHasChangeStatus, JApiHasAccessModifier, JApiHasStaticModifier, JApiHasFinalModifier, JApiHasAbstractModifier, - JApiBinaryCompatibility, JApiHasAnnotations, JApiJavaObjectSerializationCompatibility, JApiCanBeSynthetic { + JApiCompatibility, JApiHasAnnotations, JApiJavaObjectSerializationCompatibility, JApiCanBeSynthetic { private final JarArchiveComparator jarArchiveComparator; private final String fullyQualifiedName; private final JApiClassType classType; @@ -771,6 +771,51 @@ public boolean isBinaryCompatible() { return binaryCompatible; } + @Override + @XmlAttribute + public boolean isSourceCompatible() { + boolean sourceCompatible = true; + for (JApiCompatibilityChange compatibilityChange : compatibilityChanges) { + if (!compatibilityChange.isSourceCompatible()) { + sourceCompatible = false; + break; + } + } + if (sourceCompatible) { + for (JApiField field : fields) { + if (!field.isSourceCompatible()) { + sourceCompatible = false; + break; + } + } + } + if (sourceCompatible) { + for (JApiMethod method : methods) { + if (!method.isSourceCompatible()) { + sourceCompatible = false; + break; + } + } + } + if (sourceCompatible) { + for (JApiConstructor constructor : constructors) { + if (!constructor.isSourceCompatible()) { + sourceCompatible = false; + break; + } + } + } + if (sourceCompatible) { + for (JApiImplementedInterface implementedInterface : interfaces) { + if (!implementedInterface.isSourceCompatible()) { + sourceCompatible = false; + break; + } + } + } + return sourceCompatible; + } + @XmlElementWrapper(name = "annotations") @XmlElement(name = "annotation") public List getAnnotations() { diff --git a/japicmp/src/main/java/japicmp/model/JApiBinaryCompatibility.java b/japicmp/src/main/java/japicmp/model/JApiCompatibility.java similarity index 65% rename from japicmp/src/main/java/japicmp/model/JApiBinaryCompatibility.java rename to japicmp/src/main/java/japicmp/model/JApiCompatibility.java index 4263e33a7..3e3a2f210 100644 --- a/japicmp/src/main/java/japicmp/model/JApiBinaryCompatibility.java +++ b/japicmp/src/main/java/japicmp/model/JApiCompatibility.java @@ -3,15 +3,23 @@ import java.util.List; /** - * Implemented by elements which can indicate if they have changed binary compatible or not. + * Implemented by elements which can indicate if they have changed compatible or not. */ -public interface JApiBinaryCompatibility { +public interface JApiCompatibility { /** * Returns true if this element has changed binary compatible. * * @return true if this element has changed binary compatible */ boolean isBinaryCompatible(); + + /** + * Returns true if this element has changed source compatible. + * + * @return true if this element has changed source compatible + */ + boolean isSourceCompatible(); + /** * Returns all compatibility changes. * @return a list of compatibility changes diff --git a/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java b/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java index a69240ba0..ab523498d 100644 --- a/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java +++ b/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java @@ -19,6 +19,7 @@ public enum JApiCompatibilityChange { METHOD_NOW_FINAL(false, false), METHOD_NOW_STATIC(false, false), METHOD_NO_LONGER_STATIC(false, false), + METHOD_ADDED_TO_INTERFACE(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/JApiField.java b/japicmp/src/main/java/japicmp/model/JApiField.java index 3024f8d11..13c322fe4 100644 --- a/japicmp/src/main/java/japicmp/model/JApiField.java +++ b/japicmp/src/main/java/japicmp/model/JApiField.java @@ -20,7 +20,7 @@ import java.util.List; public class JApiField implements JApiHasChangeStatus, JApiHasModifiers, JApiHasAccessModifier, JApiHasStaticModifier, - JApiHasFinalModifier, JApiHasTransientModifier, JApiBinaryCompatibility, JApiHasAnnotations, JApiCanBeSynthetic { + JApiHasFinalModifier, JApiHasTransientModifier, JApiCompatibility, JApiHasAnnotations, JApiCanBeSynthetic { private final JApiChangeStatus changeStatus; private final Optional oldFieldOptional; private final Optional newFieldOptional; @@ -327,6 +327,18 @@ public boolean isBinaryCompatible() { return binaryCompatible; } + @Override + @XmlAttribute + public boolean isSourceCompatible() { + boolean sourceCompatible = true; + for (JApiCompatibilityChange compatibilityChange : compatibilityChanges) { + if (!compatibilityChange.isSourceCompatible()) { + sourceCompatible = false; + } + } + return sourceCompatible; + } + @XmlElementWrapper(name = "compatibilityChanges") @XmlElement(name = "compatibilityChange") public List getCompatibilityChanges() { diff --git a/japicmp/src/main/java/japicmp/model/JApiImplementedInterface.java b/japicmp/src/main/java/japicmp/model/JApiImplementedInterface.java index a53038b30..582394a46 100644 --- a/japicmp/src/main/java/japicmp/model/JApiImplementedInterface.java +++ b/japicmp/src/main/java/japicmp/model/JApiImplementedInterface.java @@ -8,7 +8,7 @@ import java.util.ArrayList; import java.util.List; -public class JApiImplementedInterface implements JApiHasChangeStatus, JApiBinaryCompatibility { +public class JApiImplementedInterface implements JApiHasChangeStatus, JApiCompatibility { private final String fullyQualifiedName; private final JApiChangeStatus changeStatus; private final List compatibilityChanges = new ArrayList<>(); @@ -46,6 +46,23 @@ public boolean isBinaryCompatible() { return binaryCompatible; } + @Override + @XmlAttribute + public boolean isSourceCompatible() { + boolean sourceCompatible = true; + for (JApiCompatibilityChange compatibilityChange : compatibilityChanges) { + if (!compatibilityChange.isSourceCompatible()) { + sourceCompatible = false; + } + } + if (sourceCompatible && correspondingJApiClass.isPresent()) { + if (!correspondingJApiClass.get().isSourceCompatible()) { + sourceCompatible = false; + } + } + return sourceCompatible; + } + @XmlElementWrapper(name = "compatibilityChanges") @XmlElement(name = "compatibilityChange") public List getCompatibilityChanges() { diff --git a/japicmp/src/main/java/japicmp/model/JApiSuperclass.java b/japicmp/src/main/java/japicmp/model/JApiSuperclass.java index 54a0d68a6..ee3b2e087 100644 --- a/japicmp/src/main/java/japicmp/model/JApiSuperclass.java +++ b/japicmp/src/main/java/japicmp/model/JApiSuperclass.java @@ -13,7 +13,7 @@ import java.util.LinkedList; import java.util.List; -public class JApiSuperclass implements JApiHasChangeStatus, JApiBinaryCompatibility { +public class JApiSuperclass implements JApiHasChangeStatus, JApiCompatibility { private final Optional oldSuperclassOptional; private final Optional newSuperclassOptional; private final JApiChangeStatus changeStatus; @@ -115,6 +115,23 @@ public boolean isBinaryCompatible() { return binaryCompatible; } + @Override + @XmlAttribute + public boolean isSourceCompatible() { + boolean sourceCompatible = true; + for (JApiCompatibilityChange compatibilityChange : compatibilityChanges) { + if (!compatibilityChange.isSourceCompatible()) { + sourceCompatible = false; + } + } + if (sourceCompatible && correspondingJApiClass.isPresent()) { + if (!correspondingJApiClass.get().isSourceCompatible()) { + sourceCompatible = false; + } + } + return sourceCompatible; + } + @XmlElementWrapper(name = "compatibilityChanges") @XmlElement(name = "compatibilityChange") public List getCompatibilityChanges() { diff --git a/japicmp/src/main/java/japicmp/output/semver/SemverOut.java b/japicmp/src/main/java/japicmp/output/semver/SemverOut.java index 39f7df9a1..6bcc44bfc 100644 --- a/japicmp/src/main/java/japicmp/output/semver/SemverOut.java +++ b/japicmp/src/main/java/japicmp/output/semver/SemverOut.java @@ -79,8 +79,8 @@ private SemverStatus signs(JApiHasChangeStatus hasChangeStatus) { case NEW: case REMOVED: case MODIFIED: - if (hasChangeStatus instanceof JApiBinaryCompatibility) { - JApiBinaryCompatibility binaryCompatibility = (JApiBinaryCompatibility) hasChangeStatus; + if (hasChangeStatus instanceof JApiCompatibility) { + JApiCompatibility binaryCompatibility = (JApiCompatibility) hasChangeStatus; if (binaryCompatibility.isBinaryCompatible()) { if (hasChangeStatus instanceof JApiHasAccessModifier) { JApiHasAccessModifier jApiHasAccessModifier = (JApiHasAccessModifier) hasChangeStatus; @@ -113,7 +113,7 @@ private SemverStatus signs(JApiHasChangeStatus hasChangeStatus) { } } } else { - throw new IllegalStateException("Element '" + hasChangeStatus.getClass().getCanonicalName() + " does not implement '" + JApiBinaryCompatibility.class.getCanonicalName() + "'."); + throw new IllegalStateException("Element '" + hasChangeStatus.getClass().getCanonicalName() + " does not implement '" + JApiCompatibility.class.getCanonicalName() + "'."); } default: throw new IllegalStateException("The following JApiChangeStatus is not supported: " + (changeStatus == null ? "null" : changeStatus.name())); diff --git a/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java b/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java index a64a9bfc1..8971395bd 100644 --- a/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java +++ b/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java @@ -109,8 +109,8 @@ private String signs(JApiHasChangeStatus hasChangeStatus) { break; } boolean binaryCompatible = true; - if (hasChangeStatus instanceof JApiBinaryCompatibility) { - JApiBinaryCompatibility binaryCompatibility = (JApiBinaryCompatibility) hasChangeStatus; + if (hasChangeStatus instanceof JApiCompatibility) { + JApiCompatibility binaryCompatibility = (JApiCompatibility) hasChangeStatus; binaryCompatible = binaryCompatibility.isBinaryCompatible(); } if (binaryCompatible) { diff --git a/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java b/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java index f3e95145a..63f61095a 100644 --- a/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java +++ b/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java @@ -758,10 +758,39 @@ public List createNewClasses(ClassPool classPool) throws Exception { } }); JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test"); -// assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); -// assertThat(jApiClass.isBinaryCompatible(), is(false)); + assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); + assertThat(jApiClass.isBinaryCompatible(), is(false)); JApiConstructor jApiConstructor = getJApiConstructor(jApiClass.getConstructors(), Collections.singletonList("int")); assertThat(jApiConstructor.getCompatibilityChanges(), hasItem(JApiCompatibilityChange.CONSTRUCTOR_LESS_ACCESSIBLE)); assertThat(jApiConstructor.isBinaryCompatible(), is(false)); } + + @Test + public void testMethodAddedToInterface() throws Exception { + JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); + options.setIncludeSynthetic(true); + options.setAccessModifier(AccessModifier.PRIVATE); + List jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() { + @Override + public List createOldClasses(ClassPool classPool) throws Exception { + CtClass ctClass = CtInterfaceBuilder.create().name("japicmp.Test").addToClassPool(classPool); + return Collections.singletonList(ctClass); + } + + @Override + public List createNewClasses(ClassPool classPool) throws Exception { + CtClass ctClass = CtInterfaceBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtMethodBuilder.create().publicAccess().abstractMethod().name("method").addToClass(ctClass); + return Collections.singletonList(ctClass); + } + }); + JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test"); + assertThat(jApiClass.getChangeStatus(), is(JApiChangeStatus.MODIFIED)); + assertThat(jApiClass.isBinaryCompatible(), is(true)); + assertThat(jApiClass.isSourceCompatible(), is(false)); + JApiMethod jApiMethod = getJApiMethod(jApiClass.getMethods(), "method"); + assertThat(jApiMethod.getCompatibilityChanges(), hasItem(JApiCompatibilityChange.METHOD_ADDED_TO_INTERFACE)); + assertThat(jApiMethod.isBinaryCompatible(), is(true)); + assertThat(jApiMethod.isSourceCompatible(), is(false)); + } }