View Javadoc
1   package org.djutils.reflection;
2   
3   import static org.junit.jupiter.api.Assertions.assertArrayEquals;
4   import static org.junit.jupiter.api.Assertions.assertEquals;
5   import static org.junit.jupiter.api.Assertions.assertFalse;
6   import static org.junit.jupiter.api.Assertions.assertNotEquals;
7   import static org.junit.jupiter.api.Assertions.assertNotNull;
8   import static org.junit.jupiter.api.Assertions.assertNull;
9   import static org.junit.jupiter.api.Assertions.assertTrue;
10  import static org.junit.jupiter.api.Assertions.fail;
11  
12  import java.lang.annotation.Annotation;
13  import java.lang.annotation.ElementType;
14  import java.lang.annotation.Retention;
15  import java.lang.annotation.RetentionPolicy;
16  import java.lang.annotation.Target;
17  import java.lang.reflect.Constructor;
18  import java.lang.reflect.Field;
19  import java.lang.reflect.InvocationTargetException;
20  import java.lang.reflect.Method;
21  import java.lang.reflect.Modifier;
22  import java.util.HashSet;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Set;
26  
27  import org.djutils.reflection.TestClass.InnerPublic;
28  import org.djutils.test.UnitTest;
29  import org.junit.jupiter.api.Test;
30  
31  /**
32   * The JUNIT Test for <code>ClassUtil</code>.
33   * <p>
34   * Copyright (c) 2002-2025 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
35   * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
36   * distributed under a three-clause BSD-style license, which can be found at
37   * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
38   * </p>
39   * @author <a href="https://www.linkedin.com/in/peterhmjacobs">Peter Jacobs </a>
40   * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
41   */
42  public class ClassUtilTest
43  {
44      /** */
45      @Test
46      public void testClassUtilClass()
47      {
48          assertEquals(0, ClassUtil.getClass(null).length);
49          assertEquals(String.class, ClassUtil.getClass(new Object[] {"Peter"})[0]);
50          // Note that primitive types are always autoboxed to the corresponding object types.
51          assertArrayEquals(new Class<?>[] {String.class, Double.class, Integer.class, Integer.class},
52                  ClassUtil.getClass(new Object[] {"X", 1.0d, 5, Integer.valueOf(5)}));
53          assertArrayEquals(new Class<?>[] {String.class, Double.class, null, Integer.class},
54                  ClassUtil.getClass(new Object[] {"X", 1.0d, null, 5}));
55      }
56  
57      /**
58       * Test the toDescriptor method of the FieldSignature class.
59       */
60      @Test
61      public void testToDescriptor()
62      {
63          assertEquals("I", FieldSignature.toDescriptor(int.class));
64          assertEquals("D", FieldSignature.toDescriptor(double.class));
65          assertEquals("Z", FieldSignature.toDescriptor(boolean.class));
66          assertEquals("C", FieldSignature.toDescriptor(char.class));
67          assertEquals("B", FieldSignature.toDescriptor(byte.class));
68          assertEquals("F", FieldSignature.toDescriptor(float.class));
69          assertEquals("J", FieldSignature.toDescriptor(long.class));
70          assertEquals("S", FieldSignature.toDescriptor(short.class));
71          assertEquals("[I", FieldSignature.toDescriptor(int[].class));
72          // System.out.println(FieldSignature.toDescriptor(int[].class));
73          assertEquals("Ljava/lang/Integer;", FieldSignature.toDescriptor(Integer.class));
74      }
75  
76      /**
77       * Test the toClass method of the FieldSignature class.
78       * @throws ClassNotFoundException if that happens uncaught; this test has failed
79       */
80      @Test
81      public void testToClass() throws ClassNotFoundException
82      {
83          assertEquals(int.class, FieldSignature.toClass("int"));
84          assertEquals(int.class, FieldSignature.toClass("I"));
85          assertEquals(double.class, FieldSignature.toClass("double"));
86          assertEquals(double.class, FieldSignature.toClass("D"));
87          assertEquals(boolean.class, FieldSignature.toClass("boolean"));
88          assertEquals(boolean.class, FieldSignature.toClass("Z"));
89          assertEquals(char.class, FieldSignature.toClass("C"));
90          assertEquals(char.class, FieldSignature.toClass("char"));
91          assertEquals(byte.class, FieldSignature.toClass("B"));
92          assertEquals(byte.class, FieldSignature.toClass("byte"));
93          assertEquals(float.class, FieldSignature.toClass("F"));
94          assertEquals(float.class, FieldSignature.toClass("float"));
95          assertEquals(long.class, FieldSignature.toClass("J"));
96          assertEquals(long.class, FieldSignature.toClass("long"));
97          assertEquals(short.class, FieldSignature.toClass("S"));
98          assertEquals(short.class, FieldSignature.toClass("short"));
99          assertEquals(int[].class, FieldSignature.toClass("[I"));
100         assertEquals(int[].class, FieldSignature.toClass("[I")); // repeated call uses the cache
101         assertEquals(Integer.class, FieldSignature.toClass("Ljava/lang/Integer;"));
102         assertEquals(void.class, FieldSignature.toClass("void"));
103         assertEquals(void.class, FieldSignature.toClass("V"));
104         try
105         {
106             assertNull(FieldSignature.toClass("XXX"));
107             fail("name of non existant class should have thrown a ClassNotFoundException");
108         }
109         catch (ClassNotFoundException cnfe)
110         {
111             // Ignore expected exception
112         }
113     }
114 
115     /**
116      * Test the constructors and fields of the FieldSignature class.
117      * @throws ClassNotFoundException if that happens uncaught; this test has failed
118      */
119     @Test
120     public void testFieldSignature() throws ClassNotFoundException
121     {
122         FieldSignature fs = new FieldSignature("[J");
123         assertEquals("[J", fs.toString());
124         assertEquals("[J", fs.getStringValue());
125         assertEquals(long[].class, fs.getClassValue());
126         FieldSignature fs2 = new FieldSignature(double[].class);
127         assertEquals("[D", fs2.toString());
128         assertEquals("[D", fs2.getStringValue());
129         assertEquals(double[].class, fs2.getClassValue());
130     }
131 
132     /**
133      * Test the constructors and fields of the MethodSignature class.
134      * @throws SecurityException if that happens uncaught; this test has failed
135      * @throws NoSuchMethodException if that happens uncaught; this test has failed
136      * @throws ClassNotFoundException if that happens uncaught; this test has failed
137      */
138     @Test
139     public void testMethodSignature() throws NoSuchMethodException, SecurityException, ClassNotFoundException
140     {
141         MethodSignature ms = new MethodSignature(String.class.getConstructor(String.class));
142         assertEquals(String.class, ms.getReturnType());
143         Class<?>[] parameterTypes = ms.getParameterTypes();
144         assertEquals(1, parameterTypes.length);
145         assertEquals(String.class, parameterTypes[0]);
146         assertEquals("Ljava/lang/String;", ms.getParameterDescriptor());
147         ms = new MethodSignature(String.class.getConstructor()); // Constructor for empty string
148         assertEquals(String.class, ms.getReturnType());
149         parameterTypes = ms.getParameterTypes();
150         assertEquals(0, parameterTypes.length);
151         ms = new MethodSignature("(I)[D");
152         assertEquals(double[].class, ms.getReturnType());
153         parameterTypes = ms.getParameterTypes();
154         assertEquals(1, parameterTypes.length);
155         assertEquals(int.class, parameterTypes[0]);
156         ms = new MethodSignature(String.class.getMethod("length"));
157         parameterTypes = ms.getParameterTypes();
158         assertEquals(0, parameterTypes.length);
159         Class<?> returnType = ms.getReturnType();
160         assertEquals(int.class, returnType);
161         Method[] methods = String.class.getMethods();
162         // find the substring method that takes two arguments
163         Method substring = null;
164         for (Method m : methods)
165         {
166             if (m.getName().equals("substring") && m.getParameterCount() == 2)
167             {
168                 substring = m;
169             }
170         }
171         if (null == substring)
172         {
173             System.err.println("Could not find substring(int from, int to) method");
174         }
175         else
176         {
177             ms = new MethodSignature(substring);
178             parameterTypes = ms.getParameterTypes();
179             assertEquals(2, parameterTypes.length);
180             assertEquals(int.class, parameterTypes[0]);
181             assertEquals(int.class, parameterTypes[1]);
182             returnType = ms.getReturnType();
183             assertEquals(String.class, returnType);
184             assertEquals("II", ms.getParameterDescriptor());
185             assertEquals("Ljava/lang/String;", ms.getReturnDescriptor());
186             assertEquals("(II)Ljava/lang/String;", ms.toString());
187         }
188         ms = new MethodSignature(String.class.getMethod("getBytes"));
189         returnType = ms.getReturnType();
190         assertEquals(byte[].class, returnType);
191         ms = new MethodSignature(String.class.getConstructor(byte[].class));
192         parameterTypes = ms.getParameterTypes();
193         assertEquals(1, parameterTypes.length);
194         assertEquals(byte[].class, parameterTypes[0]);
195         assertEquals("[B", ms.getParameterDescriptor());
196     }
197 
198     /**
199      * Tests the ClassUtil Constructors.
200      * @throws NoSuchMethodException on error
201      * @throws InvocationTargetException on error
202      * @throws IllegalArgumentException on error
203      * @throws IllegalAccessException on error
204      * @throws InstantiationException on error
205      */
206     @Test
207     public void testClassUtilConstructors() throws NoSuchMethodException, InstantiationException, IllegalAccessException,
208             IllegalArgumentException, InvocationTargetException
209     {
210         Constructor<TestClass> c1 = ClassUtil.resolveConstructor(TestClass.class, new Class<?>[] {});
211         TestClass o1 = c1.newInstance();
212         assertEquals("<init>", o1.getState());
213 
214         Constructor<TestClass> c2 = ClassUtil.resolveConstructor(TestClass.class, new Class<?>[] {String.class});
215         TestClass o2 = c2.newInstance("c2");
216         assertEquals("c2", o2.getState());
217 
218         Constructor<InnerPublic> c3 = ClassUtil.resolveConstructor(InnerPublic.class, new Class<?>[] {});
219         InnerPublic o3 = c3.newInstance();
220         assertEquals("<initInnerPublic>", o3.getInnerState());
221 
222         Constructor<InnerPublic> c4 = ClassUtil.resolveConstructor(InnerPublic.class, new Class<?>[] {String.class});
223         InnerPublic o4 = c4.newInstance("inner");
224         assertEquals("inner", o4.getInnerState());
225 
226         // test caching
227         Constructor<InnerPublic> c4a = ClassUtil.resolveConstructor(InnerPublic.class, new Class<?>[] {String.class});
228         InnerPublic o4a = c4a.newInstance("inner2");
229         assertEquals("inner2", o4a.getInnerState());
230 
231         // test constructor that cannot be found
232         try
233         {
234             ClassUtil.resolveConstructor(TestClass.class, new Class<?>[] {Integer.class});
235             fail("Constructor TestClass(int) does not exist and resolving should throw an exception");
236         }
237         catch (NoSuchMethodException e)
238         {
239             // ok
240         }
241 
242         // test access to public and private constructors
243         ClassUtil.resolveConstructor(TestClass.class, new Class<?>[] {boolean.class});
244         ClassUtil.resolveConstructor(TestClass.class, ClassUtilTest.class, new Class<?>[] {String.class});
245         try
246         {
247             ClassUtil.resolveConstructor(TestClass.class, ClassUtilTest.class, new Class<?>[] {boolean.class});
248             fail("Constructor TestClass(boolean) is private and resolving should throw an exception");
249         }
250         catch (IllegalAccessException e)
251         {
252             // ok
253         }
254 
255         assertEquals(3, ClassUtil.getAllConstructors(TestClass.class).length);
256     }
257 
258     /**
259      * Tests the ClassUtil Constructor lookup methods.
260      * @throws NoSuchMethodException on error
261      * @throws InvocationTargetException on error
262      * @throws IllegalArgumentException on error
263      * @throws IllegalAccessException on error
264      * @throws InstantiationException on error
265      */
266     @Test
267     public void testClassUtilConstructor() throws NoSuchMethodException, InstantiationException, IllegalAccessException,
268             IllegalArgumentException, InvocationTargetException
269     {
270         Constructor<Sup>[] cArr = ClassUtil.getAllConstructors(Sup.class);
271         assertEquals(2, cArr.length);
272         // retrieve again from cache
273         cArr = ClassUtil.getAllConstructors(Sup.class);
274         assertEquals(2, cArr.length);
275         Constructor<Sup> c1 = ClassUtil.resolveConstructor(Sup.class, new Class<?>[] {String.class, int.class});
276         assertNotNull(c1);
277         // retrieve again from cache
278         Constructor<Sup> c1a = ClassUtil.resolveConstructor(Sup.class, new Class<?>[] {String.class, int.class});
279         assertEquals(c1, c1a);
280         Constructor<Sup> c2 = ClassUtil.resolveConstructor(Sup.class, new Object[] {"abc", 1, 2.0d});
281         assertNotNull(c2);
282         // retrieve again from cache
283         Constructor<Sup> c2a = ClassUtil.resolveConstructor(Sup.class, new Object[] {"abc", 1, 2.0d});
284         assertEquals(c2, c2a);
285         Constructor<Sub> c2b = ClassUtil.resolveConstructor(Sub.class, new Object[] {"abc", 1, 2.0d});
286         assertNotEquals(c2a, c2b);
287         assertTrue(ClassUtil.isMoreSpecific(c2b, c2a));
288         // XXX: look carefully at isMoreSpecific: assertFalse(ClassUtil.isMoreSpecific(c2a, c2b));
289         assertFalse(ClassUtil.isMoreSpecific(c2a, c1));
290         assertFalse(ClassUtil.isMoreSpecific(c1, c2a));
291         assertFalse(ClassUtil.isMoreSpecific(c1, c2b));
292 
293         UnitTest.testFail(new UnitTest.Execution()
294         {
295             @Override
296             public void execute() throws Throwable
297             {
298                 Constructor<Sup> cx = ClassUtil.resolveConstructor(Sup.class, new Class<?>[] {String.class, float.class});
299                 fail("illegal retrieval of constructor " + cx.toString());
300             }
301         });
302         UnitTest.testFail(new UnitTest.Execution()
303         {
304             @Override
305             public void execute() throws Throwable
306             {
307                 Constructor<Sup> cx = ClassUtil.resolveConstructor(Sup.class, new Object[] {1.2f});
308                 fail("illegal retrieval of constructor " + cx.toString());
309             }
310         });
311 
312         Sub sup = new Sub("a", 1);
313         Constructor<Sub.Inner> csi2 = ClassUtil.resolveConstructor(Sub.Inner.class, new Object[] {sup, 123L});
314         assertNotNull(csi2);
315     }
316 
317     /**
318      * Tests the ClassUtil Field lookup methods.
319      * @throws NoSuchFieldException on error
320      */
321     @Test
322     public void testClassUtilField() throws NoSuchFieldException
323     {
324         Set<Field> fSet = ClassUtil.getAllFields(Sup.class);
325         removeJacoco(fSet);
326         assertEquals(7, fSet.size());
327 
328         testField(Sup.class, "staticFinalString", Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC);
329         testField(Sup.class, "finalString", Modifier.FINAL, Modifier.PUBLIC);
330         testField(Sup.class, "publicInt", Modifier.PUBLIC);
331         testField(Sup.class, "protectedLong", Modifier.PROTECTED);
332         testField(Sup.class, "packageDouble");
333         testField(Sup.class, "privateFloat", Modifier.PRIVATE);
334         testField(Sup.class, "publicSubInt", Modifier.PUBLIC);
335 
336         testField(Sub.class, "staticFinalString", Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC);
337         testField(Sub.class, "finalString", Modifier.FINAL, Modifier.PUBLIC);
338         testField(Sub.class, "publicInt", Modifier.PUBLIC);
339         testField(Sub.class, "protectedLong", Modifier.PROTECTED);
340         testField(Sub.class, "packageDouble");
341         testField(Sub.class, "privateFloat", Modifier.PRIVATE);
342         testField(Sub.class, "publicSuperInt", Modifier.PUBLIC);
343         testField(Sub.class, "publicSubInt", Modifier.PUBLIC);
344 
345         Sub sup = new Sub("a", 1);
346         testField(sup, "staticFinalString", Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC);
347         testField(sup, "finalString", Modifier.FINAL, Modifier.PUBLIC);
348         testField(sup, "publicInt", Modifier.PUBLIC);
349         testField(sup, "protectedLong", Modifier.PROTECTED);
350         testField(sup, "packageDouble");
351         testField(sup, "privateFloat", Modifier.PRIVATE);
352         testField(sup, "publicSuperInt", Modifier.PUBLIC);
353         testField(sup, "publicSubInt", Modifier.PUBLIC);
354 
355         UnitTest.testFail(new UnitTest.Execution()
356         {
357             @Override
358             public void execute() throws Throwable
359             {
360                 testField(sup, "xyz", Modifier.PUBLIC);
361             }
362         });
363         UnitTest.testFail(new UnitTest.Execution()
364         {
365             @Override
366             public void execute() throws Throwable
367             {
368                 testField(Sub.class, "xyz", Modifier.PUBLIC);
369             }
370         });
371         Field f1 = ClassUtil.resolveField(Sub.class, this.getClass(), "publicInt");
372         assertNotNull(f1);
373         UnitTest.testFail(new UnitTest.Execution()
374         {
375             @Override
376             public void execute() throws Throwable
377             {
378                 Field f2 = ClassUtil.resolveField(Sub.class, this.getClass(), "privateFloat");
379                 fail("should not be able to resolve private field " + f2);
380             }
381         });
382 
383         Sub.Inner supInn = new Sub("a", 1).new Inner(12L);
384         testField(supInn, "publicInnerLong", Modifier.PUBLIC);
385         testField(supInn, "staticFinalString", Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC);
386         testField(supInn, "finalString", Modifier.FINAL, Modifier.PUBLIC);
387         testField(supInn, "publicInt", Modifier.PUBLIC);
388         testField(supInn, "protectedLong", Modifier.PROTECTED);
389         testField(supInn, "packageDouble");
390         testField(supInn, "privateFloat", Modifier.PRIVATE);
391         testField(supInn, "publicSuperInt", Modifier.PUBLIC);
392         testField(supInn, "publicSubInt", Modifier.PUBLIC);
393 
394         UnitTest.testFail(new UnitTest.Execution()
395         {
396             @Override
397             public void execute() throws Throwable
398             {
399                 Field f3 = ClassUtil.resolveField((Class<?>) null, "publicSubInt");
400                 fail("should not be able to resolve field from null object" + f3);
401             }
402         });
403         UnitTest.testFail(new UnitTest.Execution()
404         {
405             @Override
406             public void execute() throws Throwable
407             {
408                 Field f3 = ClassUtil.resolveField(Sub.class, null);
409                 fail("should not be able to resolve field from null object" + f3);
410             }
411         });
412         UnitTest.testFail(new UnitTest.Execution()
413         {
414             @Override
415             public void execute() throws Throwable
416             {
417                 Field f3 = ClassUtil.resolveField((Object) null, "publicSubInt");
418                 fail("should not be able to resolve field from null object" + f3);
419             }
420         });
421         UnitTest.testFail(new UnitTest.Execution()
422         {
423             @Override
424             public void execute() throws Throwable
425             {
426                 Field f3 = ClassUtil.resolveField(supInn, null);
427                 fail("should not be able to resolve field from null object" + f3);
428             }
429         });
430     }
431 
432     /**
433      * Tests the ClassUtil Method lookup methods.
434      * @throws NoSuchMethodException on error
435      */
436     @SuppressWarnings("checkstyle:methodlength")
437     @Test
438     public void testClassUtilMethod() throws NoSuchMethodException
439     {
440         List<Method> mList0 = ClassUtil.getAllMethods(Object.class);
441         removeJacoco(mList0);
442         List<Method> mList1 = ClassUtil.getAllMethods(Sup.class);
443         removeJacoco(mList1);
444         // 12 methods in Sub
445         assertEquals(mList0.size() + 12, mList1.size());
446         List<Method> mList2 = ClassUtil.getAllMethods(Sup.class, "privateVoid");
447         assertEquals(1, mList2.size());
448 
449         testMethod(Sup.class, "publicVoid", new Class<?>[] {}, Modifier.PUBLIC);
450         testMethod(Sup.class, "publicInt", new Class<?>[] {}, Modifier.PUBLIC);
451         testMethod(Sup.class, "publicArgs", new Class<?>[] {String.class, double.class}, Modifier.PUBLIC);
452         testMethod(Sup.class, "protectedVoid", new Class<?>[] {}, Modifier.PROTECTED);
453         testMethod(Sup.class, "protectedInt", new Class<?>[] {}, Modifier.PROTECTED);
454         testMethod(Sup.class, "protectedArgs", new Class<?>[] {String.class, double.class}, Modifier.PROTECTED);
455         testMethod(Sup.class, "packageVoid", new Class<?>[] {});
456         testMethod(Sup.class, "packageInt", new Class<?>[] {});
457         testMethod(Sup.class, "packageArgs", new Class<?>[] {String.class, double.class});
458         testMethod(Sup.class, "privateVoid", new Class<?>[] {}, Modifier.PRIVATE);
459         testMethod(Sup.class, "privateInt", new Class<?>[] {}, Modifier.PRIVATE);
460         testMethod(Sup.class, "privateArgs", new Class<?>[] {String.class, double.class}, Modifier.PRIVATE);
461 
462         testMethod(Sub.class, "publicVoid", new Class<?>[] {}, Modifier.PUBLIC);
463         testMethod(Sub.class, "publicInt", new Class<?>[] {}, Modifier.PUBLIC);
464         testMethod(Sub.class, "publicArgs", new Class<?>[] {String.class, double.class}, Modifier.PUBLIC);
465         testMethod(Sub.class, "publicArgs", new Class<?>[] {String.class}, Modifier.PUBLIC);
466         testMethod(Sub.class, "protectedVoid", new Class<?>[] {}, Modifier.PROTECTED);
467         testMethod(Sub.class, "protectedInt", new Class<?>[] {}, Modifier.PROTECTED);
468         testMethod(Sub.class, "protectedArgs", new Class<?>[] {String.class, double.class}, Modifier.PROTECTED);
469         testMethod(Sub.class, "packageVoid", new Class<?>[] {});
470         testMethod(Sub.class, "packageInt", new Class<?>[] {});
471         testMethod(Sub.class, "packageArgs", new Class<?>[] {String.class, double.class});
472         testMethod(Sub.class, "privateVoid", new Class<?>[] {}, Modifier.PRIVATE);
473         testMethod(Sub.class, "privateInt", new Class<?>[] {}, Modifier.PRIVATE);
474         testMethod(Sub.class, "privateArgs", new Class<?>[] {String.class, double.class}, Modifier.PRIVATE);
475 
476         Sub sup = new Sub("a", 1);
477         testMethod(sup, "publicVoid", new Class<?>[] {}, Modifier.PUBLIC);
478         testMethod(sup, "publicInt", new Class<?>[] {}, Modifier.PUBLIC);
479         testMethod(sup, "publicArgs", new Class<?>[] {String.class, double.class}, Modifier.PUBLIC);
480         testMethod(sup, "publicArgs", new Class<?>[] {String.class}, Modifier.PUBLIC);
481         testMethod(sup, "protectedVoid", new Class<?>[] {}, Modifier.PROTECTED);
482         testMethod(sup, "protectedInt", new Class<?>[] {}, Modifier.PROTECTED);
483         testMethod(sup, "protectedArgs", new Class<?>[] {String.class, double.class}, Modifier.PROTECTED);
484         testMethod(sup, "packageVoid", new Class<?>[] {});
485         testMethod(sup, "packageInt", new Class<?>[] {});
486         testMethod(sup, "packageArgs", new Class<?>[] {String.class, double.class});
487         testMethod(sup, "privateVoid", new Class<?>[] {}, Modifier.PRIVATE);
488         testMethod(sup, "privateInt", new Class<?>[] {}, Modifier.PRIVATE);
489         testMethod(sup, "privateArgs", new Class<?>[] {String.class, double.class}, Modifier.PRIVATE);
490 
491         testMethod(sup, "publicVoid", new Object[] {}, Modifier.PUBLIC);
492         testMethod(sup, "publicInt", new Object[] {}, Modifier.PUBLIC);
493         testMethod(sup, "publicArgs", new Object[] {"abc", 12.0d}, Modifier.PUBLIC);
494         testMethod(sup, "publicArgs", new Object[] {"def"}, Modifier.PUBLIC);
495         testMethod(sup, "protectedVoid", new Object[] {}, Modifier.PROTECTED);
496         testMethod(sup, "protectedInt", new Object[] {}, Modifier.PROTECTED);
497         testMethod(sup, "protectedArgs", new Object[] {"ghi", 13.0d}, Modifier.PROTECTED);
498         testMethod(sup, "packageVoid", new Object[] {});
499         testMethod(sup, "packageInt", new Object[] {});
500         testMethod(sup, "packageArgs", new Object[] {"jkl", 14.0d});
501         testMethod(sup, "privateVoid", new Object[] {}, Modifier.PRIVATE);
502         testMethod(sup, "privateInt", new Object[] {}, Modifier.PRIVATE);
503         testMethod(sup, "privateArgs", new Object[] {"mno", 15.0d}, Modifier.PRIVATE);
504 
505         UnitTest.testFail(new UnitTest.Execution()
506         {
507             @Override
508             public void execute() throws Throwable
509             {
510                 testMethod(sup, "xyz", new Class<?>[] {}, Modifier.PUBLIC);
511             }
512         });
513         UnitTest.testFail(new UnitTest.Execution()
514         {
515             @Override
516             public void execute() throws Throwable
517             {
518                 testMethod(sup, "publicArgs", new Class<?>[] {float.class}, Modifier.PUBLIC);
519             }
520         });
521         UnitTest.testFail(new UnitTest.Execution()
522         {
523             @Override
524             public void execute() throws Throwable
525             {
526                 testMethod(sup, "xyz", new Object[] {}, Modifier.PUBLIC);
527             }
528         });
529         UnitTest.testFail(new UnitTest.Execution()
530         {
531             @Override
532             public void execute() throws Throwable
533             {
534                 testMethod(sup, "publicArgs", new Object[] {12L}, Modifier.PUBLIC);
535             }
536         });
537         UnitTest.testFail(new UnitTest.Execution()
538         {
539             @Override
540             public void execute() throws Throwable
541             {
542                 testMethod(Sub.class, "xyz", new Class<?>[] {}, Modifier.PUBLIC);
543             }
544         });
545         UnitTest.testFail(new UnitTest.Execution()
546         {
547             @Override
548             public void execute() throws Throwable
549             {
550                 testMethod(Sub.class, "publicArgs", new Class<?>[] {float.class}, Modifier.PUBLIC);
551             }
552         });
553 
554         Method m1 = ClassUtil.resolveMethod(Sub.class, this.getClass(), "publicArgs", new Class<?>[] {String.class});
555         assertNotNull(m1);
556         UnitTest.testFail(new UnitTest.Execution()
557         {
558             @Override
559             public void execute() throws Throwable
560             {
561                 Method m2 = ClassUtil.resolveMethod(Sub.class, this.getClass(), "privateArgs", new Class<?>[] {});
562                 fail("should not be able to resolve private field " + m2);
563             }
564         });
565 
566         Sub.Inner supInn = new Sub("a", 1).new Inner(12L);
567         testMethod(supInn, "publicInnerMethod", new Class<?>[] {long.class}, Modifier.PUBLIC);
568         testMethod(supInn, "publicVoid", new Class<?>[] {}, Modifier.PUBLIC);
569         testMethod(supInn, "publicInt", new Class<?>[] {}, Modifier.PUBLIC);
570         testMethod(supInn, "publicArgs", new Class<?>[] {String.class, double.class}, Modifier.PUBLIC);
571         testMethod(supInn, "publicArgs", new Class<?>[] {String.class}, Modifier.PUBLIC);
572         testMethod(supInn, "protectedVoid", new Class<?>[] {}, Modifier.PROTECTED);
573         testMethod(supInn, "protectedInt", new Class<?>[] {}, Modifier.PROTECTED);
574         testMethod(supInn, "protectedArgs", new Class<?>[] {String.class, double.class}, Modifier.PROTECTED);
575         testMethod(supInn, "packageVoid", new Class<?>[] {});
576         testMethod(supInn, "packageInt", new Class<?>[] {});
577         testMethod(supInn, "packageArgs", new Class<?>[] {String.class, double.class});
578         testMethod(supInn, "privateVoid", new Class<?>[] {}, Modifier.PRIVATE);
579         testMethod(supInn, "privateInt", new Class<?>[] {}, Modifier.PRIVATE);
580         testMethod(supInn, "privateArgs", new Class<?>[] {String.class, double.class}, Modifier.PRIVATE);
581 
582         testMethod(supInn, "publicInnerMethod", new Object[] {12L}, Modifier.PUBLIC);
583         testMethod(supInn, "publicVoid", new Object[] {}, Modifier.PUBLIC);
584         testMethod(supInn, "publicInt", new Object[] {}, Modifier.PUBLIC);
585         testMethod(supInn, "publicArgs", new Object[] {"abc", 12.0d}, Modifier.PUBLIC);
586         testMethod(supInn, "publicArgs", new Object[] {"def"}, Modifier.PUBLIC);
587         testMethod(supInn, "protectedVoid", new Object[] {}, Modifier.PROTECTED);
588         testMethod(supInn, "protectedInt", new Object[] {}, Modifier.PROTECTED);
589         testMethod(supInn, "protectedArgs", new Object[] {"ghi", 13.0d}, Modifier.PROTECTED);
590         testMethod(supInn, "packageVoid", new Object[] {});
591         testMethod(supInn, "packageInt", new Object[] {});
592         testMethod(supInn, "packageArgs", new Object[] {"jkl", 14.0d});
593         testMethod(supInn, "privateVoid", new Object[] {}, Modifier.PRIVATE);
594         testMethod(supInn, "privateInt", new Object[] {}, Modifier.PRIVATE);
595         testMethod(supInn, "privateArgs", new Object[] {"mno", 15.0d}, Modifier.PRIVATE);
596 
597         UnitTest.testFail(new UnitTest.Execution()
598         {
599             @Override
600             public void execute() throws Throwable
601             {
602                 Method m3 = ClassUtil.resolveMethod((Class<?>) null, "publicInt", new Class<?>[] {});
603                 fail("should not be able to resolve field from null object" + m3);
604             }
605         });
606         UnitTest.testFail(new UnitTest.Execution()
607         {
608             @Override
609             public void execute() throws Throwable
610             {
611                 Method m3 = ClassUtil.resolveMethod(Sub.class, null, new Class<?>[] {});
612                 fail("should not be able to resolve field from null object" + m3);
613             }
614         });
615         UnitTest.testFail(new UnitTest.Execution()
616         {
617             @Override
618             public void execute() throws Throwable
619             {
620                 Method m3 = ClassUtil.resolveMethod((Object) null, "publicInt", new Class<?>[] {});
621                 fail("should not be able to resolve field from null object" + m3);
622             }
623         });
624         UnitTest.testFail(new UnitTest.Execution()
625         {
626             @Override
627             public void execute() throws Throwable
628             {
629                 Method m3 = ClassUtil.resolveMethod(supInn, null, new Class<?>[] {});
630                 fail("should not be able to resolve field from null object" + m3);
631             }
632         });
633         UnitTest.testFail(new UnitTest.Execution()
634         {
635             @Override
636             public void execute() throws Throwable
637             {
638                 Method m3 = ClassUtil.resolveMethod((Object) null, "publicInt", new Object[] {});
639                 fail("should not be able to resolve field from null object" + m3);
640             }
641         });
642         UnitTest.testFail(new UnitTest.Execution()
643         {
644             @Override
645             public void execute() throws Throwable
646             {
647                 Method m3 = ClassUtil.resolveMethod(supInn, null, new Object[] {});
648                 fail("should not be able to resolve field from null object" + m3);
649             }
650         });
651     }
652 
653     /**
654      * Tests the ClassUtil Annotation lookup methods.
655      */
656     @Test
657     public void testClassUtilAnnotation()
658     {
659         Set<Annotation> aSet0 = ClassUtil.getAllAnnotations(Sub.class);
660         assertEquals(2, aSet0.size());
661 
662         Annotation a1 = ClassUtil.resolveAnnotation(Sub.class, AnnTag.class);
663         assertNotNull(a1);
664         UnitTest.testFail(new UnitTest.Execution()
665         {
666             @Override
667             public void execute() throws Throwable
668             {
669                 Annotation a3 = ClassUtil.resolveAnnotation(Sup.class, AnnTag.class);
670                 fail("should not be able to resolve non-existing annotation " + a3);
671             }
672         });
673         UnitTest.testFail(new UnitTest.Execution()
674         {
675             @Override
676             public void execute() throws Throwable
677             {
678                 Annotation a3 = ClassUtil.resolveAnnotation(null, AnnTag.class);
679                 fail("should not be able to resolve non-existing annotation " + a3);
680             }
681         });
682         UnitTest.testFail(new UnitTest.Execution()
683         {
684             @Override
685             public void execute() throws Throwable
686             {
687                 Annotation a3 = ClassUtil.resolveAnnotation(Sub.class, null);
688                 fail("should not be able to resolve non-existing annotation " + a3);
689             }
690         });
691 
692         Annotation a4 = ClassUtil.resolveAnnotation(Sub.Inner.class, AnnTag.class);
693         assertNotNull(a4);
694 
695         AnnString a5 = (AnnString) ClassUtil.resolveAnnotation(Sub.class, AnnString.class);
696         assertNotNull(a5);
697         assertEquals("avalue", a5.value());
698     }
699 
700     /**
701      * Clean up the fields to remove jacoco / debugger artifacts.
702      * @param fields the set to clean
703      */
704     private void removeJacoco(final Set<Field> fields)
705     {
706         for (Iterator<Field> it = fields.iterator(); it.hasNext();)
707         {
708             Field field = it.next();
709             if (field.getName().contains("jacoco"))
710             {
711                 it.remove();
712             }
713         }
714     }
715 
716     /**
717      * @param clazz the class to test
718      * @param fieldName the field to retrieve
719      * @param modifiers the expected modifiers
720      * @throws NoSuchFieldException on error
721      */
722     protected void testField(final Class<?> clazz, final String fieldName, final int... modifiers) throws NoSuchFieldException
723     {
724         Field field = ClassUtil.resolveField(clazz, fieldName);
725         assertNotNull(field);
726         assertEquals(fieldName, field.getName());
727         Set<Integer> mSet = new HashSet<>();
728         for (int m : modifiers)
729         {
730             assertTrue((field.getModifiers() & m) != 0,
731                     "failed modifier for field " + clazz.getSimpleName() + "." + fieldName + " for modifier " + m);
732             mSet.add(m);
733         }
734         for (int p = 0; p < 12; p++)
735         {
736             int m = 1 << p;
737             if (!mSet.contains(m))
738             {
739                 assertTrue((field.getModifiers() & m) == 0,
740                         "failed modifier for field " + clazz.getSimpleName() + "." + fieldName + " for bit " + p);
741             }
742         }
743     }
744 
745     /**
746      * @param object the object to test
747      * @param fieldName the field to retrieve
748      * @param modifiers the expected modifiers
749      * @throws NoSuchFieldException on error
750      */
751     protected void testField(final Object object, final String fieldName, final int... modifiers) throws NoSuchFieldException
752     {
753         Field field = ClassUtil.resolveField(object, fieldName);
754         assertNotNull(field);
755         assertEquals(fieldName, field.getName());
756         Set<Integer> mSet = new HashSet<>();
757         for (int m : modifiers)
758         {
759             assertTrue((field.getModifiers() & m) != 0,
760                     "failed modifier for field " + object.getClass().getSimpleName() + "." + fieldName + " for modifier " + m);
761             mSet.add(m);
762         }
763         for (int p = 0; p < 12; p++)
764         {
765             int m = 1 << p;
766             if (!mSet.contains(m))
767             {
768                 assertTrue((field.getModifiers() & m) == 0,
769                         "failed modifier for field " + object.getClass().getSimpleName() + "." + fieldName + " for bit " + p);
770             }
771         }
772     }
773 
774     /**
775      * Clean up the fields to remove jacoco / debugger artifacts.
776      * @param methods the set to clean
777      */
778     private void removeJacoco(final List<Method> methods)
779     {
780         for (Iterator<Method> it = methods.iterator(); it.hasNext();)
781         {
782             Method method = it.next();
783             if (method.getName().contains("jacoco"))
784             {
785                 it.remove();
786             }
787         }
788     }
789 
790     /**
791      * @param clazz the class to test
792      * @param methodName the method to retrieve
793      * @param params the parameters of the method as a class array
794      * @param modifiers the expected modifiers
795      * @throws NoSuchMethodException on error
796      */
797     protected void testMethod(final Class<?> clazz, final String methodName, final Class<?>[] params, final int... modifiers)
798             throws NoSuchMethodException
799     {
800         Method method = ClassUtil.resolveMethod(clazz, methodName, params);
801         assertNotNull(method);
802         assertEquals(methodName, method.getName());
803         Set<Integer> mSet = new HashSet<>();
804         for (int m : modifiers)
805         {
806             assertTrue((method.getModifiers() & m) != 0,
807                     "failed modifier for method " + clazz.getSimpleName() + "." + methodName + " for modifier " + m);
808             mSet.add(m);
809         }
810         for (int p = 0; p < 12; p++)
811         {
812             int m = 1 << p;
813             if (!mSet.contains(m))
814             {
815                 assertTrue((method.getModifiers() & m) == 0,
816                         "failed modifier for method " + clazz.getSimpleName() + "." + methodName + " for bit " + p);
817             }
818         }
819     }
820 
821     /**
822      * @param object the object to test
823      * @param methodName the method to retrieve
824      * @param params the parameters of the method as a class array
825      * @param modifiers the expected modifiers
826      * @throws NoSuchMethodException on error
827      */
828     protected void testMethod(final Object object, final String methodName, final Class<?>[] params, final int... modifiers)
829             throws NoSuchMethodException
830     {
831         Method method = ClassUtil.resolveMethod(object, methodName, params);
832         assertNotNull(method);
833         assertEquals(methodName, method.getName());
834         Set<Integer> mSet = new HashSet<>();
835         for (int m : modifiers)
836         {
837             assertTrue((method.getModifiers() & m) != 0, "failed modifier for method " + object.getClass().getSimpleName() + "."
838                     + methodName + " for modifier " + m);
839             mSet.add(m);
840         }
841         for (int p = 0; p < 12; p++)
842         {
843             int m = 1 << p;
844             if (!mSet.contains(m))
845             {
846                 assertTrue((method.getModifiers() & m) == 0,
847                         "failed modifier for method " + object.getClass().getSimpleName() + "." + methodName + " for bit " + p);
848             }
849         }
850     }
851 
852     /**
853      * @param object the object to test
854      * @param methodName the method to retrieve
855      * @param args the parameters of the method as an object array
856      * @param modifiers the expected modifiers
857      * @throws NoSuchMethodException on error
858      */
859     protected void testMethod(final Object object, final String methodName, final Object[] args, final int... modifiers)
860             throws NoSuchMethodException
861     {
862         Method method = ClassUtil.resolveMethod(object, methodName, args);
863         assertNotNull(method);
864         assertEquals(methodName, method.getName());
865         Set<Integer> mSet = new HashSet<>();
866         for (int m : modifiers)
867         {
868             assertTrue((method.getModifiers() & m) != 0, "failed modifier for method " + object.getClass().getSimpleName() + "."
869                     + methodName + " for modifier " + m);
870             mSet.add(m);
871         }
872         for (int p = 0; p < 12; p++)
873         {
874             int m = 1 << p;
875             if (!mSet.contains(m))
876             {
877                 assertTrue((method.getModifiers() & m) == 0,
878                         "failed modifier for method " + object.getClass().getSimpleName() + "." + methodName + " for bit " + p);
879             }
880         }
881     }
882 
883     /** subclass with fields and methods. */
884     @SuppressWarnings("unused")
885     protected static class Sup
886     {
887         /** */
888         @SuppressWarnings({"checkstyle:visibilitymodifier", "checkstyle:constantname"})
889         public static final String staticFinalString = "ABC";
890 
891         /** */
892         @SuppressWarnings("checkstyle:visibilitymodifier")
893         public final String finalString = "DEF";
894 
895         /** */
896         @SuppressWarnings("checkstyle:visibilitymodifier")
897         private float privateFloat = 8.0f;
898 
899         /** */
900         @SuppressWarnings("checkstyle:visibilitymodifier")
901         double packageDouble = 4.0d;
902 
903         /** */
904         @SuppressWarnings("checkstyle:visibilitymodifier")
905         protected long protectedLong = 2L;
906 
907         /** */
908         @SuppressWarnings("checkstyle:visibilitymodifier")
909         public int publicInt = 1;
910 
911         /** */
912         @SuppressWarnings("checkstyle:visibilitymodifier")
913         public int publicSubInt = 21;
914 
915         /**
916          * @param s String
917          * @param i int
918          * @param d double
919          */
920         public Sup(final String s, final int i, final double d)
921         {
922             //
923         }
924 
925         /**
926          * @param s String
927          * @param i int
928          */
929         public Sup(final String s, final int i)
930         {
931             //
932         }
933 
934         /** */
935         private void privateVoid()
936         {
937             //
938         }
939 
940         /** */
941         void packageVoid()
942         {
943             //
944         }
945 
946         /** */
947         protected void protectedVoid()
948         {
949             //
950         }
951 
952         /** */
953         public void publicVoid()
954         {
955             //
956         }
957 
958         /**
959          * @return int
960          */
961         private int privateInt()
962         {
963             return 1;
964         }
965 
966         /**
967          * @return int
968          */
969         int packageInt()
970         {
971             return 2;
972         }
973 
974         /**
975          * @return int
976          */
977         protected int protectedInt()
978         {
979             return 3;
980         }
981 
982         /**
983          * @return int
984          */
985         public int publicInt()
986         {
987             return 4;
988         }
989 
990         /**
991          * @param s String
992          * @param d double
993          */
994         private void privateArgs(final String s, final double d)
995         {
996             //
997         }
998 
999         /**
1000          * @param s String
1001          * @param d double
1002          */
1003         void packageArgs(final String s, final double d)
1004         {
1005             //
1006         }
1007 
1008         /**
1009          * @param s String
1010          * @param d double
1011          */
1012         protected void protectedArgs(final String s, final double d)
1013         {
1014             //
1015         }
1016 
1017         /**
1018          * @param s String
1019          * @param d double
1020          */
1021         public void publicArgs(final String s, final double d)
1022         {
1023             //
1024         }
1025     }
1026 
1027     /** superclass with fields and methods. */
1028     @SuppressWarnings({"hiding", "unused"})
1029     @AnnTag
1030     @AnnString("avalue")
1031     protected static class Sub extends Sup
1032     {
1033         /** */
1034         @SuppressWarnings({"checkstyle:visibilitymodifier", "checkstyle:constantname"})
1035         public static final String staticFinalString = "ABC";
1036 
1037         /** */
1038         @SuppressWarnings("checkstyle:visibilitymodifier")
1039         public final String finalString = "DEF";
1040 
1041         /** */
1042         @SuppressWarnings("checkstyle:visibilitymodifier")
1043         private float privateFloat = 8.0f;
1044 
1045         /** */
1046         @SuppressWarnings("checkstyle:visibilitymodifier")
1047         double packageDouble = 4.0d;
1048 
1049         /** */
1050         @SuppressWarnings("checkstyle:visibilitymodifier")
1051         protected long protectedLong = 2L;
1052 
1053         /** */
1054         @SuppressWarnings("checkstyle:visibilitymodifier")
1055         public int publicInt = 1;
1056 
1057         /** */
1058         @SuppressWarnings("checkstyle:visibilitymodifier")
1059         public int publicSuperInt = 11;
1060 
1061         /**
1062          * @param s String
1063          * @param i int
1064          * @param d double
1065          */
1066         public Sub(final String s, final int i, final double d)
1067         {
1068             super(s, i, d);
1069         }
1070 
1071         /**
1072          * @param s String
1073          * @param i int
1074          */
1075         public Sub(final String s, final int i)
1076         {
1077             super(s, i);
1078         }
1079 
1080         /** */
1081         private void privateVoid()
1082         {
1083             //
1084         }
1085 
1086         @Override
1087         void packageVoid()
1088         {
1089             //
1090         }
1091 
1092         @Override
1093         protected void protectedVoid()
1094         {
1095             //
1096         }
1097 
1098         @Override
1099         public void publicVoid()
1100         {
1101             //
1102         }
1103 
1104         /**
1105          * @return int
1106          */
1107         private int privateInt()
1108         {
1109             return 1;
1110         }
1111 
1112         @Override
1113         int packageInt()
1114         {
1115             return 2;
1116         }
1117 
1118         @Override
1119         protected int protectedInt()
1120         {
1121             return 3;
1122         }
1123 
1124         @Override
1125         public int publicInt()
1126         {
1127             return 4;
1128         }
1129 
1130         /**
1131          * @param s string
1132          * @param d double
1133          */
1134         private void privateArgs(final String s, final double d)
1135         {
1136             //
1137         }
1138 
1139         @Override
1140         void packageArgs(final String s, final double d)
1141         {
1142             //
1143         }
1144 
1145         @Override
1146         protected void protectedArgs(final String s, final double d)
1147         {
1148             //
1149         }
1150 
1151         @Override
1152         public void publicArgs(final String s, final double d)
1153         {
1154             //
1155         }
1156 
1157         /**
1158          * @param s String
1159          */
1160         public void publicArgs(final String s)
1161         {
1162             //
1163         }
1164 
1165         /** non-static inner class. */
1166         @AnnTag
1167         public class Inner
1168         {
1169             /**
1170              * @param l long
1171              */
1172             Inner(final long l)
1173             {
1174                 //
1175             }
1176 
1177             /** */
1178             @SuppressWarnings("checkstyle:visibilitymodifier")
1179             public long publicInnerLong = 123L;
1180 
1181             /**
1182              * @param l long
1183              */
1184             public void publicInnerMethod(final long l)
1185             {
1186                 //
1187             }
1188         }
1189     }
1190 
1191     /** annotation class. */
1192     @Target(ElementType.TYPE)
1193     @Retention(RetentionPolicy.RUNTIME)
1194     public @interface AnnTag
1195     {
1196         // tagging annotation
1197     }
1198 
1199     /** annotation class with String parameter. */
1200     @Target(ElementType.TYPE)
1201     @Retention(RetentionPolicy.RUNTIME)
1202     public @interface AnnString
1203     {
1204         /** @return the value */
1205         String value();
1206     }
1207 
1208 }