Project

General

Profile

Download (19 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.ContainedIn;
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
    "sex",
83
    "lifeStage",
84
    "kindOfUnit",
85
    "individualCount",
86
    "definition",
87
    "descriptions",
88
    "determinations",
89
    "derivationEvents"
90
})
91
@XmlRootElement(name = "SpecimenOrObservationBase")
92
@Entity
93
@Audited
94
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
95
@Table(appliesTo="SpecimenOrObservationBase", indexes = { @Index(name = "specimenOrObservationBaseTitleCacheIndex", columnNames = { "titleCache" }) })
96
public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCacheStrategy> extends IdentifiableEntity<S>
97
                implements IMultiLanguageTextHolder, IDescribable<DescriptionBase>, IPublishable  {
98
    private static final long serialVersionUID = 6932680139334408031L;
99
    private static final Logger logger = Logger.getLogger(SpecimenOrObservationBase.class);
100

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

    
120

    
121
    @XmlElementWrapper(name = "Descriptions")
122
    @XmlElement(name = "Description")
123
    @OneToMany(mappedBy="describedSpecimenOrObservation", fetch = FetchType.LAZY)
124
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
125
    @ContainedIn
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
    // the verbatim description of this occurrence. Free text usable when no atomised data is available.
171
    // in conjunction with titleCache which serves as the "citation" string for this object
172
    @XmlElement(name = "Description")
173
    @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
174
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
175
    @MapKeyJoinColumn(name="definition_mapkey_id")
176
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE, CascadeType.DELETE})
177
    @IndexedEmbedded
178
    @NotNull
179
    protected Map<Language,LanguageString> definition = new HashMap<Language,LanguageString>();
180

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

    
191
    @XmlAttribute(name = "publish")
192
    private boolean publish = true;
193

    
194

    
195
//********************************** CONSTRUCTOR *********************************/
196

    
197
      //for hibernate use only
198
      @Deprecated
199
      protected SpecimenOrObservationBase(){super();}
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
//************************* GETTER / SETTER ***********************/
208

    
209
    /**
210
     * @see #recordBasis
211
     * @return
212
     */
213
    public SpecimenOrObservationType getRecordBasis() {
214
        return recordBasis;
215
    }
216

    
217
    /**
218
     * @see #recordBasis
219
     * @param recordBasis
220
     */
221
    public void setRecordBasis(SpecimenOrObservationType recordBasis) {
222
        this.recordBasis = recordBasis;
223
    }
224

    
225

    
226
    /**
227
     * Returns the boolean value indicating if this specimen or observation should be withheld
228
     * (<code>publish=false</code>) or not (<code>publish=true</code>) during any publication
229
     * process to the general public.
230
     * This publish flag implementation is preliminary and may be replaced by a more general
231
     * implementation of READ rights in future.<BR>
232
     * The default value is <code>true</code>.
233
     */
234
    @Override
235
    public boolean isPublish() {
236
        return publish;
237
    }
238

    
239
    /**
240
     * @see #isPublish()
241
     * @param publish
242
     */
243
    @Override
244
    public void setPublish(boolean publish) {
245
        this.publish = publish;
246
    }
247

    
248
    /**
249
     * The descriptions this specimen or observation is part of.<BR>
250
     * A specimen can not only have it's own {@link SpecimenDescription specimen description }
251
     * but can also be part of a {@link TaxonDescription taxon description} or a
252
     * {@link TaxonNameDescription taxon name description}.<BR>
253
     * @see #getSpecimenDescriptions()
254
     * @return
255
     */
256
    @Override
257
    public Set<DescriptionBase> getDescriptions() {
258
        if(descriptions == null) {
259
            this.descriptions = new HashSet<DescriptionBase>();
260
        }
261
        return this.descriptions;
262
    }
263

    
264
    /**
265
     * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
266
     * @see #getDescriptions()
267
     * @return
268
     */
269
    @Transient
270
    public Set<SpecimenDescription> getSpecimenDescriptions() {
271
        return getSpecimenDescriptions(true);
272
    }
273

    
274
    /**
275
     * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
276
     * @see #getDescriptions()
277
     * @return
278
     */
279
    @Transient
280
    public Set<SpecimenDescription> getSpecimenDescriptions(boolean includeImageGallery) {
281
        Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
282
        for (DescriptionBase descriptionBase : getDescriptions()){
283
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
284
                if (includeImageGallery || descriptionBase.isImageGallery() == false){
285
                    specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
286
                }
287

    
288
            }
289
        }
