Project

General

Profile

Download (17.3 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
import java.util.EnumSet;
13
import java.util.HashSet;
14
import java.util.Set;
15

    
16
import javax.persistence.Entity;
17
import javax.persistence.FetchType;
18
import javax.persistence.Inheritance;
19
import javax.persistence.InheritanceType;
20
import javax.persistence.JoinColumn;
21
import javax.persistence.ManyToMany;
22
import javax.persistence.ManyToOne;
23
import javax.persistence.OneToMany;
24
import javax.persistence.Transient;
25
import javax.validation.constraints.NotNull;
26
import javax.xml.bind.annotation.XmlAccessType;
27
import javax.xml.bind.annotation.XmlAccessorType;
28
import javax.xml.bind.annotation.XmlAttribute;
29
import javax.xml.bind.annotation.XmlElement;
30
import javax.xml.bind.annotation.XmlElementWrapper;
31
import javax.xml.bind.annotation.XmlElements;
32
import javax.xml.bind.annotation.XmlIDREF;
33
import javax.xml.bind.annotation.XmlSchemaType;
34
import javax.xml.bind.annotation.XmlType;
35

    
36
import org.apache.logging.log4j.LogManager;
37
import org.apache.logging.log4j.Logger;
38
import org.hibernate.annotations.Cascade;
39
import org.hibernate.annotations.CascadeType;
40
import org.hibernate.annotations.Parameter;
41
import org.hibernate.annotations.Type;
42
import org.hibernate.envers.Audited;
43
import org.hibernate.search.annotations.ClassBridge;
44
import org.hibernate.search.annotations.ClassBridges;
45
import org.hibernate.search.annotations.ContainedIn;
46
import org.hibernate.search.annotations.FieldBridge;
47

    
48
import eu.etaxonomy.cdm.hibernate.search.DescriptionBaseClassBridge;
49
import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;
50
import eu.etaxonomy.cdm.hibernate.search.NotNullAwareIdBridge;
51
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
52
import eu.etaxonomy.cdm.model.name.NameRelationship;
53
import eu.etaxonomy.cdm.model.name.TaxonName;
54
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
55
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
56
import eu.etaxonomy.cdm.model.reference.ICdmTarget;
57
import eu.etaxonomy.cdm.model.reference.Reference;
58
import eu.etaxonomy.cdm.model.taxon.Taxon;
59
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
60

    
61
/**
62
 * The upmost (abstract) class for a description as a whole (with possibly
63
 * several {@link DescriptionElementBase elementary information data})
64
 * for a {@link SpecimenOrObservationBase specimen}, a {@link Taxon taxon}
65
 * or even a {@link TaxonName taxon name}.
66
 * <P>
67
 * This class corresponds to: <ul>
68
 * <li> DescriptionsSectionType according to the the SDD schema
69
 * <li> MeasurementOrFact according to the ABCD schema
70
 * </ul>
71
 *
72
 * @author m.doering
73
 * @since 08-Nov-2007 13:06:24
74
 */
75
@XmlAccessorType(XmlAccessType.FIELD)
76
@XmlType(name = "DescriptionBase", propOrder = {
77
    "describedSpecimenOrObservation",
78
    "descriptionSources",
79
    "descriptiveDataSets",
80
    "descriptionElements",
81
    "imageGallery",
82
    "isDefault",
83
    "types"
84
})
85
@Entity
86
@Audited
87
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
88
@ClassBridges({
89
    @ClassBridge(impl=DescriptionBaseClassBridge.class),
90
    @ClassBridge(impl=GroupByTaxonClassBridge.class)
91
})
92
public abstract class DescriptionBase<S extends IIdentifiableEntityCacheStrategy>
93
        extends IdentifiableEntity<S>
94
        implements ICdmTarget{
95

    
96
    private static final long serialVersionUID = 5504218413819040193L;
97
    private static final Logger logger = LogManager.getLogger(DescriptionBase.class);
98

    
99
    @XmlElement( name = "DescribedSpecimenOrObservation")
100
    @ManyToOne(fetch = FetchType.LAZY)
101
    @XmlIDREF
102
    @XmlSchemaType(name="IDREF")
103
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
104
    @JoinColumn(name="specimen_id")
105
    @FieldBridge(impl=NotNullAwareIdBridge.class)
106
    //TODO maybe move down to specific classes SpecimenDescription (with Cascade.Delete) and TaxonDescription (without Cascade)
107
    private SpecimenOrObservationBase<?> describedSpecimenOrObservation;
108

    
109

    
110
    @XmlElementWrapper(name = "DescriptionSources")
111
    @XmlElement(name = "DescriptionSource")
112
    @XmlIDREF
113
    @XmlSchemaType(name="IDREF")
114
    @ManyToMany(fetch = FetchType.LAZY)  //FIXME what is the difference between this and IdentifiableEntity.sources
115
    @Deprecated
116
    private Set<Reference> descriptionSources = new HashSet<>();
117

    
118
    @XmlElementWrapper(name = "DescriptiveDataSets")
119
    @XmlElement(name = "DescriptiveDataSet")
120
    @XmlIDREF
121
    @XmlSchemaType(name = "IDREF")
122
    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "descriptions")
123
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
124
    private Set<DescriptiveDataSet> descriptiveDataSets = new HashSet<>();
125

    
126
    @XmlElementWrapper(name = "DescriptionElements")
127
    @XmlElements({
128
        @XmlElement(name = "CategorialData", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = CategoricalData.class),
129
        @XmlElement(name = "CommonTaxonName", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = CommonTaxonName.class),
130
        @XmlElement(name = "Distribution", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = Distribution.class),
131
        @XmlElement(name = "IndividualsAssociation", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = IndividualsAssociation.class),
132
        @XmlElement(name = "QuantitativeData", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = QuantitativeData.class),
133
        @XmlElement(name = "TaxonInteraction", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = TaxonInteraction.class),
134
        @XmlElement(name = "TextData", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = TextData.class),
135
        @XmlElement(name = "TemporalData", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = TemporalData.class)
136
    })
137
    @OneToMany(fetch=FetchType.LAZY, mappedBy = "inDescription", orphanRemoval=true)
138
    @Cascade( { CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
139
    @ContainedIn
140
    private Set<DescriptionElementBase> descriptionElements = new HashSet<>();
141

    
142
    @XmlElement(name = "ImageGallery")
143
    private boolean imageGallery;
144

    
145
    //TODO make it a DescriptionState
146
    @XmlElement(name = "isDefault")
147
    private boolean isDefault;
148

    
149
    @XmlAttribute(name ="types")
150
    @NotNull
151
    @Type(type = "eu.etaxonomy.cdm.hibernate.EnumSetUserType",
152
        parameters = {@Parameter(name  = "enumClass", value = "eu.etaxonomy.cdm.model.description.DescriptionType")}
153
    )
154
    private EnumSet<DescriptionType> types = EnumSet.noneOf(DescriptionType.class);
155

    
156
//******************************** GETTER / SETTER ***************************/
157

    
158
    /**
159
     * Returns a {@link SpecimenOrObservationBase specimen or observation} involved in
160
     * <i>this</i> description as a whole. {@link TaxonDescription Taxon descriptions} are also often based
161
     * on concrete specimens or observations. For {@link TaxonNameDescription taxon name descriptions}
162
     * this attribute should be empty.
163
     * To handle sets of specimen or observations one may first group them by a derivation event of type
164
     * "Grouping" and then use the grouped unit here.
165
     */
166
    public SpecimenOrObservationBase getDescribedSpecimenOrObservation() {
167
		return describedSpecimenOrObservation;
168
	}
169

    
170
	/**
171
	 * @see #getDescribedSpecimenOrObservation()
172
	 * @param describedSpecimenOrObservation
173
	 */
174
	//TODO bidirectional method should maybe removed as a description should belong to its specimen or taxon
175
    public void setDescribedSpecimenOrObservation(SpecimenOrObservationBase describedSpecimenOrObservation) {
176
		if (describedSpecimenOrObservation == null ){
177
			if (this.describedSpecimenOrObservation != null){
178
			    this.describedSpecimenOrObservation.removeDescription(this);
179
			}
180
		}else if (! describedSpecimenOrObservation.getDescriptions().contains(this)){
181
			describedSpecimenOrObservation.addDescription(this);
182
		}
183
		this.describedSpecimenOrObservation = describedSpecimenOrObservation;
184
	}
185

    
186

    
187

    
188

    
189
    /**
190
     * Returns the set of {@link DescriptionElementBase elementary description data} which constitute
191
     * <i>this</i> description as a whole.
192
     *
193
     * @see    #addElement(DescriptionElementBase)
194
     * @see    #removeElement(DescriptionElementBase)
195
     */
196
    public Set<DescriptionElementBase> getElements() {
197
        return this.descriptionElements;
198
    }
199

    
200
    /**
201
     * Adds an existing {@link DescriptionElementBase elementary description} to the set of
202
     * {@link #getElements() elementary description data} which constitute <i>this</i>
203
     * description as a whole.
204
     * If the elementary descriptions already belongs to a description it is first removed from
205
     * the old description.
206
     *
207
     * @param element	the elementary description to be added to <i>this</i> description
208
     * @see    	   		#getDescriptionSources()
209
     */
210
    public void addElement(DescriptionElementBase element) {
211
        if (element.getInDescription() != null){
212
            element.getInDescription().removeElement(element);
213
        }
214
        element.setInDescription(this);
215
        this.descriptionElements.add(element);
216
    }
217

    
218
    /**
219
     * Convenience method to add multiple elements.
220
     * @param elements
221
     */
222
    public void addElements(DescriptionElementBase ... elements) {
223
        for (DescriptionElementBase element : elements){
224
    		addElement(element);
225
    	}
226
    }
227

    
228
    /**
229
     * Removes one element from the set of {@link #getElements() elementary description data} which
230
     * constitute <i>this</i> description as a whole.
231
     *
232
     * @param  element	the reference source which should be deleted
233
     * @see     		#getElements()
234
     * @see     		#addElement(DescriptionElementBase)
235
     */
236
    public void removeElement(DescriptionElementBase element) {
237
        this.descriptionElements.remove(element);
238
        element.setInDescription(null);
239
    }
240

    
241

    
242

    
243
    /**
244
     * Returns the number of {@link DescriptionElementBase elementary description data} which constitute
245
     * <i>this</i> description as a whole. This is the cardinality of the set of
246
     * elementary description data.
247
     *
248
     * @see		#getElements()
249
     * @return	the number of elements of the elementary description data set
250
     */
251
    public int size(){
252
        return this.descriptionElements.size();
253
    }
254

    
255
    public boolean isImageGallery() {
256
        return imageGallery;
257
    }
258
    public void setImageGallery(boolean imageGallery) {
259
        this.imageGallery = imageGallery;
260
    }
261

    
262
    public boolean isDefault() {
263
        return isDefault;
264
    }
265
    public void setDefault(boolean isDefault) {
266
        this.isDefault = isDefault;
267
    }
268

    
269
    public EnumSet<DescriptionType> getTypes() {
270
        return types;
271
    }
272
    public void setTypes(EnumSet<DescriptionType> types) {
273
        this.types = types;
274
    }
275
    public void addType(DescriptionType type) {
276
        this.types.add(type);
277
    }
278
    public void addTypes(Set<DescriptionType> types) {
279
        this.types.addAll(types);
280
    }
281

    
282

    
283
    public Set<DescriptiveDataSet> getDescriptiveDataSets() {
284
        return descriptiveDataSets;
285
    }
286
    public boolean addDescriptiveDataSet(DescriptiveDataSet descriptiveDataSet){
287
        boolean result = this.descriptiveDataSets.add(descriptiveDataSet);
288
        if (! descriptiveDataSet.getDescriptions().contains(this)){
289
            descriptiveDataSet.addDescription(this);
290
        }
291
        return result;
292
    }
293
    public boolean removeDescriptiveDataSet(DescriptiveDataSet descriptiveDataSet){
294
        boolean result = this.descriptiveDataSets.remove(descriptiveDataSet);
295
        if (descriptiveDataSet.getDescriptions().contains(this)){
296
            descriptiveDataSet.removeDescription(this);
297
        }
298
        return result;
299
    }
300
    protected void setDescriptiveDataSet(Set<DescriptiveDataSet> descriptiveDataSets) {
301
        this.descriptiveDataSets = descriptiveDataSets;
302
    }
303

    
304
    /**
305
     * Returns the set of {@link Reference references} used as sources for <i>this</i> description as a
306
     * whole. More than one source can be used for a general description without
307
     * assigning for each data element of the description one of those sources.
308
     *
309
     * @see    #addDescriptionSource(Reference)
310
     * @see    #removeDescriptionSource(Reference)
311
     */
312
    @Deprecated //will probably be removed in future versions due to #2240
313
    public Set<Reference> getDescriptionSources() {
314
        return this.descriptionSources;
315
    }
316

    
317
    /**
318
     * Adds an existing {@link Reference reference} to the set of
319
     * {@link #getDescriptionSources() references} used as sources for <i>this</i>
320
     * description.
321
     *
322
     * @param descriptionSource the reference source to be added to <i>this</i> description
323
     * @see                     #getDescriptionSources()
324
     */
325
    @Deprecated //will probably be removed in future versions due to #2240
326
    public void addDescriptionSource(Reference descriptionSource) {
327
        this.descriptionSources.add(descriptionSource);
328
    }
329

    
330
    /**
331
     * Removes one element from the set of {@link #getDescriptionSources() references} used as
332
     * sources for <i>this</i> description.
333
     *
334
     * @param  descriptionSource    the reference source which should be deleted
335
     * @see                         #getDescriptionSources()
336
     * @see                         #addDescriptionSource(Reference)
337
     */
338
    @Deprecated //will probably be removed in future versions due to #2240
339
    public void removeDescriptionSource(Reference descriptionSource) {
340
        this.descriptionSources.remove(descriptionSource);
341
    }
342

    
343
// *********************** METHODS ******************************/
344

    
345
    @Transient
346
    //TODO this is not correct
347
    public boolean hasStructuredData(){
348
        for (DescriptionElementBase element : this.getElements()){
349
            if (element.isInstanceOf(QuantitativeData.class) ||
350
                    element.isInstanceOf(CategoricalData.class)){
351
                return true;
352
            }
353
        }
354
        return false;
355
    }
356

    
357
    /**
358
     * if this is of type {@link DescriptionType#COMPUTED} computed.<BR><BR>
359
     * Note: Computed is a base type. It has children like {@link DescriptionType#AGGREGATED}.
360
     * Also for them this method returns <code>true</code>.
361
     */
362
    public boolean isComputed() {
363
        return DescriptionType.isComputed(types);
364
    }
365
    public boolean isAggregated() {
366
        return DescriptionType.includesType(types, DescriptionType.AGGREGATED);
367
    }
368
    public boolean isAggregatedDistribution() {
369
        return DescriptionType.includesType(types, DescriptionType.AGGREGATED_DISTRIBUTION);
370
    }
371
    public boolean isAggregatedStructuredDescription() {
372
        return DescriptionType.includesType(types, DescriptionType.AGGREGATED_STRUC_DESC);
373
    }
374
    public boolean isCloneForSource() {
375
        return DescriptionType.includesType(types, DescriptionType.CLONE_FOR_SOURCE);
376
    }
377
    public static boolean isDefaultForAggregation(EnumSet<DescriptionType> set) {
378
        return DescriptionType.includesType(set, DescriptionType.DEFAULT_VALUES_FOR_AGGREGATION);
379
    }
380
    public static boolean isSecondaryData(EnumSet<DescriptionType> set) {
381
        return DescriptionType.includesType(set, DescriptionType.SECONDARY_DATA);
382
    }
383

    
384
    public abstract IDescribable<?> describedEntity();
385

    
386

    
387
//*********************** CLONE ********************************************************/
388

    
389
    /**
390
     * Clones <i>this</i> description. This is a shortcut that enables to create
391
     * a new instance that differs only slightly from <i>this</i> description by
392
     * modifying only some of the attributes.<BR>
393
     *
394
     * Usages of this name in a taxon concept are NOT cloned.<BR>
395
     * The name is added to the same homotypical group as the original name
396
     * (CAUTION: this behavior needs to be discussed and may change in future).<BR>
397
     * {@link TaxonNameDescription Name descriptions} are cloned as XXX.<BR>
398
     * {@link TypeDesignationBase Type designations} are cloned as XXX.<BR>
399
     * {@link NameRelationship Name relation} are cloned as XXX.<BR>
400
     *
401
     * @see java.lang.Object#clone()
402
     */
403
    @Override
404
    public DescriptionBase<S> clone()  {
405
        DescriptionBase<S> result;
406
        try{
407
            result = (DescriptionBase<S>)super.clone();
408

    
409
            //descriptive dataset
410
            //TODO do we really want to add the cloned description automatically to the dataset?
411
            result.descriptiveDataSets = new HashSet<>();
412
            for (DescriptiveDataSet descriptiveDataSet : getDescriptiveDataSets()){
413
                descriptiveDataSet.addDescription(result);
414
            }
415

    
416
            //reference based descriptions
417
            //TODO remove
418
            result.descriptionSources = new HashSet<>();
419
            for (Reference reference : getDescriptionSources()){
420
                result.descriptionSources.add(reference);
421
            }
422

    
423
            //elements
424
            result.descriptionElements = new HashSet<>();
425
            for (DescriptionElementBase element : getElements()){
426
                DescriptionElementBase newElement = element.clone();
427
                result.addElement(newElement);
428
            }
429

    
430
            result.types = this.types.clone();
431

    
432
            //no changes to: imageGallery
433
            return result;
434
        } catch (CloneNotSupportedException e) {
435
            logger.warn("Object does not implement cloneable");
436
            e.printStackTrace();
437
            return null;
438
        }
439

    
440
    }
441
}
(4-4/38)