Project

General

Profile

Download (19.3 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
* Copyright (C) 2007 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
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.
8
*/
9

    
10
package eu.etaxonomy.cdm.model.occurrence;
11

    
12
import java.net.URI;
13
import java.util.ArrayList;
14
import java.util.Collection;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.Map;
18
import java.util.Set;
19

    
20
import javax.persistence.Column;
21
import javax.persistence.Entity;
22
import javax.persistence.FetchType;
23
import javax.persistence.Inheritance;
24
import javax.persistence.InheritanceType;
25
import javax.persistence.ManyToMany;
26
import javax.persistence.ManyToOne;
27
import javax.persistence.MapKeyJoinColumn;
28
import javax.persistence.OneToMany;
29
import javax.persistence.Transient;
30
import javax.validation.constraints.Min;
31
import javax.validation.constraints.NotNull;
32
import javax.xml.bind.annotation.XmlAccessType;
33
import javax.xml.bind.annotation.XmlAccessorType;
34
import javax.xml.bind.annotation.XmlAttribute;
35
import javax.xml.bind.annotation.XmlElement;
36
import javax.xml.bind.annotation.XmlElementWrapper;
37
import javax.xml.bind.annotation.XmlIDREF;
38
import javax.xml.bind.annotation.XmlRootElement;
39
import javax.xml.bind.annotation.XmlSchemaType;
40
import javax.xml.bind.annotation.XmlType;
41
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
42

    
43
import org.apache.log4j.Logger;
44
import org.hibernate.annotations.Cascade;
45
import org.hibernate.annotations.CascadeType;
46
import org.hibernate.annotations.Index;
47
import org.hibernate.annotations.Table;
48
import org.hibernate.annotations.Type;
49
import org.hibernate.envers.Audited;
50
import org.hibernate.search.annotations.Analyze;
51
import org.hibernate.search.annotations.Field;
52
import org.hibernate.search.annotations.IndexedEmbedded;
53
import org.hibernate.search.annotations.NumericField;
54

    
55
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
56
import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
57
import eu.etaxonomy.cdm.model.common.DefinedTerm;
58
import eu.etaxonomy.cdm.model.common.IMultiLanguageTextHolder;
59
import eu.etaxonomy.cdm.model.common.IPublishable;
60
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
61
import eu.etaxonomy.cdm.model.common.Language;
62
import eu.etaxonomy.cdm.model.common.LanguageString;
63
import eu.etaxonomy.cdm.model.common.MultilanguageText;
64
import eu.etaxonomy.cdm.model.common.TermType;
65
import eu.etaxonomy.cdm.model.description.DescriptionBase;
66
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
67
import eu.etaxonomy.cdm.model.description.IDescribable;
68
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
69
import eu.etaxonomy.cdm.model.description.TaxonDescription;
70
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
71
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
72

    
73
/**
74
 * type figures are observations with at least a figure object in media
75
 * @author m.doering
76
 * @created 08-Nov-2007 13:06:41
77
 */
78
@XmlAccessorType(XmlAccessType.FIELD)
79
@XmlType(name = "SpecimenOrObservationBase", propOrder = {
80
    "recordBasis",
81
    "publish",
82
    "preferredStableUri",
83
    "sex",
84
    "lifeStage",
85
    "kindOfUnit",
86
    "individualCount",
87
    "definition",
88
    "descriptions",
89
    "determinations",
90
    "derivationEvents"
91
})
92
@XmlRootElement(name = "SpecimenOrObservationBase")
93
@Entity
94
@Audited
95
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
96
@Table(appliesTo="SpecimenOrObservationBase", indexes = { @Index(name = "specimenOrObservationBaseTitleCacheIndex", columnNames = { "titleCache" }) })
97
public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCacheStrategy> extends IdentifiableEntity<S>
98
                implements IMultiLanguageTextHolder, IDescribable<DescriptionBase>, IPublishable  {
99
    private static final long serialVersionUID = 6932680139334408031L;
100
    private static final Logger logger = Logger.getLogger(SpecimenOrObservationBase.class);
101

    
102
    /**
103
     * An indication of what the unit record describes.
104
     *
105
     * NOTE: The name of the attribute was chosen against the common naming conventions of the CDM
106
     * as it is well known in common standards like ABCD and DarwinCore. According to CDM naming
107
     * conventions it would specimenOrObservationType.
108
     *
109
     * @see ABCD: DataSets/DataSet/Units/Unit/RecordBasis
110
     * @see Darwin Core: http://wiki.tdwg.org/twiki/bin/view/DarwinCore/BasisOfRecord
111
     */
112
    @XmlAttribute(name ="RecordBasis")
113
    @Column(name="recordBasis")
114
    @NotNull
115
    @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
116
        parameters = {@org.hibernate.annotations.Parameter(name  = "enumClass", value = "eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType")}
117
    )
118
    @Audited
119
    private SpecimenOrObservationType recordBasis;
120

    
121

    
122
    @XmlElementWrapper(name = "Descriptions")
123
    @XmlElement(name = "Description")
124
    @OneToMany(mappedBy="describedSpecimenOrObservation", fetch = FetchType.LAZY)
125
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
126
    @NotNull
127
    private Set<DescriptionBase> descriptions = new HashSet<DescriptionBase>();
128

    
129

    
130
    @XmlElementWrapper(name = "Determinations")
131
    @XmlElement(name = "Determination")
132
    @OneToMany(mappedBy="identifiedUnit", orphanRemoval=true)
133
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
134
    @IndexedEmbedded(depth = 2)
135
    @NotNull
136
    private Set<DeterminationEvent> determinations = new HashSet<DeterminationEvent>();
137

    
138
    @XmlElement(name = "Sex")
139
    @XmlIDREF
140
    @XmlSchemaType(name = "IDREF")
141
    @ManyToOne(fetch = FetchType.LAZY)
142
    private DefinedTerm sex;
143

    
144
    @XmlElement(name = "LifeStage")
145
    @XmlIDREF
146
    @XmlSchemaType(name = "IDREF")
147
    @ManyToOne(fetch = FetchType.LAZY)
148
    private DefinedTerm lifeStage;
149

    
150
    /**
151
     * Part(s) of organism or class of materials represented by this unit.
152
     * Example: fruits, seeds, tissue, gDNA, leaves
153
     *
154
     * @see ABCD: DataSets/DataSet/Units/Unit/KindOfUnit
155
     * @see TermType#KindOfUnit
156
     */
157
    @XmlElement(name = "KindOfUnit")
158
    @XmlIDREF
159
    @XmlSchemaType(name = "IDREF")
160
    @ManyToOne(fetch = FetchType.LAZY)
161
//    @IndexedEmbedded(depth=1)
162
    private DefinedTerm kindOfUnit;
163

    
164
    @XmlElement(name = "IndividualCount")
165
    @Field(analyze = Analyze.NO)
166
    @NumericField
167
    @Min(0)
168
    private Integer individualCount;
169

    
170
    //preferred stable identifer (URI) #5606
171
    @XmlElement(name = "PreferredStableUri")
172
    @Field(analyze = Analyze.NO)
173
    @Type(type="uriUserType")
174
    private URI preferredStableUri;
175

    
176
    // the verbatim description of this occurrence. Free text usable when no atomised data is available.
177
    // in conjunction with titleCache which serves as the "citation" string for this object
178
    @XmlElement(name = "Description")
179
    @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
180
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
181
    @MapKeyJoinColumn(name="definition_mapkey_id")
182
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
183
    @IndexedEmbedded
184
    @NotNull
185
    protected Map<Language,LanguageString> definition = new HashMap<Language,LanguageString>();
186

    
187
    // events that created derivedUnits from this unit
188
    @XmlElementWrapper(name = "DerivationEvents")
189
    @XmlElement(name = "DerivationEvent")
190
    @XmlIDREF
191
    @XmlSchemaType(name = "IDREF")
192
    @ManyToMany(fetch=FetchType.LAZY)
193
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
194
    @NotNull
195
    protected Set<DerivationEvent> derivationEvents = new HashSet<DerivationEvent>();
196

    
197
    @XmlAttribute(name = "publish")
198
    private boolean publish = true;
199

    
200

    
201
//********************************** CONSTRUCTOR *********************************/
202

    
203
    //for hibernate use only
204
    @Deprecated
205
    protected SpecimenOrObservationBase(){
206
        super();
207
    }
208

    
209
    protected SpecimenOrObservationBase(SpecimenOrObservationType recordBasis) {
210
        super();
211
        if (recordBasis == null){ throw new IllegalArgumentException("RecordBasis must not be null");}
212
        this.recordBasis = recordBasis;
213
    }
214

    
215

    
216
    /**
217
     * Subclasses should implement setting the default cache strate
218
     */
219
    protected abstract void initDefaultCacheStrategy();
220

    
221

    
222
//************************* GETTER / SETTER ***********************/
223

    
224

    
225

    
226
    /**
227
     * @see #recordBasis
228
     * @return
229
     */
230
    public SpecimenOrObservationType getRecordBasis() {
231
        return recordBasis;
232
    }
233

    
234
    /**
235
     * @see #recordBasis
236
     * @param recordBasis
237
     */
238
    public void setRecordBasis(SpecimenOrObservationType recordBasis) {
239
        this.recordBasis = recordBasis;
240
    }
241

    
242

    
243
    /**
244
     * Returns the boolean value indicating if this specimen or observation should be withheld
245
     * (<code>publish=false</code>) or not (<code>publish=true</code>) during any publication
246
     * process to the general public.
247
     * This publish flag implementation is preliminary and may be replaced by a more general
248
     * implementation of READ rights in future.<BR>
249
     * The default value is <code>true</code>.
250
     */
251
    @Override
252
    public boolean isPublish() {
253
        return publish;
254
    }
255

    
256
    /**
257
     * @see #isPublish()
258
     * @param publish
259
     */
260
    @Override
261
    public void setPublish(boolean publish) {
262
        this.publish = publish;
263
    }
264

    
265
    /**
266
     * The descriptions this specimen or observation is part of.<BR>
267
     * A specimen can not only have it's own {@link SpecimenDescription specimen description }
268
     * but can also be part of a {@link TaxonDescription taxon description} or a
269
     * {@link TaxonNameDescription taxon name description}.<BR>
270
     * @see #getSpecimenDescriptions()
271
     * @return
272
     */
273
    @Override
274
    public Set<DescriptionBase> getDescriptions() {
275
        if(descriptions == null) {
276
            this.descriptions = new HashSet<DescriptionBase>();
277
        }
278
        return this.descriptions;
279
    }
280

    
281
    /**
282
     * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
283
     * @see #getDescriptions()
284
     * @return
285
     */
286
    @Transient
287
    public Set<SpecimenDescription> getSpecimenDescriptions() {
288
        return getSpecimenDescriptions(true);
289
    }
290

    
291
    /**
292
     * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
293
     * @see #getDescriptions()
294
     * @return
295
     */
296
    @Transient
297
    public Set<SpecimenDescription> getSpecimenDescriptions(boolean includeImageGallery) {
298
        Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
299
        for (DescriptionBase descriptionBase : getDescriptions()){
300
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
301
                if (includeImageGallery || descriptionBase.isImageGallery() == false){
302
                    specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
303
                }
304

    
305
            }
306
        }
307
        return specimenDescriptions;
308
    }
