Project

General

Profile

Download (14.4 KB) Statistics
| Branch: | Tag: | Revision:
1 2faf407b Andreas Müller
/**
2
* Copyright (C) 2007 EDIT
3 62727fb6 Andreas Kohlbecker
* European Distributed Institute of Taxonomy
4 2faf407b Andreas Müller
* http://www.e-taxonomy.eu
5 62727fb6 Andreas Kohlbecker
*
6 2faf407b Andreas Müller
* 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 9479da48 Andreas Müller
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 1cfc54ab Andreas Müller
import java.lang.reflect.Method;
16 9479da48 Andreas Müller
import java.util.UUID;
17
18 f6765014 ben.clark
import javax.persistence.Basic;
19 1ea1c087 ben.clark
import javax.persistence.Column;
20 9479da48 Andreas Müller
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 a641fad9 ben.clark
import javax.validation.constraints.Min;
27 a784f00f Katja Luther
import javax.validation.constraints.NotNull;
28 70440a7d a.babadshanjan
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 ee91bcd9 ben.clark
import javax.xml.bind.annotation.XmlIDREF;
34 70440a7d a.babadshanjan
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 0d575644 Andreas Müller
import org.apache.log4j.Logger;
40 1ea1c087 ben.clark
import org.hibernate.annotations.NaturalId;
41 cfbd62fa Andreas Müller
import org.hibernate.annotations.Type;
42 a13c5f66 Andreas Müller
import org.hibernate.envers.Audited;
43
import org.hibernate.search.annotations.Analyze;
44 f6765014 ben.clark
import org.hibernate.search.annotations.DocumentId;
45 a784f00f Katja Luther
import org.hibernate.search.annotations.Field;
46
import org.hibernate.search.annotations.FieldBridge;
47 f6b17945 Andreas Kohlbecker
import org.hibernate.search.annotations.Index;
48
import org.hibernate.search.annotations.Store;
49 21f190ad Andreas Kohlbecker
import org.hibernate.search.annotations.TermVector;
50 9d0d8d15 a.babadshanjan
import org.joda.time.DateTime;
51 9479da48 Andreas Müller
52 ab5846c6 Andreas Müller
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
53 0930d1c2 Andreas Kohlbecker
import eu.etaxonomy.cdm.hibernate.search.DateTimeBridge;
54 21f190ad Andreas Kohlbecker
import eu.etaxonomy.cdm.hibernate.search.NotNullAwareIdBridge;
55 f6b17945 Andreas Kohlbecker
import eu.etaxonomy.cdm.hibernate.search.UuidBridge;
56 3f71435e Andreas Müller
import eu.etaxonomy.cdm.jaxb.DateTimeAdapter;
57 cfbd62fa Andreas Müller
import eu.etaxonomy.cdm.jaxb.UUIDAdapter;
58 5c8ababc Andreas Müller
import eu.etaxonomy.cdm.strategy.match.Match;
59
import eu.etaxonomy.cdm.strategy.match.MatchMode;
60 6f7d91bd Andreas Müller
61 9479da48 Andreas Müller
62
63 de6a1846 m.doering
64
/**
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 62727fb6 Andreas Kohlbecker
 * The UUID is the same for different versions (see {@link VersionableEntity}) of a CDM object, so a locally unique id exists in addition
68 de6a1846 m.doering
 * that allows to safely access and store several objects (=version) with the same UUID.
69 62727fb6 Andreas Kohlbecker
 *
70
 * This class together with the {@link eu.etaxonomy.cdm.aspectj.PropertyChangeAspect}
71 de6a1846 m.doering
 * 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.
73 62727fb6 Andreas Kohlbecker
 *
74 33f9b501 m.doering
 * @author m.doering
75 de6a1846 m.doering
 *
76
 */
