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
.SpecimenDescription
;
63 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
64 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
65 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
68 * type figures are observations with at least a figure object in media
70 * @created 08-Nov-2007 13:06:41
72 @XmlAccessorType(XmlAccessType
.FIELD
)
73 @XmlType(name
= "SpecimenOrObservationBase", propOrder
= {
85 @XmlRootElement(name
= "SpecimenOrObservationBase")
88 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
89 @Table(appliesTo
="SpecimenOrObservationBase", indexes
= { @Index(name
= "specimenOrObservationBaseTitleCacheIndex", columnNames
= { "titleCache" }) })
90 public abstract class SpecimenOrObservationBase
<S
extends IIdentifiableEntityCacheStrategy
> extends IdentifiableEntity
<S
> implements IMultiLanguageTextHolder
{
91 private static final long serialVersionUID
= 6932680139334408031L;
92 private static final Logger logger
= Logger
.getLogger(SpecimenOrObservationBase
.class);
95 * An indication of what the unit record describes.
97 * NOTE: The name of the attribute was chosen against the common naming conventions of the CDM
98 * as it is well known in common standards like ABCD and DarwinCore. According to CDM naming
99 * conventions it would specimenOrObservationType.
101 * @see ABCD: DataSets/DataSet/Units/Unit/RecordBasis
102 * @see Darwin Core: http://wiki.tdwg.org/twiki/bin/view/DarwinCore/BasisOfRecord
104 @XmlAttribute(name
="RecordBasis")
105 @Column(name
="recordBasis")
107 @Type(type
= "eu.etaxonomy.cdm.hibernate.EnumUserType",
108 parameters
= {@org.hibernate
.annotations
.Parameter(name
= "enumClass", value
= "eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType")}
110 private SpecimenOrObservationType recordBasis
;
113 @XmlElementWrapper(name
= "Descriptions")
114 @XmlElement(name
= "Description")
115 @OneToMany(mappedBy
="describedSpecimenOrObservation", fetch
= FetchType
.LAZY
)
116 @Cascade(CascadeType
.SAVE_UPDATE
)
119 private Set
<DescriptionBase
> descriptions
= new HashSet
<DescriptionBase
>();
122 @XmlElementWrapper(name
= "Determinations")
123 @XmlElement(name
= "Determination")
124 @OneToMany(mappedBy
="identifiedUnit", orphanRemoval
=true)
125 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
126 @IndexedEmbedded(depth
= 2)
128 private Set
<DeterminationEvent
> determinations
= new HashSet
<DeterminationEvent
>();
130 @XmlElement(name
= "Sex")
132 @XmlSchemaType(name
= "IDREF")
133 @ManyToOne(fetch
= FetchType
.LAZY
)
134 private DefinedTerm sex
;
136 @XmlElement(name
= "LifeStage")
138 @XmlSchemaType(name
= "IDREF")
139 @ManyToOne(fetch
= FetchType
.LAZY
)
140 private DefinedTerm lifeStage
;
143 * Part(s) of organism or class of materials represented by this unit.
144 * Example: fruits, seeds, tissue, gDNA, leaves
146 * @see ABCD: DataSets/DataSet/Units/Unit/KindOfUnit
147 * @see TermType#KindOfUnit
149 @XmlElement(name
= "KindOfUnit")
151 @XmlSchemaType(name
= "IDREF")
152 @ManyToOne(fetch
= FetchType
.LAZY
)
153 // @IndexedEmbedded(depth=1)
154 private DefinedTerm kindOfUnit
;
156 @XmlElement(name
= "IndividualCount")
157 @Field(analyze
= Analyze
.NO
)
160 private Integer individualCount
;
162 // the verbatim description of this occurrence. Free text usable when no atomised data is available.
163 // in conjunction with titleCache which serves as the "citation" string for this object
164 @XmlElement(name
= "Description")
165 @XmlJavaTypeAdapter(MultilanguageTextAdapter
.class)
166 @OneToMany(fetch
= FetchType
.LAZY
, orphanRemoval
=true)
167 @MapKeyJoinColumn(name
="definition_mapkey_id")
168 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.DELETE
})
171 protected Map
<Language
,LanguageString
> definition
= new HashMap
<Language
,LanguageString
>();
173 // events that created derivedUnits from this unit
174 @XmlElementWrapper(name
= "DerivationEvents")
175 @XmlElement(name
= "DerivationEvent")
177 @XmlSchemaType(name
= "IDREF")
178 @ManyToMany(fetch
=FetchType
.LAZY
)
179 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.DELETE
})
181 protected Set
<DerivationEvent
> derivationEvents
= new HashSet
<DerivationEvent
>();
183 @XmlAttribute(name
= "publish")
184 private boolean publish
= true;
187 //********************************** CONSTRUCTOR *********************************/
189 //for hibernate use only
191 protected SpecimenOrObservationBase(){super();}
193 protected SpecimenOrObservationBase(SpecimenOrObservationType recordBasis
) {
195 if (recordBasis
== null){ throw new IllegalArgumentException("RecordBasis must not be null");}
196 this.recordBasis
= recordBasis
;
199 //************************* GETTER / SETTER ***********************/
205 public SpecimenOrObservationType
getRecordBasis() {
213 public void setRecordBasis(SpecimenOrObservationType recordBasis
) {
214 this.recordBasis
= recordBasis
;
219 * Returns the boolean value indicating if this specimen or observation should be withheld
220 * (<code>publish=false</code>) or not (<code>publish=true</code>) during any publication
221 * process to the general public.
222 * This publish flag implementation is preliminary and may be replaced by a more general
223 * implementation of READ rights in future.<BR>
224 * The default value is <code>true</code>.
226 public boolean isPublish() {
234 public void setPublish(boolean publish
) {
235 this.publish
= publish
;
239 * The descriptions this specimen or observation is part of.<BR>
240 * A specimen can not only have it's own {@link SpecimenDescription specimen description }
241 * but can also be part of a {@link TaxonDescription taxon description} or a
242 * {@link TaxonNameDescription taxon name description}.<BR>
243 * @see #getSpecimenDescriptions()
246 public Set
<DescriptionBase
> getDescriptions() {
247 if(descriptions
== null) {
248 this.descriptions
= new HashSet
<DescriptionBase
>();
250 return this.descriptions
;
254 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
255 * @see #getDescriptions()
259 public Set
<SpecimenDescription
> getSpecimenDescriptions() {
260 return getSpecimenDescriptions(true);
264 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
265 * @see #getDescriptions()
269 public Set
<SpecimenDescription
> getSpecimenDescriptions(boolean includeImageGallery
) {
270 Set
<SpecimenDescription
> specimenDescriptions
= new HashSet
<SpecimenDescription
>();
271 for (DescriptionBase descriptionBase
: getDescriptions()){
272 if (descriptionBase
.isInstanceOf(SpecimenDescription
.class)){
273 if (includeImageGallery
|| descriptionBase
.isImageGallery() == false){
274 specimenDescriptions
.add(descriptionBase
.deproxy(descriptionBase
, SpecimenDescription
.class));
279 return specimenDescriptions
;
282 * Returns the {@link SpecimenDescription specimen descriptions} which act as an image gallery
283 * and which this specimen is part of.
284 * @see #getDescriptions()
288 public Set
<SpecimenDescription
> getSpecimenDescriptionImageGallery() {
289 Set
<SpecimenDescription
> specimenDescriptions
= new HashSet
<SpecimenDescription
>();
290 for (DescriptionBase descriptionBase
: getDescriptions()){
291 if (descriptionBase
.isInstanceOf(SpecimenDescription
.class)){
292 if (descriptionBase
.isImageGallery() == true){
293 specimenDescriptions
.add(descriptionBase
.deproxy(descriptionBase
, SpecimenDescription
.class));
297 return specimenDescriptions
;
302 * Adds a new description to this specimen or observation
305 public void addDescription(DescriptionBase description
) {
306 if (description
.getDescribedSpecimenOrObservation() != null){
307 description
.getDescribedSpecimenOrObservation().removeDescription(description
);
309 descriptions
.add(description
);
310 description
.setDescribedSpecimenOrObservation(this);
314 * Removes a specimen from a description (removes a description from this specimen)
317 public void removeDescription(DescriptionBase description
) {
318 boolean existed
= descriptions
.remove(description
);
320 description
.setDescribedSpecimenOrObservation(null);
324 public Set
<DerivationEvent
> getDerivationEvents() {
325 if(derivationEvents
== null) {
326 this.derivationEvents
= new HashSet
<DerivationEvent
>();
328 return this.derivationEvents
;
331 public void addDerivationEvent(DerivationEvent derivationEvent
) {
332 if (! this.derivationEvents
.contains(derivationEvent
)){
333 this.derivationEvents
.add(derivationEvent
);
334 derivationEvent
.addOriginal(this);
338 public void removeDerivationEvent(DerivationEvent derivationEvent
) {
339 this.derivationEvents
.remove(derivationEvent
);
342 public Set
<DeterminationEvent
> getDeterminations() {
343 if(determinations
== null) {
344 this.determinations
= new HashSet
<DeterminationEvent
>();
346 return this.determinations
;
349 public void addDetermination(DeterminationEvent determination
) {
350 // FIXME bidirectional integrity. Use protected Determination setter
351 this.determinations
.add(determination
);
354 public void removeDetermination(DeterminationEvent determination
) {
355 // FIXME bidirectional integrity. Use protected Determination setter
356 this.determinations
.remove(determination
);
359 public DefinedTerm
getSex() {
363 public void setSex(DefinedTerm sex
) {
367 public DefinedTerm
getLifeStage() {
371 public void setLifeStage(DefinedTerm lifeStage
) {
372 this.lifeStage
= lifeStage
;
380 public DefinedTerm
getKindOfUnit() {
388 public void setKindOfUnit(DefinedTerm kindOfUnit
) {
389 this.kindOfUnit
= kindOfUnit
;
393 public String
generateTitle(){
394 return getCacheStrategy().getTitleCache(this);
397 public Integer
getIndividualCount() {
398 return individualCount
;
401 public void setIndividualCount(Integer individualCount
) {
402 this.individualCount
= individualCount
;
405 public Map
<Language
,LanguageString
> getDefinition(){
406 return this.definition
;
410 * adds the {@link LanguageString description} to the {@link MultilanguageText multilanguage text}
411 * used to define <i>this</i> specimen or observation.
413 * @param description the languageString in with the title string and the given language
415 * @see #getDefinition()
416 * @see #putDefinition(Language, String)
418 public void putDefinition(LanguageString description
){
419 this.definition
.put(description
.getLanguage(),description
);
423 * Creates a {@link LanguageString language string} based on the given text string
424 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
425 * used to define <i>this</i> specimen or observation.
427 * @param language the language in which the title string is formulated
428 * @param text the definition in a particular language
430 * @see #getDefinition()
431 * @see #putDefinition(LanguageString)
433 public void putDefinition(Language language
, String text
){
434 this.definition
.put(language
, LanguageString
.NewInstance(text
, language
));
438 public void removeDefinition(Language lang
){
439 this.definition
.remove(lang
);
443 * for derived units get the single next higher parental/original unit.
444 * If multiple original units exist throw error
448 public SpecimenOrObservationBase
getOriginalUnit(){
449 logger
.warn("GetOriginalUnit not yet implemented");
454 //******************** CLONE **********************************************/
457 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
458 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
459 * @see java.lang.Object#clone()
462 public Object
clone() throws CloneNotSupportedException
{
463 SpecimenOrObservationBase result
= null;
464 result
= (SpecimenOrObservationBase
)super.clone();
466 //defininion (description, languageString)
467 result
.definition
= new HashMap
<Language
,LanguageString
>();
468 for(LanguageString languageString
: this.definition
.values()) {
469 LanguageString newLanguageString
= (LanguageString
)languageString
.clone();
470 result
.putDefinition(newLanguageString
);
474 result
.setSex(this.sex
);
476 result
.setLifeStage(this.lifeStage
);
479 for(DescriptionBase description
: this.descriptions
) {
480 result
.addDescription(description
);
483 //DeterminationEvent FIXME should clone() the determination
484 // as the relationship is OneToMany
485 for(DeterminationEvent determination
: this.determinations
) {
486 result
.addDetermination(determination
);
490 for(DerivationEvent derivationEvent
: this.derivationEvents
) {
491 result
.addDerivationEvent(derivationEvent
);
494 //no changes to: individualCount