Skip to content

Commit

Permalink
#392 merge changes from master
Browse files Browse the repository at this point in the history
  • Loading branch information
siom79 committed Apr 22, 2024
2 parents 02d833e + 8920ac1 commit 1ede31d
Show file tree
Hide file tree
Showing 6 changed files with 206 additions and 20 deletions.
2 changes: 1 addition & 1 deletion japicmp-testbase/japicmp-test-ant-task/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@
</path>

<taskdef resource="japicmp/ant/antlib.xml" classpathref="task.classpath" />
<japicmp oldjar="${project.build.directory}/guava-18.0.jar" newjar="${project.build.directory}/guava-19.0.jar" oldclasspath="old.classpath" newclasspath="new.classpath" onlyBinaryIncompatible="false" onlyModified="true" />
<japicmp oldjar="${project.build.directory}/guava-18.0.jar" newjar="${project.build.directory}/guava-19.0.jar" oldclasspath="old.classpath" newclasspath="new.classpath" onlyBinaryIncompatible="false" onlyModified="true" noAnnotations="true" />

</target>
</configuration>
Expand Down
38 changes: 28 additions & 10 deletions japicmp/src/main/java/japicmp/compat/CompatibilityChanges.java
Original file line number Diff line number Diff line change
Expand Up @@ -60,12 +60,12 @@ private void evaluateBinaryCompatibility(JApiClass jApiClass, Map<String, JApiCl
// section 13.4.4 of "Java Language Specification" SE7
checkIfSuperclassesOrInterfacesChangedIncompatible(jApiClass, classMap);
checkIfMethodsHaveChangedIncompatible(jApiClass, classMap);
checkIfConstructorsHaveChangedIncompatible(jApiClass);
checkIfConstructorsHaveChangedIncompatible(jApiClass, classMap);
checkIfFieldsHaveChangedIncompatible(jApiClass, classMap);
if (jApiClass.getClassType().getChangeStatus() == JApiChangeStatus.MODIFIED) {
addCompatibilityChange(jApiClass, JApiCompatibilityChangeType.CLASS_TYPE_CHANGED);
}
checkIfAnnotationDeprecatedAdded(jApiClass);
checkIfAnnotationsChanged(jApiClass, classMap);
if (hasModifierLevelDecreased(jApiClass)) {
addCompatibilityChange(jApiClass, JApiCompatibilityChangeType.CLASS_LESS_ACCESSIBLE);
}
Expand Down Expand Up @@ -193,7 +193,7 @@ private void checkIfFieldsHaveChangedIncompatible(JApiClass jApiClass, Map<Strin
if (isNotPrivate(field) && field.getType().hasChanged()) {
addCompatibilityChange(field, JApiCompatibilityChangeType.FIELD_TYPE_CHANGED);
}
checkIfAnnotationDeprecatedAdded(field);
checkIfAnnotationsChanged(field, classMap);
checkIfFieldGenericsChanged(field);
}
}
Expand Down Expand Up @@ -292,7 +292,7 @@ private boolean fieldTypeMatches(JApiField field1, JApiField field2) {
return matches;
}

