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
.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
;
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
;
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.
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.
70 @XmlAccessorType(XmlAccessType
.FIELD
)
71 @XmlType(name
= "CdmBase", propOrder
= {
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);
83 private PropertyChangeSupport propertyChangeSupport
= new PropertyChangeSupport(this);
85 //@XmlAttribute(name = "id", required = true)
88 @GeneratedValue(generator
= "system-increment")
90 @Match(MatchMode
.IGNORE
)
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
101 @Match(MatchMode
.IGNORE
)
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
;
114 @XmlElement (name
= "CreatedBy")
116 @XmlSchemaType(name
= "IDREF")
117 @ManyToOne(fetch
=FetchType
.LAZY
)
118 @Match(MatchMode
.IGNORE
)
119 private User createdBy
;
122 * Class constructor assigning a unique UUID and creation date.
123 * UUID can be changed later via setUuid method.
126 this.uuid
= UUID
.randomUUID();
127 this.created
= new DateTime().withMillisOfSecond(0);
131 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
134 public void addPropertyChangeListener(PropertyChangeListener listener
) {
135 propertyChangeSupport
.addPropertyChangeListener(listener
);
139 * see {@link PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)}
141 public void addPropertyChangeListener(String propertyName
, PropertyChangeListener listener
) {
142 propertyChangeSupport
.addPropertyChangeListener(propertyName
, listener
);
146 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
148 public void removePropertyChangeListener(PropertyChangeListener listener
) {
149 propertyChangeSupport
.removePropertyChangeListener(listener
);
153 * @see PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)
155 public void removePropertyChangeListener(String propertyName
, PropertyChangeListener listener
) {
156 propertyChangeSupport
.removePropertyChangeListener(propertyName
, listener
);
159 public boolean hasListeners(String propertyName
) {
160 return propertyChangeSupport
.hasListeners(propertyName
);
163 public void firePropertyChange(String property
, String oldval
, String newval
) {
164 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
166 public void firePropertyChange(String property
, int oldval
, int newval
) {
167 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
169 public void firePropertyChange(String property
, float oldval
, float newval
) {
170 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
172 public void firePropertyChange(String property
, boolean oldval
, boolean newval
) {
173 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
175 public void firePropertyChange(String property
, Object oldval
, Object newval
) {
176 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
178 public void firePropertyChange(PropertyChangeEvent evt
) {
179 propertyChangeSupport
.firePropertyChange(evt
);
183 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getUuid()
185 public UUID
getUuid() {
189 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setUuid(java.util.UUID)
191 public void setUuid(UUID uuid
) {
196 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getId()
202 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setId(int)
204 public void setId(int id
) {
209 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreated()
211 public DateTime
getCreated() {
215 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreated(java.util.Calendar)
217 public void setCreated(DateTime created
) {
218 if (created
!= null){
220 created
= created
.withMillisOfSecond(0);
221 //created.set(Calendar.MILLISECOND, 0); //old, can be deleted
223 this.created
= created
;
228 * @see eu.etaxonomy.cdm.model.common.ICdmBase#getCreatedBy()
230 public User
getCreatedBy() {
231 return this.createdBy
;
234 * @see eu.etaxonomy.cdm.model.common.ICdmBase#setCreatedBy(eu.etaxonomy.cdm.model.agent.Person)
236 public void setCreatedBy(User createdBy
) {
237 this.createdBy
= createdBy
;
240 // ************************** Hibernate proxies *******************/
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.
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 . . .
254 * @throws ClassCastException
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
);
261 public boolean isInstanceOf(Class
<?
extends CdmBase
> clazz
) throws ClassCastException
{
262 return HibernateProxyHelper
.isInstanceOf(this, clazz
);
265 // ************* Object overrides *************************/
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.
275 public boolean equals(Object obj
) {
282 if (!CdmBase
.class.isAssignableFrom(obj
.getClass())){
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
){
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.
301 public int hashCode() {
303 if(this.getUuid() != null) {
304 return 29 * hashCode
+ this.getUuid().hashCode();
306 return 29 * hashCode
;
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()
321 public String
toString() {
322 return this.getClass().getSimpleName()+"#"+this.getId()+"<"+this.getUuid()+">";
325 // **************** invoke methods **************************/
327 protected void invokeSetMethod(Method method
, Object object
){
329 method
.invoke(object
, this);
330 } catch (Exception e
) {
332 //TODO handle exceptioin;
336 protected void invokeSetMethodWithNull(Method method
, Object object
){
338 Object
[] nul
= new Object
[]{null};
339 method
.invoke(object
, nul
);
340 } catch (Exception e
) {
342 //TODO handle exceptioin;
346 //********************** CLONE *****************************************/
348 protected void clone(CdmBase clone
){
349 clone
.setCreatedBy(createdBy
);
351 //Constructor Attributes
352 //clone.setCreated(created);
353 //clone.setUuid(getUuid());
358 * @see java.lang.Object#clone()
361 public Object
clone() throws CloneNotSupportedException
{
362 CdmBase result
= (CdmBase
)super.clone();
366 result
.setUuid(UUID
.randomUUID());
367 result
.setCreated(new DateTime());
368 result
.setCreatedBy(null);