309
    /**
310
     * Returns the {@link SpecimenDescription specimen descriptions} which act as an image gallery
311
     * and which this specimen is part of.
312
     * @see #getDescriptions()
313
     * @return
314
     */
315
    @Transient
316
    public Set<SpecimenDescription> getSpecimenDescriptionImageGallery() {
317
        Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
318
        for (DescriptionBase descriptionBase : getDescriptions()){
319
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
320
                if (descriptionBase.isImageGallery() == true){
321
                    specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
322
                }
323
            }
324
        }
325
        return specimenDescriptions;
326
    }
327

    
328
    /**
329
     * Adds a new description to this specimen or observation
330
     * @param description
331
     */
332
    @Override
333
    public void addDescription(DescriptionBase description) {
334
        if (description.getDescribedSpecimenOrObservation() != null){
335
            description.getDescribedSpecimenOrObservation().removeDescription(description);
336
        }
337
        descriptions.add(description);
338
        description.setDescribedSpecimenOrObservation(this);
339
    }
340

    
341
    /**
342
     * Removes a specimen from a description (removes a description from this specimen)
343
     * @param description
344
     */
345
    @Override
346
    public void removeDescription(DescriptionBase description) {
347
        boolean existed = descriptions.remove(description);
348
        if (existed){
349
            description.setDescribedSpecimenOrObservation(null);
350
        }
351
    }
