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.commons.reflect;
18  
19  import static org.apache.juneau.TestUtils.*;
20  import static org.apache.juneau.junit.bct.BctAssertions.*;
21  import static org.junit.jupiter.api.Assertions.*;
22  
23  import java.lang.reflect.*;
24  import java.util.*;
25  
26  import org.apache.juneau.*;
27  import org.junit.jupiter.api.*;
28  
29  class ReflectionMap_Test extends TestBase {
30  
31  	private static ReflectionMap.Builder<Number> create() {
32  		return ReflectionMap.create(Number.class);
33  	}
34  
35  	private static void checkEntries(ReflectionMap<?> m, boolean hasClass, boolean hasMethods, boolean hasFields, boolean hasConstructors) {
36  		assertEquals(m.classEntries.size() == 0, ! hasClass);
37  		assertEquals(m.methodEntries.size() == 0, ! hasMethods);
38  		assertEquals(m.fieldEntries.size() == 0, ! hasFields);
39  		assertEquals(m.constructorEntries.size() == 0, ! hasConstructors);
40  	}
41  
42  	//------------------------------------------------------------------------------------------------------------------
43  	// Class names
44  	//------------------------------------------------------------------------------------------------------------------
45  
46  	@Nested
47  	class A_Class {
48  
49  		static class A1 {}
50  		static class A2 {}
51  
52  		// @formatter:off
53  		static ReflectionMap<Number>
54  			A1_SIMPLE = create().append("A1", 1).build(),
55  			A1b_SIMPLE = create().append("ReflectionMap_Test$A_Class$A1", 1).build(),
56  			A1_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$A_Class$A1", 1).build();  // Note this could be a static field.
57  		// @formatter:on
58  
59  		@Test void a01_checkEntries() {
60  			checkEntries(A1_SIMPLE, true, false, false, false);
61  			checkEntries(A1_FULL, true, false, true, false);
62  		}
63  
64  		private static void test(Class<?> c, boolean match_A1_SIMPLE, boolean match_A1b_SIMPLE, boolean match_A1_FULL) {
65  			assertEquals(match_A1_SIMPLE, A1_SIMPLE.find(c).findAny().isPresent());
66  			assertEquals(match_A1b_SIMPLE, A1b_SIMPLE.find(c).findAny().isPresent());
67  			assertEquals(match_A1_FULL, A1_FULL.find(c).findAny().isPresent());
68  
69  			assertEquals(match_A1_SIMPLE, A1_SIMPLE.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
70  			assertEquals(match_A1b_SIMPLE, A1b_SIMPLE.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
71  			assertEquals(match_A1_FULL, A1_FULL.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
72  
73  			assertFalse(A1_SIMPLE.find(c).filter(v -> v instanceof Long).findAny().isPresent());
74  			assertFalse(A1b_SIMPLE.find(c).filter(v -> v instanceof Long).findAny().isPresent());
75  			assertFalse(A1_FULL.find(c).filter(v -> v instanceof Long).findAny().isPresent());
76  
77  			assertEquals(match_A1_SIMPLE, A1_SIMPLE.find(c).count() > 0);
78  			assertEquals(match_A1b_SIMPLE, A1b_SIMPLE.find(c).count() > 0);
79  			assertEquals(match_A1_FULL, A1_FULL.find(c).count() > 0);
80  
81  			assertEquals(match_A1_SIMPLE, A1_SIMPLE.find(c).filter(v -> v instanceof Integer).count() > 0);
82  			assertEquals(match_A1b_SIMPLE, A1b_SIMPLE.find(c).filter(v -> v instanceof Integer).count() > 0);
83  			assertEquals(match_A1_FULL, A1_FULL.find(c).filter(v -> v instanceof Integer).count() > 0);
84  
85  			assertFalse(A1_SIMPLE.find(c).filter(v -> v instanceof Long).findAny().isPresent());
86  			assertFalse(A1b_SIMPLE.find(c).filter(v -> v instanceof Long).findAny().isPresent());
87  			assertFalse(A1_FULL.find(c).filter(v -> v instanceof Long).findAny().isPresent());
88  		}
89  
90  		@Test void a02_find() {
91  			test(A1.class, true, true, true);
92  			test(A2.class, false, false, false);
93  			test(null, false, false, false);
94  		}
95  
96  		// Test for wildcard matching (line 584)
97  		@Test void a03_wildcardMatching() {
98  			var rm = create().append("*", 1).build();
99  			assertTrue(rm.find(A1.class).findAny().isPresent());
100 			assertTrue(rm.find(A2.class).findAny().isPresent());
101 			assertTrue(rm.find(String.class).findAny().isPresent());
102 		}
103 
104 		// Test for inner class matching (lines 586, 588, 595-596)
105 		static class OuterClass {
106 			static class InnerClass1 {
107 				static class InnerClass2 {}
108 			}
109 		}
110 
111 		@Test void a04_innerClassMatching() throws Exception {
112 			var inner1 = OuterClass.InnerClass1.class;
113 			var inner2 = OuterClass.InnerClass1.InnerClass2.class;
114 
115 			// Match inner class by partial name (should match after stripping package)
116 			var rm1 = create().append("OuterClass$InnerClass1", 1).build();
117 			assertTrue(rm1.find(inner1).findAny().isPresent());
118 
119 			// Match inner class by simple name only (should match in while loop - lines 595-596)
120 			var rm2 = create().append("InnerClass1", 2).build();
121 			assertTrue(rm2.find(inner1).findAny().isPresent());
122 
123 			// Match nested inner class by simple name (should match in while loop)
124 			var rm3 = create().append("InnerClass2", 3).build();
125 			assertTrue(rm3.find(inner2).findAny().isPresent());
126 
127 			// Match nested inner class by partial path
128 			var rm4 = create().append("InnerClass1$InnerClass2", 4).build();
129 			assertTrue(rm4.find(inner2).findAny().isPresent());
130 		}
131 
132 		// Test for class without package (line 586 false branch, line 588 false branch)
133 		@Test void a05_classWithoutPackage() throws Exception {
134 			// Primitive array class has no package and no '$' in name
135 			var intArrayClass = int[].class;
136 			var rm1 = create().append("int[]", 1).build();
137 			assertTrue(rm1.find(intArrayClass).findAny().isPresent());
138 
139 			// Test with a regular non-inner class (no '$') to hit line 586 false branch
140 			var rm2 = create().append("String", 2).build();
141 			assertTrue(rm2.find(String.class).findAny().isPresent());
142 		}
143 
144 		// Test for inner class with and without package (line 588 branches)
145 		@Test void a06_innerClassPackageHandling() throws Exception {
146 			var inner1 = OuterClass.InnerClass1.class;
147 
148 			// Pattern with full path including package - tests line 588 true branch (package not null)
149 			var rm1 = create().append("ReflectionMap_Test$A_Class$OuterClass$InnerClass1", 1).build();
150 			assertTrue(rm1.find(inner1).findAny().isPresent());
151 
152 			// Pattern with partial path - tests while loop and line 588 execution
153 			var rm2 = create().append("A_Class$OuterClass$InnerClass1", 2).build();
154 			assertTrue(rm2.find(inner1).findAny().isPresent());
155 
156 			// Test line 588 false branch (package IS null but class has $ - inner class in default package)
157 			// Must use reflection to access classes from default package
158 			var defaultPackageInner = Class.forName("DefaultPackageTestClass$InnerClass");
159 			var defaultPackageNested = Class.forName("DefaultPackageTestClass$InnerClass$NestedInner");
160 
161 			// This class has '$' so line 586 is true, but getPackage() returns null, so line 588 is false
162 			var rm3 = create().append("DefaultPackageTestClass$InnerClass", 3).build();
163 			assertTrue(rm3.find(defaultPackageInner).findAny().isPresent());
164 
165 			// Test matching by simple inner class name (exercises while loop with null package)
166 			var rm4 = create().append("InnerClass", 4).build();
167 			assertTrue(rm4.find(defaultPackageInner).findAny().isPresent());
168 
169 			// Test nested inner class in default package
170 			var rm5 = create().append("NestedInner", 5).build();
171 			assertTrue(rm5.find(defaultPackageNested).findAny().isPresent());
172 		}
173 
174 		// Additional test to ensure both branches of line 586 are covered
175 		@Test void a07_nonInnerClassMatching() throws Exception {
176 			// Test classes without $ (line 586 false branch)
177 			var rm1 = create().append("String", 1).build();
178 			assertTrue(rm1.find(String.class).findAny().isPresent()); // No $ in name
179 
180 			var rm2 = create().append("Integer", 2).build();
181 			assertTrue(rm2.find(Integer.class).findAny().isPresent()); // No $ in name
182 		}
183 	}
184 
185 	//------------------------------------------------------------------------------------------------------------------
186 	// Method names
187 	//------------------------------------------------------------------------------------------------------------------
188 
189 	@Nested
190 	class B_Method {
191 
192 		static class B1 {
193 			public void m1() { /* no-op */ }
194 			public void m1(int x) { /* no-op */ }
195 			public void m1(String x) { /* no-op */ }
196 			public void m1(String x, int y) { /* no-op */ }
197 			public void m2(int x) { /* no-op */ }
198 		}
199 		static class B2 {
200 			public void m1() { /* no-op */ }
201 		}
202 
203 		// @formatter:off
204 		static ReflectionMap<Number>
205 			B1m1_SIMPLE = create().append("B1.m1", 1).build(),
206 			B1m1i_SIMPLE = create().append("B1.m1(int)", 1).build(),
207 			B1m1s_SIMPLE = create().append("B1.m1(String)", 1).build(),
208 			B1m1ss_SIMPLE = create().append("B1.m1(java.lang.String)", 1).build(),
209 			B1m1si_SIMPLE = create().append("B1.m1(String,int)", 1).build(),
210 			B1m1ssi_SIMPLE = create().append("B1.m1(java.lang.String , int)", 1).build(),
211 			B1m1_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$B_Method$B1.m1", 1).build(),
212 			B1m1i_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$B_Method$B1.m1(int)", 1).build(),
213 			B1m1s_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$B_Method$B1.m1(String)", 1).build(),
214 			B1m1ss_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$B_Method$B1.m1(java.lang.String)", 1).build(),
215 			B1m1si_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$B_Method$B1.m1(String,int)", 1).build(),
216 			B1m1ssi_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$B_Method$B1.m1(java.lang.String , int)", 1).build();
217 		// @formatter:on
218 
219 		@Test void a01_checkEntries() {
220 			checkEntries(B1m1_SIMPLE, false, true, true, false);
221 			checkEntries(B1m1i_SIMPLE, false, true, false, false);
222 			checkEntries(B1m1s_SIMPLE, false, true, false, false);
223 			checkEntries(B1m1ss_SIMPLE, false, true, false, false);
224 			checkEntries(B1m1si_SIMPLE, false, true, false, false);
225 			checkEntries(B1m1ssi_SIMPLE, false, true, false, false);
226 			checkEntries(B1m1_FULL, false, true, true, false);
227 			checkEntries(B1m1i_FULL, false, true, false, false);
228 			checkEntries(B1m1s_FULL, false, true, false, false);
229 			checkEntries(B1m1ss_FULL, false, true, false, false);
230 			checkEntries(B1m1si_FULL, false, true, false, false);
231 			checkEntries(B1m1ssi_FULL, false, true, false, false);
232 		}
233 
234 		private static void test(Method m, boolean match_B1m1_SIMPLE, boolean match_B1m1i_SIMPLE, boolean match_B1m1s_SIMPLE, boolean match_B1m1ss_SIMPLE,
235 			boolean match_B1m1si_SIMPLE, boolean match_B1m1ssi_SIMPLE, boolean match_B1m1_FULL, boolean match_B1m1i_FULL, boolean match_B1m1s_FULL,
236 			boolean match_B1m1ss_FULL, boolean match_B1m1si_FULL, boolean match_B1m1ssi_FULL) {
237 
238 			assertEquals(match_B1m1_SIMPLE, B1m1_SIMPLE.find(m).findAny().isPresent());
239 			assertEquals(match_B1m1i_SIMPLE, B1m1i_SIMPLE.find(m).findAny().isPresent());
240 			assertEquals(match_B1m1s_SIMPLE, B1m1s_SIMPLE.find(m).findAny().isPresent());
241 			assertEquals(match_B1m1ss_SIMPLE, B1m1ss_SIMPLE.find(m).findAny().isPresent());
242 			assertEquals(match_B1m1si_SIMPLE, B1m1si_SIMPLE.find(m).findAny().isPresent());
243 			assertEquals(match_B1m1ssi_SIMPLE, B1m1ssi_SIMPLE.find(m).findAny().isPresent());
244 			assertEquals(match_B1m1_FULL, B1m1_FULL.find(m).findAny().isPresent());
245 			assertEquals(match_B1m1i_FULL, B1m1i_FULL.find(m).findAny().isPresent());
246 			assertEquals(match_B1m1s_FULL, B1m1s_FULL.find(m).findAny().isPresent());
247 			assertEquals(match_B1m1ss_FULL, B1m1ss_FULL.find(m).findAny().isPresent());
248 			assertEquals(match_B1m1si_FULL, B1m1si_FULL.find(m).findAny().isPresent());
249 			assertEquals(match_B1m1ssi_FULL, B1m1ssi_FULL.find(m).findAny().isPresent());
250 
251 			assertEquals(match_B1m1_SIMPLE, B1m1_SIMPLE.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
252 			assertEquals(match_B1m1i_SIMPLE, B1m1i_SIMPLE.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
253 			assertEquals(match_B1m1s_SIMPLE, B1m1s_SIMPLE.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
254 			assertEquals(match_B1m1ss_SIMPLE, B1m1ss_SIMPLE.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
255 			assertEquals(match_B1m1si_SIMPLE, B1m1si_SIMPLE.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
256 			assertEquals(match_B1m1ssi_SIMPLE, B1m1ssi_SIMPLE.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
257 			assertEquals(match_B1m1_FULL, B1m1_FULL.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
258 			assertEquals(match_B1m1i_FULL, B1m1i_FULL.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
259 			assertEquals(match_B1m1s_FULL, B1m1s_FULL.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
260 			assertEquals(match_B1m1ss_FULL, B1m1ss_FULL.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
261 			assertEquals(match_B1m1si_FULL, B1m1si_FULL.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
262 			assertEquals(match_B1m1ssi_FULL, B1m1ssi_FULL.find(m).filter(v -> v instanceof Integer).findAny().isPresent());
263 
264 			assertFalse(B1m1_SIMPLE.find(m).filter(v -> v instanceof Long).findAny().isPresent());
265 			assertFalse(B1m1i_SIMPLE.find(m).filter(v -> v instanceof Long).findAny().isPresent());
266 			assertFalse(B1m1s_SIMPLE.find(m).filter(v -> v instanceof Long).findAny().isPresent());
267 			assertFalse(B1m1ss_SIMPLE.find(m).filter(v -> v instanceof Long).findAny().isPresent());
268 			assertFalse(B1m1si_SIMPLE.find(m).filter(v -> v instanceof Long).findAny().isPresent());
269 			assertFalse(B1m1ssi_SIMPLE.find(m).filter(v -> v instanceof Long).findAny().isPresent());
270 			assertFalse(B1m1_FULL.find(m).filter(v -> v instanceof Long).findAny().isPresent());
271 			assertFalse(B1m1i_FULL.find(m).filter(v -> v instanceof Long).findAny().isPresent());
272 			assertFalse(B1m1s_FULL.find(m).filter(v -> v instanceof Long).findAny().isPresent());
273 			assertFalse(B1m1ss_FULL.find(m).filter(v -> v instanceof Long).findAny().isPresent());
274 			assertFalse(B1m1si_FULL.find(m).filter(v -> v instanceof Long).findAny().isPresent());
275 			assertFalse(B1m1ssi_FULL.find(m).filter(v -> v instanceof Long).findAny().isPresent());
276 		}
277 
278 		@Test void a02_find() throws Exception {
279 			test(B1.class.getMethod("m1"), true, false, false, false, false, false, true, false, false, false, false, false);
280 			test(B1.class.getMethod("m1", int.class), true, true, false, false, false, false, true, true, false, false, false, false);
281 			test(B1.class.getMethod("m1", String.class), true, false, true, true, false, false, true, false, true, true, false, false);
282 			test(B1.class.getMethod("m1", String.class, int.class), true, false, false, false, true, true, true, false, false, false, true, true);
283 			test(B1.class.getMethod("m2", int.class), false, false, false, false, false, false, false, false, false, false, false, false);
284 			test(B2.class.getMethod("m1"), false, false, false, false, false, false, false, false, false, false, false, false);
285 			test(null, false, false, false, false, false, false, false, false, false, false, false, false);
286 		}
287 
288 		// Test for generic parameters in method signatures (lines 407-411)
289 		@SuppressWarnings("rawtypes")
290 		static class B2a {
291 			public void m1(List x) { /* no-op */ }
292 			public void m2(List<String> x) { /* no-op */ }
293 			public void m3(Map<String, Integer> x) { /* no-op */ }
294 			public void m4(List<List<String>> x) { /* no-op */ }
295 			public void m5(Map x, List y) { /* no-op */ }
296 		}
297 
298 		@Test void a03_generics() throws Exception {
299 			// Generic parameters should be stripped, so List<String> matches List
300 			var rm1 = create().append("B2a.m1(List<String>)", 1).build();
301 			assertTrue(rm1.find(B2a.class.getMethod("m1", List.class)).findAny().isPresent());
302 
303 			// Also works the other way - List pattern matches method declared with List<String>
304 			var rm2 = create().append("B2a.m2(List)", 2).build();
305 			assertTrue(rm2.find(B2a.class.getMethod("m2", List.class)).findAny().isPresent());
306 
307 			// Multiple generic parameters
308 			var rm3 = create().append("B2a.m3(Map<String,Integer>)", 3).build();
309 			assertTrue(rm3.find(B2a.class.getMethod("m3", Map.class)).findAny().isPresent());
310 
311 			// Nested generic parameters
312 			var rm4 = create().append("B2a.m4(List<List<String>>)", 4).build();
313 			assertTrue(rm4.find(B2a.class.getMethod("m4", List.class)).findAny().isPresent());
314 
315 			// Multiple parameters with generics
316 			var rm5 = create().append("B2a.m5(Map<String,Integer>, List<String>)", 5).build();
317 			assertTrue(rm5.find(B2a.class.getMethod("m5", Map.class, List.class)).findAny().isPresent());
318 
319 			// With fully qualified names
320 			var rm6 = create().append("B2a.m1(java.util.List<java.lang.String>)", 6).build();
321 			assertTrue(rm6.find(B2a.class.getMethod("m1", List.class)).findAny().isPresent());
322 		}
323 
324 		// Test for array parameters in method signatures
325 		static class B3 {
326 			public void m1(String[] x) { /* no-op */ }
327 			public void m2(String[][] x) { /* no-op */ }
328 			public void m3(int[] x) { /* no-op */ }
329 		}
330 
331 		@Test void a04_arrayParams() throws Exception {
332 			// Single-dimensional array with simple name
333 			var rm1 = create().append("B3.m1(String[])", 1).build();
334 			assertTrue(rm1.find(B3.class.getMethod("m1", String[].class)).findAny().isPresent());
335 
336 			// Single-dimensional array with full name
337 			var rm1b = create().append("B3.m1(java.lang.String[])", 1).build();
338 			assertTrue(rm1b.find(B3.class.getMethod("m1", String[].class)).findAny().isPresent());
339 
340 			// Multi-dimensional array
341 			var rm2 = create().append("B3.m2(String[][])", 2).build();
342 			assertTrue(rm2.find(B3.class.getMethod("m2", String[][].class)).findAny().isPresent());
343 
344 			// Primitive array
345 			var rm3 = create().append("B3.m3(int[])", 3).build();
346 			assertTrue(rm3.find(B3.class.getMethod("m3", int[].class)).findAny().isPresent());
347 		}
348 
349 		// Test for generic arrays - combining generics and arrays
350 		static class B4 {
351 			public void m1(List<String>[] x) { /* no-op */ }
352 			public void m2(Map<String, Integer>[][] x) { /* no-op */ }
353 		}
354 
355 		@Test void a05_genericArrays() throws Exception {
356 			// Array of generic types - generics should be stripped, array dimensions preserved
357 			var rm1 = create().append("B4.m1(List<String>[])", 1).build();
358 			assertTrue(rm1.find(B4.class.getMethod("m1", List[].class)).findAny().isPresent());
359 
360 			// Also works without generics in pattern
361 			var rm1b = create().append("B4.m1(List[])", 1).build();
362 			assertTrue(rm1b.find(B4.class.getMethod("m1", List[].class)).findAny().isPresent());
363 
364 			// Multi-dimensional array of generic types
365 			var rm2 = create().append("B4.m2(Map<String,Integer>[][])", 2).build();
366 			assertTrue(rm2.find(B4.class.getMethod("m2", Map[][].class)).findAny().isPresent());
367 
368 			// With fully qualified names
369 			var rm3 = create().append("B4.m1(java.util.List<java.lang.String>[])", 3).build();
370 			assertTrue(rm3.find(B4.class.getMethod("m1", List[].class)).findAny().isPresent());
371 		}
372 
373 		// Test for negative matching - array dimension mismatches and type mismatches
374 		static class B5 {
375 			public void testMethod(String[] x) { /* no-op */ }
376 			public void testMethod(String[][] x) { /* no-op */ }
377 			public void testMethod(Integer[] x) { /* no-op */ }
378 			public void testMethod(int[] x) { /* no-op */ }
379 		}
380 
381 		@Test void a06_arrayNegativeMatching() throws Exception {
382 			// Array dimension mismatch - pattern has 1 dimension, actual has 2
383 			var rm1 = create().append("B5.testMethod(String[])", 1).build();
384 			assertFalse(rm1.find(B5.class.getMethod("testMethod", String[][].class)).findAny().isPresent());
385 
386 			// Array dimension mismatch - pattern has 2 dimensions, actual has 1
387 			var rm2 = create().append("B5.testMethod(String[][])", 2).build();
388 			assertFalse(rm2.find(B5.class.getMethod("testMethod", String[].class)).findAny().isPresent());
389 
390 			// Array type mismatch - pattern is String[], actual is Integer[]
391 			var rm3 = create().append("B5.testMethod(String[])", 1).build();
392 			assertFalse(rm3.find(B5.class.getMethod("testMethod", Integer[].class)).findAny().isPresent());
393 
394 			// Array type mismatch - pattern is Integer[], actual is String[]
395 			var rm4 = create().append("B5.testMethod(Integer[])", 3).build();
396 			assertFalse(rm4.find(B5.class.getMethod("testMethod", String[].class)).findAny().isPresent());
397 
398 			// Array type mismatch - pattern is int[], actual is String[]
399 			var rm5 = create().append("B5.testMethod(int[])", 4).build();
400 			assertFalse(rm5.find(B5.class.getMethod("testMethod", String[].class)).findAny().isPresent());
401 		}
402 	}
403 
404 	//------------------------------------------------------------------------------------------------------------------
405 	// Field names
406 	//------------------------------------------------------------------------------------------------------------------
407 
408 	@Nested
409 	class C_Field {
410 
411 		static class C1 {
412 			public int f1;
413 			public int f2;
414 		}
415 		static class C2 {
416 			public int f1;
417 		}
418 
419 		// @formatter:off
420 		static ReflectionMap<Number>
421 			C1f1_SIMPLE = create().append("C1.f1", 1).build(),
422 			C1f1_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$C_Field$C1.f1", 1).build();
423 		// @formatter:on
424 
425 		@Test void a01_checkEntries() {
426 			checkEntries(C1f1_SIMPLE, false, true, true, false);
427 			checkEntries(C1f1_FULL, false, true, true, false);
428 		}
429 
430 		private static void test(Field f, boolean match_C1f1_SIMPLE, boolean match_C1f1_FULL) {
431 			assertEquals(match_C1f1_SIMPLE, C1f1_SIMPLE.find(f).findAny().isPresent());
432 			assertEquals(match_C1f1_FULL, C1f1_FULL.find(f).findAny().isPresent());
433 
434 			assertEquals(match_C1f1_SIMPLE, C1f1_SIMPLE.find(f).filter(v -> v instanceof Integer).findAny().isPresent());
435 			assertEquals(match_C1f1_FULL, C1f1_FULL.find(f).filter(v -> v instanceof Integer).findAny().isPresent());
436 
437 			assertFalse(C1f1_SIMPLE.find(f).filter(v -> v instanceof Long).findAny().isPresent());
438 			assertFalse(C1f1_FULL.find(f).filter(v -> v instanceof Long).findAny().isPresent());
439 		}
440 
441 		@Test void a02_find() throws Exception {
442 			test(C1.class.getField("f1"), true, true);
443 			test(C1.class.getField("f2"), false, false);
444 			test(C2.class.getField("f1"), false, false);
445 			test(null, false, false);
446 		}
447 	}
448 
449 	//------------------------------------------------------------------------------------------------------------------
450 	// Constructor names
451 	//------------------------------------------------------------------------------------------------------------------
452 
453 	@Nested
454 	class D_Constructor {
455 
456 		static class D1 {
457 			public D1() { /* no-op */ }
458 			public D1(int x) { /* no-op */ }
459 			public D1(String x) { /* no-op */ }
460 			public D1(String x, int y) { /* no-op */ }
461 		}
462 		static class D2 {
463 			public D2() { /* no-op */ }
464 		}
465 
466 		// @formatter:off
467 		static ReflectionMap<Number>
468 			D_SIMPLE = create().append("D1()", 1).build(),
469 			Di_SIMPLE = create().append("D1(int)", 1).build(),
470 			Ds_SIMPLE = create().append("D1(String)", 1).build(),
471 			Dss_SIMPLE = create().append("D1(java.lang.String)", 1).build(),
472 			Dsi_SIMPLE = create().append("D1(String, int)", 1).build(),
473 			Dssi_SIMPLE = create().append("D1(java.lang.String, int)", 1).build(),
474 			D_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$D_Constructor$D1()", 1).build(),
475 			Di_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$D_Constructor$D1(int)", 1).build(),
476 			Ds_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$D_Constructor$D1(String)", 1).build(),
477 			Dss_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$D_Constructor$D1(java.lang.String)", 1).build(),
478 			Dsi_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$D_Constructor$D1(String, int)", 1).build(),
479 			Dssi_FULL = create().append("org.apache.juneau.commons.reflect.ReflectionMap_Test$D_Constructor$D1(java.lang.String, int)", 1).build();
480 		// @formatter:on
481 
482 		@Test void a01_checkEntries() {
483 			checkEntries(D_SIMPLE, false, false, false, true);
484 			checkEntries(Di_SIMPLE, false, false, false, true);
485 			checkEntries(Ds_SIMPLE, false, false, false, true);
486 			checkEntries(Dss_SIMPLE, false, false, false, true);
487 			checkEntries(Dsi_SIMPLE, false, false, false, true);
488 			checkEntries(Dssi_SIMPLE, false, false, false, true);
489 			checkEntries(D_FULL, false, false, false, true);
490 			checkEntries(Di_FULL, false, false, false, true);
491 			checkEntries(Ds_FULL, false, false, false, true);
492 			checkEntries(Dss_FULL, false, false, false, true);
493 			checkEntries(Dsi_FULL, false, false, false, true);
494 			checkEntries(Dssi_FULL, false, false, false, true);
495 		}
496 
497 		private static void test(Constructor<?> c, boolean match_D_SIMPLE, boolean match_Di_SIMPLE, boolean match_Ds_SIMPLE, boolean match_Dss_SIMPLE,
498 			boolean match_Dsi_SIMPLE, boolean match_Dssi_SIMPLE, boolean match_D_FULL, boolean match_Di_FULL, boolean match_Ds_FULL,
499 			boolean match_Dss_FULL, boolean match_Dsi_FULL, boolean match_Dssi_FULL) {
500 
501 			assertEquals(match_D_SIMPLE, D_SIMPLE.find(c).findAny().isPresent());
502 			assertEquals(match_Di_SIMPLE, Di_SIMPLE.find(c).findAny().isPresent());
503 			assertEquals(match_Ds_SIMPLE, Ds_SIMPLE.find(c).findAny().isPresent());
504 			assertEquals(match_Dss_SIMPLE, Dss_SIMPLE.find(c).findAny().isPresent());
505 			assertEquals(match_Dsi_SIMPLE, Dsi_SIMPLE.find(c).findAny().isPresent());
506 			assertEquals(match_Dssi_SIMPLE, Dssi_SIMPLE.find(c).findAny().isPresent());
507 			assertEquals(match_D_FULL, D_FULL.find(c).findAny().isPresent());
508 			assertEquals(match_Di_FULL, Di_FULL.find(c).findAny().isPresent());
509 			assertEquals(match_Ds_FULL, Ds_FULL.find(c).findAny().isPresent());
510 			assertEquals(match_Dss_FULL, Dss_FULL.find(c).findAny().isPresent());
511 			assertEquals(match_Dsi_FULL, Dsi_FULL.find(c).findAny().isPresent());
512 			assertEquals(match_Dssi_FULL, Dssi_FULL.find(c).findAny().isPresent());
513 
514 			assertEquals(match_D_SIMPLE, D_SIMPLE.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
515 			assertEquals(match_Di_SIMPLE, Di_SIMPLE.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
516 			assertEquals(match_Ds_SIMPLE, Ds_SIMPLE.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
517 			assertEquals(match_Dss_SIMPLE, Dss_SIMPLE.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
518 			assertEquals(match_Dsi_SIMPLE, Dsi_SIMPLE.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
519 			assertEquals(match_Dssi_SIMPLE, Dssi_SIMPLE.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
520 			assertEquals(match_D_FULL, D_FULL.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
521 			assertEquals(match_Di_FULL, Di_FULL.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
522 			assertEquals(match_Ds_FULL, Ds_FULL.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
523 			assertEquals(match_Dss_FULL, Dss_FULL.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
524 			assertEquals(match_Dsi_FULL, Dsi_FULL.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
525 			assertEquals(match_Dssi_FULL, Dssi_FULL.find(c).filter(v -> v instanceof Integer).findAny().isPresent());
526 
527 			assertFalse(D_SIMPLE.find(c).filter(v -> v instanceof Long).findAny().isPresent());
528 			assertFalse(Di_SIMPLE.find(c).filter(v -> v instanceof Long).findAny().isPresent());
529 			assertFalse(Ds_SIMPLE.find(c).filter(v -> v instanceof Long).findAny().isPresent());
530 			assertFalse(Dss_SIMPLE.find(c).filter(v -> v instanceof Long).findAny().isPresent());
531 			assertFalse(Dsi_SIMPLE.find(c).filter(v -> v instanceof Long).findAny().isPresent());
532 			assertFalse(Dssi_SIMPLE.find(c).filter(v -> v instanceof Long).findAny().isPresent());
533 			assertFalse(D_FULL.find(c).filter(v -> v instanceof Long).findAny().isPresent());
534 			assertFalse(Di_FULL.find(c).filter(v -> v instanceof Long).findAny().isPresent());
535 			assertFalse(Ds_FULL.find(c).filter(v -> v instanceof Long).findAny().isPresent());
536 			assertFalse(Dss_FULL.find(c).filter(v -> v instanceof Long).findAny().isPresent());
537 			assertFalse(Dsi_FULL.find(c).filter(v -> v instanceof Long).findAny().isPresent());
538 			assertFalse(Dssi_FULL.find(c).filter(v -> v instanceof Long).findAny().isPresent());
539 		}
540 
541 		@Test void a02_find() throws Exception {
542 			test(D1.class.getConstructor(), true, false, false, false, false, false, true, false, false, false, false, false);
543 			test(D1.class.getConstructor(int.class), false, true, false, false, false, false, false, true, false, false, false, false);
544 			test(D1.class.getConstructor(String.class), false, false, true, true, false, false, false, false, true, true, false, false);
545 			test(D1.class.getConstructor(String.class, int.class), false, false, false, false, true, true, false, false, false, false, true, true);
546 			test(D2.class.getConstructor(), false, false, false, false, false, false, false, false, false, false, false, false);
547 			test(null, false, false, false, false, false, false, false, false, false, false, false, false);
548 		}
549 
550 		// Test for generic parameters with commas in constructor signatures
551 		static class D3 {
552 			public D3(Map<String, Integer> x) { /* no-op */ }
553 			public D3(Map<String, Integer> x, List<String> y) { /* no-op */ }
554 			public D3(Map<String, Integer>[] x) { /* no-op */ }
555 		}
556 
557 		@Test void a03_generics() throws Exception {
558 			// Generic parameters with commas should be handled correctly
559 			var rm1 = create().append("D3(Map<String,Integer>)", 1).build();
560 			assertTrue(rm1.find(D3.class.getConstructor(Map.class)).findAny().isPresent());
561 
562 			// Also works without generics in pattern
563 			var rm1b = create().append("D3(Map)", 1).build();
564 			assertTrue(rm1b.find(D3.class.getConstructor(Map.class)).findAny().isPresent());
565 
566 			// Multiple parameters with generics containing commas
567 			var rm2 = create().append("D3(Map<String,Integer>, List<String>)", 2).build();
568 			assertTrue(rm2.find(D3.class.getConstructor(Map.class, List.class)).findAny().isPresent());
569 
570 			// Generic array parameters
571 			var rm3 = create().append("D3(Map<String,Integer>[])", 3).build();
572 			assertTrue(rm3.find(D3.class.getConstructor(Map[].class)).findAny().isPresent());
573 
574 			// With fully qualified names
575 			var rm4 = create().append("D3(java.util.Map<java.lang.String,java.lang.Integer>)", 4).build();
576 			assertTrue(rm4.find(D3.class.getConstructor(Map.class)).findAny().isPresent());
577 		}
578 	}
579 
580 	//------------------------------------------------------------------------------------------------------------------
581 	// Invalid input
582 	//------------------------------------------------------------------------------------------------------------------
583 
584 	@Nested
585 	class E_InvalidInput {
586 
587 		@Test void a01_blankInput() {
588 			assertThrowsWithMessage(RuntimeException.class, "Invalid reflection signature: []", ()->create().append("", 1));
589 		}
590 
591 		@Test void a02_nullInput() {
592 			assertThrowsWithMessage(RuntimeException.class, "Invalid reflection signature: [null]", ()->create().append(null, 1));
593 		}
594 
595 		@Test void a03_badInput() {
596 			assertThrowsWithMessage(RuntimeException.class, "Invalid reflection signature: [foo)]", ()->create().append("foo)", 1));
597 		}
598 	}
599 
600 	//------------------------------------------------------------------------------------------------------------------
601 	// Other tests
602 	//------------------------------------------------------------------------------------------------------------------
603 
604 	@Nested
605 	class F_Other {
606 
607 		static class F1 {}
608 
609 		static ReflectionMap<Number> RM_F = create().append("F2, F1", 1).build();
610 
611 		@Test void a01_cdl() {
612 			assertString("1", RM_F.find(F1.class).findFirst().get());
613 		}
614 
615 		static ReflectionMap<Number> RM_G = create().build();
616 
617 		@Test void a02_emptyReflectionMap() throws Exception {
618 			assertFalse(RM_G.find(A_Class.A1.class).findAny().isPresent());
619 			assertFalse(RM_G.find(B_Method.B1.class.getMethod("m1")).findAny().isPresent());
620 			assertFalse(RM_G.find(C_Field.C1.class.getField("f1")).findAny().isPresent());
621 			assertFalse(RM_G.find(D_Constructor.D1.class.getConstructor()).findAny().isPresent());
622 		}
623 
624 		@Test void a03_toString() {
625 			var rm = create()
626 				.append("F1", 1)                    // class
627 				.append("F1.toString", 2)           // method without args (args=null)
628 				.append("F1.toString(String)", 3)   // method with args
629 				.append("F1.myField", 4)            // field
630 				.append("F1()", 5)                  // constructor
631 				.build();
632 			assertString("{classEntries=[{fullName=F1,simpleName=F1,value=1}],constructorEntries=[{fullClassName=F1,simpleClassName=F1,value=5}],fieldEntries=[{fieldName=toString,fullClassName=F1,simpleClassName=F1,value=2},{fieldName=myField,fullClassName=F1,simpleClassName=F1,value=4}],methodEntries=[{fullClassName=F1,methodName=toString,simpleClassName=F1,value=2},{args=[String],fullClassName=F1,methodName=toString,simpleClassName=F1,value=3},{fullClassName=F1,methodName=myField,simpleClassName=F1,value=4}]}", rm);
633 		}
634 	}
635 }