blob: f2ffe1da2d282bbdd9a30c6a5d27d3b989593dc9 [file] [log] [blame]
Ray Milkey22553b92014-05-06 11:06:21 -07001package net.onrc.onos.core.util;
2
3import org.hamcrest.Description;
4import org.hamcrest.StringDescription;
5
6import java.lang.reflect.Constructor;
Ray Milkey22553b92014-05-06 11:06:21 -07007import java.lang.reflect.Method;
8import java.lang.reflect.Modifier;
9
10
11/**
12 * Hamcrest style class for verifying that a class follows the
13 * accepted rules for utility classes.
14 *
15 * The rules that are enforced for utility classes:
16 * - the class must be declared final
17 * - the class must have only one constructor
18 * - the constructor must be private and inaccessible to callers
19 * - the class must have only static methods
20 */
21
22public class UtilityClassChecker {
23
24 private String failureReason = "";
25
26 /**
27 * Method to determine if a given class is a properly specified
28 * utility class. In addition to checking that the class meets the criteria
29 * for utility classes, an object of the class type is allocated to force
30 * test code coverage onto the class constructor.
31 *
32 * @param clazz the class to check
33 * @return true if the given class is a properly specified utility class.
34 */
Ray Milkeye3dadc32014-05-19 15:27:42 -070035 private boolean isProperlyDefinedUtilityClass(Class<?> clazz) {
Ray Milkey22553b92014-05-06 11:06:21 -070036 // class must be declared final
37 if (!Modifier.isFinal(clazz.getModifiers())) {
38 failureReason = "a class that is not final";
39 return false;
40 }
41
42 // class must have only one constructor
43 final Constructor<?>[] constructors = clazz.getDeclaredConstructors();
44 if (constructors.length != 1) {
45 failureReason = "a class with more than one constructor";
46 return false;
47 }
48
49 // constructor must not be accessible outside of the class
50 final Constructor<?> constructor = constructors[0];
51 if (constructor.isAccessible()) {
52 failureReason = "a class with an accessible default constructor";
53 return false;
54 }
55
56 // constructor must be private
57 if (!Modifier.isPrivate(constructor.getModifiers())) {
58 failureReason = "a class with a default constructor that is not private";
59 return false;
60 }
61
62 // class must have only static methods
63 for (final Method method : clazz.getMethods()) {
64 if (method.getDeclaringClass().equals(clazz)) {
65 if (!Modifier.isStatic(method.getModifiers())) {
66 failureReason = "a class with one or more non-static methods";
67 return false;
68 }
69 }
70
71 }
72
Ray Milkey0f42e832014-07-21 10:49:23 -070073 final Object newObject = TestUtils.callConstructor(constructor);
74 if (newObject == null) {
75 failureReason = "could not instantiate a new object";
Ray Milkey22553b92014-05-06 11:06:21 -070076 return false;
77 }
78 return true;
79 }
80
81 /**
82 * Describe why an error was reported. Uses Hamcrest style Description
83 * interfaces.
84 *
85 * @param description the Description object to use for reporting the
86 * mismatch
87 */
88 public void describeMismatch(Description description) {
89 description.appendText(failureReason);
90 }
91
92 /**
93 * Describe the source object that caused an error, using a Hamcrest
94 * Matcher style interface. In this case, it always returns
95 * that we are looking for a properly defined utility class.
96 *
97 * @param description the Description object to use to report the "to"
98 * object
99 */
100 public void describeTo(Description description) {
101 description.appendText("a properly defined utility class");
102 }
103
104 /**
105 * Assert that the given class adheres to the utility class rules.
106 *
107 * @param clazz the class to check
108 *
109 * @throws java.lang.AssertionError if the class is not a valid
110 * utility class
111 */
Ray Milkeye3dadc32014-05-19 15:27:42 -0700112 public static void assertThatClassIsUtility(Class<?> clazz) {
Ray Milkey22553b92014-05-06 11:06:21 -0700113 final UtilityClassChecker checker = new UtilityClassChecker();
114 if (!checker.isProperlyDefinedUtilityClass(clazz)) {
115 final Description toDescription = new StringDescription();
116 final Description mismatchDescription = new StringDescription();
117
118 checker.describeTo(toDescription);
119 checker.describeMismatch(mismatchDescription);
120 final String reason =
121 "\n" +
122 "Expected: is \"" + toDescription.toString() + "\"\n" +
123 " but : was \"" + mismatchDescription.toString() + "\"";
124
125 throw new AssertionError(reason);
126 }
127 }
128}