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.
10 package eu
.etaxonomy
.cdm
.model
.occurrence
;
12 import java
.util
.HashMap
;
13 import java
.util
.HashSet
;
17 import javax
.persistence
.Column
;
18 import javax
.persistence
.Entity
;
19 import javax
.persistence
.FetchType
;
20 import javax
.persistence
.Inheritance
;
21 import javax
.persistence
.InheritanceType
;
22 import javax
.persistence
.ManyToMany
;
23 import javax
.persistence
.ManyToOne
;
24 import javax
.persistence
.MapKeyJoinColumn
;
25 import javax
.persistence
.OneToMany
;
26 import javax
.persistence
.Transient
;
27 import javax
.validation
.constraints
.Min
;
28 import javax
.validation
.constraints
.NotNull
;
29 import javax
.xml
.bind
.annotation
.XmlAccessType
;
30 import javax
.xml
.bind
.annotation
.XmlAccessorType
;
31 import javax
.xml
.bind
.annotation
.XmlAttribute
;
32 import javax
.xml
.bind
.annotation
.XmlElement
;
33 import javax
.xml
.bind
.annotation
.XmlElementWrapper
;
34 import javax
.xml
.bind
.annotation
.XmlIDREF
;
35 import javax
.xml
.bind
.annotation
.XmlRootElement
;
36 import javax
.xml
.bind
.annotation
.XmlSchemaType
;
37 import javax
.xml
.bind
.annotation
.XmlType
;
38 import javax
.xml
.bind
.annotation
.adapters
.XmlJavaTypeAdapter
;
40 import org
.apache
.log4j
.Logger
;
41 import org
.hibernate
.annotations
.Cascade
;
42 import org
.hibernate
.annotations
.CascadeType
;
43 import org
.hibernate
.annotations
.Index
;
44 import org
.hibernate
.annotations
.Table
;
45 import org
.hibernate
.annotations
.Type
;
46 import org
.hibernate
.envers
.Audited
;
47 import org
.hibernate
.search
.annotations
.Analyze
;
48 import org
.hibernate
.search
.annotations
.ContainedIn
;
49 import org
.hibernate
.search
.annotations
.Field
;
50 import org
.hibernate
.search
.annotations
.IndexedEmbedded
;
51 import org
.hibernate
.search
.annotations
.NumericField
;
53 import eu
.etaxonomy
.cdm
.jaxb
.MultilanguageTextAdapter
;
54 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTerm
;
55 import eu
.etaxonomy
.cdm
.model
.common
.IMultiLanguageTextHolder
;
56 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableEntity
;
57 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
58 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
59 import eu
.etaxonomy
.cdm
.model
.common
.MultilanguageText
;
60 import eu
.etaxonomy
.cdm
.model
.common
.TermType
;
61 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
62 import eu
.etaxonomy
.cdm
.model
.description
.IDescribable
;
63 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
64 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
65 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
66 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
69 * type figures are observations with at least a figure object in media
71 * @created 08-Nov-2007 13:06:41
73 @XmlAccessorType(XmlAccessType
.FIELD
)
74 @XmlType(name
= "SpecimenOrObservationBase", propOrder
= {
86 @XmlRootElement(name
= "SpecimenOrObservationBase")
89 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
90 @Table(appliesTo
="SpecimenOrObservationBase", indexes
= { @Index(name
= "specimenOrObservationBaseTitleCacheIndex", columnNames
= { "titleCache" }) })
91 public abstract class SpecimenOrObservationBase
<S
extends IIdentifiableEntityCacheStrategy
> extends IdentifiableEntity
<S
>
92 implements IMultiLanguageTextHolder
, IDescribable
<DescriptionBase
> {
93 private static final long serialVersionUID
= 6932680139334408031L;
94 private static final Logger logger
= Logger
.getLogger(SpecimenOrObservationBase
.class);
97 * An indication of what the unit record describes.
99 * NOTE: The name of the attribute was chosen against the common naming conventions of the CDM
100 * as it is well known in common standards like ABCD and DarwinCore. According to CDM naming
101 * conventions it would specimenOrObservationType.
103 * @see ABCD: DataSets/DataSet/Units/Unit/RecordBasis
104 * @see Darwin Core: http://wiki.tdwg.org/twiki/bin/view/DarwinCore/BasisOfRecord
106 @XmlAttribute(name
="RecordBasis")
107 @Column(name
="recordBasis")
109 @Type(type
= "eu.etaxonomy.cdm.hibernate.EnumUserType",
110 parameters
= {@org.hibernate
.annotations
.Parameter(name
= "enumClass", value
= "eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType")}
112 private SpecimenOrObservationType recordBasis
;
115 @XmlElementWrapper(name
= "Descriptions")
116 @XmlElement(name
= "Description")
117 @OneToMany(mappedBy
="describedSpecimenOrObservation", fetch
= FetchType
.LAZY
)
118 @Cascade(CascadeType
.SAVE_UPDATE
)
121 private Set
<DescriptionBase
> descriptions
= new HashSet
<DescriptionBase
>();
124 @XmlElementWrapper(name
= "Determinations")
125 @XmlElement(name
= "Determination")
126 @OneToMany(mappedBy
="identifiedUnit", orphanRemoval
=true)
127 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
128 @IndexedEmbedded(depth
= 2)
130 private Set
<DeterminationEvent
> determinations
= new HashSet
<DeterminationEvent
>();
132 @XmlElement(name
= "Sex")
134 @XmlSchemaType(name
= "IDREF")
135 @ManyToOne(fetch
= FetchType
.LAZY
)
136 private DefinedTerm sex
;
138 @XmlElement(name
= "LifeStage")
140 @XmlSchemaType(name
= "IDREF")
141 @ManyToOne(fetch
= FetchType
.LAZY
)
142 private DefinedTerm lifeStage
;
145 * Part(s) of organism or class of materials represented by this unit.
146 * Example: fruits, seeds, tissue, gDNA, leaves
148 * @see ABCD: DataSets/DataSet/Units/Unit/KindOfUnit
149 * @see TermType#KindOfUnit
151 @XmlElement(name
= "KindOfUnit")
153 @XmlSchemaType(name
= "IDREF")
154 @ManyToOne(fetch
= FetchType
.LAZY
)
155 // @IndexedEmbedded(depth=1)
156 private DefinedTerm kindOfUnit
;
158 @XmlElement(name
= "IndividualCount")
159 @Field(analyze
= Analyze
.NO
)
162 private Integer individualCount
;
164 // the verbatim description of this occurrence. Free text usable when no atomised data is available.
165 // in conjunction with titleCache which serves as the "citation" string for this object
166 @XmlElement(name
= "Description")
167 @XmlJavaTypeAdapter(MultilanguageTextAdapter
.class)
168 @OneToMany(fetch
= FetchType
.LAZY
, orphanRemoval
=true)
169 @MapKeyJoinColumn(name
="definition_mapkey_id")
170 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.DELETE
})
173 protected Map
<Language
,LanguageString
> definition
= new HashMap
<Language
,LanguageString
>();
175 // events that created derivedUnits from this unit
176 @XmlElementWrapper(name
= "DerivationEvents")
177 @XmlElement(name
= "DerivationEvent")
179 @XmlSchemaType(name
= "IDREF")
180 @ManyToMany(fetch
=FetchType
.LAZY
)
181 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.DELETE
})
183 protected Set
<DerivationEvent
> derivationEvents
= new HashSet
<DerivationEvent
>();
185 @XmlAttribute(name
= "publish")
186 private boolean publish
= true;
189 //********************************** CONSTRUCTOR *********************************/
191 //for hibernate use only
193 protected SpecimenOrObservationBase(){super();}
195 protected SpecimenOrObservationBase(SpecimenOrObservationType recordBasis
) {
197 if (recordBasis
== null){ throw new IllegalArgumentException("RecordBasis must not be null");}
198 this.recordBasis
= recordBasis
;
201 //************************* GETTER / SETTER ***********************/
207 public SpecimenOrObservationType
getRecordBasis() {
215 public void setRecordBasis(SpecimenOrObservationType recordBasis
) {
216 this.recordBasis
= recordBasis
;
221 * Returns the boolean value indicating if this specimen or observation should be withheld
222 * (<code>publish=false</code>) or not (<code>publish=true</code>) during any publication
223 * process to the general public.
224 * This publish flag implementation is preliminary and may be replaced by a more general
225 * implementation of READ rights in future.<BR>
226 * The default value is <code>true</code>.
228 public boolean isPublish() {
236 public void setPublish(boolean publish
) {
237 this.publish
= publish
;
241 * The descriptions this specimen or observation is part of.<BR>
242 * A specimen can not only have it's own {@link SpecimenDescription specimen description }
243 * but can also be part of a {@link TaxonDescription taxon description} or a
244 * {@link TaxonNameDescription taxon name description}.<BR>
245 * @see #getSpecimenDescriptions()
248 public Set
<DescriptionBase
> getDescriptions() {
249 if(descriptions
== null) {
250 this.descriptions
= new HashSet
<DescriptionBase
>();
252 return this.descriptions
;
256 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
257 * @see #getDescriptions()
261 public Set
<SpecimenDescription
> getSpecimenDescriptions() {
262 return getSpecimenDescriptions(true);
266 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
267 * @see #getDescriptions()
271 public Set
<SpecimenDescription
> getSpecimenDescriptions(boolean includeImageGallery
) {
272 Set
<SpecimenDescription
> specimenDescriptions
= new HashSet
<SpecimenDescription
>();
273 for (DescriptionBase descriptionBase
: getDescriptions()){
274 if (descriptionBase
.isInstanceOf(SpecimenDescription
.class)){
275 if (includeImageGallery
|| descriptionBase
.isImageGallery() == false){
276 specimenDescriptions
.add(descriptionBase
.deproxy(descriptionBase
, SpecimenDescription
.class));
281 return specimenDescriptions
;
284 * Returns the {@link SpecimenDescription specimen descriptions} which act as an image gallery
285 * and which this specimen is part of.
286 * @see #getDescriptions()
290 public Set
<SpecimenDescription
> getSpecimenDescriptionImageGallery() {
291 Set
<SpecimenDescription
> specimenDescriptions
= new HashSet
<SpecimenDescription
>();
292 for (DescriptionBase descriptionBase
: getDescriptions()){
293 if (descriptionBase
.isInstanceOf(SpecimenDescription
.class)){
294 if (descriptionBase
.isImageGallery() == true){
295 specimenDescriptions
.add(descriptionBase
.deproxy(descriptionBase
, SpecimenDescription
.class));
299 return specimenDescriptions
;
303 * Adds a new description to this specimen or observation
306 public void addDescription(DescriptionBase description
) {
307 if (description
.getDescribedSpecimenOrObservation() != null){
308 description
.getDescribedSpecimenOrObservation().removeDescription(description
);
310 descriptions
.add(description
);
311 description
.setDescribedSpecimenOrObservation(this);
315 * Removes a specimen from a description (removes a description from this specimen)
318 public void removeDescription(DescriptionBase description
) {
319 boolean existed
= descriptions
.remove(description
);
321 description
.setDescribedSpecimenOrObservation(null);
326 public Set
<DerivationEvent
> getDerivationEvents() {
327 if(derivationEvents
== null) {
328 this.derivationEvents
= new HashSet
<DerivationEvent
>();
330 return this.derivationEvents
;
333 public void addDerivationEvent(DerivationEvent derivationEvent
) {
334 if (! this.derivationEvents
.contains(derivationEvent
)){
335 this.derivationEvents
.add(derivationEvent
);
336 derivationEvent
.addOriginal(this);
340 public void removeDerivationEvent(DerivationEvent derivationEvent
) {
341 this.derivationEvents
.remove(derivationEvent
);
344 public Set
<DeterminationEvent
> getDeterminations() {
345 if(determinations
== null) {
346 this.determinations
= new HashSet
<DeterminationEvent
>();
348 return this.determinations
;
351 public void addDetermination(DeterminationEvent determination
) {
352 // FIXME bidirectional integrity. Use protected Determination setter
353 this.determinations
.add(determination
);
356 public void removeDetermination(DeterminationEvent determination
) {
357 // FIXME bidirectional integrity. Use protected Determination setter
358 this.determinations
.remove(determination
);
361 public DefinedTerm
getSex() {
365 public void setSex(DefinedTerm sex
) {
369 public DefinedTerm
getLifeStage() {
373 public void setLifeStage(DefinedTerm lifeStage
) {
374 this.lifeStage
= lifeStage
;
382 public DefinedTerm
getKindOfUnit() {
390 public void setKindOfUnit(DefinedTerm kindOfUnit
) {
391 this.kindOfUnit
= kindOfUnit
;
394 public Integer
getIndividualCount() {
395 return individualCount
;
398 public void setIndividualCount(Integer individualCount
) {
399 this.individualCount
= individualCount
;
402 public Map
<Language
,LanguageString
> getDefinition(){
403 return this.definition
;
407 * adds the {@link LanguageString description} to the {@link MultilanguageText multilanguage text}
408 * used to define <i>this</i> specimen or observation.
410 * @param description the languageString in with the title string and the given language
412 * @see #getDefinition()
413 * @see #putDefinition(Language, String)
415 public void putDefinition(LanguageString description
){
416 this.definition
.put(description
.getLanguage(),description
);
420 * Creates a {@link LanguageString language string} based on the given text string
421 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
422 * used to define <i>this</i> specimen or observation.
424 * @param language the language in which the title string is formulated
425 * @param text the definition in a particular language
427 * @see #getDefinition()
428 * @see #putDefinition(LanguageString)
430 public void putDefinition(Language language
, String text
){
431 this.definition
.put(language
, LanguageString
.NewInstance(text
, language
));
435 public void removeDefinition(Language lang
){
436 this.definition
.remove(lang
);
440 * for derived units get the single next higher parental/original unit.
441 * If multiple original units exist throw error
445 public SpecimenOrObservationBase
getOriginalUnit(){
446 logger
.warn("GetOriginalUnit not yet implemented");
451 //******************** CLONE **********************************************/
454 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
455 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
456 * @see java.lang.Object#clone()
459 public Object
clone() throws CloneNotSupportedException
{
460 SpecimenOrObservationBase result
= null;
461 result
= (SpecimenOrObservationBase
)super.clone();
463 //defininion (description, languageString)
464 result
.definition
= new HashMap
<Language
,LanguageString
>();
465 for(LanguageString languageString
: this.definition
.values()) {
466 LanguageString newLanguageString
= (LanguageString
)languageString
.clone();
467 result
.putDefinition(newLanguageString
);
471 result
.setSex(this.sex
);
473 result
.setLifeStage(this.lifeStage
);
476 for(DescriptionBase description
: this.descriptions
) {
477 result
.addDescription(description
);
480 //DeterminationEvent FIXME should clone() the determination
481 // as the relationship is OneToMany
482 for(DeterminationEvent determination
: this.determinations
) {
483 result
.addDetermination(determination
);
487 for(DerivationEvent derivationEvent
: this.derivationEvents
) {
488 result
.addDerivationEvent(derivationEvent
);
491 //no changes to: individualCount