View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
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.common.utils.Utils.*;
22  import static org.junit.jupiter.api.Assertions.*;
23  
24  import java.lang.annotation.*;
25  import java.util.*;
26  import java.util.function.*;
27  import java.util.stream.*;
28  
29  import org.apache.juneau.*;
30  import org.apache.juneau.annotation.*;
31  import org.apache.juneau.internal.*;
32  import org.junit.jupiter.api.*;
33  
34  /**
35   * ParamInfo tests.
36   */
37  class ParamInfoTest extends TestBase {
38  
39  	@Documented
40  	@Target(METHOD)
41  	@Retention(RUNTIME)
42  	@Inherited
43  	public static @interface A {
44  		String value();
45  	}
46  
47  	@Documented
48  	@Target(METHOD)
49  	@Retention(RUNTIME)
50  	@Inherited
51  	public static @interface AX {
52  		String value();
53  	}
54  
55  	private static void check(String expected, Object o) {
56  		assertEquals(expected, TO_STRING.apply(o));
57  	}
58  
59  	private static final Function<Object,String> TO_STRING = new Function<>() {
60  		@Override
61  		public String apply(Object t) {
62  			if (t == null)
63  				return null;
64  			if (t instanceof List)
65  				return ((List<?>)t).stream().map(this).collect(Collectors.joining(","));
66  			if (isArray(t))
67  				return StreamSupport.stream(ArrayUtils.toList(t, Object.class).spliterator(), false).map(this).collect(Collectors.joining(","));
68  			if (t instanceof MethodInfo)
69  				return ((MethodInfo)t).getDeclaringClass().getSimpleName() + '.' + ((MethodInfo)t).getShortName();
70  			if (t instanceof CA)
71  				return "@CA(" + ((CA)t).value() + ")";
72  			if (t instanceof DA)
73  				return "@DA(" + ((DA)t).value() + ")";
74  			if (t instanceof ClassInfo)
75  				return ((ClassInfo)t).getSimpleName();
76  			return t.toString();
77  		}
78  	};
79  
80  	//-----------------------------------------------------------------------------------------------------------------
81  	// Instantiation.
82  	//-----------------------------------------------------------------------------------------------------------------
83  
84  	static class B {
85  		public B(int a, String b) {}
86  		public void a1(int a, String b) {}  // NOSONAR
87  		void a2(int a, String b) {}  // NOSONAR
88  	}
89  
90  	static ClassInfo b = ClassInfo.of(B.class);
91  	static ParamInfo
92  		b_b_a = b.getPublicConstructor(x -> x.hasParamTypes(int.class, String.class)).getParam(0),  // NOSONAR
93  		b_b_b = b.getPublicConstructor(x -> x.hasParamTypes(int.class, String.class)).getParam(1),  // NOSONAR
94  		b_a1_a = b.getMethod(x -> x.hasName("a1")).getParam(0),  // NOSONAR
95  		b_a1_b = b.getMethod(x -> x.hasName("a1")).getParam(1),  // NOSONAR
96  		b_a2_a = b.getMethod(x -> x.hasName("a2")).getParam(0),  // NOSONAR
97  		b_a2_b = b.getMethod(x -> x.hasName("a2")).getParam(1);  // NOSONAR
98  
99  
100 	@Test void getIndex() {
101 		assertEquals(0, b_b_a.getIndex());
102 		assertEquals(1, b_b_b.getIndex());
103 		assertEquals(0, b_a1_a.getIndex());
104 		assertEquals(1, b_a1_b.getIndex());
105 		assertEquals(0, b_a2_a.getIndex());
106 		assertEquals(1, b_a2_b.getIndex());
107 	}
108 
109 	@Test void getMethod() {
110 		check("B.a1(int,String)", b_a1_a.getMethod());
111 		check("B.a1(int,String)", b_a1_b.getMethod());
112 		check("B.a2(int,String)", b_a2_a.getMethod());
113 		check("B.a2(int,String)", b_a2_b.getMethod());
114 	}
115 
116 	@Test void getMethod_onConstrutor() {
117 		check(null, b_b_a.getMethod());
118 		check(null, b_b_b.getMethod());
119 	}
120 
121 	@Test void getConstructor() {
122 		check("B(int,String)", b_b_a.getConstructor());
123 		check("B(int,String)", b_b_b.getConstructor());
124 	}
125 
126 	@Test void getConstructor_onMethod() {
127 		check(null, b_a1_a.getConstructor());
128 		check(null, b_a1_b.getConstructor());
129 		check(null, b_a2_a.getConstructor());
130 		check(null, b_a2_b.getConstructor());
131 	}
132 
133 	@Test void getParameterType() {
134 		check("int", b_b_a.getParameterType());
135 		check("String", b_b_b.getParameterType());
136 		check("int", b_a1_a.getParameterType());
137 		check("String", b_a1_b.getParameterType());
138 		check("int", b_a2_a.getParameterType());
139 		check("String", b_a2_b.getParameterType());
140 
141 	}
142 
143 	//-----------------------------------------------------------------------------------------------------------------
144 	// Annotations.
145 	//-----------------------------------------------------------------------------------------------------------------
146 
147 	@Target({PARAMETER,TYPE})
148 	@Retention(RUNTIME)
149 	public static @interface CA {
150 		public String value();
151 	}
152 	@CA("1") public static class C1 extends C2 {}
153 	@CA("2") public static class C2 implements C3, C4 {}
154 	@CA("3") public interface C3 {}
155 	@CA("4") public interface C4 {}
156 
157 	public interface CB {
158 		void a1(@CA("5") C1 x);
159 		void a2(@CA("5") C1 x);
160 	}
161 	public static class CC implements CB {
162 		public CC(@CA("9") C1 x) {}
163 		@Override
164 		public void a1(C1 x) {}  // NOSONAR
165 		@Override
166 		public void a2(@CA("6") C1 x) {}  // NOSONAR
167 	}
168 	static ClassInfo
169 		cb = ClassInfo.of(CB.class),
170 		cc = ClassInfo.of(CC.class);
171 	static ParamInfo
172 		cc_cc = cc.getPublicConstructor(x -> x.hasParamTypes(C1.class)).getParam(0),  // NOSONAR
173 		cb_a1 = cb.getMethod(x -> x.hasName("a1")).getParam(0),  // NOSONAR
174 		cb_a2 = cb.getMethod(x -> x.hasName("a2")).getParam(0),  // NOSONAR
175 		cc_a1 = cc.getMethod(x -> x.hasName("a1")).getParam(0),  // NOSONAR
176 		cc_a2 = cc.getMethod(x -> x.hasName("a2")).getParam(0);  // NOSONAR
177 
178 	@Test void getDeclaredAnnotations() {
179 		check("@CA(5)", declaredAnnotations(cb_a1, CA.class));
180 		check("@CA(5)", declaredAnnotations(cb_a2, CA.class));
181 		check("", declaredAnnotations(cc_a1, CA.class));
182 		check("@CA(6)", declaredAnnotations(cc_a2, CA.class));
183 	}
184 
185 	@Test void getDeclaredAnnotations_constructor() {
186 		check("@CA(9)", declaredAnnotations(cc_cc, CA.class));
187 	}
188 
189 	private static <T extends Annotation> List<T> declaredAnnotations(ParamInfo pi, Class<T> type) {
190 		var l = new ArrayList<T>();
191 		pi.forEachDeclaredAnnotation(type, x -> true, l::add);
192 		return l;
193 	}
194 
195 	@Test void getDeclaredAnnotation() {
196 		check("@CA(5)", cb_a1.getDeclaredAnnotation(CA.class));
197 		check("@CA(5)", cb_a2.getDeclaredAnnotation(CA.class));
198 		check(null, cc_a1.getDeclaredAnnotation(CA.class));
199 		check("@CA(6)", cc_a2.getDeclaredAnnotation(CA.class));
200 	}
201 
202 	@Test void getDeclaredAnnotation_constructor() {
203 		check("@CA(9)", cc_cc.getDeclaredAnnotation(CA.class));
204 	}
205 
206 	@Test void getDeclaredAnnotation_notFound() {
207 		check(null, cb_a1.getDeclaredAnnotation(DA.class));
208 	}
209 
210 	@Test void getDeclaredAnnotation_notFound_constructor() {
211 		check(null, cc_cc.getDeclaredAnnotation(DA.class));
212 	}
213 
214 	@Test void getDeclaredAnnotation_null() {
215 		check(null, cb_a1.getDeclaredAnnotation(null));
216 	}
217 
218 	@Test void getDeclaredAnnotation_null_constructor() {
219 		check(null, cc_cc.getDeclaredAnnotation(null));
220 	}
221 
222 	@Test void getAnnotationsParentFirst() {
223 		check("@CA(4),@CA(3),@CA(2),@CA(1),@CA(5)", annotations(cb_a1, CA.class));
224 		check("@CA(4),@CA(3),@CA(2),@CA(1),@CA(5)", annotations(cb_a2, CA.class));
225 		check("@CA(4),@CA(3),@CA(2),@CA(1),@CA(5)", annotations(cc_a1, CA.class));
226 		check("@CA(4),@CA(3),@CA(2),@CA(1),@CA(5),@CA(6)", annotations(cc_a2, CA.class));
227 	}
228 
229 	@Test void getAnnotationsParentFirst_notFound() {
230 		check("", annotations(cb_a1, DA.class));
231 	}
232 
233 	@Test void getAnnotationsParentFirst_constructor() {
234 		check("@CA(4),@CA(3),@CA(2),@CA(1),@CA(9)", annotations(cc_cc, CA.class));
235 	}
236 
237 	@Test void getAnnotationsParentFirst_notFound_constructor() {
238 		check("", annotations(cc_cc, DA.class));
239 	}
240 
241 	@Test void getAnnotation() {
242 		check("@CA(5)", cb_a1.getAnnotation(CA.class));
243 		check("@CA(5)", cb_a2.getAnnotation(CA.class));
244 		check("@CA(5)", cc_a1.getAnnotation(CA.class));
245 		check("@CA(6)", cc_a2.getAnnotation(CA.class));
246 	}
247 
248 	@Test void getAnnotation_notFound() {
249 		check(null, cb_a1.getAnnotation(DA.class));
250 	}
251 
252 	@Test void getAnnotation_constructor() {
253 		check("@CA(9)", cc_cc.getAnnotation(CA.class));
254 	}
255 
256 	@Test void getAnnotation_notFound_constructor() {
257 		check(null, cc_cc.getAnnotation(DA.class));
258 	}
259 
260 	@Test void getAnnotation_twice() {
261 		check("@CA(5)", cb_a1.getAnnotation(CA.class));
262 		check("@CA(5)", cb_a1.getAnnotation(CA.class));
263 	}
264 
265 	@Test void getAnnotation_twice_constructor() {
266 		check("@CA(9)", cc_cc.getAnnotation(CA.class));
267 		check("@CA(9)", cc_cc.getAnnotation(CA.class));
268 	}
269 
270 	@Test void hasAnnotation() {
271 		assertTrue(cb_a1.hasAnnotation(CA.class));
272 		assertTrue(cb_a2.hasAnnotation(CA.class));
273 		assertTrue(cc_a1.hasAnnotation(CA.class));
274 		assertTrue(cc_a2.hasAnnotation(CA.class));
275 		assertFalse(cb_a1.hasAnnotation(DA.class));
276 	}
277 
278 	@Test void hasAnnotation_constructor() {
279 		assertTrue(cc_cc.hasAnnotation(CA.class));
280 		assertFalse(cc_cc.hasAnnotation(DA.class));
281 	}
282 
283 	@Target({PARAMETER,TYPE})
284 	@Retention(RUNTIME)
285 	@Inherited
286 	public static @interface DA {
287 		public String value();
288 	}
289 	@DA("1") public static class D1 extends D2 {}
290 	@DA("2") public static class D2 implements D3, D4 {}
291 	@DA("3") public interface D3 {}
292 	@DA("4") public interface D4 {}
293 
294 	public interface DB {
295 		void a1(@DA("0") D1 x);
296 	}
297 	public static class DC implements DB {
298 		@Override
299 		public void a1(@DA("5") D1 x) {}  // NOSONAR
300 	}
301 
302 	static ClassInfo
303 		db = ClassInfo.of(DB.class),
304 		dc = ClassInfo.of(DC.class);
305 	static ParamInfo
306 		db_a1 = db.getMethod(x -> x.hasName("a1")).getParam(0),  // NOSONAR
307 		dc_a1 = dc.getMethod(x -> x.hasName("a1")).getParam(0);  // NOSONAR
308 
309 	@Test void getAnnotationsParentFirst_inherited() {
310 		check("@DA(4),@DA(3),@DA(2),@DA(1),@DA(0)", annotations(db_a1, DA.class));
311 		check("@DA(4),@DA(3),@DA(2),@DA(1),@DA(0),@DA(5)", annotations(dc_a1, DA.class));
312 	}
313 
314 	@Test void getAnnotationsParentFirst_inherited_notFound() {
315 		check("", annotations(db_a1, CA.class));
316 	}
317 
318 	@Test void getAnnotation_inherited() {
319 		check("@DA(0)", db_a1.getAnnotation(DA.class));
320 		check("@DA(5)", dc_a1.getAnnotation(DA.class));
321 	}
322 
323 	@Test void getAnnotation_inherited_notFound() {
324 		check(null, db_a1.getAnnotation(CA.class));
325 	}
326 
327 	//-----------------------------------------------------------------------------------------------------------------
328 	// Other methods.
329 	//-----------------------------------------------------------------------------------------------------------------
330 
331 	static class E {
332 		public void a1(int a, @Name("b") int b) {}  // NOSONAR
333 	}
334 
335 	static ClassInfo e = ClassInfo.of(E.class);
336 	static ParamInfo
337 		e_a1_a = e.getMethod(x -> x.hasName("a1")).getParam(0),  // NOSONAR
338 		e_a1_b = e.getMethod(x -> x.hasName("a1")).getParam(1);  // NOSONAR
339 
340 	@Test void hasName() {
341 		e_a1_a.hasName();  // This might be true or false based on the JVM compiler used.
342 		assertTrue(e_a1_b.hasName());
343 	}
344 
345 	@Test void getName() {
346 		e_a1_a.getName();  // This might be null or a value based on the JVM compiler used.
347 		assertEquals("b", e_a1_b.getName());
348 	}
349 
350 	@Test void toString2() {
351 		assertEquals("a1[1]", e_a1_b.toString());
352 	}
353 
354 	//-----------------------------------------------------------------------------------------------------------------
355 	// Helpers
356 	//-----------------------------------------------------------------------------------------------------------------
357 
358 	private static <T extends Annotation> List<T> annotations(ParamInfo pi, Class<T> a) {
359 		List<T> l = list();
360 		pi.forEachAnnotation(a, x -> true, l::add);
361 		return l;
362 	}
363 }