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