352

    
353

    
354
    public Set<DerivationEvent> getDerivationEvents() {
355
        if(derivationEvents == null) {
356
            this.derivationEvents = new HashSet<DerivationEvent>();
357
        }
358
        return this.derivationEvents;
359
    }
360

    
361
    public void addDerivationEvent(DerivationEvent derivationEvent) {
362
        if (! this.derivationEvents.contains(derivationEvent)){
363
            this.derivationEvents.add(derivationEvent);
364
            derivationEvent.addOriginal(this);
365
        }
366
    }
367

    
368
    public void removeDerivationEvent(DerivationEvent derivationEvent) {
369
        if (this.derivationEvents.contains(derivationEvent)){
370
            this.derivationEvents.remove(derivationEvent);
371
            derivationEvent.removeOriginal(this);
372
        }
373
    }
374

    
375
    public Set<DeterminationEvent> getDeterminations() {
376
        if(determinations == null) {
377
            this.determinations = new HashSet<DeterminationEvent>();
378
        }
379
        return this.determinations;
380
    }
381

    
382
    public void addDetermination(DeterminationEvent determination) {
383
        // FIXME bidirectional integrity. Use protected Determination setter
384
        this.determinations.add(determination);
385
    }
386

    
387
    public void removeDetermination(DeterminationEvent determination) {
388
        // FIXME bidirectional integrity. Use protected Determination setter
389
        this.determinations.remove(determination);
390
    }
