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 }