1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.juneau.reflect;
18
19 import static java.lang.annotation.ElementType.*;
20 import static java.lang.annotation.RetentionPolicy.*;
21 import static org.apache.juneau.Context.*;
22 import static org.junit.jupiter.api.Assertions.*;
23
24 import java.lang.annotation.*;
25 import java.lang.reflect.*;
26 import java.util.*;
27 import java.util.function.*;
28 import java.util.stream.*;
29
30 import org.apache.juneau.*;
31 import org.apache.juneau.annotation.*;
32 import org.apache.juneau.svl.*;
33 import org.junit.jupiter.api.*;
34
35 class MethodInfo_Test extends TestBase {
36
37 @Documented
38 @Target({METHOD,TYPE})
39 @Retention(RUNTIME)
40 @Inherited
41 public static @interface A {
42 String value();
43 }
44
45 @Documented
46 @Target({METHOD,TYPE})
47 @Retention(RUNTIME)
48 @Inherited
49 public static @interface AX {
50 String value();
51 }
52
53 @Documented
54 @Target({METHOD,TYPE})
55 @Retention(RUNTIME)
56 @Inherited
57 @ContextApply(AConfigApply.class)
58 public static @interface AConfig {
59 String value();
60 }
61
62 public static class AConfigApply extends AnnotationApplier<AConfig,Context.Builder> {
63 protected AConfigApply(VarResolverSession vr) {
64 super(AConfig.class, Context.Builder.class, vr);
65 }
66 @Override
67 public void apply(AnnotationInfo<AConfig> ai, Context.Builder b) {}
68 }
69
70 private static void check(String expected, Object o) {
71 assertEquals(expected, TO_STRING.apply(o));
72 }
73
74 private static final Function<Object,String> TO_STRING = new Function<>() {
75 @Override
76 public String apply(Object t) {
77 if (t == null)
78 return null;
79 if (t instanceof MethodInfo t2)
80 return t2.getDeclaringClass().getSimpleName() + '.' + ((MethodInfo)t).getShortName();
81 if (t instanceof Method t2)
82 return t2.getDeclaringClass().getSimpleName() + '.' + MethodInfo.of((Method)t).getShortName();
83 if (t instanceof List<?> t2)
84 return (t2.stream().map(this).collect(Collectors.joining(",")));
85 if (t instanceof AnnotationInfo t2)
86 return apply(t2.inner());
87 if (t instanceof A t2)
88 return "@A(" + t2.value() + ")";
89 if (t instanceof PA t2)
90 return "@PA(" + t2.value() + ")";
91 if (t instanceof AConfig t2)
92 return "@AConfig(" + t2.value() + ")";
93 if (t instanceof AnnotationList t2)
94 return t2.toString();
95 if (t instanceof ClassInfo t2)
96 return t2.getSimpleName();
97 return t.toString();
98 }
99 };
100
101 private static MethodInfo ofm(Class<?> c, String name, Class<?>...pt) {
102 try {
103 return MethodInfo.of(c.getDeclaredMethod(name, pt));
104 } catch (NoSuchMethodException | SecurityException e) {
105 fail(e.getLocalizedMessage());
106 }
107 return null;
108 }
109
110
111
112
113
114 public static class A1 {
115 public void m() {}
116 }
117 static MethodInfo a_m = ofm(A1.class, "m");
118
119 @Test void of_withDeclaringClass() {
120 check("A1.m()", a_m);
121 check("A1.m()", MethodInfo.of(ClassInfo.of(A1.class), a_m.inner()));
122 }
123
124 @Test void of_withoutDeclaringClass() {
125 var mi = MethodInfo.of(a_m.inner());
126 check("A1.m()", mi);
127 }
128
129 @Test void of_null() {
130 check(null, MethodInfo.of(null));
131 check(null, MethodInfo.of((ClassInfo)null, null));
132 }
133
134
135
136
137
138 public interface B1 {
139 int foo(int x);
140 int foo(String x);
141 int foo();
142 }
143 public static class B2 {
144 public int foo(int x) { return 0; }
145 public int foo(String x) {return 0;}
146 public int foo() {return 0;}
147 }
148 public static class B3 extends B2 implements B1 {
149 @Override public int foo(int x) {return 0;}
150 @Override public int foo(String x) {return 0;}
151 @Override public int foo() {return 0;}
152 }
153
154 @Test void findMatchingMethods() throws Exception {
155 var mi = MethodInfo.of(B3.class.getMethod("foo", int.class));
156 var l = new ArrayList<MethodInfo>();
157 mi.forEachMatching(x -> true, l::add);
158 check("B3.foo(int),B2.foo(int),B1.foo(int)", l);
159 }
160
161
162
163
164
165 @A("C1")
166 public interface C1 {
167 @A("a1") void a1();
168 @A("a2a") void a2();
169 @A("a3") void a3(CharSequence foo);
170 void a4();
171 void a5();
172 }
173
174 @A("C2")
175 public static class C2 implements C1 {
176 @Override public void a1() {}
177 @Override @A("a2b") public void a2() {}
178 @Override public void a3(CharSequence s) {}
179 @Override public void a4() {}
180 @Override public void a5() {}
181 }
182
183 @A("C3")
184 public static class C3 extends C2 {
185 @Override public void a1() {}
186 @Override public void a2() {}
187 @Override public void a3(CharSequence foo) {}
188 @Override @A("a4") public void a4() {}
189 @Override public void a5() {}
190 }
191
192 static MethodInfo
193 c_a1 = ofm(C3.class, "a1"),
194 c_a2 = ofm(C3.class, "a2"),
195 c_a3 = ofm(C3.class, "a3", CharSequence.class),
196 c_a4 = ofm(C3.class, "a4"),
197 c_a5 = ofm(C3.class, "a5");
198
199 @Test void getAnnotationsParentFirst() {
200 check("@A(C1),@A(C2),@A(C3),@A(a1)", annotations(c_a1, A.class));
201 check("@A(C1),@A(C2),@A(C3),@A(a2a),@A(a2b)", annotations(c_a2, A.class));
202 check("@A(C1),@A(C2),@A(C3),@A(a3)", annotations(c_a3, A.class));
203 check("@A(C1),@A(C2),@A(C3),@A(a4)", annotations(c_a4, A.class));
204 check("@A(C1),@A(C2),@A(C3)", annotations(c_a5, A.class));
205 }
206
207 @Test void getAnnotationsParentFirst_notExistent() {
208 check("", annotations(c_a1, AX.class));
209 check("", annotations(c_a2, AX.class));
210 check("", annotations(c_a3, AX.class));
211 check("", annotations(c_a4, AX.class));
212 check("", annotations(c_a5, AX.class));
213 }
214
215 private static List<A> annotations(MethodInfo mi, Class<? extends Annotation> a) {
216 var l = new ArrayList<A>();
217 mi.forEachAnnotation(a, x -> true, x -> l.add((A)x));
218 return l;
219 }
220
221 @Test void getAnnotation() {
222 check("@A(a1)", c_a1.getAnnotation(A.class));
223 check("@A(a2b)", c_a2.getAnnotation(A.class));
224 check("@A(a3)", c_a3.getAnnotation(A.class));
225 check("@A(a4)", c_a4.getAnnotation(A.class));
226 check(null, c_a5.getAnnotation(A.class));
227 }
228
229 @Test void getAnnotationAny() {
230 check("@A(a1)", c_a1.getAnyAnnotation(AX.class, A.class));
231 check("@A(a2b)", c_a2.getAnyAnnotation(AX.class, A.class));
232 check("@A(a3)", c_a3.getAnyAnnotation(AX.class, A.class));
233 check("@A(a4)", c_a4.getAnyAnnotation(AX.class, A.class));
234 check(null, c_a5.getAnyAnnotation(AX.class, A.class));
235 }
236
237 @Test void getAnnotationsMapParentFirst() {
238 check("@PA(10),@A(C1),@A(a1),@A(C2),@A(C3)", c_a1.getAnnotationList());
239 check("@PA(10),@A(C1),@A(a2a),@A(C2),@A(a2b),@A(C3)", c_a2.getAnnotationList());
240 check("@PA(10),@A(C1),@A(a3),@A(C2),@A(C3)", c_a3.getAnnotationList());
241 check("@PA(10),@A(C1),@A(C2),@A(C3),@A(a4)", c_a4.getAnnotationList());
242 check("@PA(10),@A(C1),@A(C2),@A(C3)", c_a5.getAnnotationList());
243 }
244
245 @A("C1") @AConfig("C1")
246 public interface CB1 {
247 @A("a1") @AConfig("a1") void a1();
248 @A("a2a") @AConfig("a2a") void a2();
249 @A("a3") @AConfig("a3") void a3(CharSequence foo);
250 void a4();
251 void a5();
252 }
253
254 @A("C2") @AConfig("C2")
255 public static class CB2 implements CB1 {
256 @Override public void a1() {}
257 @Override @A("a2b") @AConfig("a2b") public void a2() {}
258 @Override public void a3(CharSequence s) {}
259 @Override public void a4() {}
260 @Override public void a5() {}
261 }
262
263 @A("C3") @AConfig("C3")
264 public static class CB3 extends CB2 {
265 @Override public void a1() {}
266 @Override public void a2() {}
267 @Override public void a3(CharSequence foo) {}
268 @Override @A("a4") @AConfig("a4") public void a4() {}
269 @Override public void a5() {}
270 }
271
272 static MethodInfo
273 cb_a1 = ofm(CB3.class, "a1"),
274 cb_a2 = ofm(CB3.class, "a2"),
275 cb_a3 = ofm(CB3.class, "a3", CharSequence.class),
276 cb_a4 = ofm(CB3.class, "a4"),
277 cb_a5 = ofm(CB3.class, "a5");
278
279 @Test void getConfigAnnotationsMapParentFirst() {
280 check("@AConfig(C1),@AConfig(a1),@AConfig(C2),@AConfig(C3)", cb_a1.getAnnotationList(CONTEXT_APPLY_FILTER));
281 check("@AConfig(C1),@AConfig(a2a),@AConfig(C2),@AConfig(a2b),@AConfig(C3)", cb_a2.getAnnotationList(CONTEXT_APPLY_FILTER));
282 check("@AConfig(C1),@AConfig(a3),@AConfig(C2),@AConfig(C3)", cb_a3.getAnnotationList(CONTEXT_APPLY_FILTER));
283 check("@AConfig(C1),@AConfig(C2),@AConfig(C3),@AConfig(a4)", cb_a4.getAnnotationList(CONTEXT_APPLY_FILTER));
284 check("@AConfig(C1),@AConfig(C2),@AConfig(C3)", cb_a5.getAnnotationList(CONTEXT_APPLY_FILTER));
285 }
286
287
288
289
290
291 public static class D {
292 public void a1() {}
293 public Integer a2() {return null;}
294 }
295 static MethodInfo
296 d_a1 = ofm(D.class, "a1"),
297 d_a2 = ofm(D.class, "a2");
298
299 @Test void getReturnType() {
300 check("void", d_a1.getReturnType());
301 check("Integer", d_a2.getReturnType());
302 }
303
304 @Test void hasReturnType() {
305 assertTrue(d_a1.hasReturnType(void.class));
306 assertFalse(d_a1.hasReturnType(Integer.class));
307 assertTrue(d_a2.hasReturnType(Integer.class));
308 assertFalse(d_a2.hasReturnType(Number.class));
309 }
310
311 @Test void hasReturnTypeParent() {
312 assertTrue(d_a1.hasReturnTypeParent(void.class));
313 assertFalse(d_a1.hasReturnTypeParent(Integer.class));
314 assertTrue(d_a2.hasReturnTypeParent(Integer.class));
315 assertTrue(d_a2.hasReturnTypeParent(Number.class));
316 }
317
318
319
320
321
322 public static class E {
323 private String f;
324 public void a1(CharSequence foo) {
325 f = foo == null ? null : foo.toString();
326 }
327 public void a2(int f1, int f2) {}
328 public void a3() {}
329 }
330 static MethodInfo
331 e_a1 = ofm(E.class, "a1", CharSequence.class),
332 e_a2 = ofm(E.class, "a2", int.class, int.class),
333 e_a3 = ofm(E.class, "a3");
334
335 @Test void invoke() throws Exception {
336 var e = new E();
337 e_a1.invoke(e, "foo");
338 assertEquals("foo", e.f);
339 e_a1.invoke(e, (CharSequence)null);
340 assertNull(e.f);
341 }
342
343 @Test void invokeFuzzy() throws Exception {
344 var e = new E();
345 e_a1.invokeFuzzy(e, "foo", 123);
346 assertEquals("foo", e.f);
347 e_a1.invokeFuzzy(e, 123, "bar");
348 assertEquals("bar", e.f);
349 }
350
351 @Test void getSignature() {
352 assertEquals("a1(java.lang.CharSequence)", e_a1.getSignature());
353 assertEquals("a2(int,int)", e_a2.getSignature());
354 assertEquals("a3", e_a3.getSignature());
355 }
356
357 @Test void argsOnlyOfType() {
358 assertTrue(e_a1.argsOnlyOfType(CharSequence.class));
359 assertTrue(e_a1.argsOnlyOfType(CharSequence.class, Map.class));
360 assertFalse(e_a1.argsOnlyOfType());
361 }
362
363 public static class F {
364 public void isA() {}
365 public void is() {}
366 public void getA() {}
367 public void get() {}
368 public void setA() {}
369 public void set() {}
370 public void foo() {}
371 }
372 static MethodInfo
373 f_isA = ofm(F.class, "isA"),
374 f_is = ofm(F.class, "is"),
375 f_getA = ofm(F.class, "getA"),
376 f_get = ofm(F.class, "get"),
377 f_setA = ofm(F.class, "setA"),
378 f_set = ofm(F.class, "set"),
379 f_foo = ofm(F.class, "foo");
380
381 @Test void getPropertyName() {
382 assertEquals("a", f_isA.getPropertyName());
383 assertEquals("is", f_is.getPropertyName());
384 assertEquals("a", f_getA.getPropertyName());
385 assertEquals("get", f_get.getPropertyName());
386 assertEquals("a", f_setA.getPropertyName());
387 assertEquals("set", f_set.getPropertyName());
388 assertEquals("foo", f_foo.getPropertyName());
389 }
390
391 @Test void isBridge() {
392 assertFalse(f_foo.isBridge());
393 }
394
395 public static class G {
396 public void a1() {}
397 public void a1(int a1) {}
398 public void a1(int a1, int a2) {}
399 public void a1(String a1) {}
400 public void a2() {}
401 public void a3() {}
402 }
403 static MethodInfo
404 g_a1a = ofm(G.class, "a1"),
405 g_a1b = ofm(G.class, "a1", int.class),
406 g_a1c = ofm(G.class, "a1", int.class, int.class),
407 g_a1d = ofm(G.class, "a1", String.class),
408 g_a2 = ofm(G.class, "a2"),
409 g_a3 = ofm(G.class, "a3");
410
411 @Test void compareTo() {
412 var s = new TreeSet<>(Arrays.asList(g_a1a, g_a1b, g_a1c, g_a1d, g_a2, g_a3));
413 check("[a1(), a1(int), a1(String), a1(int,int), a2(), a3()]", s);
414 }
415 }