Project

General

Profile

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

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

    
33
import org.apache.log4j.Logger;
34
import org.hibernate.annotations.Cascade;
35
import org.hibernate.annotations.CascadeType;
36
import org.hibernate.envers.Audited;
37
import org.hibernate.search.annotations.ClassBridge;
38
import org.hibernate.search.annotations.ClassBridges;
39
import org.hibernate.search.annotations.ContainedIn;
40
import org.hibernate.search.annotations.FieldBridge;
41

    
42
import eu.etaxonomy.cdm.hibernate.search.DescriptionBaseClassBridge;
43
import eu.etaxonomy.cdm.hibernate.search.GroupByTaxonClassBridge;
44
import eu.etaxonomy.cdm.hibernate.search.NotNullAwareIdBridge;
45
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
46
import eu.etaxonomy.cdm.model.name.NameRelationship;
47
import eu.etaxonomy.cdm.model.name.TaxonNameBase;
48
import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
49
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
50
import eu.etaxonomy.cdm.model.reference.Reference;
51
import eu.etaxonomy.cdm.model.taxon.Taxon;
52
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
53

    
54
/**
55
 * The upmost (abstract) class for a description as a whole (with possibly
56
 * several {@link DescriptionElementBase elementary information data})
57
 * for a {@link SpecimenOrObservationBase specimen}, a {@link Taxon taxon}
58
 * or even a {@link TaxonNameBase taxon name}.
59
 * <P>
60
 * This class corresponds to: <ul>
61
 * <li> DescriptionsSectionType according to the the SDD schema
62
 * <li> MeasurementOrFact according to the ABCD schema
63
 * </ul>
64
 *
65
 * @author m.doering
66
 * @version 1.0
67
 * @created 08-Nov-2007 13:06:24
68
 */
