View Javadoc
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   * &nbsp;&nbsp;<span style="color:#646464">@Override</span>&nbsp;<span style="color:#7f0055"><b>public&nbsp;</b></span><span style="color:#7f0055"><b>int&nbsp;</b></span><span style="color:#000000">hashCode() {</span><br>
17   * &nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#7f0055"><b>return&nbsp;</b></span><span style="color:#000000">Pojomatic.hashCode(</span><span style="color:#7f0055"><b>this</b></span><span style="color:#000000">);</span><br>
18   * &nbsp;&nbsp;<span style="color:#000000">}</span><br>
19   * <br>
20   * &nbsp;&nbsp;<span style="color:#646464">@Override</span>&nbsp;<span style="color:#7f0055"><b>public&nbsp;</b></span><span style="color:#7f0055"><b>boolean&nbsp;</b></span><span style="color:#000000">equals(Object other) {</span><br>
21   * &nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#7f0055"><b>return&nbsp;</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   * &nbsp;&nbsp;<span style="color:#000000">}</span><br>
23   * <br>
24   * &nbsp;&nbsp;<span style="color:#646464">@Override</span>&nbsp;<span style="color:#7f0055"><b>public&nbsp;</b></span><span style="color:#000000">String toString() {</span><br>
25   * &nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#7f0055"><b>return&nbsp;</b></span><span style="color:#000000">Pojomatic.toString(</span><span style="color:#7f0055"><b>this</b></span><span style="color:#000000">);</span><br>
26   * &nbsp;&nbsp;<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&nbsp;</b></span>org.pojomatic.annotations.AutoProperty;<br>
39   *   <span style="color:#7f0055"><b>import&nbsp;</b></span>org.pojomatic.Pojomator;<br>
40   *   <span style="color:#7f0055"><b>import&nbsp;</b></span>org.pojomatic.Pojomatic;<br>
41   *   <br>
42   *   <span style="color:#646464">@AutoProperty</span><br>
43   *   <span style="color:#7f0055"><b>public&nbsp;interface&nbsp;</b></span>Interface&nbsp;{<br>
44   *   &nbsp;&nbsp;<span style="color:#7f0055"><b>static&nbsp;</b></span>Pojomator&lt;Interface&gt;&nbsp;POJOMATOR&nbsp;=&nbsp;Pojomatic.pojomator(Interface.<span style="color:#7f0055"><b>class</b></span>);<br>
45   *   &nbsp;&nbsp;...<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&nbsp;class&nbsp;</b></span>Implementation&nbsp;<span style="color:#7f0055"><b>implements&nbsp;</b></span>Interface&nbsp;{<br>
52   *   &nbsp;&nbsp;<span style="color:#646464">@Override</span>&nbsp;<span style="color:#7f0055"><b>public&nbsp;</b></span><span style="color:#7f0055"><b>int&nbsp;</b></span>hashCode()&nbsp;{<br>
53   *   &nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#7f0055"><b>return&nbsp;</b></span>POJOMATOR.doHashCode(<span style="color:#7f0055"><b>this</b></span>);<br>
54   *   &nbsp;&nbsp;}<br>
55   *   <br>
56   *   &nbsp;&nbsp;<span style="color:#646464">@Override</span>&nbsp;<span style="color:#7f0055"><b>public&nbsp;</b></span><span style="color:#7f0055"><b>boolean&nbsp;</b></span>equals(Object&nbsp;other)&nbsp;{<br>
57   *   &nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#7f0055"><b>return&nbsp;</b></span>POJOMATOR.doEquals(this,&nbsp;other);<br>
58   *   &nbsp;&nbsp;}<br>
59   *   <br>
60   *   &nbsp;&nbsp;<span style="color:#646464">@Override</span>&nbsp;<span style="color:#7f0055"><b>public&nbsp;</b></span>String&nbsp;toString()&nbsp;{<br>
61   *   &nbsp;&nbsp;&nbsp;&nbsp;<span style="color:#7f0055"><b>return&nbsp;</b></span>POJOMATOR.doToString(<span style="color:#7f0055"><b>this</b></span>);<br>
62   *   &nbsp;&nbsp;}<br>
63   *   &nbsp;&nbsp;...<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 }