77 ee91bcd9 ben.clark
@XmlAccessorType(XmlAccessType.FIELD)
78 70440a7d a.babadshanjan
@XmlType(name = "CdmBase", propOrder = {
79
    "created",
80 353c812c n.hoffmann
    "createdBy"
81 70440a7d a.babadshanjan
})
82 9479da48 Andreas Müller
@MappedSuperclass
83 d3ff3ed4 Andreas Müller
public abstract class CdmBase implements Serializable, ICdmBase, ISelfDescriptive, Cloneable{
84 f6b17945 Andreas Kohlbecker
    private static final long serialVersionUID = -3053225700018294809L;
85
    @SuppressWarnings("unused")
86
    private static final Logger logger = Logger.getLogger(CdmBase.class);
87 62727fb6 Andreas Kohlbecker
88 f6b17945 Andreas Kohlbecker
    @Transient
89
    @XmlTransient
90
    private PropertyChangeSupport propertyChangeSupport = new PropertyChangeSupport(this);
91 62727fb6 Andreas Kohlbecker
92 f6b17945 Andreas Kohlbecker
    //@XmlAttribute(name = "id", required = true)
93
    @XmlTransient
94
    @Id
95 1d36aa54 Andreas Müller
//	@GeneratedValue(generator = "system-increment")
96 e21e244b Andreas Kohlbecker
//	@GeneratedValue(generator = "enhanced-table")
97 f6b17945 Andreas Kohlbecker
    @GeneratedValue(generator = "custom-enhanced-table")
98
    @DocumentId
99 21f190ad Andreas Kohlbecker
    @FieldBridge(impl=NotNullAwareIdBridge.class)
100
    @Field(store=Store.YES, termVector=TermVector.NO)
101 f6b17945 Andreas Kohlbecker
    @Match(MatchMode.IGNORE)
102
    @NotNull
103
    @Min(0)
104 a13c5f66 Andreas Müller
    @Audited
105 f6b17945 Andreas Kohlbecker
    private int id;
106
107
    @XmlAttribute(required = true)
108 ee91bcd9 ben.clark
    @XmlJavaTypeAdapter(UUIDAdapter.class)
109 e850e1c8 Andreas Müller
    @XmlID
110 a0b80723 Katja Luther
    @Type(type="uuidUserType")
111 f6b17945 Andreas Kohlbecker
    @NaturalId // This has the effect of placing a "unique" constraint on the database column
112 95d79c4d Andreas Müller
    @Column(length=36)  //TODO needed? Type UUID will always assure that is exactly 36
113 f6b17945 Andreas Kohlbecker
    @Match(MatchMode.IGNORE)
114
    @NotNull
115 4af7bea2 Andreas Kohlbecker
    @Field(store = Store.YES, index = Index.YES, analyze = Analyze.NO)
116 f6b17945 Andreas Kohlbecker
    @FieldBridge(impl = UuidBridge.class)
117 a13c5f66 Andreas Müller
    @Audited
118 f6b17945 Andreas Kohlbecker
    protected UUID uuid;
119
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 4af7bea2 Andreas Kohlbecker
    @Field(analyze = Analyze.NO)
126 f6b17945 Andreas Kohlbecker
    @FieldBridge(impl = DateTimeBridge.class)
127 a13c5f66 Andreas Müller
    @Audited
128 f6b17945 Andreas Kohlbecker
    private DateTime created;
129
130
    @XmlElement (name = "CreatedBy")
131
    @XmlIDREF
132
    @XmlSchemaType(name = "IDREF")
133
    @ManyToOne(fetch=FetchType.LAZY)
134 ef777b91 Andreas Müller
    @Match(MatchMode.IGNORE)
135 a13c5f66 Andreas Müller
    @Audited
136 f6b17945 Andreas Kohlbecker
    private User createdBy;
137
138
    /**
139
     * Class constructor assigning a unique UUID and creation date.
140
     * UUID can be changed later via setUuid method.
141
     */
142
    public CdmBase() {
143
        this.uuid = UUID.randomUUID();
144
        this.created = new DateTime().withMillisOfSecond(0);
145
    }
146
147
    /**
148
     * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
149
     * @param listener
150
     */
151
    public void addPropertyChangeListener(PropertyChangeListener listener) {
152
        propertyChangeSupport.addPropertyChangeListener(listener);
153
    }
154
155
    /**
156
     * see {@link PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)}
157
     */
158
    public void addPropertyChangeListener(String propertyName, PropertyChangeListener listener) {
159
        propertyChangeSupport.addPropertyChangeListener(propertyName, listener);
160
    }
161
162
    /**
163
     * see {@link PropertyChangeSupport#addPropertyChangeListener(PropertyChangeListener)}
164
     */
165
    public void removePropertyChangeListener(PropertyChangeListener listener) {
166
        propertyChangeSupport.removePropertyChangeListener(listener);
167
    }
168
169
    /**
170
     * @see PropertyChangeSupport#addPropertyChangeListener(String, PropertyChangeListener)
171
     */
172
    public void removePropertyChangeListener(String propertyName, PropertyChangeListener listener) {
173
        propertyChangeSupport.removePropertyChangeListener(propertyName, listener);
174
    }
175
176
    public boolean hasListeners(String propertyName) {
177
        return propertyChangeSupport.hasListeners(propertyName);
178
    }
179
180
    public void firePropertyChange(String property, String oldval, String newval) {
181
        propertyChangeSupport.firePropertyChange(property, oldval, newval);
182
    }
183
    public void firePropertyChange(String property, int oldval, int newval) {
184
        propertyChangeSupport.firePropertyChange(property, oldval, newval);
185
    }
186
    public void firePropertyChange(String property, float oldval, float newval) {
187
        propertyChangeSupport.firePropertyChange(property, oldval, newval);
188
    }
189
    public void firePropertyChange(String property, boolean oldval, boolean newval) {
190
        propertyChangeSupport.firePropertyChange(property, oldval, newval);
191
    }
192
    public void firePropertyChange(String property, Object oldval, Object newval) {
193
        propertyChangeSupport.firePropertyChange(property, oldval, newval);
194
    }
195
    public void firePropertyChange(PropertyChangeEvent evt) {
196
        propertyChangeSupport.firePropertyChange(evt);
197
    }
198
199 4af7bea2 Andreas Kohlbecker
    @Override
200 f6b17945 Andreas Kohlbecker
    public UUID getUuid() {
201
        return uuid;
202
    }
203 4af7bea2 Andreas Kohlbecker
    @Override
204 f6b17945 Andreas Kohlbecker
    public void setUuid(UUID uuid) {
205
        this.uuid = uuid;
206
    }
207
208 4af7bea2 Andreas Kohlbecker
    @Override
209 f6b17945 Andreas Kohlbecker
    public int getId() {
210
        return this.id;
211
    }
212 4af7bea2 Andreas Kohlbecker
    @Override
213 0af6ff2f Andreas Müller
    public void setId(int id) {  //see #265 (private ?)
214 f6b17945 Andreas Kohlbecker
        this.id = id;
215
    }
216
217 4af7bea2 Andreas Kohlbecker
    @Override
218 f6b17945 Andreas Kohlbecker
    public DateTime getCreated() {
219
        return created;
220
    }
221 4af7bea2 Andreas Kohlbecker
    @Override
222 f6b17945 Andreas Kohlbecker
    public void setCreated(DateTime created) {
223
        if (created != null){
224
            new DateTime();
225
            created = created.withMillisOfSecond(0);
226
            //created.set(Calendar.MILLISECOND, 0);  //old, can be deleted
227
        }
228
        this.created = created;
229
    }
230
231
232 4af7bea2 Andreas Kohlbecker
    @Override
233 f6b17945 Andreas Kohlbecker
    public User getCreatedBy() {
234
        return this.createdBy;
235
    }
236 4af7bea2 Andreas Kohlbecker
    @Override
237 f6b17945 Andreas Kohlbecker
    public void setCreatedBy(User createdBy) {
238
        this.createdBy = createdBy;
239
    }
240 62727fb6 Andreas Kohlbecker
241 06c0f84d Andreas Müller
// ************************** Hibernate proxies *******************/
242 f6b17945 Andreas Kohlbecker
    /**
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.
247
     *
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 . . .
251
     * @param <T>
252
     * @param object
253
     * @param clazz
254
     * @return
255
     * @throws ClassCastException
256
     */
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);
260
     }