private void checkIfConstructorsHaveChangedIncompatible(JApiClass jApiClass) {
private void checkIfConstructorsHaveChangedIncompatible(JApiClass jApiClass, Map<String, JApiClass> classMap) {
for (JApiConstructor constructor : jApiClass.getConstructors()) {
// section 13.4.6 of "Java Language Specification" SE7
if (isNotPrivate(constructor) && constructor.getChangeStatus() == JApiChangeStatus.REMOVED) {
Expand All @@ -305,7 +305,7 @@ private void checkIfConstructorsHaveChangedIncompatible(JApiClass jApiClass) {
}
}
checkIfExceptionIsNowChecked(constructor);
checkIfAnnotationDeprecatedAdded(constructor);
checkIfAnnotationsChanged(constructor, classMap);
checkIfVarargsChanged(constructor);
checkIfParametersGenericsChanged(constructor);
if (jApiClass.getChangeStatus().isNotNewOrRemoved()) {
Expand Down Expand Up @@ -425,7 +425,7 @@ private void checkIfMethodsHaveChangedIncompatible(JApiClass jApiClass, Map<Stri
}
checkAbstractMethod(jApiClass, classMap, method);
checkIfExceptionIsNowChecked(method);
checkIfAnnotationDeprecatedAdded(method);
checkIfAnnotationsChanged(method, classMap);
checkIfVarargsChanged(method);
checkIfParametersGenericsChanged(method);
if (method.getChangeStatus().isNotNewOrRemoved()) {
Expand Down Expand Up @@ -471,11 +471,29 @@ private void checkIfFieldGenericsChanged(JApiField jApiField) {
}
}

private void checkIfAnnotationDeprecatedAdded(JApiHasAnnotations jApiHasAnnotations) {
private void checkIfAnnotationsChanged(JApiHasAnnotations jApiHasAnnotations, Map<String, JApiClass> classMap) {
for (JApiAnnotation annotation : jApiHasAnnotations.getAnnotations()) {
if (annotation.getChangeStatus() == JApiChangeStatus.NEW || annotation.getChangeStatus() == JApiChangeStatus.MODIFIED) {
if (annotation.getFullyQualifiedName().equals(Deprecated.class.getName())) {
addCompatibilityChange(jApiHasAnnotations, JApiCompatibilityChangeType.ANNOTATION_DEPRECATED_ADDED);
final JApiChangeStatus status = annotation.getChangeStatus();
if (status == JApiChangeStatus.REMOVED) {
addCompatibilityChange(jApiHasAnnotations, JApiCompatibilityChangeType.ANNOTATION_REMOVED);
} else {
final boolean isNoAnnotations = this.jarArchiveComparator.getJarArchiveComparatorOptions().isNoAnnotations();
final boolean isDeprecated = annotation.getFullyQualifiedName().equals(Deprecated.class.getName());
final JApiClass annotationClass;
if (isNoAnnotations && !isDeprecated) {
annotationClass = null;
} else {
annotationClass = classMap.computeIfAbsent(annotation.getFullyQualifiedName(), fqn -> loadClass(fqn, EnumSet.allOf(Classpath.class)));
annotation.setJApiClass(annotationClass);
}
if (status == JApiChangeStatus.NEW || status == JApiChangeStatus.MODIFIED) {
addCompatibilityChange(jApiHasAnnotations, isDeprecated ? JApiCompatibilityChangeType.ANNOTATION_DEPRECATED_ADDED : JApiCompatibilityChangeType.ANNOTATION_ADDED);
} else if (annotationClass != null) {
checkIfMethodsHaveChangedIncompatible(annotationClass, classMap);
checkIfFieldsHaveChangedIncompatible(annotationClass, classMap);
if (!annotationClass.isSourceCompatible() || !annotationClass.isBinaryCompatible()) {
addCompatibilityChange(jApiHasAnnotations, JApiCompatibilityChangeType.ANNOTATION_MODIFIED_INCOMPATIBLE);
}
}
}
}
Expand Down
16 changes: 10 additions & 6 deletions japicmp/src/main/java/japicmp/model/JApiAnnotation.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,15 @@
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlTransient;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.*;

public class JApiAnnotation implements JApiHasChangeStatus, JApiCompatibility {
private final String fullyQualifiedName;
private final Optional<Annotation> oldAnnotation;
private final Optional<Annotation> newAnnotation;
private final List<JApiAnnotationElement> elements = new LinkedList<>();
private final JApiChangeStatus changeStatus;
private Optional<JApiClass> correspondingJApiClass = Optional.absent();

public JApiAnnotation(String fullyQualifiedName, Optional<Annotation> oldAnnotation, Optional<Annotation> newAnnotation, JApiChangeStatus changeStatus) {
this.fullyQualifiedName = fullyQualifiedName;
Expand Down Expand Up @@ -134,6 +130,14 @@ public String getFullyQualifiedName() {
return fullyQualifiedName;
}

public void setJApiClass(JApiClass jApiClass) {
this.correspondingJApiClass = Optional.of(jApiClass);
}

public Optional<JApiClass> getCorrespondingJApiClass() {
return correspondingJApiClass;
}

@XmlTransient
public Optional<Annotation> getOldAnnotation() {
return oldAnnotation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@

@XmlEnum
public enum JApiCompatibilityChangeType {
ANNOTATION_ADDED(true, true, JApiSemanticVersionLevel.PATCH),
ANNOTATION_DEPRECATED_ADDED(true, true, JApiSemanticVersionLevel.MINOR),
ANNOTATION_MODIFIED_INCOMPATIBLE(true, true, JApiSemanticVersionLevel.PATCH),
ANNOTATION_REMOVED(true, true, JApiSemanticVersionLevel.PATCH),
CLASS_REMOVED(false, false, JApiSemanticVersionLevel.MAJOR),
CLASS_NOW_ABSTRACT(false, false, JApiSemanticVersionLevel.MAJOR),
CLASS_NOW_FINAL(false, false, JApiSemanticVersionLevel.MAJOR),
Expand Down
146 changes: 143 additions & 3 deletions japicmp/src/test/java/japicmp/compat/CompatibilityChangesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,7 @@
import java.util.List;

import static japicmp.util.Helper.*;
import static org.hamcrest.CoreMatchers.hasItem;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertEquals;
Expand Down Expand Up @@ -1925,6 +1923,7 @@ public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiMethod jApiMethod = getJApiMethod(jApiClass.getMethods(), "method");
assertThat(jApiMethod.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_DEPRECATED_ADDED)));
assertThat(jApiMethod.getCompatibilityChanges(), not(hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_ADDED))));
}

@Test
Expand All @@ -1945,6 +1944,7 @@ public List<CtClass> createNewClasses(ClassPool classPool) {
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
assertThat(jApiClass.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_DEPRECATED_ADDED)));
assertThat(jApiClass.getCompatibilityChanges(), not(hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_ADDED))));
}

@Test
Expand All @@ -1965,6 +1965,7 @@ public List<CtClass> createNewClasses(ClassPool classPool) {
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
assertThat(jApiClass.getCompatibilityChanges(), not(hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_DEPRECATED_ADDED))));
assertThat(jApiClass.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_REMOVED)));
}