391

    
392
    public DefinedTerm getSex() {
393
        return sex;
394
    }
395

    
396
    public void setSex(DefinedTerm sex) {
397
        this.sex = sex;
398
    }
399

    
400
    public DefinedTerm getLifeStage() {
401
        return lifeStage;
402
    }
403

    
404
    public void setLifeStage(DefinedTerm lifeStage) {
405
        this.lifeStage = lifeStage;
406
    }
407

    
408

    
409
    /**
410
     * @see #kindOfUnit
411
     * @return
412
     */
413
    public DefinedTerm getKindOfUnit() {
414
        return kindOfUnit;
415
    }
416

    
417
    /**
418
     * @see #kindOfUnit
419
     * @param kindOfUnit
420
     */
421
    public void setKindOfUnit(DefinedTerm kindOfUnit) {
422
        this.kindOfUnit = kindOfUnit;
423
    }
424

    
425
    public Integer getIndividualCount() {
426
        return individualCount;
427
    }
428

    
429
    public void setIndividualCount(Integer individualCount) {
430
        this.individualCount = individualCount;
431
    }
432

    
433
    public Map<Language,LanguageString> getDefinition(){
434
        return this.definition;
435
    }
436

    
437
    /**
438
     * adds the {@link LanguageString description} to the {@link MultilanguageText multilanguage text}
439
     * used to define <i>this</i> specimen or observation.
440
     *
441
     * @param description	the languageString in with the title string and the given language
442
     *
443
     * @see    	   		#getDefinition()
444
     * @see    	   		#putDefinition(Language, String)
445
     */
