Repository navigation
Expand file tree
/
Copy pathMemberResolver57Test.java
More file actions
316 lines (277 loc) · 12.5 KB
/
Copy pathMemberResolver57Test.java
File metadata and controls
316 lines (277 loc) · 12.5 KB
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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
package com.fasterxml.classmate;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.jupiter.api.Test;
import com.fasterxml.classmate.members.RawMethod;
import com.fasterxml.classmate.members.ResolvedMethod;
import static org.junit.jupiter.api.Assertions.*;
public class MemberResolver57Test
{
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface MethodMark { String value(); }
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@interface ParameterMark { String value(); }
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.PARAMETER})
@interface InterfaceOnly { }
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.PARAMETER})
@interface ClassOnly { }
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD, ElementType.PARAMETER})
@interface InheritedMark { String value(); }
interface Foo { }
static class Bar implements Foo { }
interface Contract {
@MethodMark("interface") @InterfaceOnly @InheritedMark("interface")
Foo convert(@ParameterMark("interface") @InterfaceOnly
@InheritedMark("interface") String value);
}
static class Parent {
@MethodMark("class") @ClassOnly @InheritedMark("class")
public Bar convert(@ParameterMark("class") @ClassOnly
@InheritedMark("class") String value) { return null; }
}
static class Child extends Parent implements Contract { }
static class DirectChild extends Child {
@Override
public Bar convert(String value) { return null; }
}
abstract static class ChildMixin {
@MethodMark("child-mixin")
abstract Foo convert(@ParameterMark("child-mixin") String value);
}
abstract static class ParentMixin {
@MethodMark("parent-mixin")
abstract Bar convert(@ParameterMark("parent-mixin") String value);
}
interface GenericContract<T> { T convert(T value); }
static class GenericParent<T> {
public T convert(T value) { return value; }
}
static class GenericChild extends GenericParent<String>
implements GenericContract<String> { }
interface GenericReturn<T> { T value(); }
static class StringParent { public String value() { return "value"; } }
static class StringChild extends StringParent implements GenericReturn<CharSequence> { }
interface ValueContract {
@MethodMark("interface") @InterfaceOnly
Foo value();
}
static class ValueParent {
@MethodMark("class") @ClassOnly
public Bar value() { return null; }
}
static class ValueChild extends ValueParent implements ValueContract { }
abstract static class ChildValueMixin {
@MethodMark("child-mixin")
abstract Foo value();
}
abstract static class ParentValueMixin {
@MethodMark("parent-mixin")
abstract Bar value();
}
interface DefaultContract {
default Foo value() { return null; }
}
static class DefaultChild extends ValueParent implements DefaultContract { }
abstract static class AbstractParent { public abstract Bar value(); }
abstract static class AbstractChild extends AbstractParent implements ValueContract { }
static class PrivateParent {
@SuppressWarnings("unused")
private Bar value() { return null; }
}
abstract static class PrivateChild extends PrivateParent implements ValueContract { }
@Test
public void inheritedClassMethodHasCovariantReturn()
{
ResolvedMethod method = method(Child.class, "convert", null, null);
assertSame(Parent.class, method.getRawMember().getDeclaringClass());
assertSame(Parent.class, method.getDeclaringType().getErasedType());
assertSame(Bar.class, method.getReturnType().getErasedType());
assertSame(String.class, method.getArgumentType(0).getErasedType());
}
@Test
public void inheritedGenericMethodUsesSuperclassBindings()
{
ResolvedMethod method = method(GenericChild.class, "convert", null, null);
assertSame(GenericParent.class, method.getRawMember().getDeclaringClass());
assertSame(String.class, method.getReturnType().getErasedType());
assertSame(String.class, method.getArgumentType(0).getErasedType());
}
@Test
public void inheritedReturnCanBeNarrowerThanGenericInterface()
{
ResolvedMethod method = method(StringChild.class, "value", null, null);
assertSame(StringParent.class, method.getRawMember().getDeclaringClass());
assertSame(String.class, method.getReturnType().getErasedType());
}
@Test
public void classOnlyMethodIsUnchanged()
{
assertSame(Parent.class, method(Parent.class, "convert", null, null)
.getRawMember().getDeclaringClass());
}
@Test
public void interfaceOnlyMethodIsUnchanged()
{
assertSame(Contract.class, method(Contract.class, "convert", null, null)
.getRawMember().getDeclaringClass());
}
@Test
public void directlyOverriddenMethodKeepsPrecedence()
{
assertSame(DirectChild.class, method(DirectChild.class, "convert", null, null)
.getRawMember().getDeclaringClass());
}
@Test
public void classMethodTakesPrecedenceOverDefaultInterfaceMethod()
{
assertSame(ValueParent.class, method(DefaultChild.class, "value", null, null)
.getRawMember().getDeclaringClass());
}
@Test
public void abstractClassMethodTakesPrecedenceOverInterfaceMethod()
{
assertSame(AbstractParent.class, method(AbstractChild.class, "value", null, null)
.getRawMember().getDeclaringClass());
}
@Test
public void privateClassMethodDoesNotImplementInterface()
{
assertSame(ValueContract.class, method(PrivateChild.class, "value", null, null)
.getRawMember().getDeclaringClass());
}
@Test
public void filteredClassMethodDoesNotReplaceInterface()
{
TypeResolver types = new TypeResolver();
MemberResolver resolver = new MemberResolver(types);
resolver.setMethodFilter(new Filter<RawMethod>() {
@Override
public boolean include(RawMethod method) {
return method.getDeclaringType().getErasedType() != Parent.class;
}
});
ResolvedMethod[] methods = resolver.resolve(types.resolve(Child.class), null, null)
.getMemberMethods();
assertEquals(1, methods.length);
assertSame(Contract.class, methods[0].getRawMember().getDeclaringClass());
}
@Test
public void inheritableAnnotationsKeepExistingPriority()
{
ResolvedMethod method = annotated(Child.class, AnnotationInclusion.INCLUDE_AND_INHERIT, null);
assertEquals("interface", method.get(MethodMark.class).value());
assertEquals("interface", method.getParam(0, ParameterMark.class).value());
assertNotNull(method.get(InterfaceOnly.class));
assertNotNull(method.get(ClassOnly.class));
assertNotNull(method.getParam(0, InterfaceOnly.class));
assertNotNull(method.getParam(0, ClassOnly.class));
assertSame(Parent.class, method.getRawMember().getDeclaringClass());
}
@Test
public void nonInheritableAnnotationsComeFromSelectedClass()
{
ResolvedMethod method = annotated(Child.class, AnnotationInclusion.INCLUDE_BUT_DONT_INHERIT, null);
assertEquals("class", method.get(MethodMark.class).value());
assertEquals("class", method.getParam(0, ParameterMark.class).value());
assertNull(method.get(InterfaceOnly.class));
assertNull(method.getParam(0, InterfaceOnly.class));
assertNotNull(method.get(ClassOnly.class));
assertNotNull(method.getParam(0, ClassOnly.class));
assertSame(Parent.class, method.getRawMember().getDeclaringClass());
}
@Test
public void inheritedOnlyModeFiltersInterfaceAnnotations()
{
ResolvedMethod method = annotated(Child.class,
AnnotationInclusion.INCLUDE_AND_INHERIT_IF_INHERITED, null);
assertEquals("class", method.get(MethodMark.class).value());
assertEquals("class", method.getParam(0, ParameterMark.class).value());
assertEquals("interface", method.get(InheritedMark.class).value());
assertEquals("interface", method.getParam(0, InheritedMark.class).value());
assertNull(method.get(InterfaceOnly.class));
assertNull(method.getParam(0, InterfaceOnly.class));
}
@Test
public void excludedMethodAnnotationsStayExcluded()
{
ResolvedMethod method = annotated(Child.class, AnnotationInclusion.DONT_INCLUDE, null);
assertNull(method.get(MethodMark.class));
assertNull(method.get(InterfaceOnly.class));
assertNull(method.get(ClassOnly.class));
assertSame(Parent.class, method.getRawMember().getDeclaringClass());
}
@Test
public void childMixinKeepsInheritableMethodAndParameterPriority()
{
AnnotationOverrides mixins = AnnotationOverrides.builder()
.add(Child.class, ChildMixin.class).add(Parent.class, ParentMixin.class).build();
ResolvedMethod method = annotated(Child.class, AnnotationInclusion.INCLUDE_AND_INHERIT, mixins);
assertEquals("child-mixin", method.get(MethodMark.class).value());
assertEquals("child-mixin", method.getParam(0, ParameterMark.class).value());
assertNotNull(method.get(ClassOnly.class));
assertSame(Parent.class, method.getRawMember().getDeclaringClass());
}
@Test
public void nonInheritableChildMixinStillOverridesSelectedMethod()
{
AnnotationOverrides mixins = AnnotationOverrides.builder()
.add(ValueChild.class, ChildValueMixin.class).build();
ResolvedMethod method = method(ValueChild.class, "value",
new AnnotationConfiguration.StdConfiguration(AnnotationInclusion.INCLUDE_BUT_DONT_INHERIT), mixins);
assertEquals("child-mixin", method.get(MethodMark.class).value());
assertNull(method.get(InterfaceOnly.class));
assertSame(ValueParent.class, method.getRawMember().getDeclaringClass());
}
@Test
public void nonInheritableParentMixinMatchesClassOnlyResolution()
{
AnnotationOverrides mixins = AnnotationOverrides.builder()
.add(ValueParent.class, ParentValueMixin.class).build();
AnnotationConfiguration config = new AnnotationConfiguration.StdConfiguration(
AnnotationInclusion.INCLUDE_BUT_DONT_INHERIT);
ResolvedMethod parent = method(ValueParent.class, "value", config, mixins);
ResolvedMethod child = method(ValueChild.class, "value", config, mixins);
assertEquals("parent-mixin", parent.get(MethodMark.class).value());
assertEquals(parent.get(MethodMark.class), child.get(MethodMark.class));
assertSame(ValueParent.class, child.getRawMember().getDeclaringClass());
}
@Test
public void nonInheritableChildMixinPrecedesParentMixin()
{
AnnotationOverrides mixins = AnnotationOverrides.builder()
.add(ValueChild.class, ChildValueMixin.class).add(ValueParent.class, ParentValueMixin.class).build();
ResolvedMethod method = method(ValueChild.class, "value",
new AnnotationConfiguration.StdConfiguration(AnnotationInclusion.INCLUDE_BUT_DONT_INHERIT), mixins);
assertEquals("child-mixin", method.get(MethodMark.class).value());
assertSame(ValueParent.class, method.getRawMember().getDeclaringClass());
}
private ResolvedMethod annotated(Class<?> type, AnnotationInclusion inclusion,
AnnotationOverrides overrides)
{
return method(type, "convert", new AnnotationConfiguration.StdConfiguration(inclusion), overrides);
}
private ResolvedMethod method(Class<?> type, String name, AnnotationConfiguration config,
AnnotationOverrides overrides)
{
TypeResolver types = new TypeResolver();
ResolvedMethod found = null;
for (ResolvedMethod method : new MemberResolver(types)
.resolve(types.resolve(type), config, overrides).getMemberMethods()) {
if (method.getName().equals(name)) {
assertNull(found, "Only one method should be selected for " + name);
found = method;
}
}
assertNotNull(found, "Expected method " + name);
return found;
}
}