Project

General

Profile

Download (21.4 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.logging.log4j.LogManager;
42
import org.apache.logging.log4j.Logger;
43
import org.hibernate.annotations.Cascade;
44
import org.hibernate.annotations.CascadeType;
45
import org.hibernate.annotations.ListIndexBase;
46
import org.hibernate.envers.Audited;
47
import org.hibernate.search.annotations.IndexedEmbedded;
48

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

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

    
105
    private static final long serialVersionUID = 5000910777835755905L;
106
    @SuppressWarnings("unused")
107
    private static final Logger logger = LogManager.getLogger(DescriptionElementBase.class);
108

    
109
    //type, category of information. In structured descriptions characters
110
    @XmlElement(name = "Feature")
111
    @XmlIDREF
112
    @XmlSchemaType(name = "IDREF")
113
    @ManyToOne(fetch = FetchType.LAZY)
114
//    @Cascade(CascadeType.MERGE)   remove cascade #5755
115
    @IndexedEmbedded // no depth for terms
116
    private Feature feature;
117

    
118
    @XmlElementWrapper(name = "Modifiers")
119
    @XmlElement(name = "Modifier")
120
    @XmlIDREF
121
    @XmlSchemaType(name = "IDREF")
122
    @ManyToMany(fetch = FetchType.LAZY)
123
    @JoinTable(name="DescriptionElementBase_Modifier")
124
    @IndexedEmbedded(depth=1)
125
    private Set<DefinedTerm> modifiers = new HashSet<>();
126

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

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

    
146
    @XmlElement(name = "InDescription")
147
    @XmlIDREF
148
    @XmlSchemaType(name = "IDREF")
149
    @ManyToOne(fetch = FetchType.LAZY)
150
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
151
    @IndexedEmbedded(includeEmbeddedObjectId=true)
152
    private DescriptionBase<?> inDescription;
153

    
154
	@XmlElement(name = "TimePeriod")
155
    private TimePeriod timeperiod = TimePeriod.NewInstance();
156

    
157
    @XmlElementWrapper(name = "Sources")
158
    @XmlElement(name = "DescriptionElementSource")
159
    @OneToMany(fetch = FetchType.LAZY, mappedBy = "sourcedElement", orphanRemoval=true)
160
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
161
    @Merge(MergeMode.ADD_CLONE)
162
    private Set<DescriptionElementSource> sources = new HashSet<>();
163

    
164
    //#8004 optional sortIndex
165
    private Integer sortIndex = null;
166

    
167

    
168

    
169
    // ************* CONSTRUCTORS *************/
170
    /**
171
     * Class constructor: creates a new empty description element instance.
172
     *
173
     * @see #DescriptionElementBase(Feature)
174
     */
175
    protected DescriptionElementBase(){
176
    }
177

    
178
    /**
179
     * Class constructor: creates a new description element instance with the
180
     * given {@link Feature feature} that is described or measured.
181
     *
182
     * @param	feature	the feature described or measured
183
     * @see 			#DescriptionElementBase()
184
     */
185
    protected DescriptionElementBase(Feature feature){
186
        if (feature == null){
187
            feature = Feature.UNKNOWN();
188
        }
189
        this.feature = feature;
190
    }
191

    
192
// ******************** GETTER / SETTER ***********************************/
193

    
194
    /**
195
     * Returns the list of {@link Media media} (that is pictures, movies,
196
     * recorded sounds ...) <i>this</i> description element is based on.
197
     */
198
    public List<Media> getMedia(){
199
        return this.media;
200
    }
201

    
202
    /**
203
     * Adds a {@link Media media} to the list of {@link #getMedia() media}
204
     * <i>this</i> description element is based on.
205
     *
206
     * @param media	the media to be added to <i>this</i> description element
207
     * @see    	   	#getMedia()
208
     */
209
    public void addMedia(Media media){
210
        this.media.add(media);
211
    }
212
    /**
213
     * Removes one element from the list of {@link #getMedia() media}
214
     * <i>this</i> description element is based on.
215
     *
216
     * @param  media	the media which should be removed
217
     * @see     		#getMedia()
218
     * @see     		#addMedia(Media)
219
     */
220
    public void removeMedia(Media media){
221
        this.media.remove(media);
222
    }
223

    
224
    /**
225
     * Returns the {@link DescriptionBase description} that <i>this</i> DescriptionElement is
226
     * part of.
227
     * @return
228
     */
229
    public DescriptionBase getInDescription() {
230
        return this.inDescription;
231
    }
232

    
233
    /**
234
     * @see	#setInDescription()
235
     */
236
    protected void setInDescription(DescriptionBase inDescription) {
237
        this.inDescription = inDescription;
238
    }
239

    
240
    /**
241
     * Returns the {@link Feature feature} <i>this</i> description element is for.
242
     * A feature is a property that can be described or measured but not the
243
     * description or the measurement itself.
244
     */
245
    public Feature getFeature(){
246
        return this.feature;
247
    }
248

    
249
    /**
250
     * @see	#getFeature()
251
     */
252
    public void setFeature(Feature feature){
253
        this.feature = feature;
254
    }
255

    
256
    /**
257
	 * The point in time, the time period or the season for which this description element
258
	 * is valid. A season may be expressed by not filling the year part(s) of the time period.
259
	 */
260
	public TimePeriod getTimeperiod() {
261
		return timeperiod;
262
	}
263

    
264
	/**
265
	 * @see #getTimeperiod()
266
	 */
267
	public void setTimeperiod(TimePeriod timeperiod) {
268
		if (timeperiod == null){
269
			timeperiod = TimePeriod.NewInstance();
270
		}
271
		this.timeperiod = timeperiod;
272
	}
273

    
274
	/**
275
     * Returns the set of {@link Modifier modifiers} used to qualify the validity of
276
     * <i>this</i> description element. This is only metainformation.
277
     */
278
    @Override
279
    public Set<DefinedTerm> getModifiers(){
280
        return this.modifiers;
281
    }
282

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

    
307

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

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

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

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

    
400
    @Override
401
    public Set<DescriptionElementSource> getSources() {
402
        return this.sources;
403
    }
404

    
405
    @Override
406
    public void addSource(DescriptionElementSource source) {
407
        if (source != null){
408
            this.sources.add(source);
409
            source.setSourcedElement(this);
410
        }
411
    }
412

    
413
    @Override
414
    public DescriptionElementSource addSource(OriginalSourceType type, String id, String idNamespace,
415
            Reference reference, String microReference) {
416
        if (id == null && idNamespace == null && reference == null && microReference == null){
417
            return null;
418
        }
419
        DescriptionElementSource source = DescriptionElementSource.NewInstance(type, id, idNamespace,
420
                reference, microReference);
421
        addSource(source);
422
        return source;
423
    }
424

    
425
    @Override
426
    public void addSources(Set<DescriptionElementSource> sources){
427
    	for (DescriptionElementSource source:sources){
428
    		addSource(source);
429
    	}
430
    }
431

    
432
    @Override
433
    public DescriptionElementSource addImportSource(String id, String idNamespace, Reference reference, String microReference) {
434
        if (id == null && idNamespace == null && reference == null && microReference == null){
435
            return null;
436
        }
437
        DescriptionElementSource source = DescriptionElementSource.NewInstance(OriginalSourceType.Import, id, idNamespace, reference, microReference);
438
        addSource(source);
439
        return source;
440
    }
441

    
442
    @Override
443
    public DescriptionElementSource addPrimaryTaxonomicSource(Reference reference, String microReference) {
444
        if (reference == null && microReference == null){
445
            return null;
446
        }
447
        DescriptionElementSource source = DescriptionElementSource.NewPrimarySourceInstance(reference, microReference);
448
        addSource(source);
449
        return source;
450
    }
451

    
452
    @Override
453
    public DescriptionElementSource addPrimaryTaxonomicSource(Reference reference) {
454
        return addPrimaryTaxonomicSource(reference, null);
455
    }
456

    
457

    
458

    
459
    @Override
460
    public DescriptionElementSource addSource(OriginalSourceType type, Reference reference,
461
            String microReference, String originalInformation) {
462
        DescriptionElementSource newSource = DescriptionElementSource.NewInstance(type, null, null,
463
                reference, microReference, null, originalInformation);
464
        addSource(newSource);
465
        return newSource;
466
    }
467

    
468
    /**
469
     * Adds a {@link IOriginalSource source} to this description element.
470
     * @param type the type of the source
471
     * @param idInSource the id used in the source
472
     * @param idNamespace the namespace for the id in the source
473
     * @param reference the source as a {@link Reference reference}
474
     * @param microReference the details (e.g. page number) in the reference
475
     * @param nameUsedInSource the taxon name used in the source
476
     * @param originalInfo any information as original mentioned in the text
477
     */
478
    public DescriptionElementSource addSource(OriginalSourceType type, String idInSource, String idNamespace,
479
            Reference reference, String microReference, TaxonName nameUsedInSource, String originalInfo){
480
        DescriptionElementSource newSource = DescriptionElementSource.NewInstance(type, idInSource, idNamespace,
481
                reference, microReference, nameUsedInSource, originalInfo);
482
        addSource(newSource);
483
        return newSource;
484
    }
485

    
486
    @Override
487
    public DescriptionElementSource addAggregationSource(ICdmTarget target) {
488
        DescriptionElementSource source = DescriptionElementSource.NewInstance(
489
                OriginalSourceType.Aggregation, null, null, null, null,
490
                null, null, target);
491
        addSource(source);
492
        return source;
493
    }
494

    
495
    @Override
496
    public void removeSource(DescriptionElementSource source) {
497
        this.sources.remove(source);
498
    }
499

    
500

    
501
    public Integer getSortIndex() {
502
        return sortIndex;
503
    }
504
    public void setSortIndex(Integer sortIndex) {
505
        this.sortIndex = sortIndex;
506
    }
507

    
508
// ******************* METHODS *************************************************************/
509

    
510
    protected Map<TermVocabulary, List<DefinedTerm>> makeModifierMap(){
511
        Map<TermVocabulary, List<DefinedTerm>> result = new HashMap<>();
512
        for (DefinedTerm modifier : getModifiers()){
513
            TermVocabulary<DefinedTerm> voc = modifier.getVocabulary();
514
            if (result.get(voc) == null){
515
                result.put(voc, new ArrayList<>());
516
            }
517
            result.get(voc).add(modifier);
518
        }
519
        return result;
520
    }
521

    
522
    public List<DefinedTerm> getModifiers(TermVocabulary voc){
523
        List<DefinedTerm> result = makeModifierMap().get(voc);
524
        if (result == null){
525
            result = new ArrayList<>();
526
        }
527
        return result;
528
    }
529

    
530

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

    
543
//************************** CLONE **********************************************************/
544

    
545
    /**
546
     * Clones the description element. The element is <b>not</b> added to the same
547
     * description as the original element (inDescription is set to <code>null</null>).
548
     * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
549
     */
550
    @Override
551
    public DescriptionElementBase clone() {
552

    
553
        try {
554
            DescriptionElementBase result = (DescriptionElementBase)super.clone();
555

    
556
            //inDescription
557
            if (result.inDescription != null){
558
                result.inDescription.removeElement(result);
559
                result.inDescription = null;
560
            }
561

    
562
            //Sources
563
            result.sources = new HashSet<>();
564
            for (DescriptionElementSource source : getSources()){
565
                DescriptionElementSource newSource = source.clone();
566
                result.addSource(newSource);
567
            }
568

    
569
            //media
570
            result.media = new ArrayList<>();
571
            for (Media media : getMedia()){
572
                result.media.add(media);
573
            }
574

    
575
            //modifying text
576
            result.modifyingText = cloneLanguageString(getModifyingText());
577

    
578
            //modifiers
579
            result.modifiers = new HashSet<>();
580
            for (DefinedTerm modifier : getModifiers()){
581
                result.modifiers.add(modifier);
582
            }
583

    
584
            result.setTimeperiod(timeperiod == null? null:timeperiod.clone());
585

    
586
            //no changes to: feature
587
            return result;
588
        } catch (CloneNotSupportedException e) {
589
            throw new RuntimeException(e);
590
        }
591
    }
592

    
593
    /**
594
     * Clones the description element.<BR>
595
     * The new element is added to the <code>description</code>.<BR>
596
     * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
597
     */
598
    public DescriptionElementBase clone(DescriptionBase description) {
599
        DescriptionElementBase result = clone();
600
        description.addElement(result);
601
        return result;
602
    }
603
}
(5-5/38)