446
    public void putDefinition(LanguageString description){
447
        this.definition.put(description.getLanguage(),description);
448
    }
449

    
450
    /**
451
     * Creates a {@link LanguageString language string} based on the given text string
452
     * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
453
     * used to define <i>this</i> specimen or observation.
454
     *
455
     * @param language	the language in which the title string is formulated
456
     * @param text		the definition in a particular language
457
     *
458
     * @see    	   		#getDefinition()
459
     * @see    	   		#putDefinition(LanguageString)
460
     */
461
    public void putDefinition(Language language, String text){
462
        this.definition.put(language, LanguageString.NewInstance(text, language));
463
    }
464

    
465

    
466
    public void removeDefinition(Language lang){
467
        this.definition.remove(lang);
468
    }
469

    
470
    /**
471
     * for derived units get the single next higher parental/original unit.
472
     * If multiple original units exist throw error
473
     * @return
474
     */
475
    @Transient
476
    public SpecimenOrObservationBase getOriginalUnit(){
477
        logger.warn("GetOriginalUnit not yet implemented");
478
        return null;
479
    }
480

    
481

    
482
    public boolean hasCharacterData() {
483
        Set<DescriptionBase> descriptions = this.getDescriptions();
484
        for (DescriptionBase<?> descriptionBase : descriptions) {
485
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
486
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
487
                Set<DescriptionElementBase> elements = specimenDescription.getElements();
488
                for (DescriptionElementBase descriptionElementBase : elements) {
489
                    if (descriptionElementBase.isCharacterData()){
490
                        return true;
491
                    }
492
                }
493
            }
494
        }
495
        return false;
496
    }
497

    
498
    /**
499
     * Returns a list of all description items which
500
     * @return
501
     */
502
    @Transient
503
    public Collection<DescriptionElementBase> characterData() {
504
        Collection<DescriptionElementBase> states = new ArrayList<DescriptionElementBase>();
505
        Set<DescriptionBase> descriptions = this.getDescriptions();
506
        for (DescriptionBase<?> descriptionBase : descriptions) {
507
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
508
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
509
                Set<DescriptionElementBase> elements = specimenDescription.getElements();
510
                for (DescriptionElementBase descriptionElementBase : elements) {
511
                    if(descriptionElementBase.isCharacterData()){
512
                        states.add(descriptionElementBase);
513
                    }
514
                }
515
            }
516
        }
517
        return states;
518
    }
519

    
520

    
521

    
522
//******************** CLONE **********************************************/
523

    
524
    /* (non-Javadoc)
525
     * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
526
     * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
527
     * @see java.lang.Object#clone()
528
     */
529
    @Override
530
    public Object clone() throws CloneNotSupportedException {
531
        SpecimenOrObservationBase result = null;
532
        result = (SpecimenOrObservationBase)super.clone();
533

    
534
        //defininion (description, languageString)
535
        result.definition = new HashMap<Language,LanguageString>();
536
        for(LanguageString languageString : this.definition.values()) {
537
            LanguageString newLanguageString = (LanguageString)languageString.clone();
538
            result.putDefinition(newLanguageString);
539
        }
540

    
541
        //sex
542
        result.setSex(this.sex);
543
        //life stage
544
        result.setLifeStage(this.lifeStage);
545

    
546
        //Descriptions
547
        for(DescriptionBase description : this.descriptions) {
548
            result.addDescription(description);
549
        }
550

    
551
        //DeterminationEvent FIXME should clone() the determination
552
        // as the relationship is OneToMany
553
        for(DeterminationEvent determination : this.determinations) {
554
            result.addDetermination(determination);
555
        }
556

    
557
        //DerivationEvent
558
        for(DerivationEvent derivationEvent : this.derivationEvents) {
559
            result.addDerivationEvent(derivationEvent);
560
        }
561

    
562
        //no changes to: individualCount
563
        return result;
564
    }
565

    
566

    
567
}
(11-11/14)