minor
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / description / DescriptionElementBase.java
1 /**
2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9
10 package eu.etaxonomy.cdm.model.description;
11
12
13 import java.util.ArrayList;
14 import java.util.HashMap;
15 import java.util.HashSet;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19
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;
37
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;
44
45 import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
46 import eu.etaxonomy.cdm.model.common.DescriptionElementSource;
47 import eu.etaxonomy.cdm.model.common.ISourceable;
48 import eu.etaxonomy.cdm.model.common.Language;
49 import eu.etaxonomy.cdm.model.common.LanguageString;
50 import eu.etaxonomy.cdm.model.common.MultilanguageText;
51 import eu.etaxonomy.cdm.model.common.ReferencedEntityBase;
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.taxon.Taxon;
57 import eu.etaxonomy.cdm.strategy.merge.Merge;
58 import eu.etaxonomy.cdm.strategy.merge.MergeMode;
59
60 /**
61 * The upmost (abstract) class for a piece of information) about
62 * a {@link SpecimenOrObservationBase specimen}, a {@link Taxon taxon} or even a {@link TaxonNameBase taxon name}.
63 * A concrete description element assigns descriptive data to one {@link Feature feature}.<BR>
64 * Experts use the word feature for the property itself but not for the actual
65 * description element. Therefore naming this class FeatureBase would have
66 * leaded to confusion.
67 * <P>
68 * This class corresponds to: <ul>
69 * <li> DescriptionsBaseType according to the the SDD schema
70 * <li> InfoItem according to the TDWG ontology
71 * <li> MeasurementOrFactAtomised according to the ABCD schema
72 * </ul>
73 *
74 * @author m.doering
75 * @version 1.0
76 * @created 08-Nov-2007 13:06:24
77 */
78 @XmlAccessorType(XmlAccessType.FIELD)
79 @XmlType(name = "DescriptionElementBase", propOrder = {
80 "feature",
81 "modifiers",
82 "modifyingText",
83 "media",
84 "inDescription",
85 "nameUsedInReference",
86 "sources"
87 })
88 @Entity
89 @Audited
90 @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
91 public abstract class DescriptionElementBase extends ReferencedEntityBase implements ISourceable<DescriptionElementSource> {
92 private static final long serialVersionUID = 5000910777835755905L;
93 @SuppressWarnings("unused")
94 private static final Logger logger = Logger.getLogger(DescriptionElementBase.class);
95
96 //type, category of information. In structured descriptions characters
97 @XmlElement(name = "Feature")
98 @XmlIDREF
99 @XmlSchemaType(name = "IDREF")
100 @ManyToOne(fetch = FetchType.LAZY)
101 @IndexedEmbedded
102 private Feature feature;
103
104 @XmlElementWrapper(name = "Modifiers")
105 @XmlElement(name = "Modifier")
106 @XmlIDREF
107 @XmlSchemaType(name = "IDREF")
108 @ManyToMany(fetch = FetchType.LAZY)
109 @JoinTable(name="DescriptionElementBase_Modifier")
110 private Set<Modifier> modifiers = new HashSet<Modifier>();
111
112 @XmlElement(name = "ModifyingText")
113 @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
114 @OneToMany(fetch = FetchType.LAZY)
115 @JoinTable(name = "DescriptionElementBase_ModifyingText")
116 private Map<Language,LanguageString> modifyingText = new HashMap<Language,LanguageString>();
117
118 @XmlElementWrapper(name = "Media")
119 @XmlElement(name = "Medium")
120 @XmlIDREF
121 @XmlSchemaType(name = "IDREF")
122 @ManyToMany(fetch = FetchType.LAZY)
123 @IndexColumn(name="sortIndex", base = 0)
124 @Cascade({CascadeType.SAVE_UPDATE})
125 private List<Media> media = new ArrayList<Media>();
126
127 @XmlElement(name = "InDescription")
128 @XmlIDREF
129 @XmlSchemaType(name = "IDREF")
130 @ManyToOne(fetch = FetchType.LAZY)
131 @Cascade(CascadeType.SAVE_UPDATE)
132 @IndexedEmbedded
133 private DescriptionBase inDescription;
134
135 //TODO can this be handled together with ReferencedEntityBase.originalNameString??
136 @XmlElement(name = "nameUsedInReference")
137 @XmlIDREF
138 @XmlSchemaType(name = "IDREF")
139 @ManyToOne(fetch = FetchType.LAZY)
140 @Cascade({CascadeType.SAVE_UPDATE})
141 private TaxonNameBase nameUsedInReference;
142
143 @XmlElementWrapper(name = "Sources")
144 @XmlElement(name = "OriginalSource")
145 @OneToMany(fetch = FetchType.LAZY)
146 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
147 @Merge(MergeMode.ADD_CLONE)
148 private Set<DescriptionElementSource> sources = new HashSet<DescriptionElementSource>();
149
150
151
152 // ************* CONSTRUCTORS *************/
153 /**
154 * Class constructor: creates a new empty description element instance.
155 *
156 * @see #DescriptionElementBase(Feature)
157 */
158 protected DescriptionElementBase(){
159 }
160
161 /**
162 * Class constructor: creates a new description element instance with the
163 * given {@link Feature feature} that is described or measured.
164 *
165 * @param feature the feature described or measured
166 * @see #DescriptionElementBase()
167 */
168 protected DescriptionElementBase(Feature feature){
169 if (feature == null){
170 feature = Feature.UNKNOWN();
171 }
172 this.feature = feature;
173 }
174
175 /**
176 * Returns the set of {@link Media media} (that is pictures, movies,
177 * recorded sounds ...) <i>this</i> description element is based on.
178 */
179 public List<Media> getMedia(){
180 return this.media;
181 }
182
183 /**
184 * Adds a {@link Media media} to the set of {@link #getMedia() media}
185 * <i>this</i> description element is based on.
186 *
187 * @param media the media to be added to <i>this</i> description element
188 * @see #getMedia()
189 */
190 public void addMedia(Media media){
191 this.media.add(media);
192 }
193 /**
194 * Removes one element from the set of {@link #getMedia() media}
195 * <i>this</i> description element is based on.
196 *
197 * @param media the media which should be removed
198 * @see #getMedia()
199 * @see #addMedia(Media)
200 */
201 public void removeMedia(Media media){
202 this.media.remove(media);
203 }
204
205 /**
206 * Returns the {@link DescriptionBase description} that <i>this</i> DescriptionElement is
207 * part of.
208 * @return
209 */
210 public DescriptionBase getInDescription() {
211 return this.inDescription;
212 }
213
214 /**
215 * @see #setInDescription()
216 */
217 protected void setInDescription(DescriptionBase inDescription) {
218 this.inDescription = inDescription;
219 }
220
221 /**
222 * Does exactly the same as getFeature().
223 * @author ben.clark
224 * FIXME Is there a need to have two methods with different names which do the same thing?
225 *
226 * @see #getFeature()
227 */
228 @Transient
229 public Feature getType(){
230 return this.getFeature();
231 }
232 /**
233 * Does exactly the same as setFeature(Feature).
234 *
235 * @param type the feature to be described or measured
236 * @see #setFeature(Feature)
237 * @see #getFeature()
238 */
239 public void setType(Feature type){
240 this.setFeature(type);
241 }
242
243 /**
244 * Returns the {@link Feature feature} <i>this</i> description element is for.
245 * A feature is a property that can be described or measured but not the
246 * description or the measurement itself.
247 */
248 public Feature getFeature(){
249 return this.feature;
250 }
251
252 /**
253 * @see #getFeature()
254 */
255 public void setFeature(Feature feature){
256 this.feature = feature;
257 }
258
259 /**
260 * Returns the set of {@link Modifier modifiers} used to qualify the validity of
261 * <i>this</i> description element. This is only metainformation.
262 */
263 public Set<Modifier> getModifiers(){
264 return this.modifiers;
265 }
266
267 /**
268 * Adds a {@link Modifier modifier} to the set of {@link #getModifiers() modifiers}
269 * used to qualify the validity of <i>this</i> description element.
270 *
271 * @param modifier the modifier to be added to <i>this</i> description element
272 * @see #getModifiers()
273 */
274 public void addModifier(Modifier modifier){
275 this.modifiers.add(modifier);
276 }
277 /**
278 * Removes one element from the set of {@link #getModifiers() modifiers}
279 * used to qualify the validity of <i>this</i> description element.
280 *
281 * @param modifier the modifier which should be removed
282 * @see #getModifiers()
283 * @see #addModifier(Modifier)
284 */
285 public void removeModifier(Modifier modifier){
286 this.modifiers.remove(modifier);
287 }
288
289
290 /**
291 * Returns the {@link MultilanguageText multilanguage text} used to qualify the validity
292 * of <i>this</i> description element. The different {@link LanguageString language strings}
293 * contained in the multilanguage text should all have the same meaning.<BR>
294 * A multilanguage text does not belong to a controlled {@link TermVocabulary term vocabulary}
295 * as a {@link Modifier modifier} does.
296 * <P>
297 * NOTE: the actual content of <i>this</i> description element is NOT
298 * stored in the modifying text. This is only metainformation
299 * (like "Some experts express doubt about this assertion").
300 */
301 public Map<Language,LanguageString> getModifyingText(){
302 return this.modifyingText;
303 }
304
305 /**
306 * Adds a translated {@link LanguageString text in a particular language}
307 * to the {@link MultilanguageText multilanguage text} used to qualify the validity
308 * of <i>this</i> description element.
309 *
310 * @param description the language string describing the validity
311 * in a particular language
312 * @see #getModifyingText()
313 * @see #addModifyingText(String, Language)
314 */
315 public LanguageString addModifyingText(LanguageString description){
316 return this.modifyingText.put(description.getLanguage(),description);
317 }
318 /**
319 * Creates a {@link LanguageString language string} based on the given text string
320 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
321 * used to qualify the validity of <i>this</i> description element.
322 *
323 * @param text the string describing the validity
324 * in a particular language
325 * @param language the language in which the text string is formulated
326 * @see #getModifyingText()
327 * @see #addModifyingText(LanguageString)
328 */
329 public LanguageString addModifyingText(String text, Language language){
330 return this.modifyingText.put(language, LanguageString.NewInstance(text, language));
331 }
332 /**
333 * Removes from the {@link MultilanguageText multilanguage text} used to qualify the validity
334 * of <i>this</i> description element the one {@link LanguageString language string}
335 * with the given {@link Language language}.
336 *
337 * @param language the language in which the language string to be removed
338 * has been formulated
339 * @see #getModifyingText()
340 */
341 public LanguageString removeModifyingText(Language language){
342 return this.modifyingText.remove(language);
343 }
344
345 /* (non-Javadoc)
346 * @see eu.etaxonomy.cdm.model.common.ISourceable#getSources()
347 */
348 public Set<DescriptionElementSource> getSources() {
349 return this.sources;
350 }
351
352 /* (non-Javadoc)
353 * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
354 */
355 public void addSource(DescriptionElementSource source) {
356 if (source != null){
357 DescriptionElementBase oldSourcedObj = source.getSourcedObj();
358 if (oldSourcedObj != null && oldSourcedObj != this){
359 oldSourcedObj.getSources().remove(source);
360 }
361 this.sources.add(source);
362 source.setSourcedObj(this);
363 }
364 }
365
366 /* (non-Javadoc)
367 * @see eu.etaxonomy.cdm.model.common.ISourceable#removeSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
368 */
369 public void removeSource(DescriptionElementSource source) {
370 this.sources.remove(source);
371 }
372
373
374 /**
375 * @return the nameUsedInReference
376 */
377 public TaxonNameBase getNameUsedInReference() {
378 return nameUsedInReference;
379 }
380
381 /**
382 * @param nameUsedInReference the nameUsedInReference to set
383 */
384 public void setNameUsedInReference(TaxonNameBase nameUsedInReference) {
385 this.nameUsedInReference = nameUsedInReference;
386 }
387
388
389 }