diff --git a/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java b/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java index 12f586d8f..f137f16cf 100755 --- a/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java +++ b/japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java @@ -178,6 +178,13 @@ public Integer callback(JApiClass superclass, Map classMap, J addCompatibilityChange(field, JApiCompatibilityChange.FIELD_NO_LONGER_STATIC); } } + // section 13.4.11 of "Java Language Specification" SE7 + if (field.getTransientModifier().hasChangedFromTo(TransientModifier.NON_TRANSIENT, TransientModifier.TRANSIENT)) { + addCompatibilityChange(field, JApiCompatibilityChange.FIELD_NOW_TRANSIENT); + } + if (field.getTransientModifier().hasChangedFromTo(TransientModifier.TRANSIENT, TransientModifier.NON_TRANSIENT)) { + addCompatibilityChange(field, JApiCompatibilityChange.FIELD_NO_LONGER_TRANSIENT); + } if (isNotPrivate(field) && field.getType().hasChanged()) { addCompatibilityChange(field, JApiCompatibilityChange.FIELD_TYPE_CHANGED); } diff --git a/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java b/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java index b62071a09..2991b4c70 100644 --- a/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java +++ b/japicmp/src/main/java/japicmp/model/JApiCompatibilityChange.java @@ -51,7 +51,9 @@ public enum JApiCompatibilityChange { FIELD_STATIC_AND_OVERRIDES_STATIC(false, false, JApiSemanticVersionLevel.MAJOR), FIELD_LESS_ACCESSIBLE_THAN_IN_SUPERCLASS(false, false, JApiSemanticVersionLevel.MAJOR), FIELD_NOW_FINAL(false, false, JApiSemanticVersionLevel.MAJOR), + FIELD_NOW_TRANSIENT(true, true, JApiSemanticVersionLevel.PATCH), FIELD_NOW_STATIC(false, false, JApiSemanticVersionLevel.MAJOR), + FIELD_NO_LONGER_TRANSIENT(true, true, JApiSemanticVersionLevel.PATCH), FIELD_NO_LONGER_STATIC(false, false, JApiSemanticVersionLevel.MAJOR), FIELD_TYPE_CHANGED(false, false, JApiSemanticVersionLevel.MAJOR), FIELD_REMOVED(false, false, JApiSemanticVersionLevel.MAJOR), diff --git a/japicmp/src/main/java/japicmp/model/JApiField.java b/japicmp/src/main/java/japicmp/model/JApiField.java index 0ca2966c2..c8de409ec 100644 --- a/japicmp/src/main/java/japicmp/model/JApiField.java +++ b/japicmp/src/main/java/japicmp/model/JApiField.java @@ -270,7 +270,7 @@ public Optional getNewFieldOptional() { @XmlElementWrapper(name = "modifiers") @XmlElement(name = "modifier") public List>>> getModifiers() { - return Arrays.asList(this.accessModifier, this.staticModifier, this.finalModifier, this.syntheticModifier); + return Arrays.asList(this.accessModifier, this.staticModifier, this.finalModifier, this.transientModifier, this.syntheticModifier); } @XmlTransient diff --git a/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java b/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java index 4e9c43366..6efddc9ee 100644 --- a/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java +++ b/japicmp/src/main/java/japicmp/output/stdout/StdoutOutputGenerator.java @@ -426,7 +426,8 @@ private void processFieldChanges(StringBuilder sb, JApiClass jApiClass) { for (JApiField jApiField : jApiFields) { sb.append(tabs(1)).append(signs(jApiField)).append(" ").append(jApiField.getChangeStatus()).append(" FIELD: ") .append(accessModifierAsString(jApiField)).append(staticModifierAsString(jApiField)) - .append(finalModifierAsString(jApiField)).append(syntheticModifierAsString(jApiField)) + .append(finalModifierAsString(jApiField)).append(transientModifierAsString(jApiField)) + .append(syntheticModifierAsString(jApiField)) .append(fieldTypeChangeAsString(jApiField)); appendGenericTypes(sb, jApiField); sb.append(" ").append(jApiField.getName()).append('\n'); @@ -444,6 +445,11 @@ private String finalModifierAsString(JApiHasFinalModifier hasFinalModifier) { return modifierAsString(modifier, FinalModifier.NON_FINAL); } + private String transientModifierAsString(JApiHasTransientModifier hasTransientModifier) { + JApiModifier modifier = hasTransientModifier.getTransientModifier(); + return modifierAsString(modifier, TransientModifier.NON_TRANSIENT); + } + private String staticModifierAsString(JApiHasStaticModifier hasStaticModifier) { JApiModifier modifier = hasStaticModifier.getStaticModifier(); return modifierAsString(modifier, StaticModifier.NON_STATIC); diff --git a/japicmp/src/main/resources/html.xslt b/japicmp/src/main/resources/html.xslt index ce15221bb..1d38c9f74 100644 --- a/japicmp/src/main/resources/html.xslt +++ b/japicmp/src/main/resources/html.xslt @@ -803,6 +803,9 @@ not_final + + not_transient + not_static diff --git a/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java b/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java index ed0af88e1..c055f2205 100755 --- a/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java +++ b/japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java @@ -797,6 +797,36 @@ public List createNewClasses(ClassPool classPool) throws Exception { assertThat(jApiField.isBinaryCompatible(), is(false)); } + @Test + public void testFieldNowTransient() 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 = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtFieldBuilder.create().type(CtClass.intType).name("field").addToClass(ctClass); + return Collections.singletonList(ctClass); + } + + @Override + public List createNewClasses(ClassPool classPool) throws Exception { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtFieldBuilder.create().transientAccess().type(CtClass.intType).name("field").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(true)); + JApiField jApiField = getJApiField(jApiClass.getFields(), "field"); + assertThat(jApiField.getCompatibilityChanges(), hasItem(JApiCompatibilityChange.FIELD_NOW_TRANSIENT)); + assertThat(jApiField.isBinaryCompatible(), is(true)); + assertThat(jApiField.isBinaryCompatible(), is(true)); + } + @Test public void testFieldNowStatic() throws Exception { JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); @@ -825,6 +855,36 @@ public List createNewClasses(ClassPool classPool) throws Exception { assertThat(jApiField.isBinaryCompatible(), is(false)); } + @Test + public void testFieldNoLongerTransient() 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 = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtFieldBuilder.create().transientAccess().type(CtClass.intType).name("field").addToClass(ctClass); + return Collections.singletonList(ctClass); + } + + @Override + public List createNewClasses(ClassPool classPool) throws Exception { + CtClass ctClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool); + CtFieldBuilder.create().type(CtClass.intType).name("field").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(true)); + JApiField jApiField = getJApiField(jApiClass.getFields(), "field"); + assertThat(jApiField.getCompatibilityChanges(), hasItem(JApiCompatibilityChange.FIELD_NO_LONGER_TRANSIENT)); + assertThat(jApiField.isBinaryCompatible(), is(true)); + assertThat(jApiField.isBinaryCompatible(), is(true)); + } + @Test public void testFieldNoLongerStatic() throws Exception { JarArchiveComparatorOptions options = new JarArchiveComparatorOptions(); diff --git a/japicmp/src/test/java/japicmp/util/CtFieldBuilder.java b/japicmp/src/test/java/japicmp/util/CtFieldBuilder.java index c4784c75f..a16d837cc 100644 --- a/japicmp/src/test/java/japicmp/util/CtFieldBuilder.java +++ b/japicmp/src/test/java/japicmp/util/CtFieldBuilder.java @@ -94,6 +94,11 @@ public CtFieldBuilder protectedAccess() { return this; } + public CtFieldBuilder transientAccess() { + this.modifier = this.modifier | Modifier.TRANSIENT; + return this; + } + public CtFieldBuilder finalAccess() { this.modifier = this.modifier | Modifier.FINAL; return this;