69

    
70
@XmlAccessorType(XmlAccessType.FIELD)
71
@XmlType(name = "DescriptionBase", propOrder = {
72
    "describedSpecimenOrObservation",
73
    "descriptionSources",
74
    "workingSets",
75
    "descriptionElements",
76
    "imageGallery"
77
})
78
@Entity
79
@Audited
80
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
81
@ClassBridges({
82
    @ClassBridge(impl=DescriptionBaseClassBridge.class),
83
    @ClassBridge(impl=GroupByTaxonClassBridge.class)
84
})
85
public abstract class DescriptionBase<S extends IIdentifiableEntityCacheStrategy> extends IdentifiableEntity<S> {
86
    private static final long serialVersionUID = 5504218413819040193L;
87
    private static final Logger logger = Logger.getLogger(DescriptionBase.class);
88

    
89
    @XmlElement( name = "DescribedSpecimenOrObservation")
90
    @ManyToOne(fetch = FetchType.LAZY)
91
    @XmlIDREF
92
    @XmlSchemaType(name="IDREF")
93
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
94
    @JoinColumn(name="specimen_id")
95
    @FieldBridge(impl=NotNullAwareIdBridge.class)
96
    //TODO maybe move down to specific classes SpecimenDescription (with Cascade.Delete) and TaxonDescription (without Cascade)
97
    private SpecimenOrObservationBase<?> describedSpecimenOrObservation;
98

    
99

    
100
    @XmlElementWrapper(name = "DescriptionSources")
101
    @XmlElement(name = "DescriptionSource")
102
    @XmlIDREF
103
    @XmlSchemaType(name="IDREF")
104
    @ManyToMany(fetch = FetchType.LAZY)  //FIXME what is the difference between this and IdentifiableEntity.sources
105
    private Set<Reference> descriptionSources = new HashSet<Reference>();
106

    
107
    @XmlElementWrapper(name = "WorkingSets")
108
    @XmlElement(name = "WorkingSet")
109
    @XmlIDREF
110
    @XmlSchemaType(name = "IDREF")
111
    @ManyToMany(fetch = FetchType.LAZY, mappedBy = "descriptions")
112
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
113
    private Set<WorkingSet> workingSets = new HashSet<WorkingSet>();
114

    
115
    @XmlElementWrapper(name = "DescriptionElements")
116
    @XmlElements({
117
        @XmlElement(name = "CategorialData", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = CategoricalData.class),
118
        @XmlElement(name = "CommonTaxonName", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = CommonTaxonName.class),
119
        @XmlElement(name = "Distribution", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = Distribution.class),
120
        @XmlElement(name = "IndividualsAssociation", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = IndividualsAssociation.class),
121
        @XmlElement(name = "QuantitativeData", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = QuantitativeData.class),
122
        @XmlElement(name = "TaxonInteraction", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = TaxonInteraction.class),
123
        @XmlElement(name = "TextData", namespace = "http://etaxonomy.eu/cdm/model/description/1.0", type = TextData.class)
124
    })
125
    @OneToMany(fetch=FetchType.LAZY, mappedBy = "inDescription", orphanRemoval=true)
126
    @Cascade( { CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
127
    @ContainedIn
128
    private Set<DescriptionElementBase> descriptionElements = new HashSet<DescriptionElementBase>();
129

    
130
    @XmlElement(name = "ImageGallery")
131
    private boolean imageGallery;
132

    
133

    
134
    /**
135
     * Returns a {@link SpecimenOrObservationBase specimen or observation} involved in
136
     * <i>this</i> description as a whole. {@link TaxonDescription Taxon descriptions} are also often based
137
     * on concrete specimens or observations. For {@link TaxonNameDescription taxon name descriptions}
138
     * this attribute should be empty.
139
     * To handle sets of specimen or observations one may first group them by a derivation event of type
140
     * "Grouping" and then use the grouped unit here.
141
     * @return
142
     */
143
    public SpecimenOrObservationBase getDescribedSpecimenOrObservation() {
144
		return describedSpecimenOrObservation;
145
	}
146

    
147

    
148
	/**
149
	 * @see #getDescribedSpecimenOrObservation()
150
	 * @param describedSpecimenOrObservation
151
	 */
152
	//TODO bidirectional method should maybe removed as a description should belong to its specimen or taxon
153
    public void setDescribedSpecimenOrObservation(SpecimenOrObservationBase describedSpecimenOrObservation) {
154
		if (describedSpecimenOrObservation == null ){
155
			this.describedSpecimenOrObservation.removeDescription(this);
156
		}else if (! describedSpecimenOrObservation.getDescriptions().contains(this)){
157
			describedSpecimenOrObservation.addDescription(this);
158
		}
159
		this.describedSpecimenOrObservation = describedSpecimenOrObservation;
160
	}
161

    
162

    
163
	/**
164
     * Returns the set of {@link Reference references} used as sources for <i>this</i> description as a
165
     * whole. More than one source can be used for a general description without
166
     * assigning for each data element of the description one of those sources.
167
     *
168
     * @see    #addDescriptionSource(Reference)
169
     * @see    #removeDescriptionSource(Reference)
170
     */
171
    @Deprecated //will probably be removed in future versions due to #2240
172
    public Set<Reference> getDescriptionSources() {
173
        return this.descriptionSources;
174
    }
175

    
176
    /**
177
     * Adds an existing {@link Reference reference} to the set of
178
     * {@link #getDescriptionSources() references} used as sources for <i>this</i>
179
     * description.
180
     *
181
     * @param descriptionSource	the reference source to be added to <i>this</i> description
182
     * @see    	   				#getDescriptionSources()
183
     */
184
    @Deprecated //will probably be removed in future versions due to #2240
185
    public void addDescriptionSource(Reference descriptionSource) {
186
        this.descriptionSources.add(descriptionSource);
187
    }
188

    
189
    /**
190
     * Removes one element from the set of {@link #getDescriptionSources() references} used as
191
     * sources for <i>this</i> description.
192
     *
193
     * @param  descriptionSource	the reference source which should be deleted
194
     * @see     		  			#getDescriptionSources()
195
     * @see     		  			#addDescriptionSource(Reference)
196
     */
197
    @Deprecated //will probably be removed in future versions due to #2240
198
    public void removeDescriptionSource(Reference descriptionSource) {
199
        this.descriptionSources.remove(descriptionSource);
200
    }
201

    
202
    /**
203
     * Returns the set of {@link DescriptionElementBase elementary description data} which constitute
204
     * <i>this</i> description as a whole.
205
     *
206
     * @see    #addElement(DescriptionElementBase)
207
     * @see    #removeElement(DescriptionElementBase)
208
     */
209
    public Set<DescriptionElementBase> getElements() {
210
        return this.descriptionElements;
211
    }
212

    
213
    /**
214
     * Adds an existing {@link DescriptionElementBase elementary description} to the set of
215
     * {@link #getElements() elementary description data} which constitute <i>this</i>
216
     * description as a whole.
217
     * If the elementary descriptions already belongs to a description it is first removed from
218
     * the old description.
219
     *
220
     * @param element	the elementary description to be added to <i>this</i> description
221
     * @see    	   		#getDescriptionSources()
222
     */
223
    public void addElement(DescriptionElementBase element) {
224
        removeNullValue();
225
        if (element.getInDescription() != null){
226
            element.getInDescription().removeElement(element);
227
        }
228
        element.setInDescription(this);
229
        this.descriptionElements.add(element);
230
    }
231

    
232
    /**
233
     * Convenience method to add multiple elements.
234
     * @param elements
235
     */
236
    public void addElements(DescriptionElementBase ... elements) {
237
        removeNullValue();
238
        for (DescriptionElementBase element : elements){
239
    		addElement(element);
240
    	}
241
    }
242

    
243
    /**
244
     * Removes one element from the set of {@link #getElements() elementary description data} which
245
     * constitute <i>this</i> description as a whole.
246
     *
247
     * @param  element	the reference source which should be deleted
248
     * @see     		#getElements()
249
     * @see     		#addElement(DescriptionElementBase)
250
     */
251
    public void removeElement(DescriptionElementBase element) {
252
        removeNullValue();
253
        this.descriptionElements.remove(element);
254
        element.setInDescription(null);
255
    }
256

    
257
    private void removeNullValue(){
258
        while(this.descriptionElements.contains(null)){
259
            this.descriptionElements.remove(null);
260
        }
261
    }
262

    
263
    /**
264
     * Returns the number of {@link DescriptionElementBase elementary description data} which constitute
265
     * <i>this</i> description as a whole. This is the cardinality of the set of
266
     * elementary description data.
267
     *
268
     * @see		#getElements()
269
     * @return	the number of elements of the elementary description data set
270
     */
271
    public int size(){
272
        return this.descriptionElements.size();
273
    }
274

    
275
    /**
276
     * @return the imageGallery
277
     */
278
    public boolean isImageGallery() {
279
        return imageGallery;
280
    }
281

    
282
    /**
283
     * @param imageGallery the imageGallery to set
284
     */
285
    public void setImageGallery(boolean imageGallery) {
286
        this.imageGallery = imageGallery;
287
    }
288

    
289

    
290
    public Set<WorkingSet> getWorkingSets() {
291
        return workingSets;
292
    }
293

    
294
    public boolean addWorkingSet(WorkingSet workingSet){
295
        boolean result = this.workingSets.add(workingSet);
296
        if (! workingSet.getDescriptions().contains(this)){
297
            workingSet.addDescription(this);
298
        }
299
        return result;
300
    }
301

    
302
    public boolean removeWorkingSet(WorkingSet workingSet){
303
        boolean result = this.workingSets.remove(workingSet);
304
        if (workingSet.getDescriptions().contains(this)){
305
            workingSet.addDescription(this);
306
        }
307
        return result;
308
    }
309

    
310
    protected void setWorkingSets(Set<WorkingSet> workingSets) {
311
        this.workingSets = workingSets;
312
    }
313

    
314

    
315

    
316
    @Transient
317
    public boolean hasStructuredData(){
318
        for (DescriptionElementBase element : this.getElements()){
319
            if (element.isInstanceOf(QuantitativeData.class) ||
320
                    element.isInstanceOf(CategoricalData.class)){
321
                return true;
322
            }
323
        }
324
        return false;
325
    }
326

    
327

    
328
//*********************** CLONE ********************************************************/
329

    
330
    /**
331
     * Clones <i>this</i> descriptioin. This is a shortcut that enables to create
332
     * a new instance that differs only slightly from <i>this</i> description by
333
     * modifying only some of the attributes.<BR>
334
     *
335
     * Usages of this name in a taxon concept are NOT cloned.<BR>
336
     * The name is added to the same homotypical group as the original name
337
     * (CAUTION: this behaviour needs to be discussed and may change in future).<BR>
338
     * {@link TaxonNameDescription Name descriptions} are cloned as XXX.<BR>
339
     * {@link TypeDesignationBase Type designations} are cloned as XXX.<BR>
340
     * {@link NameRelationship Name relation} are cloned as XXX.<BR>
341
     *
342
     * @see eu.etaxonomy.cdm.model.media.IdentifiableEntity#clone()
343
     * @see java.lang.Object#clone()
344
     */
345
    @Override
346
    public Object clone()  {
347
        DescriptionBase<?> result;
348
        try{
349
            result = (DescriptionBase<?>)super.clone();
350

    
351
            //working set
352
            result.workingSets = new HashSet<WorkingSet>();
353
            for (WorkingSet workingSet : getWorkingSets()){
354
                workingSet.addDescription(result);
355
            }
356

    
357
            //descriptions
358
            result.descriptionSources = new HashSet<Reference>();
359
            for (Reference reference : getDescriptionSources()){
360
                result.descriptionSources.add(reference);
361
            }
362

    
363
            //elements
364
            result.descriptionElements = new HashSet<DescriptionElementBase>();
365
            for (DescriptionElementBase element : getElements()){
366
                DescriptionElementBase newElement = (DescriptionElementBase)element.clone();
367
                result.addElement(newElement);
368
            }
369

    
370
            //no changes to: imageGallery
371
            return result;
372
        } catch (CloneNotSupportedException e) {
373
            logger.warn("Object does not implement cloneable");
374
            e.printStackTrace();
375
            return null;
376
        }
377

    
378
    }
379
}
(3-3/36)