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.exceptions.Try;
28  import org.djutils.reflection.TestClass.InnerPublic;
29  import org.junit.jupiter.api.Test;
30  
31  /**
32   * The JUNIT Test for <code>ClassUtil</code>.
33   * <p>
34   * Copyright (c) 2002-2024 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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.Execution()
356         {
357             @Override
358             public void execute() throws Throwable
359             {
360                 testField(sup, "xyz", Modifier.PUBLIC);
361             }
362         });
363         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.Execution()
506         {
507             @Override
508             public void execute() throws Throwable
509             {
510                 testMethod(sup, "xyz", new Class<?>[] {}, Modifier.PUBLIC);
511             }
512         });
513         Try.testFail(new Try.Execution()
514         {
515             @Override
516             public void execute() throws Throwable
517             {
518                 testMethod(sup, "publicArgs", new Class<?>[] {float.class}, Modifier.PUBLIC);
519             }
520         });
521         Try.testFail(new Try.Execution()
522         {
523             @Override
524             public void execute() throws Throwable
525             {
526                 testMethod(sup, "xyz", new Object[] {}, Modifier.PUBLIC);
527             }
528         });
529         Try.testFail(new Try.Execution()
530         {
531             @Override
532             public void execute() throws Throwable
533             {
534                 testMethod(sup, "publicArgs", new Object[] {12L}, Modifier.PUBLIC);
535             }
536         });
537         Try.testFail(new Try.Execution()
538         {
539             @Override
540             public void execute() throws Throwable
541             {
542                 testMethod(Sub.class, "xyz", new Class<?>[] {}, Modifier.PUBLIC);
543             }
544         });
545         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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         Try.testFail(new Try.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(
760                     (field.getModifiers() & m) != 0,
761                     "failed modifier for field " + object.getClass().getSimpleName() + "." + fieldName + " for modifier " + m);
762             mSet.add(m);
763         }
764         for (int p = 0; p < 12; p++)
765         {
766             int m = 1 << p;
767             if (!mSet.contains(m))
768             {
769                 assertTrue((field.getModifiers() & m) == 0,
770                         "failed modifier for field " + object.getClass().getSimpleName() + "." + fieldName + " for bit " + p);
771             }
772         }
773     }
774 
775     /**
776      * Clean up the fields to remove jacoco / debugger artifacts.
777      * @param methods the set to clean
778      */
779     private void removeJacoco(final List<Method> methods)
780     {
781         for (Iterator<Method> it = methods.iterator(); it.hasNext();)
782         {
783             Method method = it.next();
784             if (method.getName().contains("jacoco"))
785             {
786                 it.remove();
787             }
788         }
789     }
790 
791     /**
792      * @param clazz the class to test
793      * @param methodName the method to retrieve
794      * @param params the parameters of the method as a class array
795      * @param modifiers the expected modifiers
796      * @throws NoSuchMethodException on error
797      */
798     protected void testMethod(final Class<?> clazz, final String methodName, final Class<?>[] params, final int... modifiers)
799             throws NoSuchMethodException
800     {
801         Method method = ClassUtil.resolveMethod(clazz, methodName, params);
802         assertNotNull(method);
803         assertEquals(methodName, method.getName());
804         Set<Integer> mSet = new HashSet<>();
805         for (int m : modifiers)
806         {
807             assertTrue((method.getModifiers() & m) != 0,
808                     "failed modifier for method " + clazz.getSimpleName() + "." + methodName + " for modifier " + m);
809             mSet.add(m);
810         }
811         for (int p = 0; p < 12; p++)
812         {
813             int m = 1 << p;
814             if (!mSet.contains(m))
815             {
816                 assertTrue((method.getModifiers() & m) == 0,
817                         "failed modifier for method " + clazz.getSimpleName() + "." + methodName + " for bit " + p);
818             }
819         }
820     }
821 
822     /**
823      * @param object the object to test
824      * @param methodName the method to retrieve
825      * @param params the parameters of the method as a class array
826      * @param modifiers the expected modifiers
827      * @throws NoSuchMethodException on error
828      */
829     protected void testMethod(final Object object, final String methodName, final Class<?>[] params, final int... modifiers)
830             throws NoSuchMethodException
831     {
832         Method method = ClassUtil.resolveMethod(object, methodName, params);
833         assertNotNull(method);
834         assertEquals(methodName, method.getName());
835         Set<Integer> mSet = new HashSet<>();
836         for (int m : modifiers)
837         {
838             assertTrue(
839                     (method.getModifiers() & m) != 0,
840                     "failed modifier for method " + object.getClass().getSimpleName() + "." + methodName + " for modifier " + m);
841             mSet.add(m);
842         }
843         for (int p = 0; p < 12; p++)
844         {
845             int m = 1 << p;
846             if (!mSet.contains(m))
847             {
848                 assertTrue(
849                         (method.getModifiers() & m) == 0,
850                         "failed modifier for method " + object.getClass().getSimpleName() + "." + methodName + " for bit " + p);
851             }
852         }
853     }
854 
855     /**
856      * @param object the object to test
857      * @param methodName the method to retrieve
858      * @param args the parameters of the method as an object array
859      * @param modifiers the expected modifiers
860      * @throws NoSuchMethodException on error
861      */
862     protected void testMethod(final Object object, final String methodName, final Object[] args, final int... modifiers)
863             throws NoSuchMethodException
864     {
865         Method method = ClassUtil.resolveMethod(object, methodName, args);
866         assertNotNull(method);
867         assertEquals(methodName, method.getName());
868         Set<Integer> mSet = new HashSet<>();
869         for (int m : modifiers)
870         {
871             assertTrue(
872                     (method.getModifiers() & m) != 0,
873                     "failed modifier for method " + object.getClass().getSimpleName() + "." + methodName + " for modifier " + m);
874             mSet.add(m);
875         }
876         for (int p = 0; p < 12; p++)
877         {
878             int m = 1 << p;
879             if (!mSet.contains(m))
880             {
881                 assertTrue(
882                         (method.getModifiers() & m) == 0,
883                         "failed modifier for method " + object.getClass().getSimpleName() + "." + methodName + " for bit " + p);
884             }
885         }
886     }
887 
888     /** subclass with fields and methods. */
889     @SuppressWarnings("unused")
890     protected static class Sup
891     {
892         /** */
893         @SuppressWarnings({"checkstyle:visibilitymodifier", "checkstyle:constantname"})
894         public static final String staticFinalString = "ABC";
895 
896         /** */
897         @SuppressWarnings("checkstyle:visibilitymodifier")
898         public final String finalString = "DEF";
899 
900         /** */
901         @SuppressWarnings("checkstyle:visibilitymodifier")
902         private float privateFloat = 8.0f;
903 
904         /** */
905         @SuppressWarnings("checkstyle:visibilitymodifier")
906         double packageDouble = 4.0d;
907 
908         /** */
909         @SuppressWarnings("checkstyle:visibilitymodifier")
910         protected long protectedLong = 2L;
911 
912         /** */
913         @SuppressWarnings("checkstyle:visibilitymodifier")
914         public int publicInt = 1;
915 
916         /** */
917         @SuppressWarnings("checkstyle:visibilitymodifier")
918         public int publicSubInt = 21;
919 
920         /**
921          * @param s String
922          * @param i int
923          * @param d double
924          */
925         public Sup(final String s, final int i, final double d)
926         {
927             //
928         }
929 
930         /**
931          * @param s String
932          * @param i int
933          */
934         public Sup(final String s, final int i)
935         {
936             //
937         }
938 
939         /** */
940         private void privateVoid()
941         {
942             //
943         }
944 
945         /** */
946         void packageVoid()
947         {
948             //
949         }
950 
951         /** */
952         protected void protectedVoid()
953         {
954             //
955         }
956 
957         /** */
958         public void publicVoid()
959         {
960             //
961         }
962 
963         /**
964          * @return int
965          */
966         private int privateInt()
967         {
968             return 1;
969         }
970 
971         /**
972          * @return int
973          */
974         int packageInt()
975         {
976             return 2;
977         }
978 
979         /**
980          * @return int
981          */
982         protected int protectedInt()
983         {
984             return 3;
985         }
986 
987         /**
988          * @return int
989          */
990         public int publicInt()
991         {
992             return 4;
993         }
994 
995         /**
996          * @param s String
997          * @param d double
998          */
999         private void privateArgs(final String s, final double d)
1000         {
1001             //
1002         }
1003 
1004         /**
1005          * @param s String
1006          * @param d double
1007          */
1008         void packageArgs(final String s, final double d)
1009         {
1010             //
1011         }
1012 
1013         /**
1014          * @param s String
1015          * @param d double
1016          */
1017         protected void protectedArgs(final String s, final double d)
1018         {
1019             //
1020         }
1021 
1022         /**
1023          * @param s String
1024          * @param d double
1025          */
1026         public void publicArgs(final String s, final double d)
1027         {
1028             //
1029         }
1030     }
1031 
1032     /** superclass with fields and methods. */
1033     @SuppressWarnings({"hiding", "unused"})
1034     @AnnTag
1035     @AnnString("avalue")
1036     protected static class Sub extends Sup
1037     {
1038         /** */
1039         @SuppressWarnings({"checkstyle:visibilitymodifier", "checkstyle:constantname"})
1040         public static final String staticFinalString = "ABC";
1041 
1042         /** */
1043         @SuppressWarnings("checkstyle:visibilitymodifier")
1044         public final String finalString = "DEF";
1045 
1046         /** */
1047         @SuppressWarnings("checkstyle:visibilitymodifier")
1048         private float privateFloat = 8.0f;
1049 
1050         /** */
1051         @SuppressWarnings("checkstyle:visibilitymodifier")
1052         double packageDouble = 4.0d;
1053 
1054         /** */
1055         @SuppressWarnings("checkstyle:visibilitymodifier")
1056         protected long protectedLong = 2L;
1057 
1058         /** */
1059         @SuppressWarnings("checkstyle:visibilitymodifier")
1060         public int publicInt = 1;
1061 
1062         /** */
1063         @SuppressWarnings("checkstyle:visibilitymodifier")
1064         public int publicSuperInt = 11;
1065 
1066         /**
1067          * @param s String
1068          * @param i int
1069          * @param d double
1070          */
1071         public Sub(final String s, final int i, final double d)
1072         {
1073             super(s, i, d);
1074         }
1075 
1076         /**
1077          * @param s String
1078          * @param i int
1079          */
1080         public Sub(final String s, final int i)
1081         {
1082             super(s, i);
1083         }
1084 
1085         /** */
1086         private void privateVoid()
1087         {
1088             //
1089         }
1090 
1091         @Override
1092         void packageVoid()
1093         {
1094             //
1095         }
1096 
1097         @Override
1098         protected void protectedVoid()
1099         {
1100             //
1101         }
1102 
1103         @Override
1104         public void publicVoid()
1105         {
1106             //
1107         }
1108 
1109         /**
1110          * @return int
1111          */
1112         private int privateInt()
1113         {
1114             return 1;
1115         }
1116 
1117         @Override
1118         int packageInt()
1119         {
1120             return 2;
1121         }
1122 
1123         @Override
1124         protected int protectedInt()
1125         {
1126             return 3;
1127         }
1128 
1129         @Override
1130         public int publicInt()
1131         {
1132             return 4;
1133         }
1134 
1135         /**
1136          * @param s string
1137          * @param d double
1138          */
1139         private void privateArgs(final String s, final double d)
1140         {
1141             //
1142         }
1143 
1144         @Override
1145         void packageArgs(final String s, final double d)
1146         {
1147             //
1148         }
1149 
1150         @Override
1151         protected void protectedArgs(final String s, final double d)
1152         {
1153             //
1154         }
1155 
1156         @Override
1157         public void publicArgs(final String s, final double d)
1158         {
1159             //
1160         }
1161 
1162         /**
1163          * @param s String
1164          */
1165         public void publicArgs(final String s)
1166         {
1167             //
1168         }
1169 
1170         /** non-static inner class. */
1171         @AnnTag
1172         public class Inner
1173         {
1174             /**
1175              * @param l long
1176              */
1177             Inner(final long l)
1178             {
1179                 //
1180             }
1181 
1182             /** */
1183             @SuppressWarnings("checkstyle:visibilitymodifier")
1184             public long publicInnerLong = 123L;
1185 
1186             /**
1187              * @param l long
1188              */
1189             public void publicInnerMethod(final long l)
1190             {
1191                 //
1192             }
1193         }
1194     }
1195 
1196     /** annotation class. */
1197     @Target(ElementType.TYPE)
1198     @Retention(RetentionPolicy.RUNTIME)
1199     public @interface AnnTag
1200     {
1201         // tagging annotation
1202     }
1203 
1204     /** annotation class with String parameter. */
1205     @Target(ElementType.TYPE)
1206     @Retention(RetentionPolicy.RUNTIME)
1207     public @interface AnnString
1208     {
1209         /** @return String; the value */
1210         String value();
1211     }
1212 
1213 }