Project

General

Profile

Download (20.1 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.TaxonName;
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 TaxonName 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
 * @since 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.MERGE)   remove cascade #5755
109
    @IndexedEmbedded // no depth for terms
110
    private Feature feature;
111

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

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

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

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

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

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

    
158

    
159

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

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

    
183
// ******************** GETTER / SETTER ***********************************/
184

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

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

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

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

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

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

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

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

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

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

    
298

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

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

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

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

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

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

    
406
    @Override
407
    public DescriptionElementSource addSource(OriginalSourceType type, String id, String idNamespace, Reference citation, String microCitation) {
408
        if (id == null && idNamespace == null && citation == null && microCitation == null){
409
            return null;
410
        }
411
        DescriptionElementSource source = DescriptionElementSource.NewInstance(type, id, idNamespace, citation, microCitation);
412
        addSource(source);
413
        return source;
414
    }
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
    @Override
434
    public DescriptionElementSource addPrimaryTaxonomicSource(Reference citation, String microCitation) {
435
        if (citation == null && microCitation == null){
436
            return null;
437
        }
438
        DescriptionElementSource source = DescriptionElementSource.NewPrimarySourceInstance(citation, microCitation);
439
        addSource(source);
440
        return source;
441
    }
442

    
443
    @Override
444
    public DescriptionElementSource addPrimaryTaxonomicSource(Reference citation) {
445
        return addPrimaryTaxonomicSource(citation, null);
446
    }
447

    
448
    /**
449
     * Adds a {@link IOriginalSource source} to this description element.
450
     * @param type the type of the source
451
     * @param idInSource the id used in the source
452
     * @param idNamespace the namespace for the id in the source
453
     * @param citation the source as a {@link Reference reference}
454
     * @param microReference the details (e.g. page number) in the reference
455
     * @param nameUsedInSource the taxon name used in the source
456
     * @param originalNameString the name as text used in the source
457
     */
458
    public void addSource(OriginalSourceType type, String idInSource, String idNamespace, Reference citation, String microReference, TaxonName nameUsedInSource, String originalNameString){
459
        DescriptionElementSource newSource = DescriptionElementSource.NewInstance(type, idInSource, idNamespace, citation, microReference, nameUsedInSource, originalNameString);
460
        addSource(newSource);
461
    }
462

    
463
    @Override
464
    public void removeSource(DescriptionElementSource source) {
465
        this.sources.remove(source);
466
    }
467

    
468
// ******************* METHODS *************************************************************/
469

    
470
    protected Map<TermVocabulary, List<DefinedTerm>> makeModifierMap(){
471
        Map<TermVocabulary, List<DefinedTerm>> result = new HashMap<TermVocabulary, List<DefinedTerm>>();
472
        for (DefinedTerm modifier : getModifiers()){
473
            TermVocabulary<DefinedTerm> voc = modifier.getVocabulary();
474
            if (result.get(voc) == null){
475
                result.put(voc, new ArrayList<DefinedTerm>());
476
            }
477
            result.get(voc).add(modifier);
478
        }
479
        return result;
480
    }
481

    
482
    public List<DefinedTerm> getModifiers(TermVocabulary voc){
483
        List<DefinedTerm> result = makeModifierMap().get(voc);
484
        if (result == null){
485
            result = new ArrayList<DefinedTerm>();
486
        }
487
        return result;
488
    }
489

    
490

    
491

    
492
//************************** CLONE **********************************************************/
493

    
494
    /**
495
     * Clones the description element. The element is <b>not</b> added to the same
496
     * description as the orginal element (inDescription is set to <code>null</null>).
497
     * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
498
     */
499
    @Override
500
    public Object clone() throws CloneNotSupportedException{
501
        DescriptionElementBase result = (DescriptionElementBase)super.clone();
502

    
503
        //inDescription
504
        if (result.inDescription != null){
505
            result.inDescription.removeElement(this);
506
            result.inDescription = null;
507
        }
508

    
509
        //Sources
510
        result.sources = new HashSet<DescriptionElementSource>();
511
        for (DescriptionElementSource source : getSources()){
512
            DescriptionElementSource newSource = (DescriptionElementSource)source.clone();
513
            result.addSource(newSource);
514
        }
515

    
516
        //media
517
        result.media = new ArrayList<Media>();
518
        for (Media media : getMedia()){
519
            result.media.add(media);
520
        }
521

    
522
        //modifying text
523
        result.modifyingText = cloneLanguageString(getModifyingText());
524

    
525
        //modifiers
526
        result.modifiers = new HashSet<DefinedTerm>();
527
        for (DefinedTerm modifier : getModifiers()){
528
            result.modifiers.add(modifier);
529
        }
530

    
531
        //no changes to: feature
532
        return result;
533
    }
534

    
535
    /**
536
     * Clones the description element.<BR>
537
     * The new element is added to the <code>description</code>.<BR>
538
     * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
539
     */
540
    public DescriptionElementBase clone(DescriptionBase description) throws CloneNotSupportedException{
541
        DescriptionElementBase result = (DescriptionElementBase)clone();
542
        description.addElement(result);
543
        return result;
544
    }
545

    
546
    /**
547
     * Is this description item of a class type which is considere to
548
     * represent character data? These classes are {@link QuantitativeData}
549
     * and {@link CategoricalData}.
550
     * To be overriden by these classes.
551
     */
552
    @Transient
553
    @XmlTransient
554
    public boolean isCharacterData() {
555
        return false;
556
    }
557

    
558

    
559
}
(5-5/37)