Project

General

Profile

Download (20.2 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.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.IOriginalSource;
59
import eu.etaxonomy.cdm.model.reference.ISourceable;
60
import eu.etaxonomy.cdm.model.reference.OriginalSourceType;
61
import eu.etaxonomy.cdm.model.reference.Reference;
62
import eu.etaxonomy.cdm.model.taxon.Taxon;
63
import eu.etaxonomy.cdm.model.term.DefinedTerm;
64
import eu.etaxonomy.cdm.model.term.TermVocabulary;
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
    "sortIndex"
95
})
96
@Entity
97
@Audited
98
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
99
public abstract class DescriptionElementBase extends AnnotatableEntity
100
        implements ISourceable<DescriptionElementSource>, IModifiable, IMultiLanguageTextHolder{
101

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

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

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

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

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

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

    
151
	@XmlElement(name = "TimePeriod")
152
    private TimePeriod timeperiod = TimePeriod.NewInstance();
153

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

    
161
    //#8004 optional sortIndex
162
    private Integer sortIndex = null;
163

    
164

    
165

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

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

    
189
// ******************** GETTER / SETTER ***********************************/
190

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

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

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

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

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

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

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

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

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

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

    
304

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

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

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

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

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

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

    
409
    @Override
410
    public DescriptionElementSource addSource(OriginalSourceType type, String id, String idNamespace, Reference citation, String microCitation) {
411
        if (id == null && idNamespace == null && citation == null && microCitation == null){
412
            return null;
413
        }
414
        DescriptionElementSource source = DescriptionElementSource.NewInstance(type, id, idNamespace, citation, microCitation);
415
        addSource(source);
416
        return source;
417
    }
418

    
419
    @Override
420
    public void addSources(Set<DescriptionElementSource> sources){
421
    	for (DescriptionElementSource source:sources){
422
    		addSource(source);
423
    	}
424
    }
425

    
426
    @Override
427
    public DescriptionElementSource addImportSource(String id, String idNamespace, Reference citation, String microCitation) {
428
        if (id == null && idNamespace == null && citation == null && microCitation == null){
429
            return null;
430
        }
431
        DescriptionElementSource source = DescriptionElementSource.NewInstance(OriginalSourceType.Import, id, idNamespace, citation, microCitation);
432
        addSource(source);
433
        return source;
434
    }
435

    
436
    @Override
437
    public DescriptionElementSource addPrimaryTaxonomicSource(Reference citation, String microCitation) {
438
        if (citation == null && microCitation == null){
439
            return null;
440
        }
441
        DescriptionElementSource source = DescriptionElementSource.NewPrimarySourceInstance(citation, microCitation);
442
        addSource(source);
443
        return source;
444
    }
445

    
446
    @Override
447
    public DescriptionElementSource addPrimaryTaxonomicSource(Reference citation) {
448
        return addPrimaryTaxonomicSource(citation, null);
449
    }
450

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

    
466
    @Override
467
    public void removeSource(DescriptionElementSource source) {
468
        this.sources.remove(source);
469
    }
470

    
471

    
472
    public Integer getSortIndex() {
473
        return sortIndex;
474
    }
475
    public void setSortIndex(Integer sortIndex) {
476
        this.sortIndex = sortIndex;
477
    }
478

    
479
// ******************* METHODS *************************************************************/
480

    
481
    protected Map<TermVocabulary, List<DefinedTerm>> makeModifierMap(){
482
        Map<TermVocabulary, List<DefinedTerm>> result = new HashMap<>();
483
        for (DefinedTerm modifier : getModifiers()){
484
            TermVocabulary<DefinedTerm> voc = modifier.getVocabulary();
485
            if (result.get(voc) == null){
486
                result.put(voc, new ArrayList<>());
487
            }
488
            result.get(voc).add(modifier);
489
        }
490
        return result;
491
    }
492

    
493
    public List<DefinedTerm> getModifiers(TermVocabulary voc){
494
        List<DefinedTerm> result = makeModifierMap().get(voc);
495
        if (result == null){
496
            result = new ArrayList<>();
497
        }
498
        return result;
499
    }
500

    
501

    
502
    /**
503
     * Is this description item of a class type which is considere to
504
     * represent character data? These classes are {@link QuantitativeData}
505
     * and {@link CategoricalData}.
506
     * To be overriden by these classes.
507
     */
508
    @Transient
509
    @XmlTransient
510
    public boolean isCharacterData() {
511
        return false;
512
    }
513

    
514

    
515

    
516
//************************** CLONE **********************************************************/
517

    
518
    /**
519
     * Clones the description element. The element is <b>not</b> added to the same
520
     * description as the orginal element (inDescription is set to <code>null</null>).
521
     * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
522
     */
523
    @Override
524
    public Object clone() throws CloneNotSupportedException{
525
        DescriptionElementBase result = (DescriptionElementBase)super.clone();
526

    
527
        //inDescription
528
        if (result.inDescription != null){
529
            result.inDescription.removeElement(result);
530
            result.inDescription = null;
531
        }
532

    
533
        //Sources
534
        result.sources = new HashSet<>();
535
        for (DescriptionElementSource source : getSources()){
536
            DescriptionElementSource newSource = (DescriptionElementSource)source.clone();
537
            result.addSource(newSource);
538
        }
539

    
540
        //media
541
        result.media = new ArrayList<>();
542
        for (Media media : getMedia()){
543
            result.media.add(media);
544
        }
545

    
546
        //modifying text
547
        result.modifyingText = cloneLanguageString(getModifyingText());
548

    
549
        //modifiers
550
        result.modifiers = new HashSet<>();
551
        for (DefinedTerm modifier : getModifiers()){
552
            result.modifiers.add(modifier);
553
        }
554

    
555
        //no changes to: feature
556
        return result;
557
    }
558

    
559
    /**
560
     * Clones the description element.<BR>
561
     * The new element is added to the <code>description</code>.<BR>
562
     * @see eu.etaxonomy.cdm.model.common.AnnotatableEntity#clone()
563
     */
564
    public DescriptionElementBase clone(DescriptionBase description) throws CloneNotSupportedException{
565
        DescriptionElementBase result = (DescriptionElementBase)clone();
566
        description.addElement(result);
567
        return result;
568
    }
569

    
570
}
(5-5/37)