1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.i18nsystem.bytebudddy;
35
36 import java.lang.annotation.Annotation;
37 import java.util.Objects;
38 import java.util.stream.Stream;
39
40 import lombok.EqualsAndHashCode;
41 import net.bytebuddy.description.method.MethodDescription;
42 import net.bytebuddy.description.type.TypeDescription;
43 import net.bytebuddy.matcher.ElementMatcher;
44 import net.bytebuddy.matcher.ElementMatchers;
45
46
47
48
49
50
51 @EqualsAndHashCode(callSuper = false)
52 public class MethodOrSuperMethodMatcher<T extends MethodDescription> extends ElementMatcher.Junction.AbstractBase<T> {
53
54
55
56
57 public static MethodOrSuperMethodMatcher<MethodDescription> annotationIsPresentInHierarchy(Class<? extends Annotation> annotationType) {
58 return new MethodOrSuperMethodMatcher<>(ElementMatchers.isAnnotatedWith(annotationType));
59 }
60
61 private final ElementMatcher<? super MethodDescription> matcher;
62
63 public MethodOrSuperMethodMatcher(ElementMatcher<? super MethodDescription> matcher) {
64 this.matcher = matcher;
65 }
66
67 @Override
68 public boolean matches(T target) {
69 return matcher.matches(target) || doMatch(target, target.getDeclaringType().asGenericType());
70 }
71
72 public boolean doMatch(T target, TypeDescription.Generic declaringType) {
73 boolean matches = getImplementedTypes(declaringType)
74 .flatMap(type -> getTargetMethod(type, target))
75 .anyMatch(matcher::matches);
76
77 if (!matches && declaringType.getSuperClass() != null) {
78 matches = this.doMatch(target, declaringType.getSuperClass());
79 }
80
81 return matches;
82 }
83
84 private Stream<TypeDescription.Generic> getImplementedTypes(TypeDescription.Generic declaringType) {
85 return Stream.concat(
86 Stream.of(declaringType),
87 declaringType.getInterfaces().stream()
88 .flatMap(this::getImplementedTypes)
89 );
90 }
91
92 private Stream<MethodDescription.InGenericShape> getTargetMethod(TypeDescription.Generic type, T targetMethod) {
93 return type.getDeclaredMethods().stream()
94 .filter(method ->
95 Objects.equals(targetMethod.getName(), method.getName()) &&
96 Objects.equals(targetMethod.getReturnType(), method.getReturnType()) &&
97 Objects.equals(targetMethod.getParameters(), method.getParameters()))
98 .findFirst()
99 .map(Stream::of)
100 .orElse(Stream.empty());
101 }
102 }