1 package org.pojomatic;
2
3 import org.pojomatic.diff.Differences;
4 import org.pojomatic.internal.PojomatorFactory;
5 import org.pojomatic.internal.SelfPopulatingMap;
6
7 /**
8 * Static methods for implementing the {@link java.lang.Object#equals(Object)},
9 * {@link java.lang.Object#hashCode()} and {@link java.lang.Object#toString()} methods on a
10 * annotated POJO. The actual work for a given class is done by a {@link Pojomator} created for
11 * that class. This class is careful to create only a single {@code Pojomator} per POJO class.
12 * The overhead for looking up the {@code Pojomator} by POJO class is light, so a typical use in a
13 * POJO class would be
14 * <p style="margin-left: 2em">
15 * <code>
16 * <span style="color:#646464">@Override</span> <span style="color:#7f0055"><b>public </b></span><span style="color:#7f0055"><b>int </b></span><span style="color:#000000">hashCode() {</span><br>
17 * <span style="color:#7f0055"><b>return </b></span><span style="color:#000000">Pojomatic.hashCode(</span><span style="color:#7f0055"><b>this</b></span><span style="color:#000000">);</span><br>
18 * <span style="color:#000000">}</span><br>
19 * <br>
20 * <span style="color:#646464">@Override</span> <span style="color:#7f0055"><b>public </b></span><span style="color:#7f0055"><b>boolean </b></span><span style="color:#000000">equals(Object other) {</span><br>
21 * <span style="color:#7f0055"><b>return </b></span><span style="color:#000000">Pojomatic.equals(</span><span style="color:#7f0055"><b>this</b></span><span style="color:#000000">, other);</span><br>
22 * <span style="color:#000000">}</span><br>
23 * <br>
24 * <span style="color:#646464">@Override</span> <span style="color:#7f0055"><b>public </b></span><span style="color:#000000">String toString() {</span><br>
25 * <span style="color:#7f0055"><b>return </b></span><span style="color:#000000">Pojomatic.toString(</span><span style="color:#7f0055"><b>this</b></span><span style="color:#000000">);</span><br>
26 * <span style="color:#000000">}</span><br>
27 * <br>
28 * </code>
29 * </p>
30 * Under the covers, these methods are referencing a {@link org.pojomatic.Pojomator Pojomator} instance
31 * which is created lazily and cached on a per-class basis. The performance penalty for this is
32 * negligible, but if an interface is annotated for Pojomation, using the {@code Pojomator} directly
33 * is required, since the {@code Pojomator} for a class will only reference properties in the class
34 * and it's superclasses, but not any implemented interfaces. To do this, first define a static
35 * constant {@code POJOMATOR} in the interface:
36 * <p style="margin-left: 2em">
37 * <code>
38 * <span style="color:#7f0055"><b>import </b></span>org.pojomatic.annotations.AutoProperty;<br>
39 * <span style="color:#7f0055"><b>import </b></span>org.pojomatic.Pojomator;<br>
40 * <span style="color:#7f0055"><b>import </b></span>org.pojomatic.Pojomatic;<br>
41 * <br>
42 * <span style="color:#646464">@AutoProperty</span><br>
43 * <span style="color:#7f0055"><b>public interface </b></span>Interface {<br>
44 * <span style="color:#7f0055"><b>static </b></span>Pojomator<Interface> POJOMATOR = Pojomatic.pojomator(Interface.<span style="color:#7f0055"><b>class</b></span>);<br>
45 * ...<br>
46 * }</code>
47 * </p>
48 * and then delegate to {@code POJOMATOR} in the implementing classes:
49 * <p style="margin-left: 2em">
50 * <code>
51 * <span style="color:#7f0055"><b>public class </b></span>Implementation <span style="color:#7f0055"><b>implements </b></span>Interface {<br>
52 * <span style="color:#646464">@Override</span> <span style="color:#7f0055"><b>public </b></span><span style="color:#7f0055"><b>int </b></span>hashCode() {<br>
53 * <span style="color:#7f0055"><b>return </b></span>POJOMATOR.doHashCode(<span style="color:#7f0055"><b>this</b></span>);<br>
54 * }<br>
55 * <br>
56 * <span style="color:#646464">@Override</span> <span style="color:#7f0055"><b>public </b></span><span style="color:#7f0055"><b>boolean </b></span>equals(Object other) {<br>
57 * <span style="color:#7f0055"><b>return </b></span>POJOMATOR.doEquals(this, other);<br>
58 * }<br>
59 * <br>
60 * <span style="color:#646464">@Override</span> <span style="color:#7f0055"><b>public </b></span>String toString() {<br>
61 * <span style="color:#7f0055"><b>return </b></span>POJOMATOR.doToString(<span style="color:#7f0055"><b>this</b></span>);<br>
62 * }<br>
63 * ...<br>
64 * }</code>
65 * </p>
66 *
67 * @see Pojomator
68 */
69 public class Pojomatic {
70
71 private final static SelfPopulatingMap<Class<?>, Pojomator<?>> POJOMATORS =
72 new SelfPopulatingMap<Class<?>, Pojomator<?>>() {
73 @Override
74 // compiler does not know that the type parameter to Pojomator is the same as the type
75 // parameter to Class
76 protected Pojomator<?> create(Class<?> key) {
77 return PojomatorFactory.makePojomator(key);
78 }
79 };
80
81 private Pojomatic() {}
82
83 /**
84 * Compute the {@code toString} representation for a POJO.
85 * @param <T> the type of the POJO
86 * @param pojo the POJO - must not be null
87 * @return the {@code toString} representation of {@code pojo}.
88 * @throws NoPojomaticPropertiesException if {@code pojo}'s class has no properties annotated for
89 * use with Pojomatic
90 * @see Pojomator#doToString(Object)
91 */
92 public static <T> String toString(T pojo) throws NoPojomaticPropertiesException {
93 return pojomator(getClass(pojo)).doToString(pojo);
94 }
95
96 /**
97 * Compute the {@code hashCode} for a POJO.
98 * @param <T> the type of the POJO
99 * @param pojo the POJO - must not be null
100 * @return the {@code hashCode} for {@code pojo}.
101 * @throws NoPojomaticPropertiesException if {@code pojo}'s class has no properties annotated for
102 * use with Pojomatic
103 * @see Pojomator#doHashCode(Object)
104 */
105 public static <T> int hashCode(T pojo) throws NoPojomaticPropertiesException {
106 return pojomator(getClass(pojo)).doHashCode(pojo);
107 }
108
109 /**
110 * Compute whether {@code pojo} and {@code other} are equal to each other in the sense of
111 * {@code Object}'s {@code equals} method.
112 * @param <T> the type of the POJO
113 * @param pojo the POJO - must not be null
114 * @param other the object to compare to for equality
115 * @return whether {@code pojo} and {@code other} are equal to each other in the sense of
116 * {@code Object}'s {@code equals} method.
117 * @throws NoPojomaticPropertiesException if {@code pojo}'s class has no properties annotated for
118 * use with Pojomatic
119 * @see Pojomator#doEquals(Object, Object)
120 */
121 public static <T> boolean equals(T pojo, Object other) throws NoPojomaticPropertiesException {
122 return pojomator(getClass(pojo)).doEquals(pojo, other);
123 }
124
125 /**
126 * Compute whether {@code classA} and {@code classB} are compatible for equality as specified
127 * by the documentation for {@link Pojomator#isCompatibleForEquality(Class)}.
128 * @param classA the first class to check for compatibility for equality
129 * @param classB the second class to check for compatibility for equality
130 * @return {@code true} if the two classes are compatible for equality, or {@code false}
131 * otherwise.
132 */
133 public static boolean areCompatibleForEquals(Class<?> classA, Class<?> classB) {
134 return pojomator(classA).isCompatibleForEquality(classB);
135 }
136
137 /**
138 * Compute the differences between {@code pojo} and {@code other} among the properties
139 * examined by {@link #equals(Object, Object)} for type {@code T}.
140 *
141 * @param <T> the static type of the first object to compare
142 * @param <S> the static type of the first object to compare
143 * @param pojo the instance to diff against
144 * @param other the instance to diff
145 * @return the list of differences (possibly empty) between {@code instance} and {@code other}
146 * among the properties examined by {@link #equals(Object, Object)} for type {@code T}.
147 * @throws NullPointerException if {@code pojo} or {@code other} are null
148 * (this behavior may change in future releases).
149 * @throws NoPojomaticPropertiesException if {@code pojo}'s class has no properties
150 * annotated for use with Pojomatic, or if the types of {@code pojo} and {@code other} are not
151 * compatible for equality with each other (this behavior may change in future releases).
152 */
153 public static <T, S extends T> Differences diff(T pojo, S other)
154 throws NullPointerException, NoPojomaticPropertiesException {
155 if (pojo == null) {
156 throw new NullPointerException("pojo is null");
157 }
158 if (other == null) {
159 throw new NullPointerException("other is null");
160 }
161 return pojomator(getClass(pojo)).doDiff(pojo, other);
162 }
163
164 /**
165 * Get the {@code Pojomator} for {@code pojoClass}. While the same instance will be returned every time
166 * for a given value of {@code pojoClass}, highly performance-sensitive applications may want to cache the value
167 * returned in a static variable on the class in question. Note that a static Pojomator for a parent class
168 * will miss any additional properties when used on a child class.
169 * @param <T> the type represented by {@code pojoClass}
170 * @param pojoClass the class to create a {@code Pojomator} for.
171 * @return a {@code Pojomator<T>}
172 * @throws NoPojomaticPropertiesException if {@code pojoClass} has no properties annotated for use
173 * with Pojomatic
174 */
175 @SuppressWarnings("unchecked") // compiler does not know that the type parameter to Pojomator is T
176 public static <T> Pojomator<T> pojomator(Class<T> pojoClass)
177 throws NoPojomaticPropertiesException {
178 return (Pojomator<T>) POJOMATORS.get(pojoClass);
179 }
180
181 @SuppressWarnings("unchecked") // Since Object.getClass returns Class<?>
182 private static <T> Class<T> getClass(T pojo) {
183 return (Class<T>) pojo.getClass();
184 }
185 }