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 java.lang.annotation.ElementType.*;
20  import static java.lang.annotation.RetentionPolicy.*;
21  import static org.apache.juneau.TestUtils.*;
22  import static org.apache.juneau.commons.reflect.ElementFlag.*;
23  import static org.apache.juneau.commons.utils.CollectionUtils.*;
24  import static org.junit.jupiter.api.Assertions.*;
25  
26  import java.io.*;
27  import java.lang.annotation.*;
28  import java.util.*;
29  import java.util.function.*;
30  import java.util.stream.*;
31  
32  import org.apache.juneau.*;
33  import org.junit.jupiter.api.*;
34  
35  class ExecutableInfo_Test extends TestBase {
36  
37  	private static void check(String expected, Object o) {
38  		assertEquals(expected, TO_STRING.apply(o));
39  	}
40  
41  	private static final Function<Object,String> TO_STRING = new Function<>() {
42  		@Override
43  		public String apply(Object t) {
44  			if (t == null)
45  				return null;
46  			if (t instanceof List)
47  				return ((List<?>)t).stream().map(this).collect(Collectors.joining(","));
48  			if (isArray(t))
49  				return StreamSupport.stream(toList(t, Object.class).spliterator(), false).map(this).collect(Collectors.joining(","));
50  			if (t instanceof AnnotationInfo)
51  				return "@" + ((AnnotationInfo<?>)t).inner().annotationType().getSimpleName() + "()";
52  			if (t instanceof Annotation)
53  				return "@" + ((Annotation)t).annotationType().getSimpleName() + "()";
54  			if (t instanceof Class)
55  				return ((Class<?>)t).getSimpleName();
56  			if (t instanceof ClassInfo)
57  				return ((ClassInfo)t).getNameSimple();
58  			if (t instanceof ConstructorInfo)
59  				return ((ConstructorInfo)t).getShortName();
60  			if (t instanceof ParameterInfo)
61  				return apply(((ParameterInfo)t).toString());
62  			return t.toString();
63  		}
64  	};
65  
66  	//-----------------------------------------------------------------------------------------------------------------
67  	// Test classes
68  	//-----------------------------------------------------------------------------------------------------------------
69  
70  	static class A {
71  		public A() {}  // NOSONAR
72  		public void foo() {}  // NOSONAR
73  	}
74  	static ClassInfo a = ClassInfo.of(A.class);
75  
76  	static class B {
77  		public B() {}
78  		public B(String s) {}  // NOSONAR
79  		public void m() {}  // NOSONAR
80  		public int m(String s) { return 0; }  // NOSONAR
81  	}
82  	static ClassInfo b = ClassInfo.of(B.class);
83  	static ExecutableInfo
84  		b_c1 = b.getPublicConstructor(cons -> cons.getParameterCount() == 0).get(),
85  		b_c2 = b.getPublicConstructor(x -> x.hasParameterTypes(String.class)).get(),
86  		b_m1 = b.getPublicMethod(x -> x.hasName("m") && x.getParameterCount() == 0).get(),
87  		b_m2 = b.getPublicMethod(x -> x.hasName("m") && x.hasParameterTypes(String.class)).get()
88  	;
89  
90  	enum B1 {
91  		FOO;
92  	}
93  
94  	@Documented
95  	@Target({PARAMETER,METHOD,java.lang.annotation.ElementType.CONSTRUCTOR})
96  	@Retention(RUNTIME)
97  	@Inherited
98  	public static @interface CA {}
99  
100 	static class C {
101 		public C() {}
102 		public C(@CA String foo) {}  // NOSONAR
103 		public @CA C(int bar) {}  // NOSONAR
104 		public void m() {}  // NOSONAR
105 		public void m(@CA String foo) {}  // NOSONAR
106 		public @CA void m(int bar) {}  // NOSONAR
107 	}
108 	static ClassInfo c = ClassInfo.of(C.class);
109 	static ConstructorInfo
110 		c_c1=c.getPublicConstructor(cons -> cons.getParameterCount() == 0).get(),
111 		c_c2=c.getPublicConstructor(x -> x.hasParameterTypes(String.class)).get(),
112 		c_c3=c.getPublicConstructor(x -> x.hasParameterTypes(int.class)).get()
113 	;
114 	static MethodInfo
115 		c_m1=c.getPublicMethod(x -> x.hasName("m") && x.getParameterCount() == 0).get(),
116 		c_m2=c.getPublicMethod(x -> x.hasName("m") && x.hasParameterTypes(String.class)).get(),
117 		c_m3=c.getPublicMethod(x -> x.hasName("m") && x.hasParameterTypes(int.class)).get()
118 	;
119 
120 	@SuppressWarnings("unused")
121 	static class D {
122 		public D() throws IOException {}  // NOSONAR
123 		public void m() throws IOException {}  // NOSONAR
124 	}
125 	static ClassInfo d = ClassInfo.of(D.class);
126 	static ExecutableInfo
127 		d_c=d.getPublicConstructor(cons -> cons.getParameterCount() == 0).get(),
128 		d_m=d.getPublicMethod(x -> x.hasName("m")).get()
129 	;
130 
131 	abstract static class E {
132 		@Deprecated public void deprecated() {}
133 		public void notDeprecated() {}
134 		public void hasParams(int foo) {}
135 		public void hasStringParam(String foo) {}
136 		public void hasNoParams() {}
137 		public void isPublic() {}
138 		protected void isNotPublic() {}
139 		public static void isStatic() {}
140 		public void isNotStatic() {}
141 		public abstract void isAbstract();
142 		public void isNotAbstract() {}
143 	}
144 	static ClassInfo e = ClassInfo.of(E.class);
145 	static ExecutableInfo
146 		e_deprecated = e.getPublicMethod(x -> x.hasName("deprecated")).get(),
147 		e_notDeprecated = e.getPublicMethod(x -> x.hasName("notDeprecated")).get(),
148 		e_hasParams = e.getPublicMethod(x -> x.hasName("hasParams")).get(),
149 		e_hasStringParam = e.getPublicMethod(x -> x.hasName("hasStringParam")).get(),
150 		e_hasNoParams = e.getPublicMethod(x -> x.hasName("hasNoParams")).get(),
151 		e_isPublic = e.getPublicMethod(x -> x.hasName("isPublic")).get(),
152 		e_isNotPublic = e.getMethod(x -> x.hasName("isNotPublic")).get(),
153 		e_isStatic = e.getPublicMethod(x -> x.hasName("isStatic")).get(),
154 		e_isNotStatic = e.getPublicMethod(x -> x.hasName("isNotStatic")).get(),
155 		e_isAbstract = e.getPublicMethod(x -> x.hasName("isAbstract")).get(),
156 		e_isNotAbstract = e.getPublicMethod(x -> x.hasName("isNotAbstract")).get()
157 	;
158 
159 	abstract static class F {
160 		public void isPublic() {}
161 		protected void isProtected() {}
162 		@SuppressWarnings("unused")
163 		private void isPrivate() {}
164 		void isDefault() {}
165 	}
166 	static ClassInfo f = ClassInfo.of(F.class);
167 	static ExecutableInfo
168 		f_isPublic = f.getPublicMethod(x -> x.hasName("isPublic")).get(),
169 		f_isProtected = f.getMethod(x -> x.hasName("isProtected")).get(),
170 		f_isPrivate = f.getMethod(x -> x.hasName("isPrivate")).get(),
171 		f_isDefault = f.getMethod(x -> x.hasName("isDefault")).get();
172 
173 	static class X {
174 		public X() {}
175 		public X(String foo) {}  // NOSONAR
176 		public X(Map<String,Object> foo) {}  // NOSONAR
177 		public void foo(){}  // NOSONAR
178 		public void foo(String foo){}  // NOSONAR
179 		public void foo(Map<String,Object> foo){}  // NOSONAR
180 	}
181 	static ClassInfo x2 = ClassInfo.of(X.class);
182 
183 	public static class VarArgsClass {
184 		public VarArgsClass(String...args) {}
185 	}
186 
187 	//====================================================================================================
188 	// accessible()
189 	//====================================================================================================
190 	@Test
191 	void a001_accessible() {
192 		assertDoesNotThrow(()->f_isPublic.accessible());
193 		assertDoesNotThrow(()->f_isProtected.accessible());
194 		assertDoesNotThrow(()->f_isPrivate.accessible());
195 		assertDoesNotThrow(()->f_isDefault.accessible());
196 		
197 		// Verify it returns this for chaining
198 		var result = f_isPublic.accessible();
199 		assertSame(f_isPublic, result);
200 	}
201 
202 	//====================================================================================================
203 	// canAccept(Object...)
204 	//====================================================================================================
205 	@Test
206 	void a002_canAccept() {
207 		// Exact match
208 		assertTrue(b_c2.canAccept("test"));
209 		assertFalse(b_c2.canAccept(123));
210 		assertFalse(b_c2.canAccept("test", "extra"));
211 		
212 		// No parameters
213 		assertTrue(b_c1.canAccept());
214 		assertFalse(b_c1.canAccept("test"));
215 		
216 		// Multiple parameters
217 		assertTrue(b_m2.canAccept("test"));
218 		assertFalse(b_m2.canAccept());
219 	}
220 
221 	//====================================================================================================
222 	// getAnnotatedExceptionTypes()
223 	//====================================================================================================
224 	@Test
225 	void a003_getAnnotatedExceptionTypes() {
226 		var types = d_c.getAnnotatedExceptionTypes();
227 		assertNotNull(types);
228 		assertEquals(1, types.length);
229 		assertEquals(IOException.class, types[0].getType());
230 	}
231 
232 	//====================================================================================================
233 	// getAnnotatedParameterTypes()
234 	//====================================================================================================
235 	@Test
236 	void a004_getAnnotatedParameterTypes() {
237 		var types = b_c2.getAnnotatedParameterTypes();
238 		assertNotNull(types);
239 		assertEquals(1, types.length);
240 		assertEquals(String.class, types[0].getType());
241 	}
242 
243 	//====================================================================================================
244 	// getAnnotatedReceiverType()
245 	//====================================================================================================
246 	@Test
247 	void a005_getAnnotatedReceiverType() {
248 		// Top-level class executable should return null
249 		var receiverType = b_c1.getAnnotatedReceiverType();
250 		assertNull(receiverType);
251 	}
252 
253 	//====================================================================================================
254 	// getDeclaredAnnotations()
255 	//====================================================================================================
256 	@Test
257 	void a006_getDeclaredAnnotations() {
258 		var annotations = c_c1.getDeclaredAnnotations();
259 		assertNotNull(annotations);
260 		assertTrue(annotations.isEmpty());
261 		
262 		// Constructor with annotation
263 		var annotations2 = c_c3.getDeclaredAnnotations();
264 		assertNotNull(annotations2);
265 		assertEquals(1, annotations2.size());
266 		assertTrue(annotations2.get(0).isType(CA.class));
267 	}
268 
269 	//====================================================================================================
270 	// getDeclaredAnnotations(Class<A>)
271 	//====================================================================================================
272 	@Test
273 	void a007_getDeclaredAnnotations_typed() {
274 		var annotations = c_c3.getDeclaredAnnotations(CA.class);
275 		assertEquals(1, annotations.count());
276 		
277 		var annotations2 = c_c1.getDeclaredAnnotations(CA.class);
278 		assertEquals(0, annotations2.count());
279 	}
280 
281 	//====================================================================================================
282 	// getDeclaringClass()
283 	//====================================================================================================
284 	@Test
285 	void a008_getDeclaringClass() {
286 		check("A", a.getPublicConstructor(cons -> cons.getParameterCount() == 0).get().getDeclaringClass());
287 		check("A", a.getPublicMethod(x -> x.hasName("foo")).get().getDeclaringClass());
288 		check("B", b_c1.getDeclaringClass());
289 		check("B", b_m1.getDeclaringClass());
290 	}
291 
292 	//====================================================================================================
293 	// getExceptionTypes()
294 	//====================================================================================================
295 	@Test
296 	void a009_getExceptionTypes() {
297 		check("IOException", d_c.getExceptionTypes());
298 		check("IOException", d_m.getExceptionTypes());
299 		
300 		// Test caching - should return same result
301 		check("IOException", d_c.getExceptionTypes());
302 	}
303 
304 	//====================================================================================================
305 	// getFullName()
306 	//====================================================================================================
307 	@Test
308 	void a010_getFullName() throws Exception {
309 		// Method
310 		assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X.foo()", x2.getPublicMethod(x -> x.hasName("foo") && x.getParameterCount() == 0).get().getFullName());
311 		assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X.foo(java.lang.String)", x2.getPublicMethod(x -> x.hasName("foo") && x.hasParameterTypes(String.class)).get().getFullName());
312 		assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X.foo(java.util.Map<java.lang.String,java.lang.Object>)", x2.getPublicMethod(x -> x.hasName("foo") && x.hasParameterTypes(Map.class)).get().getFullName());
313 		
314 		// Constructor
315 		assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X()", x2.getPublicConstructor(cons -> cons.getParameterCount() == 0).get().getFullName());
316 		assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X(java.lang.String)", x2.getPublicConstructor(x -> x.hasParameterTypes(String.class)).get().getFullName());
317 		assertEquals("org.apache.juneau.commons.reflect.ExecutableInfo_Test$X(java.util.Map<java.lang.String,java.lang.Object>)", x2.getPublicConstructor(x -> x.hasParameterTypes(Map.class)).get().getFullName());
318 		
319 		// Test line 752: getPackage() returns null branch
320 		// Primitive types don't have packages, but we can't get methods/constructors from primitives.
321 		// However, we can test with a class in the default package (no package declaration).
322 		// Note: Some classloaders may assign a package even to default package classes.
323 		try {
324 			Class<?> defaultPkgClassType = Class.forName("DefaultPackageTestClass");
325 			ClassInfo defaultPkgClass = ClassInfo.of(defaultPkgClassType);
326 			PackageInfo pkg = defaultPkgClass.getPackage();
327 			if (pkg == null) {
328 				// Test the false branch of line 752: when package is null, don't append package name
329 				ConstructorInfo defaultPkgCtor = defaultPkgClass.getPublicConstructor(cons -> cons.getParameterCount() == 0).get();
330 				String fullName = defaultPkgCtor.getFullName();
331 				// When package is null, getFullName() should not include package prefix
332 				assertTrue(fullName.startsWith("DefaultPackageTestClass("), "Full name should start with class name when package is null: " + fullName);
333 				// Verify no package prefix (no dots before the class name, except for inner classes)
334 				assertFalse(fullName.matches("^[a-z][a-z0-9]*(\\.[a-z][a-z0-9]*)+\\."), "Full name should not have package prefix when package is null: " + fullName);
335 			}
336 		} catch (ClassNotFoundException e) {
337 			// If class not found, skip this part of the test
338 		}
339 	}
340 
341 	//====================================================================================================
342 	// getParameter(int)
343 	//====================================================================================================
344 	@Test
345 	void a011_getParameter() {
346 		check("B[0]", b_c2.getParameter(0));
347 		check("m[0]", b_m2.getParameter(0));
348 		
349 		// Index out of bounds
350 		assertThrowsWithMessage(IndexOutOfBoundsException.class, "Invalid index '0'.  No parameters.", ()->b_c1.getParameter(0));
351 		assertThrowsWithMessage(IndexOutOfBoundsException.class, "Invalid index '-1'.  Parameter count: 1", ()->b_c2.getParameter(-1));
352 		assertThrowsWithMessage(IndexOutOfBoundsException.class, "Invalid index '1'.  Parameter count: 1", ()->b_c2.getParameter(1));
353 	}
354 
355 	//====================================================================================================
356 	// getParameterCount()
357 	//====================================================================================================
358 	@Test
359 	void a012_getParameterCount() {
360 		assertEquals(0, b_c1.getParameterCount());
361 		assertEquals(1, b_c2.getParameterCount());
362 		assertEquals(0, b_m1.getParameterCount());
363 		assertEquals(1, b_m2.getParameterCount());
364 	}
365 
366 	//====================================================================================================
367 	// getParameters()
368 	//====================================================================================================
369 	@Test
370 	void a013_getParameters() {
371 		check("", b_c1.getParameters());
372 		check("B[0]", b_c2.getParameters());
373 		check("", b_m1.getParameters());
374 		check("m[0]", b_m2.getParameters());
375 		
376 		// Test caching - should return same result
377 		check("", b_c1.getParameters());
378 		
379 		// Test enum constructor parameters
380 		var b1 = ClassInfo.of(B1.class);
381 		check("String,int", b1.getDeclaredConstructors().get(0).getParameters().stream().map(ParameterInfo::getParameterType).toList());
382 	}
383 
384 	//====================================================================================================
385 	// getParameterTypes()
386 	//====================================================================================================
387 	@Test
388 	void a013a_getParameterTypes() {
389 		// Test with no parameters
390 		check("", b_c1.getParameterTypes());
391 		check("", b_m1.getParameterTypes());
392 		
393 		// Test with single parameter
394 		check("String", b_c2.getParameterTypes());
395 		check("String", b_m2.getParameterTypes());
396 		
397 		// Test with multiple parameters
398 		var b1 = ClassInfo.of(B1.class);
399 		check("String,int", b1.getDeclaredConstructors().get(0).getParameterTypes());
400 		
401 		// Test with different parameter types
402 		check("int", c_c3.getParameterTypes());
403 		check("int", c_m3.getParameterTypes());
404 		
405 		// Test caching - should return same result
406 		var types1 = b_c2.getParameterTypes();
407 		var types2 = b_c2.getParameterTypes();
408 		assertSame(types1, types2, "getParameterTypes() should return cached result");
409 		
410 		// Verify that getParameterTypes() returns the same types as getParameters().stream().map(ParameterInfo::getParameterType)
411 		var typesFromMethod = b_c2.getParameterTypes();
412 		var typesFromParams = b_c2.getParameters().stream().map(ParameterInfo::getParameterType).toList();
413 		assertEquals(typesFromMethod, typesFromParams, "getParameterTypes() should match types from getParameters()");
414 	}
415 
416 	//====================================================================================================
417 	// getShortName()
418 	//====================================================================================================
419 	@Test
420 	void a014_getShortName() {
421 		// Method
422 		assertEquals("foo()", x2.getPublicMethod(x -> x.hasName("foo") && x.getParameterCount() == 0).get().getShortName());
423 		assertEquals("foo(String)", x2.getPublicMethod(x -> x.hasName("foo") && x.hasParameterTypes(String.class)).get().getShortName());
424 		assertEquals("foo(Map)", x2.getPublicMethod(x -> x.hasName("foo") && x.hasParameterTypes(Map.class)).get().getShortName());
425 		
426 		// Constructor
427 		assertEquals("X()", x2.getPublicConstructor(cons -> cons.getParameterCount() == 0).get().getShortName());
428 		assertEquals("X(String)", x2.getPublicConstructor(x -> x.hasParameterTypes(String.class)).get().getShortName());
429 		assertEquals("X(Map)", x2.getPublicConstructor(x -> x.hasParameterTypes(Map.class)).get().getShortName());
430 	}
431 
432 	//====================================================================================================
433 	// getSimpleName()
434 	//====================================================================================================
435 	@Test
436 	void a015_getSimpleName() {
437 		// Method
438 		assertEquals("foo", x2.getPublicMethod(x -> x.hasName("foo") && x.getParameterCount() == 0).get().getSimpleName());
439 		assertEquals("foo", x2.getPublicMethod(x -> x.hasName("foo") && x.hasParameterTypes(String.class)).get().getSimpleName());
440 		
441 		// Constructor
442 		assertEquals("X", x2.getPublicConstructor(cons -> cons.getParameterCount() == 0).get().getSimpleName());
443 		assertEquals("X", x2.getPublicConstructor(x -> x.hasParameterTypes(String.class)).get().getSimpleName());
444 	}
445 
446 	//====================================================================================================
447 	// getTypeParameters()
448 	//====================================================================================================
449 	@Test
450 	void a016_getTypeParameters() {
451 		var typeParams = b_c1.getTypeParameters();
452 		assertNotNull(typeParams);
453 		assertEquals(0, typeParams.length);
454 	}
455 
456 	//====================================================================================================
457 	// hasAnnotation(Class<A>)
458 	//====================================================================================================
459 	@Test
460 	void a017_hasAnnotation() {
461 		assertFalse(c_c1.hasAnnotation(CA.class));
462 		assertFalse(c_c2.hasAnnotation(CA.class));
463 		assertTrue(c_c3.hasAnnotation(CA.class));
464 		assertFalse(c_m1.hasAnnotation(CA.class));
465 		assertFalse(c_m2.hasAnnotation(CA.class));
466 		assertTrue(c_m3.hasAnnotation(CA.class));
467 	}
468 
469 	//====================================================================================================
470 	// hasAnyName(Collection<String>)
471 	//====================================================================================================
472 	@Test
473 	void a018_hasAnyName_collection() {
474 		assertTrue(b_m1.hasAnyName(Arrays.asList("m", "n")));
475 		assertFalse(b_m1.hasAnyName(Arrays.asList("n", "o")));
476 	}
477 
478 	//====================================================================================================
479 	// hasAnyName(String...)
480 	//====================================================================================================
481 	@Test
482 	void a019_hasAnyName_varargs() {
483 		assertTrue(b_m1.hasAnyName("m", "n"));
484 		assertFalse(b_m1.hasAnyName("n", "o"));
485 	}
486 
487 	//====================================================================================================
488 	// hasMatchingParameters(List<ParameterInfo>)
489 	//====================================================================================================
490 	@Test
491 	void a020_hasMatchingParameters() {
492 		var params1 = b_c2.getParameters();
493 		assertTrue(b_c2.hasMatchingParameters(params1));
494 		
495 		// Test with parameters from a different executable that has different parameter types
496 		var params2 = b_c1.getParameters();  // b_c1 has no parameters, b_c2 has one String parameter
497 		assertFalse(b_c2.hasMatchingParameters(params2));
498 		
499 		// Test that b_c2 and b_m2 DO match (both have String parameter)
500 		var params3 = b_m2.getParameters();
501 		assertTrue(b_c2.hasMatchingParameters(params3));
502 	}
503 
504 	//====================================================================================================
505 	// hasName(String)
506 	//====================================================================================================
507 	@Test
508 	void a021_hasName() {
509 		assertTrue(b_m1.hasName("m"));
510 		assertFalse(b_m1.hasName("n"));
511 	}
512 
513 	//====================================================================================================
514 	// hasNumParameters(int)
515 	//====================================================================================================
516 	@Test
517 	void a022_hasNumParameters() {
518 		assertFalse(b_c1.hasNumParameters(1));
519 		assertTrue(b_c2.hasNumParameters(1));
520 		assertFalse(b_m1.hasNumParameters(1));
521 		assertTrue(b_m2.hasNumParameters(1));
522 	}
523 
524 	//====================================================================================================
525 	// hasParameters()
526 	//====================================================================================================
527 	@Test
528 	void a023_hasParameters() {
529 		assertEquals(false, b_c1.hasParameters());
530 		assertEquals(true, b_c2.hasParameters());
531 		assertEquals(false, b_m1.hasParameters());
532 		assertEquals(true, b_m2.hasParameters());
533 	}
534 
535 	//====================================================================================================
536 	// hasParameterTypeParents(Class<?>...)
537 	//====================================================================================================
538 	@Test
539 	void a024_hasParameterTypeParents_class() {
540 		assertTrue(e_hasStringParam.hasParameterTypeParents(String.class));
541 		assertFalse(e_hasStringParam.hasParameterTypeParents(CharSequence.class));
542 		assertFalse(e_hasStringParam.hasParameterTypeParents(StringBuilder.class));
543 		assertFalse(e_hasStringParam.hasParameterTypeParents(new Class[0]));
544 		assertFalse(e_hasStringParam.hasParameterTypeParents(String.class, String.class));
545 		assertFalse(e_hasStringParam.hasParameterTypeParents(long.class));
546 	}
547 
548 	//====================================================================================================
549 	// hasParameterTypeParents(ClassInfo...)
550 	//====================================================================================================
551 	@Test
552 	void a025_hasParameterTypeParents_classInfo() {
553 		var stringClass = ClassInfo.of(String.class);
554 		var charSequenceClass = ClassInfo.of(CharSequence.class);
555 		assertTrue(e_hasStringParam.hasParameterTypeParents(stringClass));
556 		assertFalse(e_hasStringParam.hasParameterTypeParents(charSequenceClass));
557 	}
558 
559 	//====================================================================================================
560 	// hasParameterTypes(Class<?>...)
561 	//====================================================================================================
562 	@Test
563 	void a026_hasParameterTypes_class() {
564 		assertTrue(e_hasParams.hasParameterTypes(int.class));
565 		assertFalse(e_hasParams.hasParameterTypes(new Class[0]));
566 		assertFalse(e_hasParams.hasParameterTypes(long.class));
567 		assertTrue(e_hasNoParams.hasParameterTypes(new Class[0]));
568 		assertFalse(e_hasNoParams.hasParameterTypes(long.class));
569 	}
570 
571 	//====================================================================================================
572 	// hasParameterTypes(ClassInfo...)
573 	//====================================================================================================
574 	@Test
575 	void a027_hasParameterTypes_classInfo() {
576 		var intClass = ClassInfo.of(int.class);
577 		var longClass = ClassInfo.of(long.class);
578 		assertTrue(e_hasParams.hasParameterTypes(intClass));
579 		assertFalse(e_hasParams.hasParameterTypes(longClass));
580 		
581 		// Test line 484 branches:
582 		// Branch 1: params.size() == args.length is false (short-circuit) - when sizes don't match
583 		// e_hasParams has 1 parameter, so passing 0 or 2 args should return false
584 		assertFalse(e_hasParams.hasParameterTypes(new ClassInfo[0]));  // Empty args array, but method has 1 parameter
585 		var intClass2 = ClassInfo.of(int.class);
586 		assertFalse(e_hasParams.hasParameterTypes(intClass, intClass2));  // 2 args, but method has 1 parameter
587 		
588 		// Branch 2: params.size() == args.length is true, args.length == 0 (empty stream, allMatch returns true)
589 		// e_hasNoParams has 0 parameters, so passing empty args should return true
590 		assertTrue(e_hasNoParams.hasParameterTypes(new ClassInfo[0]));  // Empty args array, method has 0 parameters
591 		
592 		// Branch 3: params.size() == args.length is true, args.length > 0, allMatch returns true - already covered above
593 		// Branch 4: params.size() == args.length is true, args.length > 0, allMatch returns false - already covered above
594 	}
595 
596 	//====================================================================================================
597 	// hasParameterTypesLenient(Class<?>...)
598 	//====================================================================================================
599 	@Test
600 	void a028_hasParameterTypesLenient_class() {
601 		assertTrue(e_hasParams.hasParameterTypesLenient(int.class));
602 		assertTrue(e_hasParams.hasParameterTypesLenient(int.class, long.class));
603 		assertFalse(e_hasParams.hasParameterTypesLenient(long.class));
604 		assertTrue(e_hasNoParams.hasParameterTypesLenient(new Class[0]));
605 		assertTrue(e_hasNoParams.hasParameterTypesLenient(long.class));
606 	}
607 
608 	//====================================================================================================
609 	// hasParameterTypesLenient(ClassInfo...)
610 	//====================================================================================================
611 	@Test
612 	void a029_hasParameterTypesLenient_classInfo() {
613 		var intClass = ClassInfo.of(int.class);
614 		var longClass = ClassInfo.of(long.class);
615 		assertTrue(e_hasParams.hasParameterTypesLenient(intClass));
616 		assertTrue(e_hasParams.hasParameterTypesLenient(intClass, longClass));
617 		assertFalse(e_hasParams.hasParameterTypesLenient(longClass));
618 	}
619 
620 	//====================================================================================================
621 	// is(ElementFlag)
622 	//====================================================================================================
623 	@Test
624 	void a030_is() {
625 		assertTrue(e_deprecated.is(DEPRECATED));
626 		assertTrue(e_notDeprecated.is(NOT_DEPRECATED));
627 		assertTrue(e_hasParams.is(HAS_PARAMS));
628 		assertTrue(e_hasNoParams.is(HAS_NO_PARAMS));
629 		assertTrue(e_isPublic.is(PUBLIC));
630 		assertTrue(e_isNotPublic.is(NOT_PUBLIC));
631 		assertTrue(e_isStatic.is(STATIC));
632 		assertTrue(e_isNotStatic.is(NOT_STATIC));
633 		assertTrue(e_isAbstract.is(ABSTRACT));
634 		assertTrue(e_isNotAbstract.is(NOT_ABSTRACT));
635 
636 		assertFalse(e_deprecated.is(NOT_DEPRECATED));
637 		assertFalse(e_notDeprecated.is(DEPRECATED));
638 		assertFalse(e_hasParams.is(HAS_NO_PARAMS));
639 		assertFalse(e_hasNoParams.is(HAS_PARAMS));
640 		assertFalse(e_isPublic.is(NOT_PUBLIC));
641 		assertFalse(e_isNotPublic.is(PUBLIC));
642 		assertFalse(e_isStatic.is(NOT_STATIC));
643 		assertFalse(e_isNotStatic.is(STATIC));
644 		assertFalse(e_isAbstract.is(NOT_ABSTRACT));
645 		assertFalse(e_isNotAbstract.is(ABSTRACT));
646 		
647 		// Constructor vs method
648 		assertTrue(a.getPublicConstructor(cons -> cons.getParameterCount() == 0).get().isConstructor());
649 		assertTrue(a.getPublicConstructor(cons -> cons.getParameterCount() == 0).get().is(ElementFlag.CONSTRUCTOR));
650 		assertFalse(a.getPublicMethod(x -> x.hasName("foo")).get().isConstructor());
651 		assertFalse(a.getPublicMethod(x -> x.hasName("foo")).get().is(ElementFlag.CONSTRUCTOR));
652 		assertTrue(a.getPublicMethod(x -> x.hasName("foo")).get().is(NOT_CONSTRUCTOR));
653 		
654 		// SYNTHETIC and NOT_SYNTHETIC (lines 531, 532)
655 		// Regular executables are not synthetic
656 		assertFalse(b_c1.isSynthetic());
657 		assertFalse(b_c1.is(SYNTHETIC));
658 		assertTrue(b_c1.is(NOT_SYNTHETIC));
659 		assertFalse(b_m1.isSynthetic());
660 		assertFalse(b_m1.is(SYNTHETIC));
661 		assertTrue(b_m1.is(NOT_SYNTHETIC));
662 		
663 		// VARARGS and NOT_VARARGS (lines 532, 533)
664 		var varArgsCi = ClassInfo.of(VarArgsClass.class);
665 		var varArgsCtor = varArgsCi.getPublicConstructor(cons -> cons.isVarArgs()).get();
666 		assertTrue(varArgsCtor.isVarArgs());
667 		assertTrue(varArgsCtor.is(VARARGS));
668 		assertFalse(varArgsCtor.is(NOT_VARARGS));
669 		
670 		// Non-varargs executables
671 		assertFalse(b_c1.isVarArgs());
672 		assertFalse(b_c1.is(VARARGS));
673 		assertTrue(b_c1.is(NOT_VARARGS));
674 		assertFalse(b_m1.isVarArgs());
675 		assertFalse(b_m1.is(VARARGS));
676 		assertTrue(b_m1.is(NOT_VARARGS));
677 		
678 		// TRANSIENT is a valid modifier flag but doesn't apply to executables
679 		assertFalse(e_deprecated.is(TRANSIENT));
680 		
681 		// CLASS is not a modifier flag and doesn't apply to executables, should throw exception
682 		assertThrowsWithMessage(RuntimeException.class, "Invalid flag for element: CLASS", () -> e_deprecated.is(ElementFlag.CLASS));
683 	}
684 
685 	//====================================================================================================
686 	// isAccessible()
687 	//====================================================================================================
688 	@Test
689 	void a031_isAccessible() {
690 		// Test isAccessible() before and after setAccessible()
691 		var privateBefore = f_isPrivate.isAccessible();
692 		var protectedBefore = f_isProtected.isAccessible();
693 		var defaultBefore = f_isDefault.isAccessible();
694 		
695 		// Make them accessible
696 		f_isPrivate.setAccessible();
697 		f_isProtected.setAccessible();
698 		f_isDefault.setAccessible();
699 		
700 		// After setAccessible(), they should be accessible (if Java 9+)
701 		var privateAfter = f_isPrivate.isAccessible();
702 		var protectedAfter = f_isProtected.isAccessible();
703 		var defaultAfter = f_isDefault.isAccessible();
704 		
705 		// Verify the method doesn't throw and returns a boolean
706 		assertTrue(privateAfter || !privateBefore, "After setAccessible(), isAccessible() should return true (Java 9+) or false (Java 8)");
707 		assertTrue(protectedAfter || !protectedBefore, "After setAccessible(), isAccessible() should return true (Java 9+) or false (Java 8)");
708 		assertTrue(defaultAfter || !defaultBefore, "After setAccessible(), isAccessible() should return true (Java 9+) or false (Java 8)");
709 		
710 		// Public methods might already be accessible
711 		var publicAccessible = f_isPublic.isAccessible();
712 		assertNotNull(publicAccessible);
713 	}
714 
715 	//====================================================================================================
716 	// isAll(ElementFlag...)
717 	//====================================================================================================
718 	@Test
719 	void a032_isAll() {
720 		assertTrue(e_deprecated.isAll(DEPRECATED));
721 		assertTrue(e_isPublic.isAll(PUBLIC, NOT_PRIVATE));
722 		assertFalse(e_deprecated.isAll(DEPRECATED, NOT_DEPRECATED));
723 	}
724 
725 	//====================================================================================================
726 	// isAny(ElementFlag...)
727 	//====================================================================================================
728 	@Test
729 	void a033_isAny() {
730 		assertTrue(e_deprecated.isAny(DEPRECATED, NOT_DEPRECATED));
731 		assertTrue(e_isPublic.isAny(PUBLIC, PRIVATE));
732 		assertFalse(e_deprecated.isAny(NOT_DEPRECATED));
733 	}
734 
735 	//====================================================================================================
736 	// isConstructor()
737 	//====================================================================================================
738 	@Test
739 	void a034_isConstructor() {
740 		assertTrue(a.getPublicConstructor(cons -> cons.getParameterCount() == 0).get().isConstructor());
741 		assertFalse(a.getPublicMethod(x -> x.hasName("foo")).get().isConstructor());
742 	}
743 
744 	//====================================================================================================
745 	// isDeprecated()
746 	//====================================================================================================
747 	@Test
748 	void a035_isDeprecated() {
749 		assertTrue(e_deprecated.isDeprecated());
750 		assertFalse(e_notDeprecated.isDeprecated());
751 	}
752 
753 	//====================================================================================================
754 	// isNotDeprecated()
755 	//====================================================================================================
756 	@Test
757 	void a036_isNotDeprecated() {
758 		assertFalse(e_deprecated.isNotDeprecated());
759 		assertTrue(e_notDeprecated.isNotDeprecated());
760 	}
761 
762 	//====================================================================================================
763 	// isSynthetic()
764 	//====================================================================================================
765 	@Test
766 	void a037_isSynthetic() {
767 		// Regular executables are not synthetic
768 		assertFalse(b_c1.isSynthetic());
769 		assertFalse(b_m1.isSynthetic());
770 	}
771 
772 	//====================================================================================================
773 	// isVarArgs()
774 	//====================================================================================================
775 	@Test
776 	void a038_isVarArgs() {
777 		var ci = ClassInfo.of(VarArgsClass.class);
778 		var ctor = ci.getPublicConstructor(x -> x.hasParameterTypes(String[].class)).get();
779 		assertTrue(ctor.isVarArgs());
780 		assertFalse(b_c1.isVarArgs());
781 	}
782 
783 	//====================================================================================================
784 	// isVisible(Visibility)
785 	//====================================================================================================
786 	@Test
787 	void a039_isVisible() {
788 		assertTrue(f_isPublic.isVisible(Visibility.PUBLIC));
789 		assertTrue(f_isPublic.isVisible(Visibility.PROTECTED));
790 		assertTrue(f_isPublic.isVisible(Visibility.PRIVATE));
791 		assertTrue(f_isPublic.isVisible(Visibility.DEFAULT));
792 
793 		assertFalse(f_isProtected.isVisible(Visibility.PUBLIC));
794 		assertTrue(f_isProtected.isVisible(Visibility.PROTECTED));
795 		assertTrue(f_isProtected.isVisible(Visibility.PRIVATE));
796 		assertTrue(f_isProtected.isVisible(Visibility.DEFAULT));
797 
798 		assertFalse(f_isPrivate.isVisible(Visibility.PUBLIC));
799 		assertFalse(f_isPrivate.isVisible(Visibility.PROTECTED));
800 		assertTrue(f_isPrivate.isVisible(Visibility.PRIVATE));
801 		assertFalse(f_isPrivate.isVisible(Visibility.DEFAULT));
802 
803 		assertFalse(f_isDefault.isVisible(Visibility.PUBLIC));
804 		assertFalse(f_isDefault.isVisible(Visibility.PROTECTED));
805 		assertTrue(f_isDefault.isVisible(Visibility.PRIVATE));
806 		assertTrue(f_isDefault.isVisible(Visibility.DEFAULT));
807 	}
808 
809 	//====================================================================================================
810 	// parameterMatchesLenientCount(Class<?>...)
811 	//====================================================================================================
812 	@Test
813 	void a040_parameterMatchesLenientCount_class() {
814 		// Exact match
815 		assertEquals(1, e_hasParams.parameterMatchesLenientCount(int.class));
816 		// Parent type match
817 		assertEquals(1, e_hasStringParam.parameterMatchesLenientCount(String.class));
818 		// No match
819 		assertEquals(-1, e_hasParams.parameterMatchesLenientCount(long.class));
820 		// Multiple args, some match
821 		assertEquals(1, e_hasParams.parameterMatchesLenientCount(int.class, long.class));
822 	}
823 
824 	//====================================================================================================
825 	// parameterMatchesLenientCount(ClassInfo...)
826 	//====================================================================================================
827 	@Test
828 	void a041_parameterMatchesLenientCount_classInfo() {
829 		var intClass = ClassInfo.of(int.class);
830 		var longClass = ClassInfo.of(long.class);
831 		assertEquals(1, e_hasParams.parameterMatchesLenientCount(intClass));
832 		assertEquals(-1, e_hasParams.parameterMatchesLenientCount(longClass));
833 	}
834 
835 	//====================================================================================================
836 	// parameterMatchesLenientCount(Object...)
837 	//====================================================================================================
838 	@Test
839 	void a042_parameterMatchesLenientCount_object() {
840 		assertEquals(1, e_hasParams.parameterMatchesLenientCount(123));
841 		assertEquals(1, e_hasStringParam.parameterMatchesLenientCount("test"));
842 		assertEquals(-1, e_hasParams.parameterMatchesLenientCount("test"));
843 	}
844 
845 	//====================================================================================================
846 	// setAccessible()
847 	//====================================================================================================
848 	@Test
849 	void a043_setAccessible() {
850 		assertDoesNotThrow(()->f_isPublic.setAccessible());
851 		assertDoesNotThrow(()->f_isProtected.setAccessible());
852 		assertDoesNotThrow(()->f_isPrivate.setAccessible());
853 		assertDoesNotThrow(()->f_isDefault.setAccessible());
854 	}
855 
856 	//====================================================================================================
857 	// toGenericString()
858 	//====================================================================================================
859 	@Test
860 	void a044_toGenericString() {
861 		var str = b_c2.toGenericString();
862 		assertNotNull(str);
863 		assertTrue(str.contains("B"));
864 		assertTrue(str.contains("String"));
865 	}
866 
867 	//====================================================================================================
868 	// toString()
869 	//====================================================================================================
870 	@Test
871 	void a045_toString() {
872 		check("B()", b_c1.toString());
873 		check("B(String)", b_c2.toString());
874 		check("m()", b_m1.toString());
875 		check("m(String)", b_m2.toString());
876 	}
877 }
878