/**
Expand Down Expand Up @@ -2563,4 +2564,143 @@ public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
assertThat(jApiMethod.getCompatibilityChanges().size(), is(0));
}
}

@Test
public void testAnnotationAddedToClass() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) {
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
return Collections.singletonList(aClass);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").withAnnotation("japicmp.MyAnnotation").addToClassPool(classPool);
return Arrays.asList(aClass, anAnnotation);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
assertThat(jApiClass.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_ADDED)));
}

@Test
public void testAnnotationRemovedFromClass() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").withAnnotation("japicmp.MyAnnotation").addToClassPool(classPool);
return Arrays.asList(aClass, anAnnotation);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) {
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
return Collections.singletonList(aClass);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
assertThat(jApiClass.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_REMOVED)));
}

@Test
public void testAnnotationOnClassModified() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtMethodBuilder.create().name("foo").returnType(CtClass.intType).publicAccess().addToClass(anAnnotation);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").withAnnotation("japicmp.MyAnnotation").addToClassPool(classPool);
return Arrays.asList(aClass, anAnnotation);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").withAnnotation("japicmp.MyAnnotation").addToClassPool(classPool);
return Arrays.asList(aClass, anAnnotation);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
assertThat(jApiClass.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_MODIFIED_INCOMPATIBLE)));
}

@Test
public void testAnnotationAddedToMethod() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").addToClass(aClass);
return Collections.singletonList(aClass);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().withAnnotation("japicmp.MyAnnotation").name("method").addToClass(aClass);
return Arrays.asList(aClass, anAnnotation);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiMethod jApiMethod = getJApiMethod(jApiClass.getMethods(), "method");
assertThat(jApiMethod.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_ADDED)));
}

@Test
public void testAnnotationRemovedFromMethod() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").withAnnotation("japicmp.MyAnnotation").addToClass(aClass);
return Arrays.asList(aClass, anAnnotation);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").addToClass(aClass);
return Collections.singletonList(aClass);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiMethod jApiMethod = getJApiMethod(jApiClass.getMethods(), "method");
assertThat(jApiMethod.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_REMOVED)));
}

@Test
public void testAnnotationOnMethodModified() throws Exception {
JarArchiveComparatorOptions options = new JarArchiveComparatorOptions();
List<JApiClass> jApiClasses = ClassesHelper.compareClasses(options, new ClassesHelper.ClassesGenerator() {
@Override
public List<CtClass> createOldClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtMethodBuilder.create().name("foo").returnType(CtClass.intType).publicAccess().addToClass(anAnnotation);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").withAnnotation("japicmp.MyAnnotation").addToClass(aClass);
return Arrays.asList(aClass, anAnnotation);
}

@Override
public List<CtClass> createNewClasses(ClassPool classPool) throws Exception {
CtClass anAnnotation = CtAnnotationBuilder.create().name("japicmp.MyAnnotation").addToClassPool(classPool);
CtClass aClass = CtClassBuilder.create().name("japicmp.Test").addToClassPool(classPool);
CtMethodBuilder.create().publicAccess().name("method").withAnnotation("japicmp.MyAnnotation").addToClass(aClass);
return Arrays.asList(aClass, anAnnotation);
}
});
JApiClass jApiClass = getJApiClass(jApiClasses, "japicmp.Test");
JApiMethod jApiMethod = getJApiMethod(jApiClass.getMethods(), "method");
assertThat(jApiMethod.getCompatibilityChanges(), hasItem(new JApiCompatibilityChange(JApiCompatibilityChangeType.ANNOTATION_MODIFIED_INCOMPATIBLE)));
}
}
21 changes: 21 additions & 0 deletions japicmp/src/test/java/japicmp/util/CtAnnotationBuilder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package japicmp.util;

import javassist.ClassPool;
import javassist.CtClass;

public class CtAnnotationBuilder {
private String name = "japicmp.Test";

public CtAnnotationBuilder name(String name) {
this.name = name;
return this;
}

public CtClass addToClassPool(ClassPool classPool) {
return classPool.makeAnnotation(this.name);
}

public static CtAnnotationBuilder create() {
return new CtAnnotationBuilder();
}
}

0 comments on commit 1ede31d

Please sign in to comment.