290
        return specimenDescriptions;
291
    }
292
    /**
293
     * Returns the {@link SpecimenDescription specimen descriptions} which act as an image gallery
294
     * and which this specimen is part of.
295
     * @see #getDescriptions()
296
     * @return
297
     */
298
    @Transient
299
    public Set<SpecimenDescription> getSpecimenDescriptionImageGallery() {
300
        Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
301
        for (DescriptionBase descriptionBase : getDescriptions()){
302
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
303
                if (descriptionBase.isImageGallery() == true){
304
                    specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
305
                }
306
            }
307
        }
308
        return specimenDescriptions;
309
    }
310

    
311
    /**
312
     * Adds a new description to this specimen or observation
313
     * @param description
314
     */
315
    @Override
316
    public void addDescription(DescriptionBase description) {
317
        if (description.getDescribedSpecimenOrObservation() != null){
318
            description.getDescribedSpecimenOrObservation().removeDescription(description);
319
        }
320
        descriptions.add(description);
321
        description.setDescribedSpecimenOrObservation(this);
322
    }
323

    
324
    /**
325
     * Removes a specimen from a description (removes a description from this specimen)
326
     * @param description
327
     */
328
    @Override
329
    public void removeDescription(DescriptionBase description) {
330
        boolean existed = descriptions.remove(description);
331
        if (existed){
332
            description.setDescribedSpecimenOrObservation(null);
333
        }
334
    }
335

    
336

    
337
    public Set<DerivationEvent> getDerivationEvents() {
338
        if(derivationEvents == null) {
339
            this.derivationEvents = new HashSet<DerivationEvent>();
340
        }
341
        return this.derivationEvents;
342
    }
343

    
344
    public void addDerivationEvent(DerivationEvent derivationEvent) {
345
        if (! this.derivationEvents.contains(derivationEvent)){
346
            this.derivationEvents.add(derivationEvent);
347
            derivationEvent.addOriginal(this);
348
        }
349
    }
350

    
351
    public void removeDerivationEvent(DerivationEvent derivationEvent) {
352
        if (this.derivationEvents.contains(derivationEvent)){
353
            this.derivationEvents.remove(derivationEvent);
354
            derivationEvent.removeOriginal(this);
355
        }
356
    }
357

    
358
    public Set<DeterminationEvent> getDeterminations() {
359
        if(determinations == null) {
360
            this.determinations = new HashSet<DeterminationEvent>();
361
        }
362
        return this.determinations;
363
    }
364

    
365
    public void addDetermination(DeterminationEvent determination) {
366
        // FIXME bidirectional integrity. Use protected Determination setter
367
        this.determinations.add(determination);
368
    }
369

    
370
    public void removeDetermination(DeterminationEvent determination) {
371
        // FIXME bidirectional integrity. Use protected Determination setter
372
        this.determinations.remove(determination);
373
    }
374

    
375
    public DefinedTerm getSex() {
376
        return sex;
377
    }
378

    
379
    public void setSex(DefinedTerm sex) {
380
        this.sex = sex;
381
    }
382

    
383
    public DefinedTerm getLifeStage() {
384
        return lifeStage;
385
    }
386

    
387
    public void setLifeStage(DefinedTerm lifeStage) {
388
        this.lifeStage = lifeStage;
389
    }
390

    
391

    
392
    /**
393
     * @see #kindOfUnit
394
     * @return
395
     */
396
    public DefinedTerm getKindOfUnit() {
397
        return kindOfUnit;
398
    }
399

    
400
    /**
401
     * @see #kindOfUnit
402
     * @param kindOfUnit
403
     */
404
    public void setKindOfUnit(DefinedTerm kindOfUnit) {
405
        this.kindOfUnit = kindOfUnit;
406
    }
407

    
408
    public Integer getIndividualCount() {
409
        return individualCount;
410
    }
411

    
412
    public void setIndividualCount(Integer individualCount) {
413
        this.individualCount = individualCount;
414
    }
415

    
416
    public Map<Language,LanguageString> getDefinition(){
417
        return this.definition;
418
    }
