View Javadoc
1   package org.djunits;
2   
3   import static org.junit.jupiter.api.Assertions.assertEquals;
4   import static org.junit.jupiter.api.Assertions.fail;
5   
6   import java.io.Serializable;
7   import java.lang.reflect.Method;
8   import java.lang.reflect.Modifier;
9   import java.util.Collection;
10  
11  import org.junit.jupiter.api.Test;
12  
13  /**
14   * Verify that all classes have a toString method (unless the class in non-instantiable, or an enum, or abstract. <br>
15   * Verify that no class overrides equals without overriding hashCode. <br>
16   * Verify that classes that can be instantiated are Serializable for those classes that this makes sense.
17   * <p>
18   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
19   * BSD-style license. See <a href="https://djunits.org/docs/license.html">DJUNITS License</a>.
20   * </p>
21   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Apr 13, 2016 <br>
22   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
23   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
24   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
25   */
26  public class VerifyRequiredMethods
27  {
28      /**
29       * Name of the top level package.
30       */
31      private static String packageName = "org.djunits";
32  
33      /**
34       * Check that all classes have a toString method.
35       */
36      // FIXME: @Test
37      public final void toStringTest()
38      {
39          int failures = 0;
40          Collection<Class<?>> classList = ClassList.classList(packageName, true);
41          for (Class<?> c : classList)
42          {
43              if (Exception.class.isAssignableFrom(c))
44              {
45                  continue;
46              }
47              if (ClassList.isAnonymousInnerClass(c))
48              {
49                  continue;
50              }
51              Method toStringMethod = null;
52              boolean isAbstract = false;
53              for (String modifierString : Modifier.toString(c.getModifiers()).split(" "))
54              {
55                  if (modifierString.equals("abstract"))
56                  {
57                      isAbstract = true;
58                      break;
59                  }
60              }
61              for (Method m : c.getDeclaredMethods())
62              {
63                  if (m.getName().equals("toString") && m.getParameterCount() == 0)
64                  {
65                      toStringMethod = m;
66                  }
67              }
68              if (null == toStringMethod)
69              {
70                  // Get the nearest toString method from the parent tree
71                  try
72                  {
73                      toStringMethod = c.getMethod("toString");
74                  }
75                  catch (NoSuchMethodException | SecurityException exception)
76                  {
77                      exception.printStackTrace();
78                      fail("Cannot happen: getMethod(\"toString\") should never fail - there is one in Object");
79                  }
80                  boolean isFinal = false;
81                  for (String modifierString : Modifier.toString(toStringMethod.getModifiers()).split(" "))
82                  {
83                      if ("final".equals(modifierString))
84                      {
85                          isFinal = true;
86                          break;
87                      }
88                  }
89                  if (isFinal)
90                  {
91                      // system.out.println("Class " + c.getName() + " can not override the toString method because a super "
92                      // + "class implements a final toString method");
93                  }
94                  else if (!ClassList.hasNonStaticFields(c))
95                  {
96                      // system.out.println("Class " + c.getName()
97                      // + " does not have to override the toString method because it does not have non-static fields");
98                  }
99                  else if (isAbstract)
100                 {
101                     // system.out.println("Class " + c.getName() + " does not have to override the toString method because "
102                     // + "it is an abstract class");
103                 }
104                 else if (c.isEnum())
105                 {
106                     // system.out.println(
107                     // "Class " + c.getName() + " does not have to override toString because this class " + "is an enum");
108                 }
109                 else if (toStringMethod.getDeclaringClass().getPackage().getName().startsWith(packageName))
110                 {
111                     // system.out.println("Class " + c.getName()
112                     // + " does not have to override the toString method because a super class with the package "
113                     // + toStringMethod.getName() + " overrides toString (though not in a final way)");
114                 }
115                 else
116                 {
117                     System.err.println("Class " + c.getName() + " does not (but should) override toString");
118                     failures++;
119                 }
120             }
121         }
122         assertEquals(0, failures, "Failures to implement toString");
123     }
124 
125     /**
126      * Check that all classes implement the Serializable interface.
127      */
128     // FIXME: @Test
129     @SuppressWarnings({"checkstyle:methodlength", "checkstyle:emptyblock"})
130     public final void serializableTest()
131     {
132         int failures = 0;
133         Collection<Class<?>> classList = ClassList.classList(packageName, true);
134         for (Class<?> c : classList)
135         {
136             if (Serializable.class.isAssignableFrom(c))
137             {
138                 if (c.isEnum())
139                 {
140                     // System.out.println("Class " + c.getName() + " is an enum and (by inheritance) implements Serializable");
141                 }
142                 else if (!ClassList.hasNonStaticFields(c))
143                 {
144                     System.err.println("Class " + c.getName()
145                             + " does not contain non-static fields and should NOT implement Serializable");
146                     failures++;
147                 }
148                 else if (Thread.class.isAssignableFrom(c))
149                 {
150                     System.err.println("Class " + c.getName() + " is a thread and should NOT implement Serializable");
151                     failures++;
152                 }
153                 else if (ClassList.isAnonymousInnerClass(c))
154                 {
155                     System.err.println(
156                             "Class " + c.getName() + " is an anonymous inner class and should NOT implement Serializable");
157                     failures++;
158                 }
159                 else if (Exception.class.isAssignableFrom(c))
160                 {
161                     // system.out.println("Class " + c.getName() + " is an Exception and (correctly) implements Serializable");
162                 }
163                 else
164                 {
165                     // System.out.println("Class " + c.getName() + " should (and does) implement Serializable");
166                 }
167             }
168             else
169             {
170                 if (c.isEnum())
171                 {
172                     System.err
173                             .println("Class " + c.getName() + " is an enum and should (by inheritence) implement Serializable");
174                     failures++;
175                 }
176                 else if (!ClassList.hasNonStaticFields(c))
177                 {
178                     // System.out.println("Class " + c.getName()
179                     // + " does not contain non-static fields and (correctly does not implement Serializable");
180                 }
181                 else if (Thread.class.isAssignableFrom(c))
182                 {
183                     // System.out.println("Class " + c.getName() +
184                     // " is a thread and (correctly) does not implement Serializable");
185                 }
186                 else if (ClassList.isAnonymousInnerClass(c))
187                 {
188                     // System.out.println("Class " + c.getName()
189                     // + " is an anonymous inner class and (correctly) does not implement Serializable");
190                 }
191                 else if (Exception.class.isAssignableFrom(c))
192                 {
193                     System.err.println(
194                             "Class " + c.getName() + " is an Exception and should (but does NOT) implement Serializable");
195                     failures++;
196                 }
197                 else
198                 {
199                     System.err.println("Class " + c.getName() + " should (but does NOT) implement Serializable");
200                     failures++;
201                 }
202             }
203         }
204         assertEquals(0, failures, "Failures to implement serializable");
205     }
206 
207     /**
208      * Check that all classes that implement equals also implement hashCode.
209      */
210     @Test
211     @SuppressWarnings("checkstyle:emptyblock")
212     public final void equalsAndHashCodeTest()
213     {
214         int failures = 0;
215         Collection<Class<?>> classList = ClassList.classList(packageName, true);
216         for (Class<?> c : classList)
217         {
218             if (Exception.class.isAssignableFrom(c))
219             {
220                 continue;
221             }
222             Method equalsMethod = null;
223             Method hashCodeMethod = null;
224             for (Method m : c.getDeclaredMethods())
225             {
226                 if (m.getName().equals("equals"))
227                 {
228                     equalsMethod = m;
229                 }
230                 else if (m.getName().equals("hashCode"))
231                 {
232                     hashCodeMethod = m;
233                 }
234             }
235             if (null == equalsMethod)
236             {
237                 if (null == hashCodeMethod)
238                 {
239                     // System.out.println("Class " + c.getName() + " implements neither equals nor hashCode");
240                 }
241                 else
242                 {
243                     System.err.println("Class " + c.getName() + " implements hashCode, but not equals");
244                     failures++;
245                 }
246             }
247             else if (null == hashCodeMethod)
248             {
249                 System.err.println("Class " + c.getName() + " implements equals but NOT hashCode");
250                 failures++;
251             }
252             else
253             {
254                 // System.out.println("Class " + c.getName() + " implements equals and hashCode (good)");
255             }
256         }
257         assertEquals(0, failures, "Failures to implement both hashCode and equals");
258     }
259 
260 }