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
.Entity
;
18 import javax
.persistence
.FetchType
;
19 import javax
.persistence
.Inheritance
;
20 import javax
.persistence
.InheritanceType
;
21 import javax
.persistence
.ManyToMany
;
22 import javax
.persistence
.ManyToOne
;
23 import javax
.persistence
.MapKeyJoinColumn
;
24 import javax
.persistence
.OneToMany
;
25 import javax
.persistence
.Transient
;
26 import javax
.validation
.constraints
.Min
;
27 import javax
.validation
.constraints
.NotNull
;
28 import javax
.xml
.bind
.annotation
.XmlAccessType
;
29 import javax
.xml
.bind
.annotation
.XmlAccessorType
;
30 import javax
.xml
.bind
.annotation
.XmlElement
;
31 import javax
.xml
.bind
.annotation
.XmlElementWrapper
;
32 import javax
.xml
.bind
.annotation
.XmlIDREF
;
33 import javax
.xml
.bind
.annotation
.XmlRootElement
;
34 import javax
.xml
.bind
.annotation
.XmlSchemaType
;
35 import javax
.xml
.bind
.annotation
.XmlType
;
36 import javax
.xml
.bind
.annotation
.adapters
.XmlJavaTypeAdapter
;
38 import org
.apache
.log4j
.Logger
;
39 import org
.hibernate
.annotations
.Cascade
;
40 import org
.hibernate
.annotations
.CascadeType
;
41 import org
.hibernate
.annotations
.Index
;
42 import org
.hibernate
.annotations
.Table
;
43 import org
.hibernate
.envers
.Audited
;
44 import org
.hibernate
.search
.annotations
.Analyze
;
45 import org
.hibernate
.search
.annotations
.Field
;
46 import org
.hibernate
.search
.annotations
.IndexedEmbedded
;
48 import eu
.etaxonomy
.cdm
.jaxb
.MultilanguageTextAdapter
;
49 import eu
.etaxonomy
.cdm
.model
.common
.IMultiLanguageTextHolder
;
50 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
51 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
52 import eu
.etaxonomy
.cdm
.model
.common
.MultilanguageText
;
53 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
54 import eu
.etaxonomy
.cdm
.model
.description
.Sex
;
55 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
56 import eu
.etaxonomy
.cdm
.model
.description
.Stage
;
57 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
58 import eu
.etaxonomy
.cdm
.model
.description
.TaxonNameDescription
;
59 import eu
.etaxonomy
.cdm
.model
.media
.IdentifiableMediaEntity
;
60 import eu
.etaxonomy
.cdm
.strategy
.cache
.common
.IIdentifiableEntityCacheStrategy
;
63 * type figures are observations with at least a figure object in media
66 * @created 08-Nov-2007 13:06:41
68 @XmlAccessorType(XmlAccessType
.FIELD
)
69 @XmlType(name
= "SpecimenOrObservationBase", propOrder
= {
78 @XmlRootElement(name
= "SpecimenOrObservationBase")
81 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
82 @Table(appliesTo
="SpecimenOrObservationBase", indexes
= { @Index(name
= "specimenOrObservationBaseTitleCacheIndex", columnNames
= { "titleCache" }) })
83 public abstract class SpecimenOrObservationBase
<S
extends IIdentifiableEntityCacheStrategy
> extends IdentifiableMediaEntity
<S
> implements IMultiLanguageTextHolder
{
85 private static final Logger logger
= Logger
.getLogger(SpecimenOrObservationBase
.class);
87 @XmlElementWrapper(name
= "Descriptions")
88 @XmlElement(name
= "Description")
89 @ManyToMany(fetch
= FetchType
.LAZY
,mappedBy
="describedSpecimenOrObservations",targetEntity
=DescriptionBase
.class)
90 @Cascade(CascadeType
.SAVE_UPDATE
)
92 private Set
<DescriptionBase
> descriptions
= new HashSet
<DescriptionBase
>();
94 @XmlElementWrapper(name
= "Determinations")
95 @XmlElement(name
= "Determination")
96 @OneToMany(mappedBy
="identifiedUnit")
97 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
, CascadeType
.DELETE_ORPHAN
})
98 @IndexedEmbedded(depth
= 2)
100 private Set
<DeterminationEvent
> determinations
= new HashSet
<DeterminationEvent
>();
102 @XmlElement(name
= "Sex")
104 @XmlSchemaType(name
= "IDREF")
105 @ManyToOne(fetch
= FetchType
.LAZY
)
108 @XmlElement(name
= "LifeStage")
110 @XmlSchemaType(name
= "IDREF")
111 @ManyToOne(fetch
= FetchType
.LAZY
)
112 private Stage lifeStage
;
114 @XmlElement(name
= "IndividualCount")
115 @Field(analyze
= Analyze
.NO
)
117 private Integer individualCount
;
119 // the verbatim description of this occurrence. Free text usable when no atomised data is available.
120 // in conjunction with titleCache which serves as the "citation" string for this object
121 @XmlElement(name
= "Description")
122 @XmlJavaTypeAdapter(MultilanguageTextAdapter
.class)
123 @OneToMany(fetch
= FetchType
.LAZY
)
124 @MapKeyJoinColumn(name
="definition_mapkey_id")
125 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.DELETE
, CascadeType
.DELETE_ORPHAN
})
128 protected Map
<Language
,LanguageString
> definition
= new HashMap
<Language
,LanguageString
>();
130 // events that created derivedUnits from this unit
131 @XmlElementWrapper(name
= "DerivationEvents")
132 @XmlElement(name
= "DerivationEvent")
134 @XmlSchemaType(name
= "IDREF")
135 @ManyToMany(fetch
=FetchType
.LAZY
)
136 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.DELETE
, CascadeType
.DELETE_ORPHAN
})
138 protected Set
<DerivationEvent
> derivationEvents
= new HashSet
<DerivationEvent
>();
143 protected SpecimenOrObservationBase(){
148 * The descriptions this specimen or observation is part of.<BR>
149 * A specimen can not only have it's own {@link SpecimenDescription specimen description }
150 * but can also be part of a {@link TaxonDescription taxon description} or a
151 * {@link TaxonNameDescription taxon name description}.<BR>
152 * @see #getSpecimenDescriptions()
155 public Set
<DescriptionBase
> getDescriptions() {
156 if(descriptions
== null) {
157 this.descriptions
= new HashSet
<DescriptionBase
>();
159 return this.descriptions
;
163 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
164 * @see #getDescriptions()
168 public Set
<SpecimenDescription
> getSpecimenDescriptions() {
169 return getSpecimenDescriptions(true);
173 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
174 * @see #getDescriptions()
178 public Set
<SpecimenDescription
> getSpecimenDescriptions(boolean includeImageGallery
) {
179 Set
<SpecimenDescription
> specimenDescriptions
= new HashSet
<SpecimenDescription
>();
180 for (DescriptionBase descriptionBase
: getDescriptions()){
181 if (descriptionBase
.isInstanceOf(SpecimenDescription
.class)){
182 if (includeImageGallery
|| descriptionBase
.isImageGallery() == false){
183 specimenDescriptions
.add(descriptionBase
.deproxy(descriptionBase
, SpecimenDescription
.class));
188 return specimenDescriptions
;
191 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
192 * @see #getDescriptions()
196 public Set
<SpecimenDescription
> getSpecimenDescriptionImageGallery() {
197 Set
<SpecimenDescription
> specimenDescriptions
= new HashSet
<SpecimenDescription
>();
198 for (DescriptionBase descriptionBase
: getDescriptions()){
199 if (descriptionBase
.isInstanceOf(SpecimenDescription
.class)){
200 if (descriptionBase
.isImageGallery() == true){
201 specimenDescriptions
.add(descriptionBase
.deproxy(descriptionBase
, SpecimenDescription
.class));
205 return specimenDescriptions
;
209 * Adds a new description to this specimen or observation
212 public void addDescription(DescriptionBase description
) {
213 this.descriptions
.add(description
);
214 if (! description
.getDescribedSpecimenOrObservations().contains(this)){
215 description
.addDescribedSpecimenOrObservation(this);
217 // Method method = ReflectionUtils.findMethod(SpecimenDescription.class, "addDescribedSpecimenOrObservation", new Class[] {SpecimenOrObservationBase.class});
218 // ReflectionUtils.makeAccessible(method);
219 // ReflectionUtils.invokeMethod(method, description, new Object[] {this});
223 * Removes a specimen from a description (removes a description from this specimen)
226 public void removeDescription(DescriptionBase description
) {
227 this.descriptions
.remove(description
);
228 if (description
.getDescribedSpecimenOrObservations().contains(this)){
229 description
.removeDescribedSpecimenOrObservation(this);
231 // Method method = ReflectionUtils.findMethod(SpecimenDescription.class, "removeDescribedSpecimenOrObservations", new Class[] {SpecimenOrObservationBase.class});
232 // ReflectionUtils.makeAccessible(method);
233 // ReflectionUtils.invokeMethod(method, description, new Object[] {this});
236 public Set
<DerivationEvent
> getDerivationEvents() {
237 if(derivationEvents
== null) {
238 this.derivationEvents
= new HashSet
<DerivationEvent
>();
240 return this.derivationEvents
;
243 public void addDerivationEvent(DerivationEvent derivationEvent
) {
244 if (! this.derivationEvents
.contains(derivationEvent
)){
245 this.derivationEvents
.add(derivationEvent
);
246 derivationEvent
.addOriginal(this);
250 public void removeDerivationEvent(DerivationEvent derivationEvent
) {
251 this.derivationEvents
.remove(derivationEvent
);
254 public Set
<DeterminationEvent
> getDeterminations() {
255 if(determinations
== null) {
256 this.determinations
= new HashSet
<DeterminationEvent
>();
258 return this.determinations
;
261 public void addDetermination(DeterminationEvent determination
) {
262 // FIXME bidirectional integrity. Use protected Determination setter
263 this.determinations
.add(determination
);
266 public void removeDetermination(DeterminationEvent determination
) {
267 // FIXME bidirectional integrity. Use protected Determination setter
268 this.determinations
.remove(determination
);
271 public Sex
getSex() {
275 public void setSex(Sex sex
) {
279 public Stage
getLifeStage() {
283 public void setLifeStage(Stage lifeStage
) {
284 this.lifeStage
= lifeStage
;
288 public String
generateTitle(){
289 return getCacheStrategy().getTitleCache(this);
292 public Integer
getIndividualCount() {
293 return individualCount
;
296 public void setIndividualCount(Integer individualCount
) {
297 this.individualCount
= individualCount
;
300 public Map
<Language
,LanguageString
> getDefinition(){
301 return this.definition
;
305 * adds the {@link LanguageString description} to the {@link MultilanguageText multilanguage text}
306 * used to define <i>this</i> specimen or observation.
308 * @param description the languageString in with the title string and the given language
310 * @see #getDefinition()
311 * @see #putDefinition(Language, String)
312 * @deprecated should follow the put semantic of maps, this method will be removed in v4.0
313 * Use the {@link #putDefinition(LanguageString) putDefinition} method instead
316 public void addDefinition(LanguageString description
){
317 this.putDefinition(description
);
320 * adds the {@link LanguageString description} to the {@link MultilanguageText multilanguage text}
321 * used to define <i>this</i> specimen or observation.
323 * @param description the languageString in with the title string and the given language
325 * @see #getDefinition()
326 * @see #putDefinition(Language, String)
328 public void putDefinition(LanguageString description
){
329 this.definition
.put(description
.getLanguage(),description
);
332 * Creates a {@link LanguageString language string} based on the given text string
333 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
334 * used to define <i>this</i> specimen or observation.
336 * @param language the language in which the title string is formulated
337 * @param text the definition in a particular language
339 * @see #getDefinition()
340 * @see #putDefinition(LanguageString)
341 * @deprecated should follow the put semantic of maps, this method will be removed in v4.0
342 * Use the {@link #putDefinition(Language String) putDefinition} method instead
345 public void addDefinition( String text
, Language language
){
346 this.putDefinition(language
, text
);
349 * Creates a {@link LanguageString language string} based on the given text string
350 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
351 * used to define <i>this</i> specimen or observation.
353 * @param language the language in which the title string is formulated
354 * @param text the definition in a particular language
356 * @see #getDefinition()
357 * @see #putDefinition(LanguageString)
359 public void putDefinition(Language language
, String text
){
360 this.definition
.put(language
, LanguageString
.NewInstance(text
, language
));
364 public void removeDefinition(Language lang
){
365 this.definition
.remove(lang
);
369 * for derived units get the single next higher parental/original unit.
370 * If multiple original units exist throw error
374 public SpecimenOrObservationBase
getOriginalUnit(){
375 logger
.warn("GetOriginalUnit not yet implemented");
380 //******************** CLONE **********************************************/
383 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
384 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
385 * @see java.lang.Object#clone()
388 public Object
clone() throws CloneNotSupportedException
{
389 SpecimenOrObservationBase result
= null;
390 result
= (SpecimenOrObservationBase
)super.clone();
392 //defininion (description, languageString)
393 result
.definition
= new HashMap
<Language
,LanguageString
>();
394 for(LanguageString languageString
: this.definition
.values()) {
395 LanguageString newLanguageString
= (LanguageString
)languageString
.clone();
396 result
.putDefinition(newLanguageString
);
400 result
.setSex(this.sex
);
402 result
.setLifeStage(this.lifeStage
);
405 for(DescriptionBase description
: this.descriptions
) {
406 result
.addDescription(description
);
409 //DeterminationEvent FIXME should clone() the determination
410 // as the relationship is OneToMany
411 for(DeterminationEvent determination
: this.determinations
) {
412 result
.addDetermination(determination
);
416 for(DerivationEvent derivationEvent
: this.derivationEvents
) {
417 result
.addDerivationEvent(derivationEvent
);
420 //no changes to: individualCount