Project

General

Profile

Download (19.9 KB) Statistics
| Branch: | Tag: | Revision:
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.persistence.OrderColumn;
30
import javax.persistence.Transient;
31
import javax.xml.bind.annotation.XmlAccessType;
32
import javax.xml.bind.annotation.XmlAccessorType;
33
import javax.xml.bind.annotation.XmlElement;
34
import javax.xml.bind.annotation.XmlElementWrapper;
35
import javax.xml.bind.annotation.XmlIDREF;
36
import javax.xml.bind.annotation.XmlSchemaType;
37
import javax.xml.bind.annotation.XmlTransient;
38
import javax.xml.bind.annotation.XmlType;
39
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
40

    
41
import org.apache.log4j.Logger;
42
import org.hibernate.annotations.Cascade;
43
import org.hibernate.annotations.CascadeType;
44
import org.hibernate.annotations.ListIndexBase;
45
import org.hibernate.envers.Audited;
46
import org.hibernate.search.annotations.IndexedEmbedded;
47

    
48
import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
49
import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
50
import eu.etaxonomy.cdm.model.common.DefinedTerm;
51
import eu.etaxonomy.cdm.model.common.IMultiLanguageTextHolder;
52
import eu.etaxonomy.cdm.model.common.IOriginalSource;
53
import eu.etaxonomy.cdm.model.common.ISourceable;
54
import eu.etaxonomy.cdm.model.common.Language;
55
import eu.etaxonomy.cdm.model.common.LanguageString;
56
import eu.etaxonomy.cdm.model.common.MultilanguageText;
57
import eu.etaxonomy.cdm.model.common.OriginalSourceType;
58
import eu.etaxonomy.cdm.model.common.TermVocabulary;
59
import eu.etaxonomy.cdm.model.common.TimePeriod;
60
import eu.etaxonomy.cdm.model.media.Media;
61
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
62
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
63
import eu.etaxonomy.cdm.model.reference.Reference;
64
import eu.etaxonomy.cdm.model.taxon.Taxon;
65
import eu.etaxonomy.cdm.strategy.merge.Merge;
66
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
67

    
68
/**
69
 * The upmost (abstract) class for a piece of information) about
70
 * a {@link SpecimenOrObservationBase specimen}, a {@link Taxon taxon} or even a {@link TaxonNameBase taxon name}.
71
 * A concrete description element assigns descriptive data to one {@link Feature feature}.<BR>
72
 * Experts use the word feature for the property itself but not for the actual
73
 * description element. Therefore naming this class FeatureBase would have
74
 * leaded to confusion.
75
 * <P>
76
 * This class corresponds to: <ul>
77
 * <li> DescriptionsBaseType according to the the SDD schema
78
 * <li> InfoItem according to the TDWG ontology
79
 * <li> MeasurementOrFactAtomised according to the ABCD schema
80
 * </ul>
81
 *
82
 * @author m.doering
83
 * @created 08-Nov-2007 13:06:24
84
 */
