2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.model
.common
;
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
;
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
.NotNull
;
27 import javax
.validation
.constraints
.Min
;
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
;
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
.joda
.time
.DateTime
;
45 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
46 import eu
.etaxonomy
.cdm
.jaxb
.DateTimeAdapter
;
47 import eu
.etaxonomy
.cdm
.jaxb
.UUIDAdapter
;
48 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
;
49 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchMode
;
55 * The base class for all CDM domain classes implementing UUIDs and bean property change event firing.
56 * It provides a globally unique UUID and keeps track of creation date and person.
57 * The UUID is the same for different versions (see {@link VersionableEntity}) of a CDM object, so a locally unique id exists in addition
58 * that allows to safely access and store several objects (=version) with the same UUID.
60 * This class together with the {@link eu.etaxonomy.cdm.aspectj.PropertyChangeAspect}
61 * will fire bean change events to all registered listeners. Listener registration and event firing
62 * is done with the help of the {@link PropertyChangeSupport} class.
67 @XmlAccessorType(XmlAccessType
.FIELD
)
68 @XmlType(name
= "CdmBase", propOrder
= {
73 public abstract class CdmBase
implements Serializable
, ICdmBase
{
74 private static final long serialVersionUID
= -3053225700018294809L;
75 @SuppressWarnings("unused")
76 private static final Logger logger
= Logger
.getLogger(CdmBase
.class);
80 private PropertyChangeSupport propertyChangeSupport
= new PropertyChangeSupport(this);
82 @XmlAttribute(name
= "id", required
= true)
84 @GeneratedValue(generator
= "system-increment")
86 @Match(MatchMode
.IGNORE
)
91 @XmlAttribute(required
= true)
92 @XmlJavaTypeAdapter(UUIDAdapter
.class)
94 @Type(type
="uuidUserType")
95 @NaturalId // This has the effect of placing a "unique" constraint on the database column
97 @Match(MatchMode
.IGNORE
)
101 @XmlElement (name
= "Created", type
= String
.class)
102 @XmlJavaTypeAdapter(DateTimeAdapter
.class)
103 @Type(type
="dateTimeUserType")
104 @Basic(fetch
= FetchType
.LAZY
)
105 @Match(MatchMode
.IGNORE
)
106 private DateTime created
;
108 @XmlElement (name
= "CreatedBy")
110 @XmlSchemaType(name
= "IDREF")
111 @ManyToOne(fetch
=FetchType
.LAZY
)
112 @Match(MatchMode
.IGNORE
)
113 private User createdBy
;
116 * Class constructor assigning a unique UUID and creation date.
117 * UUID can be changed later via setUuid method.
120 this.uuid
= UUID
.randomUUID();
121 this.created
= new DateTime().withMillisOfSecond(0);
125 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
128 public void addPropertyChangeListener(PropertyChangeListener listener
) {
129 propertyChangeSupport
.addPropertyChangeListener(listener
);
133 * see {@link PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)}
135 public void addPropertyChangeListener(String propertyName
, PropertyChangeListener listener
) {
136 propertyChangeSupport
.addPropertyChangeListener(propertyName
, listener
);
140 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
142 public void removePropertyChangeListener(PropertyChangeListener listener
) {
143 propertyChangeSupport
.removePropertyChangeListener(listener
);
147 * @see PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)
149 public void removePropertyChangeListener(String propertyName
, PropertyChangeListener listener
) {
150 propertyChangeSupport
.removePropertyChangeListener(propertyName
, listener
);
153 public boolean hasListeners(String propertyName
) {
154 return propertyChangeSupport
.hasListeners(propertyName
);
157 public void firePropertyChange(String property
, String oldval
, String newval
) {
158 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
160 public void firePropertyChange(String property
, int oldval
, int newval
) {
161 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
163 public void firePropertyChange(String property
, float oldval
, float newval
) {
164 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
166 public void firePropertyChange(String property
, boolean oldval
, boolean newval
) {
167 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
169 public void firePropertyChange(String property
, Object oldval
, Object newval
) {
170 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
172 public void firePropertyChange(PropertyChangeEvent evt
) {
173 propertyChangeSupport
.firePropertyChange(evt
);
177 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getUuid()
179 public UUID
getUuid() {
183 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setUuid(java.util.UUID)
185 public void setUuid(UUID uuid
) {
190 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getId()
196 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setId(int)
198 public void setId(int id
) {
203 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreated()
205 public DateTime
getCreated() {
209 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreated(java.util.Calendar)
211 public void setCreated(DateTime created
) {
212 if (created
!= null){
214 created
= created
.withMillisOfSecond(0);
215 //created.set(Calendar.MILLISECOND, 0); //old, can be deleted
217 this.created
= created
;
222 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreatedBy()
224 public User
getCreatedBy() {
225 return this.createdBy
;
228 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreatedBy(eu.etaxonomy.cdm.model.agent.Person)
230 public void setCreatedBy(User createdBy
) {
231 this.createdBy
= createdBy
;
234 // ************************** Hibernate proxies *******************/
236 * These methods are present due to HHH-1517 - that in a one-to-many
237 * relationship with a superclass at the "one" end, the proxy created
238 * by hibernate is the superclass, and not the subclass, resulting in
239 * a classcastexception when you try to cast it.
241 * Hopefully this will be resolved through improvements with the creation of
242 * proxy objects by hibernate and the following methods will become redundant,
243 * but for the time being . . .
248 * @throws ClassCastException
250 //non-static does not work because javassist already unwrapps the proxy before calling the method
251 public static <T
extends CdmBase
> T
deproxy(Object object
, Class
<T
> clazz
) throws ClassCastException
{
252 return HibernateProxyHelper
.deproxy(object
, clazz
);
255 public boolean isInstanceOf(Class
<?
extends CdmBase
> clazz
) throws ClassCastException
{
256 return HibernateProxyHelper
.isInstanceOf(this, clazz
);
259 // ************* Object overrides *************************/
262 * Is true if UUID is the same for the passed Object and this one.
263 * @see java.lang.Object#equals(java.lang.Object)
264 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities}
265 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
266 * for more information about equals and hashcode.
269 public boolean equals(Object obj
) {
276 if (!CdmBase
.class.isAssignableFrom(obj
.getClass())){
279 ICdmBase cdmObj
= (ICdmBase
)obj
;
280 boolean uuidEqual
= cdmObj
.getUuid().equals(this.getUuid());
281 boolean createdEqual
= cdmObj
.getCreated().equals(this.getCreated());
282 if (! uuidEqual
|| !createdEqual
){
289 /** Overrides {@link java.lang.Object#hashCode()}
290 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities}
291 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
292 * for more information about equals and hashcode.
295 public int hashCode() {
297 hashCode
= 29 * hashCode
+ this.getUuid().hashCode();
302 * Overrides {@link java.lang.Object#toString()}.
303 * This returns an String that identifies the object well without beeing necessarily unique.
304 * Specification: This method should never call other object' methods so it can be well used for debugging
305 * without problems like lazy loading, unreal states etc.
306 * Note: If overriding this method's javadoc always copy or link the above requirement.
307 * If not overwritten by a subclass method returns the class, id and uuid as a string for any CDM object.
308 * For example: Taxon#13<b5938a98-c1de-4dda-b040-d5cc5bfb3bc0>
309 * @see java.lang.Object#toString()
312 public String
toString() {
313 return this.getClass().getSimpleName()+"#"+this.getId()+"<"+this.getUuid()+">";
316 // **************** invoke methods **************************/
318 protected void invokeSetMethod(Method method
, Object object
){
320 method
.invoke(object
, this);
321 } catch (Exception e
) {
323 //TODO handle exceptioin;
327 protected void invokeSetMethodWithNull(Method method
, Object object
){
329 Object
[] nul
= new Object
[]{null};
330 method
.invoke(object
, nul
);
331 } catch (Exception e
) {
333 //TODO handle exceptioin;
337 //********************** CLONE *****************************************/
339 protected void clone(CdmBase clone
){
340 clone
.setCreatedBy(createdBy
);
342 //Constructor Attributes
343 //clone.setCreated(created);
344 //clone.setUuid(getUuid());
349 * @see java.lang.Object#clone()
352 public Object
clone() throws CloneNotSupportedException
{
353 CdmBase result
= (CdmBase
)super.clone();
357 result
.setUuid(UUID
.randomUUID());
358 result
.setCreated(new DateTime());
359 result
.setCreatedBy(null);