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-2019 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.isEnum())
124                 {
125                     // System.out.println("Class " + c.getName() + " is an enum and (by inheritance) implements Serializable");
126                 }
127                 else if (!ClassList.hasNonStaticFields(c))
128                 {
129                     System.err.println("Class " + c.getName()
130                             + " does not contain non-static fields and should NOT implement Serializable");
131                 }
132                 else if (Thread.class.isAssignableFrom(c))
133                 {
134                     System.err.println("Class " + c.getName() + " is a thread and should NOT implement Serializable");
135                 }
136                 else if (ClassList.isAnonymousInnerClass(c))
137                 {
138                     System.err.println(
139                             "Class " + c.getName() + " is an anonymous inner class and should NOT implement Serializable");
140                 }
141                 else if (Exception.class.isAssignableFrom(c))
142                 {
143                     System.out.println("Class " + c.getName() + " is an Exception and (correctly) implements Serializable");
144                 }
145                 else
146                 {
147                     // System.out.println("Class " + c.getName() + " should (and does) implement Serializable");
148                 }
149             }
150             else
151             {
152                 if (c.isEnum())
153                 {
154                     System.err
155                             .println("Class " + c.getName() + " is an enum and should (by inheritence) implement Serializable");
156                 }
157                 else if (!ClassList.hasNonStaticFields(c))
158                 {
159                     // System.out.println("Class " + c.getName()
160                     // + " does not contain non-static fields and (correctly does not implement Serializable");
161                 }
162                 else if (Thread.class.isAssignableFrom(c))
163                 {
164                     // System.out.println("Class " + c.getName() +
165                     // " is a thread and (correctly) does not implement Serializable");
166                 }
167                 else if (ClassList.isAnonymousInnerClass(c))
168                 {
169                     // System.out.println("Class " + c.getName()
170                     // + " is an anonymous inner class and (correctly) does not implement Serializable");
171                 }
172                 else if (Exception.class.isAssignableFrom(c))
173                 {
174                     System.err.println(
175                             "Class " + c.getName() + " is an Exception and should (but does NOT) implement Serializable");
176                 }
177                 else
178                 {
179                     System.err.println("Class " + c.getName() + " should (but does NOT) implement Serializable");
180                 }
181             }
182         }
183     }
184 
185     /**
186      * Check that all classes that implement equals also implement hashCode.
187      */
188     @Test
189     public final void equalsAndHashCodeTest()
190     {
191         Collection<Class<?>> classList = ClassList.classList("org.opentrafficsim", true);
192         for (Class<?> c : classList)
193         {
194             if (Exception.class.isAssignableFrom(c))
195             {
196                 continue;
197             }
198             Method equalsMethod = null;
199             Method hashCodeMethod = null;
200             for (Method m : c.getDeclaredMethods())
201             {
202                 if (m.getName().equals("equals"))
203                 {
204                     equalsMethod = m;
205                 }
206                 else if (m.getName().equals("hashCode"))
207                 {
208                     hashCodeMethod = m;
209                 }
210             }
211             if (null == equalsMethod)
212             {
213                 if (null == hashCodeMethod)
214                 {
215                     // System.out.println("Class " + c.getName() + " implements neither equals nor hashCode");
216                 }
217                 else
218                 {
219                     System.out.println("Class " + c.getName() + " implements hashCode, but not equals");
220                 }
221             }
222             else if (null == hashCodeMethod)
223             {
224                 fail("Class " + c.getName() + " implements equals but NOT hashCode");
225             }
226             else
227             {
228                 // System.out.println("Class " + c.getName() + " implements equals and hashCode (good)");
229             }
230         }
231     }
232 
233 }