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 }