419

    
420
    /**
421
     * adds the {@link LanguageString description} to the {@link MultilanguageText multilanguage text}
422
     * used to define <i>this</i> specimen or observation.
423
     *
424
     * @param description	the languageString in with the title string and the given language
425
     *
426
     * @see    	   		#getDefinition()
427
     * @see    	   		#putDefinition(Language, String)
428
     */
429
    public void putDefinition(LanguageString description){
430
        this.definition.put(description.getLanguage(),description);
431
    }
432

    
433
    /**
434
     * Creates a {@link LanguageString language string} based on the given text string
435
     * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
436
     * used to define <i>this</i> specimen or observation.
437
     *
438
     * @param language	the language in which the title string is formulated
439
     * @param text		the definition in a particular language
440
     *
441
     * @see    	   		#getDefinition()
442
     * @see    	   		#putDefinition(LanguageString)
443
     */
444
    public void putDefinition(Language language, String text){
445
        this.definition.put(language, LanguageString.NewInstance(text, language));
446
    }
447

    
448

    
449
    public void removeDefinition(Language lang){
450
        this.definition.remove(lang);
451
    }
452

    
453
    /**
454
     * for derived units get the single next higher parental/original unit.
455
     * If multiple original units exist throw error
456
     * @return
457
     */
458
    @Transient
459
    public SpecimenOrObservationBase getOriginalUnit(){
460
        logger.warn("GetOriginalUnit not yet implemented");
461
        return null;
462
    }
463

    
464

    
465
    public boolean hasCharacterData() {
466
        Set<DescriptionBase> descriptions = this.getDescriptions();
467
        for (DescriptionBase<?> descriptionBase : descriptions) {
468
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
469
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
470
                Set<DescriptionElementBase> elements = specimenDescription.getElements();
471
                for (DescriptionElementBase descriptionElementBase : elements) {
472
                    if (descriptionElementBase.isCharacterData()){
473
                        return true;
474
                    }
475
                }
476
            }
477
        }
478
        return false;
479
    }
480

    
481
    /**
482
     * Returns a list of all description items which
483
     * @return
484
     */
485
    @Transient
486
    public Collection<DescriptionElementBase> characterData() {
487
        Collection<DescriptionElementBase> states = new ArrayList<DescriptionElementBase>();
488
        Set<DescriptionBase> descriptions = this.getDescriptions();
489
        for (DescriptionBase<?> descriptionBase : descriptions) {
490
            if (descriptionBase.isInstanceOf(SpecimenDescription.class)) {
491
                SpecimenDescription specimenDescription = HibernateProxyHelper.deproxy(descriptionBase, SpecimenDescription.class);
492
                Set<DescriptionElementBase> elements = specimenDescription.getElements();
493
                for (DescriptionElementBase descriptionElementBase : elements) {
494
                    if(descriptionElementBase.isCharacterData()){
495
                        states.add(descriptionElementBase);
496
                    }
497
                }
498
            }
499
        }
500
        return states;
501
    }
502

    
503

    
504

    
505
//******************** CLONE **********************************************/
506

    
507
    /* (non-Javadoc)
508
     * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
509
     * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
510
     * @see java.lang.Object#clone()
511
     */
512
    @Override
513
    public Object clone() throws CloneNotSupportedException {
514
        SpecimenOrObservationBase result = null;
515
        result = (SpecimenOrObservationBase)super.clone();
516

    
517
        //defininion (description, languageString)
518
        result.definition = new HashMap<Language,LanguageString>();
519
        for(LanguageString languageString : this.definition.values()) {
520
            LanguageString newLanguageString = (LanguageString)languageString.clone();
521
            result.putDefinition(newLanguageString);
522
        }
523

    
524
        //sex
525
        result.setSex(this.sex);
526
        //life stage
527
        result.setLifeStage(this.lifeStage);
528

    
529
        //Descriptions
530
        for(DescriptionBase description : this.descriptions) {
531
            result.addDescription(description);
532
        }
533

    
534
        //DeterminationEvent FIXME should clone() the determination
535
        // as the relationship is OneToMany
536
        for(DeterminationEvent determination : this.determinations) {
537
            result.addDetermination(determination);
538
        }
539

    
540
        //DerivationEvent
541
        for(DerivationEvent derivationEvent : this.derivationEvents) {
542
            result.addDerivationEvent(derivationEvent);
543
        }
544

    
545
        //no changes to: individualCount
546
        return result;
547
    }
548

    
549

    
550
}
(11-11/14)