minor
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / common / CdmBase.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
8 */
9 package eu.etaxonomy.cdm.model.common;
10
11 import java.beans.PropertyChangeEvent;
12 import java.beans.PropertyChangeListener;
13 import java.beans.PropertyChangeSupport;
14 import java.io.Serializable;
15 import java.lang.reflect.Method;
16 import java.util.UUID;
17
18 import javax.persistence.Basic;
19 import javax.persistence.Column;
20 import javax.persistence.FetchType;
21 import javax.persistence.GeneratedValue;
22 import javax.persistence.Id;
23 import javax.persistence.ManyToOne;
24 import javax.persistence.MappedSuperclass;
25 import javax.persistence.Transient;
26 import javax.validation.constraints.Min;
27 import javax.validation.constraints.NotNull;
28 import javax.xml.bind.annotation.XmlAccessType;
29 import javax.xml.bind.annotation.XmlAccessorType;
30 import javax.xml.bind.annotation.XmlAttribute;
31 import javax.xml.bind.annotation.XmlElement;
32 import javax.xml.bind.annotation.XmlID;
33 import javax.xml.bind.annotation.XmlIDREF;
34 import javax.xml.bind.annotation.XmlSchemaType;
35 import javax.xml.bind.annotation.XmlTransient;
36 import javax.xml.bind.annotation.XmlType;
37 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
38
39 import org.apache.log4j.Logger;
40 import org.hibernate.annotations.NaturalId;
41 import org.hibernate.annotations.Type;
42 import org.hibernate.search.annotations.DocumentId;
43 import org.hibernate.search.annotations.Field;
44 import org.hibernate.search.annotations.FieldBridge;
45 import org.joda.time.DateTime;
46
47 import eu.etaxonomy.cdm.hibernate.DateTimeBridge;
48 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
49 import eu.etaxonomy.cdm.jaxb.DateTimeAdapter;
50 import eu.etaxonomy.cdm.jaxb.UUIDAdapter;
51 import eu.etaxonomy.cdm.strategy.match.Match;
52 import eu.etaxonomy.cdm.strategy.match.MatchMode;
53
54
55
56
57 /**
58 * The base class for all CDM domain classes implementing UUIDs and bean property change event firing.
59 * It provides a globally unique UUID and keeps track of creation date and person.
60 * The UUID is the same for different versions (see {@link VersionableEntity}) of a CDM object, so a locally unique id exists in addition
61 * that allows to safely access and store several objects (=version) with the same UUID.
62 *
63 * This class together with the {@link eu.etaxonomy.cdm.aspectj.PropertyChangeAspect}
64 * will fire bean change events to all registered listeners. Listener registration and event firing
65 * is done with the help of the {@link PropertyChangeSupport} class.
66 *
67 * @author m.doering
68 *
69 */
70 @XmlAccessorType(XmlAccessType.FIELD)
71 @XmlType(name = "CdmBase", propOrder = {
72 "created",
73 "createdBy"
74 })
75 @MappedSuperclass
76 public abstract class CdmBase implements Serializable, ICdmBase{
77 private static final long serialVersionUID = -3053225700018294809L;
78 @SuppressWarnings("unused")
79 private static final Logger logger = Logger.getLogger(CdmBase.class);
80
81 @Transient
82 @XmlTransient
83 private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
84
85 //@XmlAttribute(name = "id", required = true)
86 @XmlTransient
87 @Id
88 @GeneratedValue(generator = "system-increment")
89 @DocumentId
90 @Match(MatchMode.IGNORE)
91 @NotNull
92 @Min(0)
93 private int id;
94
95 @XmlAttribute(required = true)
96 @XmlJavaTypeAdapter(UUIDAdapter.class)
97 @Type(type="uuidUserType")
98 @NaturalId // This has the effect of placing a "unique" constraint on the database column
99 @XmlID
100 @Column(length=36)
101 @Match(MatchMode.IGNORE)
102 @NotNull
103 protected UUID uuid;
104
105 @XmlElement (name = "Created", type= String.class)
106 @XmlJavaTypeAdapter(DateTimeAdapter.class)
107 @Type(type="dateTimeUserType")
108 @Basic(fetch = FetchType.LAZY)
109 @Match(MatchMode.IGNORE)
110 @Field(index = org.hibernate.search.annotations.Index.UN_TOKENIZED)
111 @FieldBridge(impl = DateTimeBridge.class)
112 private DateTime created;
113
114 @XmlElement (name = "CreatedBy")
115 @XmlIDREF
116 @XmlSchemaType(name = "IDREF")
117 @ManyToOne(fetch=FetchType.LAZY)
118 @Match(MatchMode.IGNORE)
119 private User createdBy;
120
121 /**
122 * Class constructor assigning a unique UUID and creation date.
123 * UUID can be changed later via setUuid method.
124 */
125 public CdmBase() {
126 this.uuid = UUID.randomUUID();
127 this.created = new DateTime().withMillisOfSecond(0);
128 }
129
130 /**
131 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
132 * @param listener
133 */
134 public void addPropertyChangeListener(PropertyChangeListener listener) {
135 propertyChangeSupport.addPropertyChangeListener(listener);
136 }
137
138 /**
139 * see {@link PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)}
140 */
141 public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
142 propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
143 }
144
145 /**
146 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
147 */
148 public void removePropertyChangeListener(PropertyChangeListener listener) {
149 propertyChangeSupport.removePropertyChangeListener(listener);
150 }
151
152 /**
153 * @see PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)
154 */
155 public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
156 propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
157 }
158
159 public boolean hasListeners(String propertyName) {
160 return propertyChangeSupport.hasListeners(propertyName);
161 }
162
163 public void firePropertyChange(String property, String oldval, String newval) {
164 propertyChangeSupport.firePropertyChange(property, oldval, newval);
165 }
166 public void firePropertyChange(String property, int oldval, int newval) {
167 propertyChangeSupport.firePropertyChange(property, oldval, newval);
168 }
169 public void firePropertyChange(String property, float oldval, float newval) {
170 propertyChangeSupport.firePropertyChange(property, oldval, newval);
171 }
172 public void firePropertyChange(String property, boolean oldval, boolean newval) {
173 propertyChangeSupport.firePropertyChange(property, oldval, newval);
174 }
175 public void firePropertyChange(String property, Object oldval, Object newval) {
176 propertyChangeSupport.firePropertyChange(property, oldval, newval);
177 }
178 public void firePropertyChange(PropertyChangeEvent evt) {
179 propertyChangeSupport.firePropertyChange(evt);
180 }
181
182 /* (non-Javadoc)
183 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getUuid()
184 */
185 public UUID getUuid() {
186 return uuid;
187 }
188 /* (non-Javadoc)
189 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setUuid(java.util.UUID)
190 */
191 public void setUuid(UUID uuid) {
192 this.uuid = uuid;
193 }
194
195 /* (non-Javadoc)
196 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getId()
197 */
198 public int getId() {
199 return this.id;
200 }
201 /* (non-Javadoc)
202 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setId(int)
203 */
204 public void setId(int id) {
205 this.id = id;
206 }
207
208 /* (non-Javadoc)
209 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreated()
210 */
211 public DateTime getCreated() {
212 return created;
213 }
214 /* (non-Javadoc)
215 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreated(java.util.Calendar)
216 */
217 public void setCreated(DateTime created) {
218 if (created != null){
219 new DateTime();
220 created = created.withMillisOfSecond(0);
221 //created.set(Calendar.MILLISECOND, 0); //old, can be deleted
222 }
223 this.created = created;
224 }
225
226
227 /* (non-Javadoc)
228 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreatedBy()
229 */
230 public User getCreatedBy() {
231 return this.createdBy;
232 }
233 /* (non-Javadoc)
234 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreatedBy(eu.etaxonomy.cdm.model.agent.Person)
235 */
236 public void setCreatedBy(User createdBy) {
237 this.createdBy = createdBy;
238 }
239
240 // ************************** Hibernate proxies *******************/
241 /**
242 * These methods are present due to HHH-1517 - that in a one-to-many
243 * relationship with a superclass at the "one" end, the proxy created
244 * by hibernate is the superclass, and not the subclass, resulting in
245 * a classcastexception when you try to cast it.
246 *
247 * Hopefully this will be resolved through improvements with the creation of
248 * proxy objects by hibernate and the following methods will become redundant,
249 * but for the time being . . .
250 * @param <T>
251 * @param object
252 * @param clazz
253 * @return
254 * @throws ClassCastException
255 */
256 //non-static does not work because javassist already unwrapps the proxy before calling the method
257 public static <T extends CdmBase> T deproxy(Object object, Class<T> clazz) throws ClassCastException {
258 return HibernateProxyHelper.deproxy(object, clazz);
259 }
260
261 public boolean isInstanceOf(Class<? extends CdmBase> clazz) throws ClassCastException {
262 return HibernateProxyHelper.isInstanceOf(this, clazz);
263 }
264
265 // ************* Object overrides *************************/
266
267 /**
268 * Is true if UUID is the same for the passed Object and this one.
269 * @see java.lang.Object#equals(java.lang.Object)
270 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities}
271 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
272 * for more information about equals and hashcode.
273 */
274 @Override
275 public boolean equals(Object obj) {
276 if (obj == this){
277 return true;
278 }
279 if (obj == null){
280 return false;
281 }
282 if (!CdmBase.class.isAssignableFrom(obj.getClass())){
283 return false;
284 }
285 ICdmBase cdmObj = (ICdmBase)obj;
286 boolean uuidEqual = cdmObj.getUuid().equals(this.getUuid());
287 boolean createdEqual = cdmObj.getCreated().equals(this.getCreated());
288 if (! uuidEqual || !createdEqual){
289 return false;
290 }
291 return true;
292 }
293
294
295 /** Overrides {@link java.lang.Object#hashCode()}
296 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities}
297 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
298 * for more information about equals and hashcode.
299 */
300 @Override
301 public int hashCode() {
302 int hashCode = 7;
303 if(this.getUuid() != null) {
304 return 29 * hashCode + this.getUuid().hashCode();
305 } else {
306 return 29 * hashCode;
307 }
308 }
309
310 /**
311 * Overrides {@link java.lang.Object#toString()}.
312 * This returns an String that identifies the object well without beeing necessarily unique.
313 * Specification: This method should never call other object' methods so it can be well used for debugging
314 * without problems like lazy loading, unreal states etc.
315 * Note: If overriding this method's javadoc always copy or link the above requirement.
316 * If not overwritten by a subclass method returns the class, id and uuid as a string for any CDM object.
317 * For example: Taxon#13<b5938a98-c1de-4dda-b040-d5cc5bfb3bc0>
318 * @see java.lang.Object#toString()
319 */
320 @Override
321 public String toString() {
322 return this.getClass().getSimpleName()+"#"+this.getId()+"<"+this.getUuid()+">";
323 }
324
325 // **************** invoke methods **************************/
326
327 protected void invokeSetMethod(Method method, Object object){
328 try {
329 method.invoke(object, this);
330 } catch (Exception e) {
331 e.printStackTrace();
332 //TODO handle exceptioin;
333 }
334 }
335
336 protected void invokeSetMethodWithNull(Method method, Object object){
337 try {
338 Object[] nul = new Object[]{null};
339 method.invoke(object, nul);
340 } catch (Exception e) {
341 e.printStackTrace();
342 //TODO handle exceptioin;
343 }
344 }
345
346 //********************** CLONE *****************************************/
347
348 protected void clone(CdmBase clone){
349 clone.setCreatedBy(createdBy);
350 clone.setId(id);
351 //Constructor Attributes
352 //clone.setCreated(created);
353 //clone.setUuid(getUuid());
354
355 }
356
357 /* (non-Javadoc)
358 * @see java.lang.Object#clone()
359 */
360 @Override
361 public Object clone() throws CloneNotSupportedException{
362 CdmBase result = (CdmBase)super.clone();
363
364 //TODO ?
365 result.setId(0);
366 result.setUuid(UUID.randomUUID());
367 result.setCreated(new DateTime());
368 result.setCreatedBy(null);
369
370 //no changes to: -
371 return result;
372 }
373
374 }