Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,13 @@
import java.util.stream.Collectors;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.FieldInfo;
import org.jboss.jandex.MethodInfo;
import org.jboss.jandex.Type;
import org.jboss.jandex.TypeTarget;

public class ClassComparisonUtil {
private static final Set<DotName> IGNORED_ANNOTATIONS = Set.of(
Expand Down Expand Up @@ -130,10 +130,12 @@ static boolean compareMethodAnnotations(Collection<AnnotationInstance> a, Collec
}
List<AnnotationInstance> method1 = new ArrayList<>();
Map<Integer, List<AnnotationInstance>> params1 = new HashMap<>();
methodMap(a, method1, params1);
Map<Integer, List<AnnotationInstance>> paramTypes1 = new HashMap<>();
methodMap(a, method1, params1, paramTypes1);
List<AnnotationInstance> method2 = new ArrayList<>();
Map<Integer, List<AnnotationInstance>> params2 = new HashMap<>();
methodMap(b, method2, params2);
Map<Integer, List<AnnotationInstance>> paramTypes2 = new HashMap<>();
methodMap(b, method2, params2, paramTypes2);
if (!compareAnnotations(method1, method2)) {
return false;
}
Expand All @@ -146,21 +148,38 @@ static boolean compareMethodAnnotations(Collection<AnnotationInstance> a, Collec
return false;
}
}
for (Map.Entry<Integer, List<AnnotationInstance>> entry : paramTypes1.entrySet()) {
List<AnnotationInstance> other = paramTypes2.get(entry.getKey());
if (!compareAnnotations(other, entry.getValue())) {
return false;
}
}
return true;
}

private static void methodMap(Collection<AnnotationInstance> b, List<AnnotationInstance> method2,
Map<Integer, List<AnnotationInstance>> params2) {
Map<Integer, List<AnnotationInstance>> params2, Map<Integer, List<AnnotationInstance>> paramTypes2) {
for (AnnotationInstance i : b) {
if (i.target().kind() == AnnotationTarget.Kind.METHOD) {
method2.add(i);
} else {
int index = i.target().asMethodParameter().position();
List<AnnotationInstance> instances = params2.get(index);
if (instances == null) {
params2.put(index, instances = new ArrayList<>());
}
instances.add(i);
int index;
switch (i.target().kind()) {
case METHOD:
method2.add(i);
break;
case METHOD_PARAMETER:
index = i.target().asMethodParameter().position();
params2.computeIfAbsent(index, k -> new ArrayList<>()).add(i);
break;
case TYPE:
TypeTarget.Usage usage = i.target().asType().usage();
if (usage == TypeTarget.Usage.METHOD_PARAMETER) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm fairly sure at least Usage.EMPTY is quite realistic (type annotation on return type), and there's a few other kinds of usages that may legitimately occur. But I agree we can deal with those as they come.

index = i.target().asType().asMethodParameterType().position();
paramTypes2.computeIfAbsent(index, k -> new ArrayList<>()).add(i);
} else {
throw new IllegalArgumentException("Unsupported type annotation usage: " + usage);
}
break;
default:
throw new IllegalArgumentException("Unsupported annotation target kind: " + i.target().kind());
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package io.quarkus.deployment.dev;

import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.MethodParameterInfo;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;

class ClassComparisonUtilTest {

@Nested
class CompareMethodAnnotations {

@Test
public void annotationsEqual() {
AnnotationInstance instance1 = methodParameterAnnotation(AnnotationForTest1.class);
AnnotationInstance instance2 = methodParameterAnnotation(AnnotationForTest1.class);

List<AnnotationInstance> instances1 = List.of(instance1);
List<AnnotationInstance> instances2 = List.of(instance2);

Assertions.assertTrue(ClassComparisonUtil.compareMethodAnnotations(instances1, instances2));
}

@Test
public void annotationsNotEqual() {
AnnotationInstance instance1 = methodParameterAnnotation(AnnotationForTest1.class);
AnnotationInstance instance2 = methodParameterAnnotation(AnnotationForTest2.class);

List<AnnotationInstance> instances1 = List.of(instance1);
List<AnnotationInstance> instances2 = List.of(instance2);

Assertions.assertFalse(ClassComparisonUtil.compareMethodAnnotations(instances1, instances2));
}

@Test
public void compareMethodAnnotationsSizeDiffer() {
AnnotationInstance instance = methodParameterAnnotation(AnnotationForTest1.class);

List<AnnotationInstance> instances = List.of(instance);

Assertions.assertFalse(ClassComparisonUtil.compareMethodAnnotations(instances, List.of()));
Assertions.assertFalse(ClassComparisonUtil.compareMethodAnnotations(List.of(), instances));
}

@Test
public void multipleAnnotationsAtSamePosition() {
List<AnnotationInstance> instances1 = List.of(
methodParameterAnnotation(AnnotationForTest1.class),
methodParameterAnnotation(AnnotationForTest2.class));
List<AnnotationInstance> instances2 = List.of(
methodParameterAnnotation(AnnotationForTest2.class),
methodParameterAnnotation(AnnotationForTest1.class));

Assertions.assertTrue(ClassComparisonUtil.compareMethodAnnotations(instances1, instances2));
}

@Test
public void multipleAnnotations() {
List<AnnotationInstance> instances1 = List.of(
methodParameterAnnotation(AnnotationForTest1.class, 1),
methodParameterAnnotation(AnnotationForTest2.class, 2));

List<AnnotationInstance> instances2 = List.of(
methodParameterAnnotation(AnnotationForTest1.class, 2),
methodParameterAnnotation(AnnotationForTest2.class, 1));

Assertions.assertFalse(ClassComparisonUtil.compareMethodAnnotations(instances1, instances2));
}

private static AnnotationInstance methodParameterAnnotation(
Class<? extends Annotation> annotation) {
return methodParameterAnnotation(annotation, 1);
}

private static AnnotationInstance methodParameterAnnotation(
Class<? extends Annotation> annotation, int position) {
MethodParameterInfo target = MethodParameterInfo.create(null, (short) position);
return AnnotationInstance.builder(annotation).buildWithTarget(target);
}

@Target({ ElementType.PARAMETER, ElementType.TYPE_USE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
private @interface AnnotationForTest1 {
}

@Target({ ElementType.PARAMETER, ElementType.TYPE_USE })
@Retention(RetentionPolicy.RUNTIME)
@Documented
private @interface AnnotationForTest2 {
}
}

}
Loading