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
.envers
.Audited
;
43 import org
.hibernate
.search
.annotations
.Analyze
;
44 import org
.hibernate
.search
.annotations
.DocumentId
;
45 import org
.hibernate
.search
.annotations
.Field
;
46 import org
.hibernate
.search
.annotations
.FieldBridge
;
47 import org
.hibernate
.search
.annotations
.Index
;
48 import org
.hibernate
.search
.annotations
.Store
;
49 import org
.hibernate
.search
.annotations
.TermVector
;
50 import org
.joda
.time
.DateTime
;
52 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
53 import eu
.etaxonomy
.cdm
.hibernate
.search
.DateTimeBridge
;
54 import eu
.etaxonomy
.cdm
.hibernate
.search
.NotNullAwareIdBridge
;
55 import eu
.etaxonomy
.cdm
.hibernate
.search
.UuidBridge
;
56 import eu
.etaxonomy
.cdm
.jaxb
.DateTimeAdapter
;
57 import eu
.etaxonomy
.cdm
.jaxb
.UUIDAdapter
;
58 import eu
.etaxonomy
.cdm
.strategy
.match
.Match
;
59 import eu
.etaxonomy
.cdm
.strategy
.match
.MatchMode
;
65 * The base class for all CDM domain classes implementing UUIDs and bean property change event firing.
66 * It provides a globally unique UUID and keeps track of creation date and person.
67 * The UUID is the same for different versions (see {@link VersionableEntity}) of a CDM object, so a locally unique id exists in addition
68 * that allows to safely access and store several objects (=version) with the same UUID.
70 * This class together with the {@link eu.etaxonomy.cdm.aspectj.PropertyChangeAspect}
71 * will fire bean change events to all registered listeners. Listener registration and event firing
72 * is done with the help of the {@link PropertyChangeSupport} class.
77 @XmlAccessorType(XmlAccessType
.FIELD
)
78 @XmlType(name
= "CdmBase", propOrder
= {
83 public abstract class CdmBase
implements Serializable
, ICdmBase
, Cloneable
{
84 private static final long serialVersionUID
= -3053225700018294809L;
85 @SuppressWarnings("unused")
86 private static final Logger logger
= Logger
.getLogger(CdmBase
.class);
90 private PropertyChangeSupport propertyChangeSupport
= new PropertyChangeSupport(this);
92 //@XmlAttribute(name = "id", required = true)
95 // @GeneratedValue(generator = "system-increment")
96 // @GeneratedValue(generator = "enhanced-table")
97 @GeneratedValue(generator
= "custom-enhanced-table")
99 @FieldBridge(impl
=NotNullAwareIdBridge
.class)
100 @Field(store
=Store
.YES
, termVector
=TermVector
.NO
)
101 @Match(MatchMode
.IGNORE
)
107 @XmlAttribute(required
= true)
108 @XmlJavaTypeAdapter(UUIDAdapter
.class)
110 @Type(type
="uuidUserType")
111 @NaturalId // This has the effect of placing a "unique" constraint on the database column
112 @Column(length
=36) //TODO needed? Type UUID will always assure that is exactly 36
113 @Match(MatchMode
.IGNORE
)
115 @Field(store
= Store
.YES
, index
= Index
.YES
, analyze
= Analyze
.NO
)
116 @FieldBridge(impl
= UuidBridge
.class)
120 @XmlElement (name
= "Created", type
= String
.class)
121 @XmlJavaTypeAdapter(DateTimeAdapter
.class)
122 @Type(type
="dateTimeUserType")
123 @Basic(fetch
= FetchType
.LAZY
)
124 @Match(MatchMode
.IGNORE
)
125 @Field(analyze
= Analyze
.NO
)
126 @FieldBridge(impl
= DateTimeBridge
.class)
128 private DateTime created
;
130 @XmlElement (name
= "CreatedBy")
132 @XmlSchemaType(name
= "IDREF")
133 @ManyToOne(fetch
=FetchType
.LAZY
)
134 @Match(MatchMode
.IGNORE
)
136 private User createdBy
;
139 * Class constructor assigning a unique UUID and creation date.
140 * UUID can be changed later via setUuid method.
143 this.uuid
= UUID
.randomUUID();
144 this.created
= new DateTime().withMillisOfSecond(0);
148 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
151 public void addPropertyChangeListener(PropertyChangeListener listener
) {
152 propertyChangeSupport
.addPropertyChangeListener(listener
);
156 * see {@link PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)}
158 public void addPropertyChangeListener(String propertyName
, PropertyChangeListener listener
) {
159 propertyChangeSupport
.addPropertyChangeListener(propertyName
, listener
);
163 * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
165 public void removePropertyChangeListener(PropertyChangeListener listener
) {
166 propertyChangeSupport
.removePropertyChangeListener(listener
);
170 * @see PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)
172 public void removePropertyChangeListener(String propertyName
, PropertyChangeListener listener
) {
173 propertyChangeSupport
.removePropertyChangeListener(propertyName
, listener
);
176 public boolean hasListeners(String propertyName
) {
177 return propertyChangeSupport
.hasListeners(propertyName
);
180 public void firePropertyChange(String property
, String oldval
, String newval
) {
181 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
183 public void firePropertyChange(String property
, int oldval
, int newval
) {
184 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
186 public void firePropertyChange(String property
, float oldval
, float newval
) {
187 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
189 public void firePropertyChange(String property
, boolean oldval
, boolean newval
) {
190 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
192 public void firePropertyChange(String property
, Object oldval
, Object newval
) {
193 propertyChangeSupport
.firePropertyChange(property
, oldval
, newval
);
195 public void firePropertyChange(PropertyChangeEvent evt
) {
196 propertyChangeSupport
.firePropertyChange(evt
);
200 public UUID
getUuid() {
204 public void setUuid(UUID uuid
) {
213 public void setId(int id
) { //see #265 (private ?)
218 public DateTime
getCreated() {
222 public void setCreated(DateTime created
) {
223 if (created
!= null){
225 created
= created
.withMillisOfSecond(0);
226 //created.set(Calendar.MILLISECOND, 0); //old, can be deleted
228 this.created
= created
;
233 public User
getCreatedBy() {
234 return this.createdBy
;
237 public void setCreatedBy(User createdBy
) {
238 this.createdBy
= createdBy
;
241 // ************************** Hibernate proxies *******************/
243 * These methods are present due to HHH-1517 - that in a one-to-many
244 * relationship with a superclass at the "one" end, the proxy created
245 * by hibernate is the superclass, and not the subclass, resulting in
246 * a classcastexception when you try to cast it.
248 * Hopefully this will be resolved through improvements with the creation of
249 * proxy objects by hibernate and the following methods will become redundant,
250 * but for the time being . . .
255 * @throws ClassCastException
257 //non-static does not work because javassist already unwrapps the proxy before calling the method
258 public static <T
extends CdmBase
> T
deproxy(Object object
, Class
<T
> clazz
) throws ClassCastException
{
259 return HibernateProxyHelper
.deproxy(object
, clazz
);
262 public boolean isInstanceOf(Class
<?
extends CdmBase
> clazz
) throws ClassCastException
{
263 return HibernateProxyHelper
.isInstanceOf(this, clazz
);
266 // ************* Object overrides *************************/
269 * Is true if UUID is the same for the passed Object and this one.
270 * @see java.lang.Object#equals(java.lang.Object)
271 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities}
272 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
273 * for more information about equals and hashcode.
276 public boolean equals(Object obj
) {
283 if (!CdmBase
.class.isAssignableFrom(obj
.getClass())){
286 ICdmBase cdmObj
= (ICdmBase
)obj
;
287 boolean uuidEqual
= cdmObj
.getUuid().equals(this.getUuid());
288 boolean createdEqual
= cdmObj
.getCreated().equals(this.getCreated());
289 if (! uuidEqual
|| !createdEqual
){
296 /** Overrides {@link java.lang.Object#hashCode()}
297 * See {@link http://www.hibernate.org/109.html hibernate109}, {@link http://www.geocities.com/technofundo/tech/java/equalhash.html geocities}
298 * or {@link http://www.ibm.com/developerworks/java/library/j-jtp05273.html ibm}
299 * for more information about equals and hashcode.
302 public int hashCode() {
304 if(this.getUuid() != null) {
305 //this unfortunately leads to errors when loading maps via hibernate
306 //as hibernate computes hash values for CdmBase objects used as key at
307 // a time when the uuid is not yet loaded from the database. Therefore
308 //the hash values later change and give wrong results when retrieving
309 //data from the map (map.get(key) returns null, though there is an entry
310 //for key in the map.
311 //see further comments in #2114
312 int result
= 29 * hashCode
+ this.getUuid().hashCode();
313 // int shresult = 29 * hashCode + Integer.valueOf(this.getId()).hashCode();
316 return 29 * hashCode
;
321 * Overrides {@link java.lang.Object#toString()}.
322 * This returns an String that identifies the object well without being necessarily unique. Internally the method is delegating the
323 * call to {link {@link #instanceToString()}.<br>
324 * <b>Specification:</b> This method should never call other object' methods so it can be well used for debugging
325 * without problems like lazy loading, unreal states etc.
327 * <b>Note</b>: 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.
330 * <b>For example</b>: Taxon#13<b5938a98-c1de-4dda-b040-d5cc5bfb3bc0>
331 * @see java.lang.Object#toString()
334 public String
toString() {
335 return instanceToString();
339 * This returns an String that identifies the cdm instacne well without being necessarily unique.
340 * The string representation combines the class name the {@link #id} and {@link #uuid}.
342 * <b>For example</b>: Taxon#13<b5938a98-c1de-4dda-b040-d5cc5bfb3bc0>
345 public String
instanceToString() {
346 return this.getClass().getSimpleName()+"#"+this.getId()+"<"+this.getUuid()+">";
349 // **************** invoke methods **************************/
351 protected void invokeSetMethod(Method method
, Object object
){
353 method
.invoke(object
, this);
354 } catch (Exception e
) {
356 //TODO handle exceptioin;
360 protected void invokeSetMethodWithNull(Method method
, Object object
){
362 Object
[] nul
= new Object
[]{null};
363 method
.invoke(object
, nul
);
364 } catch (Exception e
) {
366 //TODO handle exceptioin;
370 //********************** CLONE *****************************************/
372 // protected void clone(CdmBase clone){
373 // clone.setCreatedBy(createdBy);
375 // clone.propertyChangeSupport=new PropertyChangeSupport(clone);
376 // //Constructor Attributes
377 // //clone.setCreated(created);
378 // //clone.setUuid(getUuid());
383 public Object
clone() throws CloneNotSupportedException
{
384 CdmBase result
= (CdmBase
)super.clone();
385 result
.propertyChangeSupport
=new PropertyChangeSupport(result
);
389 result
.setUuid(UUID
.randomUUID());
390 result
.setCreated(new DateTime());
391 result
.setCreatedBy(null);