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
.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
;
39 import org
.apache
.log4j
.Logger
;
40 import org
.hibernate
.annotations
.GenericGenerator
;
41 import org
.hibernate
.annotations
.NaturalId
;
42 import org
.hibernate
.annotations
.Type
;
43 import org
.hibernate
.search
.annotations
.DocumentId
;
44 import org
.hibernate
.search
.annotations
.Field
;
45 import org
.hibernate
.search
.annotations
.FieldBridge
;
46 import org
.joda
.time
.DateTime
;
48 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
49 import eu
.etaxonomy
.cdm
.hibernate
.search
.DateTimeBridge
;
50 import eu
.etaxonomy
.cdm
.jaxb
.DateTimeAdapter
;
51 import eu
.etaxonomy
.cdm
.jaxb
.UUIDAdapter
;
52 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
;
53 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchMode
;
59 * The base class for all CDM domain classes implementing UUIDs and bean property change event firing.
60 * It provides a globally unique UUID and keeps track of creation date and person.
61 * The UUID is the same for different versions (see {@link VersionableEntity}) of a CDM object, so a locally unique id exists in addition
62 * that allows to safely access and store several objects (=version) with the same UUID.
64 * This class together with the {@link eu.etaxonomy.cdm.aspectj.PropertyChangeAspect}
65 * will fire bean change events to all registered listeners. Listener registration and event firing
66 * is done with the help of the {@link PropertyChangeSupport} class.
71 @XmlAccessorType(XmlAccessType
.FIELD
)
72 @XmlType(name
= "CdmBase", propOrder
= {
77 public abstract class CdmBase
implements Serializable
, ICdmBase
, Cloneable
{
78 private static final long serialVersionUID
= -3053225700018294809L;
79 @SuppressWarnings("unused")
80 private static final Logger logger
= Logger
.getLogger(CdmBase
.class);
84 private PropertyChangeSupport propertyChangeSupport
= new PropertyChangeSupport(this);
86 //@XmlAttribute(name = "id", required = true)
89 // @GeneratedValue(generator = "system-increment")
90 // @GeneratedValue(generator = "enhanced-table")
91 @GeneratedValue(generator
= "custom-enhanced-table")
93 @Match(MatchMode
.IGNORE
)
98 @XmlAttribute(required
= true)
99 @XmlJavaTypeAdapter(UUIDAdapter
.class)
100 @Type(type
="uuidUserType")
101 @NaturalId // This has the effect of placing a "unique" constraint on the database column
104 @Match(MatchMode
.IGNORE
)
108 @XmlElement (name
= "Created", type
= String
.class)
109 @XmlJavaTypeAdapter(DateTimeAdapter
.class)
110 @Type(type
="dateTimeUserType")
111 @Basic(fetch
= FetchType
.LAZY
)
112 @Match(MatchMode
.IGNORE
)
113 @Field(index
= org
.hibernate
.search
.annotations
.Index
.UN_TOKENIZED
)
114 @FieldBridge(impl
= DateTimeBridge
.class)
115 private DateTime created
;
117 @XmlElement (name
= "CreatedBy")
119 @XmlSchemaType(name
= "IDREF")
120 @ManyToOne(fetch
=FetchType
.LAZY
)
121 @Match(MatchMode
.IGNORE
)
122 private User createdBy
;
125 * Class constructor assigning a unique UUID and creation date.
126 * UUID can be changed later via setUuid method.
129 this.uuid
= UUID
.randomUUID();
130 this.created
= new DateTime().withMillisOfSecond(0);
134 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
137 public void addPropertyChangeListener(PropertyChangeListener listener
) {
138 propertyChangeSupport
.addPropertyChangeListener(listener
);
142 * see {@link PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)}
144 public void addPropertyChangeListener(String propertyName
, PropertyChangeListener listener
) {
145 propertyChangeSupport
.addPropertyChangeListener(propertyName
, listener
);
149 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
151 public void removePropertyChangeListener(PropertyChangeListener listener
) {
152 propertyChangeSupport
.removePropertyChangeListener(listener
);
156 * @see PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)
158 public void removePropertyChangeListener(String propertyName
, PropertyChangeListener listener
) {
159 propertyChangeSupport
.removePropertyChangeListener(propertyName
, listener
);
162 public boolean hasListeners(String propertyName
) {
163 return propertyChangeSupport
.hasListeners(propertyName
);
166 public void firePropertyChange(String property
, String oldval
, String newval
) {
167 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
169 public void firePropertyChange(String property
, int oldval
, int newval
) {
170 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
172 public void firePropertyChange(String property
, float oldval
, float newval
) {
173 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
175 public void firePropertyChange(String property
, boolean oldval
, boolean newval
) {
176 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
178 public void firePropertyChange(String property
, Object oldval
, Object newval
) {
179 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
181 public void firePropertyChange(PropertyChangeEvent evt
) {
182 propertyChangeSupport
.firePropertyChange(evt
);
186 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getUuid()
188 public UUID
getUuid() {
192 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setUuid(java.util.UUID)
194 public void setUuid(UUID uuid
) {
199 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getId()
205 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setId(int)
207 public void setId(int id
) {
212 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreated()
214 public DateTime
getCreated() {
218 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreated(java.util.Calendar)
220 public void setCreated(DateTime created
) {
221 if (created
!= null){
223 created
= created
.withMillisOfSecond(0);
224 //created.set(Calendar.MILLISECOND, 0); //old, can be deleted
226 this.created
= created
;
231 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreatedBy()
233 public User
getCreatedBy() {
234 return this.createdBy
;
237 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreatedBy(eu.etaxonomy.cdm.model.agent.Person)
239 public void setCreatedBy(User createdBy
) {
240 this.createdBy
= createdBy
;
243 // ************************** Hibernate proxies *******************/
245 * These methods are present due to HHH-1517 - that in a one-to-many
246 * relationship with a superclass at the "one" end, the proxy created
247 * by hibernate is the superclass, and not the subclass, resulting in
248 * a classcastexception when you try to cast it.
250 * Hopefully this will be resolved through improvements with the creation of
251 * proxy objects by hibernate and the following methods will become redundant,
252 * but for the time being . . .
257 * @throws ClassCastException
259 //non-static does not work because javassist already unwrapps the proxy before calling the method
260 public static <T
extends CdmBase
> T
deproxy(Object object
, Class
<T
> clazz
) throws ClassCastException
{
261 return HibernateProxyHelper
.deproxy(object
, clazz
);
264 public boolean isInstanceOf(Class
<?
extends CdmBase
> clazz
) throws ClassCastException
{
265 return HibernateProxyHelper
.isInstanceOf(this, clazz
);
268 // ************* Object overrides *************************/
271 * Is true if UUID is the same for the passed Object and this one.
272 * @see java.lang.Object#equals(java.lang.Object)
273 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities}
274 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
275 * for more information about equals and hashcode.
278 public boolean equals(Object obj
) {
285 if (!CdmBase
.class.isAssignableFrom(obj
.getClass())){
288 ICdmBase cdmObj
= (ICdmBase
)obj
;
289 boolean uuidEqual
= cdmObj
.getUuid().equals(this.getUuid());
290 boolean createdEqual
= cdmObj
.getCreated().equals(this.getCreated());
291 if (! uuidEqual
|| !createdEqual
){
298 /** Overrides {@link java.lang.Object#hashCode()}
299 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities}
300 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
301 * for more information about equals and hashcode.
304 public int hashCode() {
306 if(this.getUuid() != null) {
307 //this unfortunately leads to errors when loading maps via hibernate
308 //as hibernate computes hash values for CdmBase objects used as key at
309 // a time when the uuid is not yet loaded from the database. Therefore
310 //the hash values later change and give wrong results when retrieving
311 //data from the map (map.get(key) returns null, though there is an entry
312 //for key in the map.
313 //see further comments in #2114
314 int result
= 29 * hashCode
+ this.getUuid().hashCode();
315 // int shresult = 29 * hashCode + Integer.valueOf(this.getId()).hashCode();
318 return 29 * hashCode
;
323 * Overrides {@link java.lang.Object#toString()}.
324 * This returns an String that identifies the object well without beeing necessarily unique.
325 * Specification: This method should never call other object' methods so it can be well used for debugging
326 * without problems like lazy loading, unreal states etc.
327 * Note: If overriding this method's javadoc always copy or link the above requirement.
328 * If not overwritten by a subclass method returns the class, id and uuid as a string for any CDM object.
329 * For example: Taxon#13<b5938a98-c1de-4dda-b040-d5cc5bfb3bc0>
330 * @see java.lang.Object#toString()
333 public String
toString() {
334 return this.getClass().getSimpleName()+"#"+this.getId()+"<"+this.getUuid()+">";
337 // **************** invoke methods **************************/
339 protected void invokeSetMethod(Method method
, Object object
){
341 method
.invoke(object
, this);
342 } catch (Exception e
) {
344 //TODO handle exceptioin;
348 protected void invokeSetMethodWithNull(Method method
, Object object
){
350 Object
[] nul
= new Object
[]{null};
351 method
.invoke(object
, nul
);
352 } catch (Exception e
) {
354 //TODO handle exceptioin;
358 //********************** CLONE *****************************************/
360 protected void clone(CdmBase clone
){
361 clone
.setCreatedBy(createdBy
);
363 clone
.propertyChangeSupport
=new PropertyChangeSupport(clone
);
364 //Constructor Attributes
365 //clone.setCreated(created);
366 //clone.setUuid(getUuid());
371 * @see java.lang.Object#clone()
374 public Object
clone() throws CloneNotSupportedException
{
375 CdmBase result
= (CdmBase
)super.clone();
376 result
.propertyChangeSupport
=new PropertyChangeSupport(result
);
380 result
.setUuid(UUID
.randomUUID());
381 result
.setCreated(new DateTime());
382 result
.setCreatedBy(null);