removing @Override from interface implementation
[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.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.XmlSchemaType;
34 import javax.xml.bind.annotation.XmlType;
35 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
36
37 import org.apache.log4j.Logger;
38 import org.hibernate.annotations.Cascade;
39 import org.hibernate.annotations.CascadeType;
40 import org.hibernate.annotations.IndexColumn;
41 import org.hibernate.envers.Audited;
42 import org.hibernate.search.annotations.Field;
43 import org.hibernate.search.annotations.FieldBridge;
44 import org.hibernate.search.annotations.IndexedEmbedded;
45
46 import eu.etaxonomy.cdm.hibernate.search.DefinedTermBaseFieldBridge;
47 import eu.etaxonomy.cdm.hibernate.search.MultilanguageTextFieldBridge;
48 import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
49 import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
50 import eu.etaxonomy.cdm.model.common.DescriptionElementSource;
51 import eu.etaxonomy.cdm.model.common.IMultiLanguageTextHolder;
52 import eu.etaxonomy.cdm.model.common.ISourceable;
53 import eu.etaxonomy.cdm.model.common.Language;
54 import eu.etaxonomy.cdm.model.common.LanguageString;
55 import eu.etaxonomy.cdm.model.common.MultilanguageText;
56 import eu.etaxonomy.cdm.model.common.TermVocabulary;
57 import eu.etaxonomy.cdm.model.media.Media;
58 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
59 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
60 import eu.etaxonomy.cdm.model.reference.Reference;
61 import eu.etaxonomy.cdm.model.taxon.Taxon;
62 import eu.etaxonomy.cdm.strategy.merge.Merge;
63 import eu.etaxonomy.cdm.strategy.merge.MergeMode;
64
65 /**
66 * The upmost (abstract) class for a piece of information) about
67 * a {@link SpecimenOrObservationBase specimen}, a {@link Taxon taxon} or even a {@link TaxonNameBase taxon name}.
68 * A concrete description element assigns descriptive data to one {@link Feature feature}.<BR>
69 * Experts use the word feature for the property itself but not for the actual
70 * description element. Therefore naming this class FeatureBase would have
71 * leaded to confusion.
72 * <P>
73 * This class corresponds to: <ul>
74 * <li> DescriptionsBaseType according to the the SDD schema
75 * <li> InfoItem according to the TDWG ontology
76 * <li> MeasurementOrFactAtomised according to the ABCD schema
77 * </ul>
78 *
79 * @author m.doering
80 * @version 1.0
81 * @created 08-Nov-2007 13:06:24
82 */
83 @XmlAccessorType(XmlAccessType.FIELD)
84 @XmlType(name = "DescriptionElementBase", propOrder = {
85 "feature",
86 "modifiers",
87 "modifyingText",
88 "media",
89 "inDescription",
90 "sources"
91 })
92 @Entity
93 @Audited
94 @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
95 public abstract class DescriptionElementBase extends AnnotatableEntity implements ISourceable<DescriptionElementSource>, IModifiable, IMultiLanguageTextHolder{
96 private static final long serialVersionUID = 5000910777835755905L;
97 @SuppressWarnings("unused")
98 private static final Logger logger = Logger.getLogger(DescriptionElementBase.class);
99
100 //type, category of information. In structured descriptions characters
101 @XmlElement(name = "Feature")
102 @XmlIDREF
103 @XmlSchemaType(name = "IDREF")
104 @ManyToOne(fetch = FetchType.LAZY)
105 //@Cascade(CascadeType.SAVE_UPDATE)
106 @Cascade(CascadeType.MERGE)
107 @IndexedEmbedded(depth=3)
108 private Feature feature;
109
110 @XmlElementWrapper(name = "Modifiers")
111 @XmlElement(name = "Modifier")
112 @XmlIDREF
113 @XmlSchemaType(name = "IDREF")
114 @ManyToMany(fetch = FetchType.LAZY)
115 @JoinTable(name="DescriptionElementBase_Modifier")
116 @IndexedEmbedded(depth=3)
117 private Set<Modifier> modifiers = new HashSet<Modifier>();
118
119 @XmlElement(name = "ModifyingText")
120 @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
121 @OneToMany(fetch = FetchType.LAZY)
122 @JoinTable(name = "DescriptionElementBase_ModifyingText")
123 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
124 @Field
125 @FieldBridge(impl=MultilanguageTextFieldBridge.class)
126 private Map<Language,LanguageString> modifyingText = new HashMap<Language,LanguageString>();
127
128 @XmlElementWrapper(name = "Media")
129 @XmlElement(name = "Medium")
130 @XmlIDREF
131 @XmlSchemaType(name = "IDREF")
132 @ManyToMany(fetch = FetchType.LAZY)
133 @IndexColumn(name="sortIndex", base = 0)
134 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
135 private List<Media> media = new ArrayList<Media>();
136
137 @XmlElement(name = "InDescription")
138 @XmlIDREF
139 @XmlSchemaType(name = "IDREF")
140 @ManyToOne(fetch = FetchType.LAZY)
141 @Cascade(CascadeType.SAVE_UPDATE)
142 @IndexedEmbedded
143 private DescriptionBase inDescription;
144
145 @XmlElementWrapper(name = "Sources")
146 @XmlElement(name = "DescriptionElementSource")
147 @OneToMany(fetch = FetchType.LAZY)
148 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
149 @Merge(MergeMode.ADD_CLONE)
150 private Set<DescriptionElementSource> sources = new HashSet<DescriptionElementSource>();
151
152
153
154 // ************* CONSTRUCTORS *************/
155 /**
156 * Class constructor: creates a new empty description element instance.
157 *
158 * @see #DescriptionElementBase(Feature)
159 */
160 protected DescriptionElementBase(){
161 }
162
163 /**
164 * Class constructor: creates a new description element instance with the
165 * given {@link Feature feature} that is described or measured.
166 *
167 * @param feature the feature described or measured
168 * @see #DescriptionElementBase()
169 */
170 protected DescriptionElementBase(Feature feature){
171 if (feature == null){
172 feature = Feature.UNKNOWN();
173 }
174 this.feature = feature;
175 }
176
177 /**
178 * Returns the list of {@link Media media} (that is pictures, movies,
179 * recorded sounds ...) <i>this</i> description element is based on.
180 */
181 public List<Media> getMedia(){
182 return this.media;
183 }
184
185 /**
186 * Adds a {@link Media media} to the list of {@link #getMedia() media}
187 * <i>this</i> description element is based on.
188 *
189 * @param media the media to be added to <i>this</i> description element
190 * @see #getMedia()
191 */
192 public void addMedia(Media media){
193 this.media.add(media);
194 }
195 /**
196 * Removes one element from the list of {@link #getMedia() media}
197 * <i>this</i> description element is based on.
198 *
199 * @param media the media which should be removed
200 * @see #getMedia()
201 * @see #addMedia(Media)
202 */
203 public void removeMedia(Media media){
204 this.media.remove(media);
205 }
206
207 /**
208 * Returns the {@link DescriptionBase description} that <i>this</i> DescriptionElement is
209 * part of.
210 * @return
211 */
212 public DescriptionBase getInDescription() {
213 return this.inDescription;
214 }
215
216 /**
217 * @see #setInDescription()
218 */
219 protected void setInDescription(DescriptionBase inDescription) {
220 this.inDescription = inDescription;
221 }
222
223 /**
224 * Returns the {@link Feature feature} <i>this</i> description element is for.
225 * A feature is a property that can be described or measured but not the
226 * description or the measurement itself.
227 */
228 public Feature getFeature(){
229 return this.feature;
230 }
231
232 /**
233 * @see #getFeature()
234 */
235 public void setFeature(Feature feature){
236 this.feature = feature;
237 }
238
239 /**
240 * Returns the set of {@link Modifier modifiers} used to qualify the validity of
241 * <i>this</i> description element. This is only metainformation.
242 */
243 public Set<Modifier> getModifiers(){
244 return this.modifiers;
245 }
246
247 /**
248 * Adds a {@link Modifier modifier} to the set of {@link #getModifiers() modifiers}
249 * used to qualify the validity of <i>this</i> description element.
250 *
251 * @param modifier the modifier to be added to <i>this</i> description element
252 * @see #getModifiers()
253 */
254 public void addModifier(Modifier modifier){
255 this.modifiers.add(modifier);
256 }
257 /**
258 * Removes one element from the set of {@link #getModifiers() modifiers}
259 * used to qualify the validity of <i>this</i> description element.
260 *
261 * @param modifier the modifier which should be removed
262 * @see #getModifiers()
263 * @see #addModifier(Modifier)
264 */
265 public void removeModifier(Modifier modifier){
266 this.modifiers.remove(modifier);
267 }
268
269
270 /**
271 * Returns the {@link MultilanguageText multilanguage text} used to qualify the validity
272 * of <i>this</i> description element. The different {@link LanguageString language strings}
273 * contained in the multilanguage text should all have the same meaning.<BR>
274 * A multilanguage text does not belong to a controlled {@link TermVocabulary term vocabulary}
275 * as a {@link Modifier modifier} does.
276 * <P>
277 * NOTE: the actual content of <i>this</i> description element is NOT
278 * stored in the modifying text. This is only metainformation
279 * (like "Some experts express doubt about this assertion").
280 */
281 public Map<Language,LanguageString> getModifyingText(){
282 return this.modifyingText;
283 }
284
285 /**
286 * Adds a translated {@link LanguageString text in a particular language}
287 * to the {@link MultilanguageText multilanguage text} used to qualify the validity
288 * of <i>this</i> description element.
289 *
290 * @param description the language string describing the validity
291 * in a particular language
292 * @see #getModifyingText()
293 * @see #putModifyingText(Language, String)
294 * @deprecated should follow the put semantic of maps, this method will be removed in v4.0
295 * Use the {@link #putModifyingText(LanguageString) putModifyingText} method
296 */
297 public LanguageString addModifyingText(LanguageString description){
298 return this.putModifyingText(description);
299 }
300
301 /**
302 * Adds a translated {@link LanguageString text in a particular language}
303 * to the {@link MultilanguageText multilanguage text} used to qualify the validity
304 * of <i>this</i> description element.
305 *
306 * @param description the language string describing the validity
307 * in a particular language
308 * @see #getModifyingText()
309 * @see #putModifyingText(Language, String)
310 */
311 public LanguageString putModifyingText(LanguageString description){
312 return this.modifyingText.put(description.getLanguage(),description);
313 }
314 /**
315 * Creates a {@link LanguageString language string} based on the given text string
316 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
317 * used to qualify the validity of <i>this</i> description element.
318 *
319 * @param text the string describing the validity
320 * in a particular language
321 * @param language the language in which the text string is formulated
322 * @see #getModifyingText()
323 * @see #putModifyingText(LanguageString)
324 * @deprecated should follow the put semantic of maps, this method will be removed in v4.0
325 * Use the {@link #putModifyingText(Language, String) putModifyingText} method
326 */
327 public LanguageString addModifyingText(String text, Language language){
328 return this.putModifyingText(language, text);
329 }
330
331 /**
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 qualify the validity of <i>this</i> description element.
335 *
336 * @param language the language in which the text string is formulated
337 * @param text the string describing the validity
338 * in a particular language
339 *
340 * @see #getModifyingText()
341 * @see #putModifyingText(LanguageString)
342 *
343 */
344 public LanguageString putModifyingText(Language language, String text){
345 return this.modifyingText.put(language, LanguageString.NewInstance(text, language));
346 }
347 /**
348 * Removes from the {@link MultilanguageText multilanguage text} used to qualify the validity
349 * of <i>this</i> description element the one {@link LanguageString language string}
350 * with the given {@link Language language}.
351 *
352 * @param language the language in which the language string to be removed
353 * has been formulated
354 * @see #getModifyingText()
355 */
356 public LanguageString removeModifyingText(Language language){
357 return this.modifyingText.remove(language);
358 }
359
360 /* (non-Javadoc)
361 * @see eu.etaxonomy.cdm.model.common.ISourceable#getSources()
362 */
363 public Set<DescriptionElementSource> getSources() {
364 return this.sources;
365 }
366
367 /* (non-Javadoc)
368 * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
369 */
370 public void addSource(DescriptionElementSource source) {
371 if (source != null){
372 DescriptionElementBase oldSourcedObj = source.getSourcedObj();
373 if (oldSourcedObj != null && oldSourcedObj != this){
374 oldSourcedObj.getSources().remove(source);
375 }
376 this.sources.add(source);
377 source.setSourcedObj(this);
378 }
379 }
380
381 /* (non-Javadoc)
382 * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String)
383 */
384 public DescriptionElementSource addSource(String id, String idNamespace, Reference citation, String microCitation) {
385 if (id == null && idNamespace == null && citation == null && microCitation == null){
386 return null;
387 }
388 DescriptionElementSource source = DescriptionElementSource.NewInstance(id, idNamespace, citation, microCitation);
389 addSource(source);
390 return source;
391 }
392
393 public void addSource(String id, String idNamespace, Reference citation, String microReference, TaxonNameBase nameUsedInSource, String originalNameString){
394 DescriptionElementSource newSource = DescriptionElementSource.NewInstance(id, idNamespace, citation, microReference, nameUsedInSource, originalNameString);
395 addSource(newSource);
396 }
397
398 /* (non-Javadoc)
399 * @see eu.etaxonomy.cdm.model.common.ISourceable#removeSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
400 */
401 public void removeSource(DescriptionElementSource source) {
402 this.sources.remove(source);
403 }
404
405 // ******************* METHODS *************************************************************/
406
407 protected Map<TermVocabulary, List<Modifier>> makeModifierMap(){
408 Map<TermVocabulary, List<Modifier>> result = new HashMap<TermVocabulary, List<Modifier>>();
409 for (Modifier modifier : getModifiers()){
410 TermVocabulary<Modifier> voc = modifier.getVocabulary();
411 if (result.get(voc) == null){
412 result.put(voc, new ArrayList<Modifier>());
413 }
414 result.get(voc).add(modifier);
415 }
416 return result;
417 }
418
419 public List<Modifier> getModifiers(TermVocabulary voc){
420 List<Modifier> result = makeModifierMap().get(voc);
421 if (result == null){
422 result = new ArrayList<Modifier>();
423 }
424 return result;
425 }
426
427
428
429 //************************** CLONE **********************************************************/
430
431 /**
432 * Clones the description element. The element is <b>not</b> added to the same
433 * description as the orginal element (inDescription is set to <code>null</null>).
434 * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
435 */
436 @Override
437 public Object clone() throws CloneNotSupportedException{
438 DescriptionElementBase result = (DescriptionElementBase)super.clone();
439
440 //inDescription
441 result.inDescription = null;
442
443 //Sources
444 result.sources = new HashSet<DescriptionElementSource>();
445 for (DescriptionElementSource source : getSources()){
446 DescriptionElementSource newSource = (DescriptionElementSource)source.clone();
447 result.addSource(newSource);
448 }
449
450 //media
451 result.media = new ArrayList<Media>();
452 for (Media media : getMedia()){
453 result.media.add(media);
454 }
455
456 //modifying text
457 result.modifyingText = new HashMap<Language, LanguageString>();
458 for (Language language : getModifyingText().keySet()){
459 //TODO clone needed? See also IndividualsAssociation
460 LanguageString newLanguageString = (LanguageString)getModifyingText().get(language).clone();
461 result.modifyingText.put(language, newLanguageString);
462 }
463
464 //modifiers
465 result.modifiers = new HashSet<Modifier>();
466 for (Modifier modifier : getModifiers()){
467 result.modifiers.add(modifier);
468 }
469
470 //no changes to: feature
471 return result;
472 }
473
474 /**
475 * Clones the description element.<BR>
476 * The new element is added to the <code>description</code>.<BR>
477 * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
478 */
479 public DescriptionElementBase clone(DescriptionBase description) throws CloneNotSupportedException{
480 DescriptionElementBase result = (DescriptionElementBase)clone();
481 description.addElement(result);
482 return result;
483 }
484
485
486 }