261
262
     public boolean isInstanceOf(Class<? extends CdmBase> clazz) throws ClassCastException {
263
         return HibernateProxyHelper.isInstanceOf(this, clazz);
264
     }
265 9479da48 Andreas Müller
266 62727fb6 Andreas Kohlbecker
// ************* Object overrides *************************/
267
268 f6b17945 Andreas Kohlbecker
    /**
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.
274
     */
275
    @Override
276
    public boolean equals(Object obj) {
277
        if (obj == this){
278
            return true;
279
        }
280
        if (obj == null){
281
            return false;
282
        }
283
        if (!CdmBase.class.isAssignableFrom(obj.getClass())){
284
            return false;
285
        }
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){
290
                return false;
291
        }
292
        return true;
293
    }
294
295
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.
300
     */
301
    @Override
302
    public int hashCode() {
303
           int hashCode = 7;
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 1d36aa54 Andreas Müller
//		       int shresult = 29 * hashCode + Integer.valueOf(this.getId()).hashCode();
314 f6b17945 Andreas Kohlbecker
               return result;
315
           } else {
316
               return 29 * hashCode;
317
           }
318
    }
319
320
    /**
321
     * Overrides {@link java.lang.Object#toString()}.
322 2ba2d16b Andreas Kohlbecker
     * 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 f6b17945 Andreas Kohlbecker
     * without problems like lazy loading, unreal states etc.
326 2ba2d16b Andreas Kohlbecker
     * <p>
327
     * <b>Note</b>: If overriding this method's javadoc always copy or link the above requirement.
328 f6b17945 Andreas Kohlbecker
     * If not overwritten by a subclass method returns the class, id and uuid as a string for any CDM object.
329 2ba2d16b Andreas Kohlbecker
     * <p>
330
     * <b>For example</b>: Taxon#13&lt;b5938a98-c1de-4dda-b040-d5cc5bfb3bc0&gt;
331 f6b17945 Andreas Kohlbecker
     * @see java.lang.Object#toString()
332
     */
