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
.MapKeyJoinColumn
;
28 import javax
.persistence
.OneToMany
;
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
.IMultiLanguageTextHolder
;
49 import eu
.etaxonomy
.cdm
.model
.common
.IOriginalSource
;
50 import eu
.etaxonomy
.cdm
.model
.common
.ISourceable
;
51 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
52 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
53 import eu
.etaxonomy
.cdm
.model
.common
.MultilanguageText
;
54 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
55 import eu
.etaxonomy
.cdm
.model
.common
.TermVocabulary
;
56 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
57 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
58 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
59 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
60 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
61 import eu
.etaxonomy
.cdm
.strategy
.merge
.Merge
;
62 import eu
.etaxonomy
.cdm
.strategy
.merge
.MergeMode
;
65 * The upmost (abstract) class for a piece of information) about
66 * a {@link SpecimenOrObservationBase specimen}, a {@link Taxon taxon} or even a {@link TaxonNameBase taxon name}.
67 * A concrete description element assigns descriptive data to one {@link Feature feature}.<BR>
68 * Experts use the word feature for the property itself but not for the actual
69 * description element. Therefore naming this class FeatureBase would have
70 * leaded to confusion.
72 * This class corresponds to: <ul>
73 * <li> DescriptionsBaseType according to the the SDD schema
74 * <li> InfoItem according to the TDWG ontology
75 * <li> MeasurementOrFactAtomised according to the ABCD schema
80 * @created 08-Nov-2007 13:06:24
82 @XmlAccessorType(XmlAccessType
.FIELD
)
83 @XmlType(name
= "DescriptionElementBase", propOrder
= {
93 @Inheritance(strategy
=InheritanceType
.SINGLE_TABLE
)
94 public abstract class DescriptionElementBase
extends AnnotatableEntity
implements ISourceable
<DescriptionElementSource
>, IModifiable
, IMultiLanguageTextHolder
{
95 private static final long serialVersionUID
= 5000910777835755905L;
96 @SuppressWarnings("unused")
97 private static final Logger logger
= Logger
.getLogger(DescriptionElementBase
.class);
99 //type, category of information. In structured descriptions characters
100 @XmlElement(name
= "Feature")
102 @XmlSchemaType(name
= "IDREF")
103 @ManyToOne(fetch
= FetchType
.LAZY
)
104 //@Cascade(CascadeType.SAVE_UPDATE)
105 @Cascade(CascadeType
.MERGE
)
106 @IndexedEmbedded // no depth for terms
107 private Feature feature
;
109 @XmlElementWrapper(name
= "Modifiers")
110 @XmlElement(name
= "Modifier")
112 @XmlSchemaType(name
= "IDREF")
113 @ManyToMany(fetch
= FetchType
.LAZY
)
114 @JoinTable(name
="DescriptionElementBase_Modifier")
115 @IndexedEmbedded(depth
=1)
116 private Set
<Modifier
> modifiers
= new HashSet
<Modifier
>();
118 @XmlElement(name
= "ModifyingText")
119 @XmlJavaTypeAdapter(MultilanguageTextAdapter
.class)
120 @OneToMany(fetch
= FetchType
.LAZY
)
121 @JoinTable(name
= "DescriptionElementBase_ModifyingText")
122 @MapKeyJoinColumn(name
="modifyingtext_mapkey_id")
123 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
125 private Map
<Language
,LanguageString
> modifyingText
= new HashMap
<Language
,LanguageString
>();
127 @XmlElementWrapper(name
= "Media")
128 @XmlElement(name
= "Medium")
130 @XmlSchemaType(name
= "IDREF")
131 @ManyToMany(fetch
= FetchType
.LAZY
)
132 @IndexColumn(name
="sortIndex", base
= 0)
133 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
})
134 private List
<Media
> media
= new ArrayList
<Media
>();
136 @XmlElement(name
= "InDescription")
138 @XmlSchemaType(name
= "IDREF")
139 @ManyToOne(fetch
= FetchType
.LAZY
)
140 @Cascade(CascadeType
.SAVE_UPDATE
)
142 private DescriptionBase inDescription
;
144 @XmlElementWrapper(name
= "Sources")
145 @XmlElement(name
= "DescriptionElementSource")
146 @OneToMany(fetch
= FetchType
.LAZY
, orphanRemoval
=true)
147 @Cascade({CascadeType
.SAVE_UPDATE
, CascadeType
.MERGE
, CascadeType
.DELETE
})
148 @Merge(MergeMode
.ADD_CLONE
)
149 private Set
<DescriptionElementSource
> sources
= new HashSet
<DescriptionElementSource
>();
153 // ************* CONSTRUCTORS *************/
155 * Class constructor: creates a new empty description element instance.
157 * @see #DescriptionElementBase(Feature)
159 protected DescriptionElementBase(){
163 * Class constructor: creates a new description element instance with the
164 * given {@link Feature feature} that is described or measured.
166 * @param feature the feature described or measured
167 * @see #DescriptionElementBase()
169 protected DescriptionElementBase(Feature feature
){
170 if (feature
== null){
171 feature
= Feature
.UNKNOWN();
173 this.feature
= feature
;
177 * Returns the list of {@link Media media} (that is pictures, movies,
178 * recorded sounds ...) <i>this</i> description element is based on.
180 public List
<Media
> getMedia(){
185 * Adds a {@link Media media} to the list of {@link #getMedia() media}
186 * <i>this</i> description element is based on.
188 * @param media the media to be added to <i>this</i> description element
191 public void addMedia(Media media
){
192 this.media
.add(media
);
195 * Removes one element from the list of {@link #getMedia() media}
196 * <i>this</i> description element is based on.
198 * @param media the media which should be removed
200 * @see #addMedia(Media)
202 public void removeMedia(Media media
){
203 this.media
.remove(media
);
207 * Returns the {@link DescriptionBase description} that <i>this</i> DescriptionElement is
211 public DescriptionBase
getInDescription() {
212 return this.inDescription
;
216 * @see #setInDescription()
218 protected void setInDescription(DescriptionBase inDescription
) {
219 this.inDescription
= inDescription
;
223 * Returns the {@link Feature feature} <i>this</i> description element is for.
224 * A feature is a property that can be described or measured but not the
225 * description or the measurement itself.
227 public Feature
getFeature(){
234 public void setFeature(Feature feature
){
235 this.feature
= feature
;
239 * Returns the set of {@link Modifier modifiers} used to qualify the validity of
240 * <i>this</i> description element. This is only metainformation.
242 public Set
<Modifier
> getModifiers(){
243 return this.modifiers
;
247 * Adds a {@link Modifier modifier} to the set of {@link #getModifiers() modifiers}
248 * used to qualify the validity of <i>this</i> description element.
250 * @param modifier the modifier to be added to <i>this</i> description element
251 * @see #getModifiers()
253 public void addModifier(Modifier modifier
){
254 this.modifiers
.add(modifier
);
257 * Removes one element from the set of {@link #getModifiers() modifiers}
258 * used to qualify the validity of <i>this</i> description element.
260 * @param modifier the modifier which should be removed
261 * @see #getModifiers()
262 * @see #addModifier(Modifier)
264 public void removeModifier(Modifier modifier
){
265 this.modifiers
.remove(modifier
);
270 * Returns the {@link MultilanguageText multilanguage text} used to qualify the validity
271 * of <i>this</i> description element. The different {@link LanguageString language strings}
272 * contained in the multilanguage text should all have the same meaning.<BR>
273 * A multilanguage text does not belong to a controlled {@link TermVocabulary term vocabulary}
274 * as a {@link Modifier modifier} does.
276 * NOTE: the actual content of <i>this</i> description element is NOT
277 * stored in the modifying text. This is only metainformation
278 * (like "Some experts express doubt about this assertion").
280 public Map
<Language
,LanguageString
> getModifyingText(){
281 return this.modifyingText
;
285 * Adds a translated {@link LanguageString text in a particular language}
286 * to the {@link MultilanguageText multilanguage text} used to qualify the validity
287 * of <i>this</i> description element.
289 * @param description the language string describing the validity
290 * in a particular language
291 * @see #getModifyingText()
292 * @see #putModifyingText(Language, String)
293 * @deprecated should follow the put semantic of maps, this method will be removed in v4.0
294 * Use the {@link #putModifyingText(LanguageString) putModifyingText} method
296 public LanguageString
addModifyingText(LanguageString description
){
297 return this.putModifyingText(description
);
301 * Adds a translated {@link LanguageString text in a particular language}
302 * to the {@link MultilanguageText multilanguage text} used to qualify the validity
303 * of <i>this</i> description element.
305 * @param description the language string describing the validity
306 * in a particular language
307 * @see #getModifyingText()
308 * @see #putModifyingText(Language, String)
310 public LanguageString
putModifyingText(LanguageString description
){
311 return this.modifyingText
.put(description
.getLanguage(),description
);
314 * Creates a {@link LanguageString language string} based on the given text string
315 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
316 * used to qualify the validity of <i>this</i> description element.
318 * @param text the string describing the validity
319 * in a particular language
320 * @param language the language in which the text string is formulated
321 * @see #getModifyingText()
322 * @see #putModifyingText(LanguageString)
323 * @deprecated should follow the put semantic of maps, this method will be removed in v4.0
324 * Use the {@link #putModifyingText(Language, String) putModifyingText} method
326 public LanguageString
addModifyingText(String text
, Language language
){
327 return this.putModifyingText(language
, text
);
331 * Creates a {@link LanguageString language string} based on the given text string
332 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
333 * used to qualify the validity of <i>this</i> description element.
335 * @param language the language in which the text string is formulated
336 * @param text the string describing the validity
337 * in a particular language
339 * @see #getModifyingText()
340 * @see #putModifyingText(LanguageString)
343 public LanguageString
putModifyingText(Language language
, String text
){
344 return this.modifyingText
.put(language
, LanguageString
.NewInstance(text
, language
));
347 * Removes from the {@link MultilanguageText multilanguage text} used to qualify the validity
348 * of <i>this</i> description element the one {@link LanguageString language string}
349 * with the given {@link Language language}.
351 * @param language the language in which the language string to be removed
352 * has been formulated
353 * @see #getModifyingText()
355 public LanguageString
removeModifyingText(Language language
){
356 return this.modifyingText
.remove(language
);
360 * @see eu.etaxonomy.cdm.model.common.ISourceable#getSources()
362 public Set
<DescriptionElementSource
> getSources() {
367 * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
369 public void addSource(DescriptionElementSource source
) {
371 DescriptionElementBase oldSourcedObj
= source
.getSourcedObj();
372 if (oldSourcedObj
!= null && oldSourcedObj
!= this){
373 oldSourcedObj
.getSources().remove(source
);
375 this.sources
.add(source
);
376 source
.setSourcedObj(this);
381 * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(eu.etaxonomy.cdm.model.common.OriginalSourceType, java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String)
383 public DescriptionElementSource
addSource(OriginalSourceType type
, String id
, String idNamespace
, Reference citation
, String microCitation
) {
384 if (id
== null && idNamespace
== null && citation
== null && microCitation
== null){
387 DescriptionElementSource source
= DescriptionElementSource
.NewInstance(type
, id
, idNamespace
, citation
, microCitation
);
394 * @see eu.etaxonomy.cdm.model.common.ISourceable#addImportSource(java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String)
397 public DescriptionElementSource
addImportSource(String id
, String idNamespace
, Reference
<?
> citation
, String microCitation
) {
398 if (id
== null && idNamespace
== null && citation
== null && microCitation
== null){
401 DescriptionElementSource source
= DescriptionElementSource
.NewInstance(OriginalSourceType
.Import
, id
, idNamespace
, citation
, microCitation
);
407 * Adds a {@link IOriginalSource source} to this description element.
408 * @param type the type of the source
409 * @param idInSource the id used in the source
410 * @param idNamespace the namespace for the id in the source
411 * @param citation the source as a {@link Reference reference}
412 * @param microReference the details (e.g. page number) in the reference
413 * @param nameUsedInSource the taxon name used in the source
414 * @param originalNameString the name as text used in the source
416 public void addSource(OriginalSourceType type
, String idInSource
, String idNamespace
, Reference citation
, String microReference
, TaxonNameBase nameUsedInSource
, String originalNameString
){
417 DescriptionElementSource newSource
= DescriptionElementSource
.NewInstance(type
, idInSource
, idNamespace
, citation
, microReference
, nameUsedInSource
, originalNameString
);
418 addSource(newSource
);
422 * @see eu.etaxonomy.cdm.model.common.ISourceable#removeSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
424 public void removeSource(DescriptionElementSource source
) {
425 this.sources
.remove(source
);
428 // ******************* METHODS *************************************************************/
430 protected Map
<TermVocabulary
, List
<Modifier
>> makeModifierMap(){
431 Map
<TermVocabulary
, List
<Modifier
>> result
= new HashMap
<TermVocabulary
, List
<Modifier
>>();
432 for (Modifier modifier
: getModifiers()){
433 TermVocabulary
<Modifier
> voc
= modifier
.getVocabulary();
434 if (result
.get(voc
) == null){
435 result
.put(voc
, new ArrayList
<Modifier
>());
437 result
.get(voc
).add(modifier
);
442 public List
<Modifier
> getModifiers(TermVocabulary voc
){
443 List
<Modifier
> result
= makeModifierMap().get(voc
);
445 result
= new ArrayList
<Modifier
>();
452 //************************** CLONE **********************************************************/
455 * Clones the description element. The element is <b>not</b> added to the same
456 * description as the orginal element (inDescription is set to <code>null</null>).
457 * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
460 public Object
clone() throws CloneNotSupportedException
{
461 DescriptionElementBase result
= (DescriptionElementBase
)super.clone();
464 result
.inDescription
= null;
467 result
.sources
= new HashSet
<DescriptionElementSource
>();
468 for (DescriptionElementSource source
: getSources()){
469 DescriptionElementSource newSource
= (DescriptionElementSource
)source
.clone();
470 result
.addSource(newSource
);
474 result
.media
= new ArrayList
<Media
>();
475 for (Media media
: getMedia()){
476 result
.media
.add(media
);
480 result
.modifyingText
= new HashMap
<Language
, LanguageString
>();
481 for (Language language
: getModifyingText().keySet()){
482 //TODO clone needed? See also IndividualsAssociation
483 LanguageString newLanguageString
= (LanguageString
)getModifyingText().get(language
).clone();
484 result
.modifyingText
.put(language
, newLanguageString
);
488 result
.modifiers
= new HashSet
<Modifier
>();
489 for (Modifier modifier
: getModifiers()){
490 result
.modifiers
.add(modifier
);
493 //no changes to: feature
498 * Clones the description element.<BR>
499 * The new element is added to the <code>description</code>.<BR>
500 * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
502 public DescriptionElementBase
clone(DescriptionBase description
) throws CloneNotSupportedException
{
503 DescriptionElementBase result
= (DescriptionElementBase
)clone();
504 description
.addElement(result
);