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;import org.apache.logging.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.IMultiLanguageTextHolder;
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.TimePeriod;
55
import eu.etaxonomy.cdm.model.media.Media;
56
import eu.etaxonomy.cdm.model.name.TaxonName;
57
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
58
import eu.etaxonomy.cdm.model.reference.ICdmTarget;
59
import eu.etaxonomy.cdm.model.reference.IOriginalSource;
60
import eu.etaxonomy.cdm.model.reference.ISourceable;
61
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
62
import eu.etaxonomy.cdm.model.reference.Reference;
63
import eu.etaxonomy.cdm.model.taxon.Taxon;
64
import eu.etaxonomy.cdm.model.term.DefinedTerm;
65
import eu.etaxonomy.cdm.model.term.TermVocabulary;
66
import eu.etaxonomy.cdm.strategy.merge.Merge;
67
import eu.etaxonomy.cdm.strategy.merge.MergeMode;
68

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

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

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

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

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

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

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

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

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

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

    
166

    
167

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

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

    
191
// ******************** GETTER / SETTER ***********************************/
192

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

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

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

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

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

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

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

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

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

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

    
306

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

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

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

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

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

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

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

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

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

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

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

    
456

    
457

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

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

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

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

    
499

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

    
507
// ******************* METHODS *************************************************************/
508

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

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

    
529

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

    
542
//************************** CLONE **********************************************************/
543

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

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

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

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

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

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

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

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

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

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