85
@XmlAccessorType(XmlAccessType.FIELD)
86
@XmlType(name = "DescriptionElementBase", propOrder = {
87
    "feature",
88
    "inDescription",
89
    "timeperiod",
90
    "modifiers",
91
    "modifyingText",
92
    "media",
93
    "sources"
94
})
95
@Entity
96
@Audited
97
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
98
public abstract class DescriptionElementBase extends AnnotatableEntity implements ISourceable<DescriptionElementSource>, IModifiable, IMultiLanguageTextHolder{
99
    private static final long serialVersionUID = 5000910777835755905L;
100
    @SuppressWarnings("unused")
101
    private static final Logger logger = Logger.getLogger(DescriptionElementBase.class);
102

    
103
    //type, category of information. In structured descriptions characters
104
    @XmlElement(name = "Feature")
105
    @XmlIDREF
106
    @XmlSchemaType(name = "IDREF")
107
    @ManyToOne(fetch = FetchType.LAZY)
108
    //@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
109
    @Cascade(CascadeType.MERGE)
110
    @IndexedEmbedded // no depth for terms
111
    private Feature feature;
112

    
113
    @XmlElementWrapper(name = "Modifiers")
114
    @XmlElement(name = "Modifier")
115
    @XmlIDREF
116
    @XmlSchemaType(name = "IDREF")
117
    @ManyToMany(fetch = FetchType.LAZY)
118
    @JoinTable(name="DescriptionElementBase_Modifier")
119
    @IndexedEmbedded(depth=1)
120
    private Set<DefinedTerm> modifiers = new HashSet<DefinedTerm>();
121

    
122
    @XmlElement(name = "ModifyingText")
123
    @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
124
    @OneToMany(fetch = FetchType.LAZY)
125
    @JoinTable(name = "DescriptionElementBase_ModifyingText")
126
    @MapKeyJoinColumn(name="modifyingtext_mapkey_id")
127
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
128
    @IndexedEmbedded
129
    private Map<Language,LanguageString> modifyingText = new HashMap<Language,LanguageString>();
130

    
131
    @XmlElementWrapper(name = "Media")
132
    @XmlElement(name = "Medium")
133
    @XmlIDREF
134
    @XmlSchemaType(name = "IDREF")
135
    @ManyToMany(fetch = FetchType.LAZY)
136
    @OrderColumn(name="sortIndex")
137
    @ListIndexBase(value=0)  //not really needed as this is the default
138
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
139
    private List<Media> media = new ArrayList<Media>();
140

    
141
    @XmlElement(name = "InDescription")
142
    @XmlIDREF
143
    @XmlSchemaType(name = "IDREF")
144
    @ManyToOne(fetch = FetchType.LAZY)
145
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
146
    @IndexedEmbedded(includeEmbeddedObjectId=true)
147
    private DescriptionBase<?> inDescription;
148

    
149
	@XmlElement(name = "TimePeriod")
150
    private TimePeriod timeperiod = TimePeriod.NewInstance();
151

    
152
    @XmlElementWrapper(name = "Sources")
153
    @XmlElement(name = "DescriptionElementSource")
154
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
155
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
156
    @Merge(MergeMode.ADD_CLONE)
157
    private Set<DescriptionElementSource> sources = new HashSet<DescriptionElementSource>();
158

    
159

    
160

    
161
    // ************* CONSTRUCTORS *************/
162
    /**
163
     * Class constructor: creates a new empty description element instance.
164
     *
165
     * @see #DescriptionElementBase(Feature)
166
     */
167
    protected DescriptionElementBase(){
168
    }
169

    
170
    /**
171
     * Class constructor: creates a new description element instance with the
172
     * given {@link Feature feature} that is described or measured.
173
     *
174
     * @param	feature	the feature described or measured
175
     * @see 			#DescriptionElementBase()
176
     */
177
    protected DescriptionElementBase(Feature feature){
178
        if (feature == null){
179
            feature = Feature.UNKNOWN();
180
        }
181
        this.feature = feature;
182
    }
183

    
184
// ******************** GETTER / SETTER ***********************************/
185

    
186
    /**
187
     * Returns the list of {@link Media media} (that is pictures, movies,
188
     * recorded sounds ...) <i>this</i> description element is based on.
189
     */
190
    public List<Media> getMedia(){
191
        return this.media;
192
    }
193

    
194
    /**
195
     * Adds a {@link Media media} to the list of {@link #getMedia() media}
196
     * <i>this</i> description element is based on.
197
     *
198
     * @param media	the media to be added to <i>this</i> description element
199
     * @see    	   	#getMedia()
200
     */
201
    public void addMedia(Media media){
202
        this.media.add(media);
203
    }
204
    /**
205
     * Removes one element from the list of {@link #getMedia() media}
206
     * <i>this</i> description element is based on.
207
     *
208
     * @param  media	the media which should be removed
209
     * @see     		#getMedia()
210
     * @see     		#addMedia(Media)
211
     */
212
    public void removeMedia(Media media){
213
        this.media.remove(media);
214
    }
215

    
216
    /**
217
     * Returns the {@link DescriptionBase description} that <i>this</i> DescriptionElement is
218
     * part of.
219
     * @return
220
     */
221
    public DescriptionBase getInDescription() {
222
        return this.inDescription;
223
    }
224

    
225
    /**
226
     * @see	#setInDescription()
227
     */
228
    protected void setInDescription(DescriptionBase inDescription) {
229
        this.inDescription = inDescription;
230
    }
231

    
232
    /**
233
     * Returns the {@link Feature feature} <i>this</i> description element is for.
234
     * A feature is a property that can be described or measured but not the
235
     * description or the measurement itself.
236
     */
237
    public Feature getFeature(){
238
        return this.feature;
239
    }
240

    
241
    /**
242
     * @see	#getFeature()
243
     */
244
    public void setFeature(Feature feature){
245
        this.feature = feature;
246
    }
247

    
248
    /**
249
	 * The point in time, the time period or the season for which this description element
250
	 * is valid. A season may be expressed by not filling the year part(s) of the time period.
251
	 */
252
	public TimePeriod getTimeperiod() {
253
		return timeperiod;
254
	}
255

    
256
	/**
257
	 * @see #getTimeperiod()
258
	 */
259
	public void setTimeperiod(TimePeriod timeperiod) {
260
		if (timeperiod == null){
261
			timeperiod = TimePeriod.NewInstance();
262
		}
263
		this.timeperiod = timeperiod;
264
	}
265

    
266
	/**
267
     * Returns the set of {@link Modifier modifiers} used to qualify the validity of
268
     * <i>this</i> description element. This is only metainformation.
269
     */
270
    @Override
271
    public Set<DefinedTerm> getModifiers(){
272
        return this.modifiers;
273
    }
274

    
275
    /**
276
     * Adds a {@link Modifier modifier} to the set of {@link #getModifiers() modifiers}
277
     * used to qualify the validity of <i>this</i> description element.
278
     *
279
     * @param modifier	the modifier to be added to <i>this</i> description element
280
     * @see    	   		#getModifiers()
281
     */
282
    @Override
283
    public void addModifier(DefinedTerm modifier){
284
        this.modifiers.add(modifier);
285
    }
286
    /**
287
     * Removes one element from the set of {@link #getModifiers() modifiers}
288
     * used to qualify the validity of <i>this</i> description element.
289
     *
290
     * @param  modifier	the modifier which should be removed
291
     * @see     		#getModifiers()
292
     * @see     		#addModifier(Modifier)
293
     */
294
    @Override
295
    public void removeModifier(DefinedTerm modifier){
296
        this.modifiers.remove(modifier);
297
    }
298

    
299

    
300
    /**
301
     * Returns the {@link MultilanguageText multilanguage text} used to qualify the validity
302
     * of <i>this</i> description element.  The different {@link LanguageString language strings}
303
     * contained in the multilanguage text should all have the same meaning.<BR>
304
     * A multilanguage text does not belong to a controlled {@link TermVocabulary term vocabulary}
305
     * as a {@link Modifier modifier} does.
306
     * <P>
307
     * NOTE: the actual content of <i>this</i> description element is NOT
308
     * stored in the modifying text. This is only metainformation
309
     * (like "Some experts express doubt about this assertion").
310
     */
311
    public Map<Language,LanguageString> getModifyingText(){
312
        return this.modifyingText;
313
    }
314

    
315
    /**
316
     * Adds a translated {@link LanguageString text in a particular language}
317
     * to the {@link MultilanguageText multilanguage text} used to qualify the validity
318
     * of <i>this</i> description element.
319
     *
320
     * @param description	the language string describing the validity
321
     * 						in a particular language
322
     * @see    	   			#getModifyingText()
323
     * @see    	   			#putModifyingText(Language, String)
324
     * @deprecated 			should follow the put semantic of maps, this method will be removed in v4.0
325
     * 						Use the {@link #putModifyingText(LanguageString) putModifyingText} method
326
     */
327
    @Deprecated
328
    public LanguageString addModifyingText(LanguageString description){
329
        return this.putModifyingText(description);
330
    }
331

    
332
    /**
333
     * Adds a translated {@link LanguageString text in a particular language}
334
     * to the {@link MultilanguageText multilanguage text} used to qualify the validity
335
     * of <i>this</i> description element.
336
     *
337
     * @param description	the language string describing the validity
338
     * 						in a particular language
339
     * @see    	   			#getModifyingText()
340
     * @see    	   			#putModifyingText(Language, String)
341
     */
342
    public LanguageString putModifyingText(LanguageString description){
343
        return this.modifyingText.put(description.getLanguage(),description);
344
    }
345
    /**
346
     * Creates a {@link LanguageString language string} based on the given text string
347
     * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
348
     * used to qualify the validity of <i>this</i> description element.
349
     *
350
     * @param text		the string describing the validity
351
     * 					in a particular language
352
     * @param language	the language in which the text string is formulated
353
     * @see    	   		#getModifyingText()
354
     * @see    	   		#putModifyingText(LanguageString)
355
     * @deprecated 		should follow the put semantic of maps, this method will be removed in v4.0
356
     * 					Use the {@link #putModifyingText(Language, String) putModifyingText} method
357
     */
358
    @Deprecated
359
    public LanguageString addModifyingText(String text, Language language){
360
        return this.putModifyingText(language, text);
361
    }
362

    
363
    /**
364
     * Creates a {@link LanguageString language string} based on the given text string
365
     * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
366
     * used to qualify the validity of <i>this</i> description element.
367
     *
368
     * @param language	the language in which the text string is formulated
369
     * @param text		the string describing the validity
370
     * 					in a particular language
371
     *
372
     * @see    	   		#getModifyingText()
373
     * @see    	   		#putModifyingText(LanguageString)
374
     *
375
     */
376
    public LanguageString putModifyingText(Language language, String text){
377
        return this.modifyingText.put(language, LanguageString.NewInstance(text, language));
378
    }
379
    /**
380
     * Removes from the {@link MultilanguageText multilanguage text} used to qualify the validity
381
     * of <i>this</i> description element the one {@link LanguageString language string}
382
     * with the given {@link Language language}.
383
     *
384
     * @param  language	the language in which the language string to be removed
385
     * 					has been formulated
386
     * @see     		#getModifyingText()
387
     */
388
    public LanguageString removeModifyingText(Language language){
389
        return this.modifyingText.remove(language);
390
    }
391

    
392
    /* (non-Javadoc)
393
     * @see eu.etaxonomy.cdm.model.common.ISourceable#getSources()
394
     */
395
    @Override
396
    public Set<DescriptionElementSource> getSources() {
397
        return this.sources;
398
    }
399

    
400
    @Override
401
    public void addSource(DescriptionElementSource source) {
402
        if (source != null){
403
            this.sources.add(source);
404
        }
405
    }
406

    
407
    @Override
408
    public DescriptionElementSource addSource(OriginalSourceType type, String id, String idNamespace, Reference citation, String microCitation) {
409
        if (id == null && idNamespace == null && citation == null && microCitation == null){
410
            return null;
411
        }
412
        DescriptionElementSource source = DescriptionElementSource.NewInstance(type, id, idNamespace, citation, microCitation);
413
        addSource(source);
414
        return source;
415
    }
416
    @Override
417
    public void addSources(Set<DescriptionElementSource> sources){
418
    	for (DescriptionElementSource source:sources){
419
    		addSource(source);
420
    	}
421
    }
422

    
423
    @Override
424
    public DescriptionElementSource addImportSource(String id, String idNamespace, Reference<?> citation, String microCitation) {
425
        if (id == null && idNamespace == null && citation == null && microCitation == null){
426
            return null;
427
        }
428
        DescriptionElementSource source = DescriptionElementSource.NewInstance(OriginalSourceType.Import, id, idNamespace, citation, microCitation);
429
        addSource(source);
430
        return source;
431
    }
432

    
433
    /**
434
     * Adds a {@link IOriginalSource source} to this description element.
435
     * @param type the type of the source
436
     * @param idInSource the id used in the source
437
     * @param idNamespace the namespace for the id in the source
438
     * @param citation the source as a {@link Reference reference}
439
     * @param microReference the details (e.g. page number) in the reference
440
     * @param nameUsedInSource the taxon name used in the source
441
     * @param originalNameString the name as text used in the source
442
     */
443
    public void addSource(OriginalSourceType type, String idInSource, String idNamespace, Reference citation, String microReference, TaxonNameBase nameUsedInSource, String originalNameString){
444
        DescriptionElementSource newSource = DescriptionElementSource.NewInstance(type, idInSource, idNamespace, citation, microReference, nameUsedInSource, originalNameString);
445
        addSource(newSource);
446
    }
447

    
448
    @Override
449
    public void removeSource(DescriptionElementSource source) {
450
        this.sources.remove(source);
451
    }
452

    
453
// ******************* METHODS *************************************************************/
454

    
455
    protected Map<TermVocabulary, List<DefinedTerm>> makeModifierMap(){
456
        Map<TermVocabulary, List<DefinedTerm>> result = new HashMap<TermVocabulary, List<DefinedTerm>>();
457
        for (DefinedTerm modifier : getModifiers()){
458
            TermVocabulary<DefinedTerm> voc = modifier.getVocabulary();
459
            if (result.get(voc) == null){
460
                result.put(voc, new ArrayList<DefinedTerm>());
461
            }
462
            result.get(voc).add(modifier);
463
        }
464
        return result;
465
    }
466

    
467
    public List<DefinedTerm> getModifiers(TermVocabulary voc){
468
        List<DefinedTerm> result = makeModifierMap().get(voc);
469
        if (result == null){
470
            result = new ArrayList<DefinedTerm>();
471
        }
472
        return result;
473
    }
474

    
475

    
476

    
477
//************************** CLONE **********************************************************/
478

    
479
    /**
480
     * Clones the description element. The element is <b>not</b> added to the same
481
     * description as the orginal element (inDescription is set to <code>null</null>).
482
     * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
483
     */
484
    @Override
485
    public Object clone() throws CloneNotSupportedException{
486
        DescriptionElementBase result = (DescriptionElementBase)super.clone();
487

    
488
        //inDescription
489
        result.inDescription = null;
490

    
491
        //Sources
492
        result.sources = new HashSet<DescriptionElementSource>();
493
        for (DescriptionElementSource source : getSources()){
494
            DescriptionElementSource newSource = (DescriptionElementSource)source.clone();
495
            result.addSource(newSource);
496
        }
497

    
498
        //media
499
        result.media = new ArrayList<Media>();
500
        for (Media media : getMedia()){
501
            result.media.add(media);
502
        }
503

    
504
        //modifying text
505
        result.modifyingText = new HashMap<Language, LanguageString>();
506
        for (Language language : getModifyingText().keySet()){
507
            //TODO clone needed? See also IndividualsAssociation
508
            LanguageString newLanguageString = (LanguageString)getModifyingText().get(language).clone();
509
            result.modifyingText.put(language, newLanguageString);
510
        }
511

    
512
        //modifiers
513
        result.modifiers = new HashSet<DefinedTerm>();
514
        for (DefinedTerm modifier : getModifiers()){
515
            result.modifiers.add(modifier);
516
        }
517

    
518
        //no changes to: feature
519
        return result;
520
    }
521

    
522
    /**
523
     * Clones the description element.<BR>
524
     * The new element is added to the <code>description</code>.<BR>
525
     * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
526
     */
527
    public DescriptionElementBase clone(DescriptionBase description) throws CloneNotSupportedException{
528
        DescriptionElementBase result = (DescriptionElementBase)clone();
529
        description.addElement(result);
530
        return result;
531
    }
532

    
533
    /**
534
     * Is this description item of a class type which is considere to
535
     * represent character data? These classes are {@link QuantitativeData}
536
     * and {@link CategoricalData}.
537
     * To be overriden by these classes.
538
     */
539
    @Transient
540
    @XmlTransient
541
    public boolean isCharacterData() {
542
        return false;
543
    }
544

    
545

    
546
}
(4-4/36)