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
.description
;
13 import java
.util
.ArrayList
;
14 import java
.util
.HashMap
;
15 import java
.util
.HashSet
;
16 import java
.util
.List
;
20 import javax
.persistence
.Entity
;
21 import javax
.persistence
.FetchType
;
22 import javax
.persistence
.Inheritance
;
23 import javax
.persistence
.InheritanceType
;
24 import javax
.persistence
.JoinTable
;
25 import javax
.persistence
.ManyToMany
;
26 import javax
.persistence
.ManyToOne
;
27 import javax
.persistence
.OneToMany
;
28 import javax
.persistence
.Transient
;
29 import javax
.xml
.bind
.annotation
.XmlAccessType
;
30 import javax
.xml
.bind
.annotation
.XmlAccessorType
;
31 import javax
.xml
.bind
.annotation
.XmlElement
;
32 import javax
.xml
.bind
.annotation
.XmlElementWrapper
;
33 import javax
.xml
.bind
.annotation
.XmlIDREF
;
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
.IndexColumn
;
42 import org
.hibernate
.envers
.Audited
;
43 import org
.hibernate
.search
.annotations
.IndexedEmbedded
;
45 import eu
.etaxonomy
.cdm
.jaxb
.MultilanguageTextAdapter
;
46 import eu
.etaxonomy
.cdm
.model
.common
.AnnotatableEntity
;
47 import eu
.etaxonomy
.cdm
.model
.common
.DescriptionElementSource
;
48 import eu
.etaxonomy
.cdm
.model
.common
.ISourceable
;
49 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
50 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
51 import eu
.etaxonomy
.cdm
.model
.common
.MultilanguageText
;
52 import eu
.etaxonomy
.cdm
.model
.common
.TermVocabulary
;
53 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
54 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
55 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
56 import eu
.etaxonomy
.cdm
.model
.reference
.ReferenceBase
;
57 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
58 import eu
.etaxonomy
.cdm
.strategy
.merge
.Merge
;
59 import eu
.etaxonomy
.cdm
.strategy
.merge
.MergeMode
;
62 * The upmost (abstract) class for a piece of information) about
63 * a {@link SpecimenOrObservationBase specimen}, a {@link Taxon taxon} or even a {@link TaxonNameBase taxon name}.
64 * A concrete description element assigns descriptive data to one {@link Feature feature}.<BR>
65 * Experts use the word feature for the property itself but not for the actual
66 * description element. Therefore naming this class FeatureBase would have
67 * leaded to confusion.
69 * This class corresponds to: <ul>
70 * <li> DescriptionsBaseType according to the the SDD schema
71 * <li> InfoItem according to the TDWG ontology
72 * <li> MeasurementOrFactAtomised according to the ABCD schema
77 * @created 08-Nov-2007 13:06:24
79 @XmlAccessorType(XmlAccessType
.FIELD
)
80 @XmlType(name
= "DescriptionElementBase", propOrder
= {
90 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
91 public abstract class DescriptionElementBase
extends AnnotatableEntity
implements ISourceable
<DescriptionElementSource
>, IModifiable
{
92 private static final long serialVersionUID
= 5000910777835755905L;
93 @SuppressWarnings("unused")
94 private static final Logger logger
= Logger
.getLogger(DescriptionElementBase
.class);
96 //type, category of information. In structured descriptions characters
97 @XmlElement(name
= "Feature")
99 @XmlSchemaType(name
= "IDREF")
100 @ManyToOne(fetch
= FetchType
.LAZY
)
101 //@Cascade(CascadeType.SAVE_UPDATE)
102 @Cascade(CascadeType
.MERGE
)
104 private Feature feature
;
106 @XmlElementWrapper(name
= "Modifiers")
107 @XmlElement(name
= "Modifier")
109 @XmlSchemaType(name
= "IDREF")
110 @ManyToMany(fetch
= FetchType
.LAZY
)
111 @JoinTable(name
="DescriptionElementBase_Modifier")
112 private Set
<Modifier
> modifiers
= new HashSet
<Modifier
>();
114 @XmlElement(name
= "ModifyingText")
115 @XmlJavaTypeAdapter(MultilanguageTextAdapter
.class)
116 @OneToMany(fetch
= FetchType
.LAZY
)
117 @JoinTable(name
= "DescriptionElementBase_ModifyingText")
118 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
119 private Map
<Language
,LanguageString
> modifyingText
= new HashMap
<Language
,LanguageString
>();
121 @XmlElementWrapper(name
= "Media")
122 @XmlElement(name
= "Medium")
124 @XmlSchemaType(name
= "IDREF")
125 @ManyToMany(fetch
= FetchType
.LAZY
)
126 @IndexColumn(name
="sortIndex", base
= 0)
127 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
128 private List
<Media
> media
= new ArrayList
<Media
>();
130 @XmlElement(name
= "InDescription")
132 @XmlSchemaType(name
= "IDREF")
133 @ManyToOne(fetch
= FetchType
.LAZY
)
134 @Cascade(CascadeType
.SAVE_UPDATE
)
136 private DescriptionBase inDescription
;
138 @XmlElementWrapper(name
= "Sources")
139 @XmlElement(name
= "DescriptionElementSource")
140 @OneToMany(fetch
= FetchType
.LAZY
)
141 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
, CascadeType
.DELETE_ORPHAN
})
142 @Merge(MergeMode
.ADD_CLONE
)
143 private Set
<DescriptionElementSource
> sources
= new HashSet
<DescriptionElementSource
>();
147 // ************* CONSTRUCTORS *************/
149 * Class constructor: creates a new empty description element instance.
151 * @see #DescriptionElementBase(Feature)
153 protected DescriptionElementBase(){
157 * Class constructor: creates a new description element instance with the
158 * given {@link Feature feature} that is described or measured.
160 * @param feature the feature described or measured
161 * @see #DescriptionElementBase()
163 protected DescriptionElementBase(Feature feature
){
164 if (feature
== null){
165 feature
= Feature
.UNKNOWN();
167 this.feature
= feature
;
171 * Returns the list of {@link Media media} (that is pictures, movies,
172 * recorded sounds ...) <i>this</i> description element is based on.
174 public List
<Media
> getMedia(){
179 * Adds a {@link Media media} to the list of {@link #getMedia() media}
180 * <i>this</i> description element is based on.
182 * @param media the media to be added to <i>this</i> description element
185 public void addMedia(Media media
){
186 this.media
.add(media
);
189 * Removes one element from the list of {@link #getMedia() media}
190 * <i>this</i> description element is based on.
192 * @param media the media which should be removed
194 * @see #addMedia(Media)
196 public void removeMedia(Media media
){
197 this.media
.remove(media
);
201 * Returns the {@link DescriptionBase description} that <i>this</i> DescriptionElement is
205 public DescriptionBase
getInDescription() {
206 return this.inDescription
;
210 * @see #setInDescription()
212 protected void setInDescription(DescriptionBase inDescription
) {
213 this.inDescription
= inDescription
;
217 * Does exactly the same as getFeature().
219 * FIXME Is there a need to have two methods with different names which do the same thing?
224 @Deprecated //will be removed in version 3.
225 public Feature
getType(){
226 return this.getFeature();
230 * Does exactly the same as setFeature(Feature).
232 * @param type the feature to be described or measured
233 * @see #setFeature(Feature)
236 @Deprecated //will be removed in version 3
237 public void setType(Feature type
){
238 this.setFeature(type
);
242 * Returns the {@link Feature feature} <i>this</i> description element is for.
243 * A feature is a property that can be described or measured but not the
244 * description or the measurement itself.
246 public Feature
getFeature(){
253 public void setFeature(Feature feature
){
254 this.feature
= feature
;
258 * Returns the set of {@link Modifier modifiers} used to qualify the validity of
259 * <i>this</i> description element. This is only metainformation.
261 public Set
<Modifier
> getModifiers(){
262 return this.modifiers
;
266 * Adds a {@link Modifier modifier} to the set of {@link #getModifiers() modifiers}
267 * used to qualify the validity of <i>this</i> description element.
269 * @param modifier the modifier to be added to <i>this</i> description element
270 * @see #getModifiers()
272 public void addModifier(Modifier modifier
){
273 this.modifiers
.add(modifier
);
276 * Removes one element from the set of {@link #getModifiers() modifiers}
277 * used to qualify the validity of <i>this</i> description element.
279 * @param modifier the modifier which should be removed
280 * @see #getModifiers()
281 * @see #addModifier(Modifier)
283 public void removeModifier(Modifier modifier
){
284 this.modifiers
.remove(modifier
);
289 * Returns the {@link MultilanguageText multilanguage text} used to qualify the validity
290 * of <i>this</i> description element. The different {@link LanguageString language strings}
291 * contained in the multilanguage text should all have the same meaning.<BR>
292 * A multilanguage text does not belong to a controlled {@link TermVocabulary term vocabulary}
293 * as a {@link Modifier modifier} does.
295 * NOTE: the actual content of <i>this</i> description element is NOT
296 * stored in the modifying text. This is only metainformation
297 * (like "Some experts express doubt about this assertion").
299 public Map
<Language
,LanguageString
> getModifyingText(){
300 return this.modifyingText
;
304 * Adds a translated {@link LanguageString text in a particular language}
305 * to the {@link MultilanguageText multilanguage text} used to qualify the validity
306 * of <i>this</i> description element.
308 * @param description the language string describing the validity
309 * in a particular language
310 * @see #getModifyingText()
311 * @see #addModifyingText(String, Language)
313 public LanguageString
addModifyingText(LanguageString description
){
314 return this.modifyingText
.put(description
.getLanguage(),description
);
317 * Creates a {@link LanguageString language string} based on the given text string
318 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
319 * used to qualify the validity of <i>this</i> description element.
321 * @param text the string describing the validity
322 * in a particular language
323 * @param language the language in which the text string is formulated
324 * @see #getModifyingText()
325 * @see #addModifyingText(LanguageString)
327 public LanguageString
addModifyingText(String text
, Language language
){
328 return this.modifyingText
.put(language
, LanguageString
.NewInstance(text
, language
));
331 * Removes from the {@link MultilanguageText multilanguage text} used to qualify the validity
332 * of <i>this</i> description element the one {@link LanguageString language string}
333 * with the given {@link Language language}.
335 * @param language the language in which the language string to be removed
336 * has been formulated
337 * @see #getModifyingText()
339 public LanguageString
removeModifyingText(Language language
){
340 return this.modifyingText
.remove(language
);
344 * @see eu.etaxonomy.cdm.model.common.ISourceable#getSources()
346 public Set
<DescriptionElementSource
> getSources() {
351 * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
353 public void addSource(DescriptionElementSource source
) {
355 DescriptionElementBase oldSourcedObj
= source
.getSourcedObj();
356 if (oldSourcedObj
!= null && oldSourcedObj
!= this){
357 oldSourcedObj
.getSources().remove(source
);
359 this.sources
.add(source
);
360 source
.setSourcedObj(this);
365 * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.reference.ReferenceBase, java.lang.String)
367 public DescriptionElementSource
addSource(String id
, String idNamespace
, ReferenceBase citation
, String microCitation
) {
368 if (id
== null && idNamespace
== null && citation
== null && microCitation
== null){
371 DescriptionElementSource source
= DescriptionElementSource
.NewInstance(id
, idNamespace
, citation
, microCitation
);
376 public void addSource(String id
, String idNamespace
, ReferenceBase citation
, String microReference
, TaxonNameBase nameUsedInSource
, String originalNameString
){
377 DescriptionElementSource newSource
= DescriptionElementSource
.NewInstance(id
, idNamespace
, citation
, microReference
, nameUsedInSource
, originalNameString
);
378 addSource(newSource
);
382 * @see eu.etaxonomy.cdm.model.common.ISourceable#removeSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
384 public void removeSource(DescriptionElementSource source
) {
385 this.sources
.remove(source
);
390 * Gets the citation micro reference of the first source. This method is deprecated and exists only to be compliant with version 2.0.
391 * It will be removed in v2.3
396 public String
getCitationMicroReference(){
397 if (this.sources
.size() < 1){
400 return this.sources
.iterator().next().getCitationMicroReference();
405 * Sets the citation micro reference of the first source. This method is deprecated and exists only to be compliant with version 2.0.
406 * It will be removed in v2.3
407 * If more than one source exists an IllegalStateException is thrown
411 public void setCitationMicroReference(String citationMicroReference
){
412 if (this.sources
.size() < 1){
413 ReferenceBase citation
= null;
414 this.addSource(DescriptionElementSource
.NewInstance(null, null, citation
, citationMicroReference
));
415 }else if (this.sources
.size() > 1){
416 throw new IllegalStateException("When adding a microcitation via the setCitationMicroReference method there must be only one source available");
418 this.sources
.iterator().next().setCitationMicroReference(citationMicroReference
);
423 * Gets the citation of the first source. This method is deprecated and exists only to be compliant with version 2.0.
424 * It will be removed in v2.3
428 public ReferenceBase
getCitation(){
429 if (this.sources
.size() < 1){
432 return this.sources
.iterator().next().getCitation();
437 * Sets the citation of the first source. This method is deprecated and exists only to be compliant with version 2.0.
438 * It will be removed in v2.3
439 * If more than one source exists an IllegalStateException is thrown
442 public void setCitation(ReferenceBase citation
) {
443 if (this.sources
.size() < 1){
444 this.addSource(DescriptionElementSource
.NewInstance(null, null, citation
, null));
445 }else if (this.sources
.size() > 1){
446 throw new IllegalStateException("When adding a citation via the setCitation method there must be only one source available");
448 this.sources
.iterator().next().setCitation(citation
);
454 * Gets the original name string of the first source. This method is deprecated and exists only to be compliant with version 2.0.
455 * It will be removed in v2.3
460 public String
getOriginalNameString(){
461 if (this.sources
.size() < 1){
464 return this.sources
.iterator().next().getOriginalNameString();
469 * Sets the original name string of the first source. This method is deprecated and exists only to be compliant with version 2.0.
470 * It will be removed in v2.3
471 * If more than one source exists an IllegalStateException is thrown
475 public void setOriginalNameString(String originalNameString
){
476 if (this.sources
.size() < 1){
477 this.addSource(DescriptionElementSource
.NewInstance(null, null, null, null, null, originalNameString
));
478 }else if (this.sources
.size() > 1){
479 throw new IllegalStateException("When adding a microcitation via the setCitationMicroReference method there must be only one source available");
481 this.sources
.iterator().next().setOriginalNameString(originalNameString
);
487 * Gets the name used in source of the first source. This method is deprecated and exists only to be compliant with version 2.0.
488 * It will be removed in v2.3
492 public TaxonNameBase
getNameUsedInReference(){
493 if (this.sources
.size() < 1){
496 return this.sources
.iterator().next().getNameUsedInSource();
501 * Sets the name used in reference of the first source. This method is deprecated and exists only to be compliant with version 2.0.
502 * It will be removed in v2.3
503 * If more than one source exists an IllegalStateException is thrown
506 public void setNameUsedInReference(TaxonNameBase nameUsedInSource
) {
507 if (this.sources
.size() < 1){
508 this.addSource(DescriptionElementSource
.NewInstance(null, null, null, null, nameUsedInSource
, null));
509 }else if (this.sources
.size() > 1){
510 throw new IllegalStateException("When adding a citation via the setCitation method there must be only one source available");
512 this.sources
.iterator().next().setNameUsedInSource(nameUsedInSource
);
516 //************************** CLONE **********************************************************/
519 * Clones the description element. The element is <b>not</b> added to the same
520 * description as the orginal element (inDescription is set to <code>null</null>.
521 * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
524 public Object
clone() throws CloneNotSupportedException
{
525 DescriptionElementBase result
= (DescriptionElementBase
)super.clone();
528 result
.sources
= new HashSet
<DescriptionElementSource
>();
529 for (DescriptionElementSource source
: getSources()){
530 DescriptionElementSource newSource
= (DescriptionElementSource
)source
.clone();
531 result
.addSource(newSource
);
535 result
.inDescription
= null;
541 * Clones the description element.<BR>
542 * The new element is added to the <code>description</code>.<BR>
543 * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
545 public DescriptionElementBase
clone(DescriptionBase description
) throws CloneNotSupportedException
{
546 DescriptionElementBase result
= (DescriptionElementBase
)clone();
547 description
.addElement(result
);