Project

General

Profile

Download (19.1 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.util.ArrayList;
13
import java.util.Collection;
14
import java.util.HashMap;
15
import java.util.HashSet;
16
import java.util.Map;
17
import java.util.Set;
18

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

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

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

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

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

    
119

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

    
127

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

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

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

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

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

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

    
179
    // events that created derivedUnits from this unit
180
    @XmlElementWrapper(name = "DerivationEvents")
181
    @XmlElement(name = "DerivationEvent")
182
    @XmlIDREF
183
    @XmlSchemaType(name = "IDREF")
184
    @ManyToMany(fetch=FetchType.LAZY)
185
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
186
    @NotNull
187
    protected Set<DerivationEvent> derivationEvents = new HashSet<DerivationEvent>();
188

    
189
    @XmlAttribute(name = "publish")
190
    private boolean publish = true;
191

    
192

    
193
//********************************** CONSTRUCTOR *********************************/
194

    
195
    //for hibernate use only
196
    @Deprecated
197
    protected SpecimenOrObservationBase(){
198
        super();
199
    }
200

    
201
    protected SpecimenOrObservationBase(SpecimenOrObservationType recordBasis) {
202
        super();
203
        if (recordBasis == null){ throw new IllegalArgumentException("RecordBasis must not be null");}
204
        this.recordBasis = recordBasis;
205
    }
206

    
207

    
208
    /**
209
     * Subclasses should implement setting the default cache strate
210
     */
211
    protected abstract void initDefaultCacheStrategy();
212

    
213

    
214
//************************* GETTER / SETTER ***********************/
215

    
216

    
217

    
218
    /**
219
     * @see #recordBasis
220
     * @return
221
     */
222
    public SpecimenOrObservationType getRecordBasis() {
223
        return recordBasis;
224
    }
225

    
226
    /**
227
     * @see #recordBasis
228
     * @param recordBasis
229
     */
230
    public void setRecordBasis(SpecimenOrObservationType recordBasis) {
231
        this.recordBasis = recordBasis;
232
    }
233

    
234

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

    
248
    /**
249
     * @see #isPublish()
250
     * @param publish
251
     */
252
    @Override
253
    public void setPublish(boolean publish) {
254
        this.publish = publish;
255
    }
256

    
257
    /**
258
     * The descriptions this specimen or observation is part of.<BR>
259
     * A specimen can not only have it's own {@link SpecimenDescription specimen description }
260
     * but can also be part of a {@link TaxonDescription taxon description} or a
261
     * {@link TaxonNameDescription taxon name description}.<BR>
262
     * @see #getSpecimenDescriptions()
263
     * @return
264
     */
265
    @Override
266
    public Set<DescriptionBase> getDescriptions() {
267
        if(descriptions == null) {
268
            this.descriptions = new HashSet<DescriptionBase>();
269
        }
270
        return this.descriptions;
271
    }
272

    
273
    /**
274
     * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
275
     * @see #getDescriptions()
276
     * @return
277
     */
278
    @Transient
279
    public Set<SpecimenDescription> getSpecimenDescriptions() {
280
        return getSpecimenDescriptions(true);
281
    }
282

    
283
    /**
284
     * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
285
     * @see #getDescriptions()
286
     * @return
287
     */
288
    @Transient
289
    public Set<SpecimenDescription> getSpecimenDescriptions(boolean includeImageGallery) {
290
        Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
291
        for (DescriptionBase descriptionBase : getDescriptions()){
292
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
293
                if (includeImageGallery || descriptionBase.isImageGallery() == false){
294
                    specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
295
                }
296

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

    
320
    /**
321
     * Adds a new description to this specimen or observation
322
     * @param description
323
     */
324
    @Override
325
    public void addDescription(DescriptionBase description) {
326
        if (description.getDescribedSpecimenOrObservation() != null){
327
            description.getDescribedSpecimenOrObservation().removeDescription(description);
328
        }
329
        descriptions.add(description);
330
        description.setDescribedSpecimenOrObservation(this);
331
    }
332

    
333
    /**
334
     * Removes a specimen from a description (removes a description from this specimen)
335
     * @param description
336
     */
337
    @Override
338
    public void removeDescription(DescriptionBase description) {
339
        boolean existed = descriptions.remove(description);
340
        if (existed){
341
            description.setDescribedSpecimenOrObservation(null);
342
        }
343
    }
344

    
345

    
346
    public Set<DerivationEvent> getDerivationEvents() {
347
        if(derivationEvents == null) {
348
            this.derivationEvents = new HashSet<DerivationEvent>();
349
        }
350
        return this.derivationEvents;
351
    }
352

    
353
    public void addDerivationEvent(DerivationEvent derivationEvent) {
354
        if (! this.derivationEvents.contains(derivationEvent)){
355
            this.derivationEvents.add(derivationEvent);
356
            derivationEvent.addOriginal(this);
357
        }
358
    }
359

    
360
    public void removeDerivationEvent(DerivationEvent derivationEvent) {
361
        if (this.derivationEvents.contains(derivationEvent)){
362
            this.derivationEvents.remove(derivationEvent);
363
            derivationEvent.removeOriginal(this);
364
        }
365
    }
366

    
367
    public Set<DeterminationEvent> getDeterminations() {
368
        if(determinations == null) {
369
            this.determinations = new HashSet<DeterminationEvent>();
370
        }
371
        return this.determinations;
372
    }
373

    
374
    public void addDetermination(DeterminationEvent determination) {
375
        // FIXME bidirectional integrity. Use protected Determination setter
376
        this.determinations.add(determination);
377
    }
378

    
379
    public void removeDetermination(DeterminationEvent determination) {
380
        // FIXME bidirectional integrity. Use protected Determination setter
381
        this.determinations.remove(determination);
382
    }
383

    
384
    public DefinedTerm getSex() {
385
        return sex;
386
    }
387

    
388
    public void setSex(DefinedTerm sex) {
389
        this.sex = sex;
390
    }
391

    
392
    public DefinedTerm getLifeStage() {
393
        return lifeStage;
394
    }
395

    
396
    public void setLifeStage(DefinedTerm lifeStage) {
397
        this.lifeStage = lifeStage;
398
    }
399

    
400

    
401
    /**
402
     * @see #kindOfUnit
403
     * @return
404
     */
405
    public DefinedTerm getKindOfUnit() {
406
        return kindOfUnit;
407
    }
408

    
409
    /**
410
     * @see #kindOfUnit
411
     * @param kindOfUnit
412
     */
413
    public void setKindOfUnit(DefinedTerm kindOfUnit) {
414
        this.kindOfUnit = kindOfUnit;
415
    }
416

    
417
    public Integer getIndividualCount() {
418
        return individualCount;
419
    }
420

    
421
    public void setIndividualCount(Integer individualCount) {
422
        this.individualCount = individualCount;
423
    }
424

    
425
    public Map<Language,LanguageString> getDefinition(){
426
        return this.definition;
427
    }
428

    
429
    /**
430
     * adds the {@link LanguageString description} to the {@link MultilanguageText multilanguage text}
431
     * used to define <i>this</i> specimen or observation.
432
     *
433
     * @param description	the languageString in with the title string and the given language
434
     *
435
     * @see    	   		#getDefinition()
436
     * @see    	   		#putDefinition(Language, String)
437
     */
438
    public void putDefinition(LanguageString description){
439
        this.definition.put(description.getLanguage(),description);
440
    }
441

    
442
    /**
443
     * Creates a {@link LanguageString language string} based on the given text string
444
     * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
445
     * used to define <i>this</i> specimen or observation.
446
     *
447
     * @param language	the language in which the title string is formulated
448
     * @param text		the definition in a particular language
449
     *
450
     * @see    	   		#getDefinition()
451
     * @see    	   		#putDefinition(LanguageString)
452
     */
453
    public void putDefinition(Language language, String text){
454
        this.definition.put(language, LanguageString.NewInstance(text, language));
455
    }
456

    
457

    
458
    public void removeDefinition(Language lang){
459
        this.definition.remove(lang);
460
    }
461

    
462
    /**
463
     * for derived units get the single next higher parental/original unit.
464
     * If multiple original units exist throw error
465
     * @return
466
     */
467
    @Transient
468
    public SpecimenOrObservationBase getOriginalUnit(){
469
        logger.warn("GetOriginalUnit not yet implemented");
470
        return null;
471
    }
472

    
473

    
474
    public boolean hasCharacterData() {
475
        Set<DescriptionBase> descriptions = this.getDescriptions();
476
        for (DescriptionBase<?> descriptionBase : descriptions) {
477
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
478
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
479
                Set<DescriptionElementBase> elements = specimenDescription.getElements();
480
                for (DescriptionElementBase descriptionElementBase : elements) {
481
                    if (descriptionElementBase.isCharacterData()){
482
                        return true;
483
                    }
484
                }
485
            }
486
        }
487
        return false;
488
    }
489

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

    
512

    
513

    
514
//******************** CLONE **********************************************/
515

    
516
    /* (non-Javadoc)
517
     * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
518
     * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
519
     * @see java.lang.Object#clone()
520
     */
521
    @Override
522
    public Object clone() throws CloneNotSupportedException {
523
        SpecimenOrObservationBase result = null;
524
        result = (SpecimenOrObservationBase)super.clone();
525

    
526
        //defininion (description, languageString)
527
        result.definition = new HashMap<Language,LanguageString>();
528
        for(LanguageString languageString : this.definition.values()) {
529
            LanguageString newLanguageString = (LanguageString)languageString.clone();
530
            result.putDefinition(newLanguageString);
531
        }
532

    
533
        //sex
534
        result.setSex(this.sex);
535
        //life stage
536
        result.setLifeStage(this.lifeStage);
537

    
538
        //Descriptions
539
        for(DescriptionBase description : this.descriptions) {
540
            result.addDescription(description);
541
        }
542

    
543
        //DeterminationEvent FIXME should clone() the determination
544
        // as the relationship is OneToMany
545
        for(DeterminationEvent determination : this.determinations) {
546
            result.addDetermination(determination);
547
        }
548

    
549
        //DerivationEvent
550
        for(DerivationEvent derivationEvent : this.derivationEvents) {
551
            result.addDerivationEvent(derivationEvent);
552
        }
553

    
554
        //no changes to: individualCount
555
        return result;
556
    }
557

    
558

    
559
}
(11-11/14)