update factory methods for original sources #1549
[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.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;
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.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;
63
64 /**
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.
71 * <P>
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
76 * </ul>
77 *
78 * @author m.doering
79 * @version 1.0
80 * @created 08-Nov-2007 13:06:24
81 */
82 @XmlAccessorType(XmlAccessType.FIELD)
83 @XmlType(name = "DescriptionElementBase", propOrder = {
84 "feature",
85 "modifiers",
86 "modifyingText",
87 "media",
88 "inDescription",
89 "sources"
90 })
91 @Entity
92 @Audited
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);
98
99 //type, category of information. In structured descriptions characters
100 @XmlElement(name = "Feature")
101 @XmlIDREF
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;
108
109 @XmlElementWrapper(name = "Modifiers")
110 @XmlElement(name = "Modifier")
111 @XmlIDREF
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>();
117
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})
124 @IndexedEmbedded
125 private Map<Language,LanguageString> modifyingText = new HashMap<Language,LanguageString>();
126
127 @XmlElementWrapper(name = "Media")
128 @XmlElement(name = "Medium")
129 @XmlIDREF
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>();
135
136 @XmlElement(name = "InDescription")
137 @XmlIDREF
138 @XmlSchemaType(name = "IDREF")
139 @ManyToOne(fetch = FetchType.LAZY)
140 @Cascade(CascadeType.SAVE_UPDATE)
141 @IndexedEmbedded
142 private DescriptionBase inDescription;
143
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>();
150
151
152
153 // ************* CONSTRUCTORS *************/
154 /**
155 * Class constructor: creates a new empty description element instance.
156 *
157 * @see #DescriptionElementBase(Feature)
158 */
159 protected DescriptionElementBase(){
160 }
161
162 /**
163 * Class constructor: creates a new description element instance with the
164 * given {@link Feature feature} that is described or measured.
165 *
166 * @param feature the feature described or measured
167 * @see #DescriptionElementBase()
168 */
169 protected DescriptionElementBase(Feature feature){
170 if (feature == null){
171 feature = Feature.UNKNOWN();
172 }
173 this.feature = feature;
174 }
175
176 /**
177 * Returns the list of {@link Media media} (that is pictures, movies,
178 * recorded sounds ...) <i>this</i> description element is based on.
179 */
180 public List<Media> getMedia(){
181 return this.media;
182 }
183
184 /**
185 * Adds a {@link Media media} to the list of {@link #getMedia() media}
186 * <i>this</i> description element is based on.
187 *
188 * @param media the media to be added to <i>this</i> description element
189 * @see #getMedia()
190 */
191 public void addMedia(Media media){
192 this.media.add(media);
193 }
194 /**
195 * Removes one element from the list of {@link #getMedia() media}
196 * <i>this</i> description element is based on.
197 *
198 * @param media the media which should be removed
199 * @see #getMedia()
200 * @see #addMedia(Media)
201 */
202 public void removeMedia(Media media){
203 this.media.remove(media);
204 }
205
206 /**
207 * Returns the {@link DescriptionBase description} that <i>this</i> DescriptionElement is
208 * part of.
209 * @return
210 */
211 public DescriptionBase getInDescription() {
212 return this.inDescription;
213 }
214
215 /**
216 * @see #setInDescription()
217 */
218 protected void setInDescription(DescriptionBase inDescription) {
219 this.inDescription = inDescription;
220 }
221
222 /**
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.
226 */
227 public Feature getFeature(){
228 return this.feature;
229 }
230
231 /**
232 * @see #getFeature()
233 */
234 public void setFeature(Feature feature){
235 this.feature = feature;
236 }
237
238 /**
239 * Returns the set of {@link Modifier modifiers} used to qualify the validity of
240 * <i>this</i> description element. This is only metainformation.
241 */
242 public Set<Modifier> getModifiers(){
243 return this.modifiers;
244 }
245
246 /**
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.
249 *
250 * @param modifier the modifier to be added to <i>this</i> description element
251 * @see #getModifiers()
252 */
253 public void addModifier(Modifier modifier){
254 this.modifiers.add(modifier);
255 }
256 /**
257 * Removes one element from the set of {@link #getModifiers() modifiers}
258 * used to qualify the validity of <i>this</i> description element.
259 *
260 * @param modifier the modifier which should be removed
261 * @see #getModifiers()
262 * @see #addModifier(Modifier)
263 */
264 public void removeModifier(Modifier modifier){
265 this.modifiers.remove(modifier);
266 }
267
268
269 /**
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.
275 * <P>
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").
279 */
280 public Map<Language,LanguageString> getModifyingText(){
281 return this.modifyingText;
282 }
283
284 /**
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.
288 *
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
295 */
296 public LanguageString addModifyingText(LanguageString description){
297 return this.putModifyingText(description);
298 }
299
300 /**
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.
304 *
305 * @param description the language string describing the validity
306 * in a particular language
307 * @see #getModifyingText()
308 * @see #putModifyingText(Language, String)
309 */
310 public LanguageString putModifyingText(LanguageString description){
311 return this.modifyingText.put(description.getLanguage(),description);
312 }
313 /**
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.
317 *
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
325 */
326 public LanguageString addModifyingText(String text, Language language){
327 return this.putModifyingText(language, text);
328 }
329
330 /**
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.
334 *
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
338 *
339 * @see #getModifyingText()
340 * @see #putModifyingText(LanguageString)
341 *
342 */
343 public LanguageString putModifyingText(Language language, String text){
344 return this.modifyingText.put(language, LanguageString.NewInstance(text, language));
345 }
346 /**
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}.
350 *
351 * @param language the language in which the language string to be removed
352 * has been formulated
353 * @see #getModifyingText()
354 */
355 public LanguageString removeModifyingText(Language language){
356 return this.modifyingText.remove(language);
357 }
358
359 /* (non-Javadoc)
360 * @see eu.etaxonomy.cdm.model.common.ISourceable#getSources()
361 */
362 public Set<DescriptionElementSource> getSources() {
363 return this.sources;
364 }
365
366 /* (non-Javadoc)
367 * @see eu.etaxonomy.cdm.model.common.ISourceable#addSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
368 */
369 public void addSource(DescriptionElementSource source) {
370 if (source != null){
371 DescriptionElementBase oldSourcedObj = source.getSourcedObj();
372 if (oldSourcedObj != null && oldSourcedObj != this){
373 oldSourcedObj.getSources().remove(source);
374 }
375 this.sources.add(source);
376 source.setSourcedObj(this);
377 }
378 }
379
380 /* (non-Javadoc)
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)
382 */
383 public DescriptionElementSource addSource(OriginalSourceType type, String id, String idNamespace, Reference citation, String microCitation) {
384 if (id == null && idNamespace == null && citation == null && microCitation == null){
385 return null;
386 }
387 DescriptionElementSource source = DescriptionElementSource.NewInstance(type, id, idNamespace, citation, microCitation);
388 addSource(source);
389 return source;
390 }
391
392
393 /* (non-Javadoc)
394 * @see eu.etaxonomy.cdm.model.common.ISourceable#addImportSource(java.lang.String, java.lang.String, eu.etaxonomy.cdm.model.reference.Reference, java.lang.String)
395 */
396 @Override
397 public DescriptionElementSource addImportSource(String id, String idNamespace, Reference<?> citation, String microCitation) {
398 if (id == null && idNamespace == null && citation == null && microCitation == null){
399 return null;
400 }
401 DescriptionElementSource source = DescriptionElementSource.NewInstance(OriginalSourceType.Import, id, idNamespace, citation, microCitation);
402 addSource(source);
403 return source;
404 }
405
406 /**
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
415 */
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);
419 }
420
421 /* (non-Javadoc)
422 * @see eu.etaxonomy.cdm.model.common.ISourceable#removeSource(eu.etaxonomy.cdm.model.common.IOriginalSource)
423 */
424 public void removeSource(DescriptionElementSource source) {
425 this.sources.remove(source);
426 }
427
428 // ******************* METHODS *************************************************************/
429
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>());
436 }
437 result.get(voc).add(modifier);
438 }
439 return result;
440 }
441
442 public List<Modifier> getModifiers(TermVocabulary voc){
443 List<Modifier> result = makeModifierMap().get(voc);
444 if (result == null){
445 result = new ArrayList<Modifier>();
446 }
447 return result;
448 }
449
450
451
452 //************************** CLONE **********************************************************/
453
454 /**
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()
458 */
459 @Override
460 public Object clone() throws CloneNotSupportedException{
461 DescriptionElementBase result = (DescriptionElementBase)super.clone();
462
463 //inDescription
464 result.inDescription = null;
465
466 //Sources
467 result.sources = new HashSet<DescriptionElementSource>();
468 for (DescriptionElementSource source : getSources()){
469 DescriptionElementSource newSource = (DescriptionElementSource)source.clone();
470 result.addSource(newSource);
471 }
472
473 //media
474 result.media = new ArrayList<Media>();
475 for (Media media : getMedia()){
476 result.media.add(media);
477 }
478
479 //modifying text
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);
485 }
486
487 //modifiers
488 result.modifiers = new HashSet<Modifier>();
489 for (Modifier modifier : getModifiers()){
490 result.modifiers.add(modifier);
491 }
492
493 //no changes to: feature
494 return result;
495 }
496
497 /**
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()
501 */
502 public DescriptionElementBase clone(DescriptionBase description) throws CloneNotSupportedException{
503 DescriptionElementBase result = (DescriptionElementBase)clone();
504 description.addElement(result);
505 return result;
506 }
507
508
509 }