333
    @Override
334
    public String toString() {
335 2ba2d16b Andreas Kohlbecker
        return instanceToString();
336
    }
337
338
    /**
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}.
341
     * <p>
342
     * <b>For example</b>: Taxon#13&lt;b5938a98-c1de-4dda-b040-d5cc5bfb3bc0&gt;
343
     * @return
344
     */
345 d5fe74be Andreas Kohlbecker
    public String instanceToString() {
346 f6b17945 Andreas Kohlbecker
        return this.getClass().getSimpleName()+"#"+this.getId()+"<"+this.getUuid()+">";
347
    }
348 62727fb6 Andreas Kohlbecker
349 06c0f84d Andreas Müller
// **************** invoke methods **************************/
350 62727fb6 Andreas Kohlbecker
351 f6b17945 Andreas Kohlbecker
    protected void invokeSetMethod(Method method, Object object){
352
        try {
353
            method.invoke(object, this);
354
        } catch (Exception e) {
355
            e.printStackTrace();
356
            //TODO handle exceptioin;
357
        }
358
    }
359
360
    protected void invokeSetMethodWithNull(Method method, Object object){
361
        try {
362
            Object[] nul = new Object[]{null};
363
            method.invoke(object, nul);
364
        } catch (Exception e) {
365
            e.printStackTrace();
366
            //TODO handle exceptioin;
367
        }
368
    }
369 d3ff3ed4 Andreas Müller
    
370
371
	@Override
372
	public String getUserFriendlyTypeName(){
373
		return getClass().getSimpleName();
374
	}
375
376
	@Override
377
	public String getUserFriendlyDescription(){
378
		return toString();
379
	}
380
381
	@Override
382
	public String getUserFriendlyFieldName(String field){
383
		return field;
384
	}
385 62727fb6 Andreas Kohlbecker
386 835c12dd Andreas Müller
//********************** CLONE *****************************************/
387 62727fb6 Andreas Kohlbecker
388 0af6ff2f Andreas Müller
//    protected void clone(CdmBase clone){
389
//        clone.setCreatedBy(createdBy);
390
//        clone.setId(id);
391
//        clone.propertyChangeSupport=new PropertyChangeSupport(clone);
392
//        //Constructor Attributes
393
//        //clone.setCreated(created);
394
//        //clone.setUuid(getUuid());
395
//
396
//    }
397 f6b17945 Andreas Kohlbecker
398
    @Override
399
    public Object clone() throws CloneNotSupportedException{
400
        CdmBase result = (CdmBase)super.clone();
401
        result.propertyChangeSupport=new PropertyChangeSupport(result);
402
403
        //TODO ?
404
        result.setId(0);
405
        result.setUuid(UUID.randomUUID());
406
        result.setCreated(new DateTime());
407
        result.setCreatedBy(null);
408
409
        //no changes to: -
410
        return result;
411
    }
412 62727fb6 Andreas Kohlbecker
413 9479da48 Andreas Müller
}