Project

General

Profile

Download (16.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
package eu.etaxonomy.cdm.model.occurrence;
10

    
11
import java.util.HashSet;
12
import java.util.Set;
13

    
14
import javax.persistence.Column;
15
import javax.persistence.Entity;
16
import javax.persistence.FetchType;
17
import javax.persistence.Lob;
18
import javax.persistence.ManyToOne;
19
import javax.persistence.OneToMany;
20
import javax.persistence.Transient;
21
import javax.xml.bind.annotation.XmlAccessType;
22
import javax.xml.bind.annotation.XmlAccessorType;
23
import javax.xml.bind.annotation.XmlElement;
24
import javax.xml.bind.annotation.XmlElementWrapper;
25
import javax.xml.bind.annotation.XmlIDREF;
26
import javax.xml.bind.annotation.XmlRootElement;
27
import javax.xml.bind.annotation.XmlSchemaType;
28
import javax.xml.bind.annotation.XmlType;
29

    
30
import org.apache.log4j.Logger;
31
import org.hibernate.annotations.Cascade;
32
import org.hibernate.annotations.CascadeType;
33
import org.hibernate.envers.Audited;
34
import org.hibernate.search.annotations.Analyze;
35
import org.hibernate.search.annotations.Field;
36
import org.hibernate.search.annotations.Indexed;
37
import org.hibernate.search.annotations.IndexedEmbedded;
38

    
39
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
40
import eu.etaxonomy.cdm.model.molecular.DnaSample;
41
import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
42
import eu.etaxonomy.cdm.model.name.TaxonName;
43
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
44
import eu.etaxonomy.cdm.strategy.cache.occurrence.DerivedUnitDefaultCacheStrategy;
45

    
46
/**
47
 * A derived unit is regarded as derived from a field unit,
48
 * so locality and gathering related information is captured as a separate FieldUnit object
49
 * related to a specimen via a derivation event
50
 *
51
 * http://www.bgbm.org/biodivinf/docs/CollectionModel/ReprintTNR.pdf
52
 * http://www.bgbm.org/biodivinf/docs/CollectionModel/
53
 * <BR>
54
 * Type figures are derived units with at least a figure object in media
55
 *
56
 * @author m.doering
57
 * @since 08-Nov-2007 13:06:52
58
 */
59
@XmlAccessorType(XmlAccessType.FIELD)
60
@XmlType(name = "DerivedUnit", propOrder = {
61
    "collection",
62
    "catalogNumber",
63
    "storedUnder",
64
    "derivedFrom",
65
    "accessionNumber",
66
    "collectorsNumber",
67
    "barcode",
68
	"preservation",
69
	"exsiccatum",
70
	"originalLabelInfo",
71
    "specimenTypeDesignations"
72
})
73
@XmlRootElement(name = "DerivedUnit")
74
@Entity
75
@Audited
76
// even if hibernate complains "Abstract classes can never insert index documents. Remove @Indexed."
77
// this is needed, otherwise the fields of the also abstract super class are missed during indexing
78
@Indexed(index = "eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase")
79
public class DerivedUnit
80
        extends SpecimenOrObservationBase<IIdentifiableEntityCacheStrategy<? extends DerivedUnit>> {
81

    
82
    private static final long serialVersionUID = -3525746216270843517L;
83
	private static final Logger logger = Logger.getLogger(DerivedUnit.class);
84

    
85
	@XmlElement(name = "Collection")
86
	@XmlIDREF
87
	@XmlSchemaType(name = "IDREF")
88
	@ManyToOne(fetch = FetchType.LAZY)
89
	@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
90
	@IndexedEmbedded
91
	private Collection collection;
92

    
93
	@XmlElement(name = "CatalogNumber")
94
	@Field(analyze = Analyze.NO)
95
    //TODO Val #3379
96
//	@NullOrNotEmpty
97
	@Column(length=255)
98
	private String catalogNumber;
99

    
100
	@XmlElement(name = "AccessionNumber")
101
	@Field(analyze = Analyze.NO)
102
    //TODO Val #3379
103
//	@NullOrNotEmpty
104
	@Column(length=255)
105
	private String accessionNumber;
106

    
107
	@XmlElement(name = "CollectorsNumber")
108
	@Field(analyze = Analyze.NO)
109
    //TODO Val #3379
110
//	@NullOrNotEmpty
111
	@Column(length=255)
112
	private String collectorsNumber;
113

    
114
	@XmlElement(name = "Barcode")
115
	@Field(analyze = Analyze.NO)
116
    //TODO Val #3379
117
//	@NullOrNotEmpty
118
	@Column(length=255)
119
	private String barcode;
120

    
121
	@XmlElement(name = "StoredUnder")
122
	@XmlIDREF
123
	@XmlSchemaType(name = "IDREF")
124
	@ManyToOne(fetch = FetchType.LAZY)
125
	@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
126
	@IndexedEmbedded(includeEmbeddedObjectId=true)
127
	private TaxonName storedUnder;
128

    
129
	@XmlElement(name = "DerivedFrom")
130
	@XmlIDREF
131
	@XmlSchemaType(name = "IDREF")
132
	@ManyToOne(fetch = FetchType.LAZY)
133
	@Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
134
	@IndexedEmbedded(depth = 4)
135
	private DerivationEvent derivedFrom;
136

    
137
	@XmlElement(name = "OriginalLabelInfo")
138
	@Lob
139
    private String originalLabelInfo;
140

    
141
	@XmlElementWrapper(name = "SpecimenTypeDesignations")
142
	@XmlElement(name = "SpecimenTypeDesignation")
143
	@OneToMany(fetch = FetchType.LAZY, mappedBy = "typeSpecimen")
144
	@Cascade({ CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE })
145
	private final Set<SpecimenTypeDesignation> specimenTypeDesignations = new HashSet<SpecimenTypeDesignation>();
146

    
147

    
148
//*** attributes valid only for preserved specimen (PreservedSpecimen, Fossil, DnaSample)
149

    
150
	@XmlElement(name = "Preservation")
151
	@XmlIDREF
152
	@XmlSchemaType(name = "IDREF")
153
	@ManyToOne(fetch = FetchType.LAZY)
154
    @Cascade({CascadeType.SAVE_UPDATE,CascadeType.MERGE})
155
	private PreservationMethod preservation;
156

    
157

    
158
	@XmlElement(name = "Exsiccatum")
159
    //TODO Val #3379
160
//	@NullOrNotEmpty
161
	@Field
162
    @Column(length=255)
163
    private String exsiccatum;
164

    
165
// ******************** FACTORY METHOD **********************************/
166

    
167
	public static DerivedUnit NewInstance(SpecimenOrObservationType type) {
168
		if (type.isMedia()){
169
			return MediaSpecimen.NewInstance(type);
170
		}else if (type.equals(SpecimenOrObservationType.DnaSample) || type.isKindOf(SpecimenOrObservationType.DnaSample)){
171
			return DnaSample.NewInstance();
172
		}else if (type.equals(SpecimenOrObservationType.TissueSample) || type.isKindOf(SpecimenOrObservationType.TissueSample)){
173
            //for now we store TissueSample as DnaSample to allow adding sequences and other DnaSample data to it directly
174
		    //this is because sometimes explicit DnaSample data does not exist as it is not preserved
175
		    //In this case a Sequence or Amplification is directly added to the Tissue Sample.
176
		    //In theory also TissueSample could be missing so this should be possible also for other
177
		    //SpecimenOrObservationType units.
178
		    //This is a reason why DnaSample and DerivedUnit should be unified.
179
		    return DnaSample.NewInstance();
180
        }else{
181
			return new DerivedUnit(type);
182
		}
183
	}
184

    
185
	public static DerivedUnit NewPreservedSpecimenInstance(){
186
		DerivedUnit result = new DerivedUnit(SpecimenOrObservationType.PreservedSpecimen);
187
		return result;
188
	}
189

    
190
//************************** CONSTRUCTOR *********************************/
191

    
192
	//Constructor: For hibernate use only
193
	@SuppressWarnings("deprecation")
194
    protected DerivedUnit() {
195
	    super();
196
        initDefaultCacheStrategy();
197
	}
198

    
199
    /**
200
	 * Constructor
201
	 * @param recordBasis
202
	 */
203
	protected DerivedUnit(SpecimenOrObservationType recordBasis) {
204
		super(recordBasis);
205
        initDefaultCacheStrategy();
206
	}
207

    
208

    
209
	/**
210
	 * Create new unit derived from an existing field unit
211
	 * @param fieldUnit existing field unit from where this unit is derived
212
	 */
213
	protected DerivedUnit(SpecimenOrObservationType recordBasis, FieldUnit fieldUnit) {
214
		this(recordBasis);
215
		DerivationEvent derivedFrom = new DerivationEvent();
216
		// TODO: should be done in a more controlled way. Probably by making derivation event implement a general relationship interface (for bidirectional add/remove etc)
217
		fieldUnit.addDerivationEvent(derivedFrom);
218
		derivedFrom.getOriginals().add(fieldUnit);
219
		derivedFrom.getDerivatives().add(this);
220
		this.setDerivedFrom(derivedFrom);
221
	}
222

    
223
	/**
224
	 * create new unit derived from an existing gathering event,
225
	 * thereby creating a new empty field unit
226
	 * @param gatheringEvent the gathering event this unit was collected at
227
	 */
228
	protected DerivedUnit(SpecimenOrObservationType recordBasis, GatheringEvent gatheringEvent) {
229
		this(recordBasis, new FieldUnit());
230
		FieldUnit field = (FieldUnit)this.getOriginalUnit();
231
		field.setGatheringEvent(gatheringEvent);
232
	}
233

    
234
    /**
235
     * Sets the default cache strategy
236
     */
237
	@Override
238
    protected void initDefaultCacheStrategy() {
239
        this.cacheStrategy = new DerivedUnitDefaultCacheStrategy();
240
    }
241

    
242
    @Override
243
    public String getTitleCache() {
244
        //specimen are complex and changes in other objects often, therefore we compute titleCache each time from scratch
245
        if (!this.protectedTitleCache){
246
            this.titleCache = null;
247
        }
248
        return super.getTitleCache();
249
    }
250

    
251
// ******************** GETTER / SETTER *************************************/
252

    
253
	public DerivationEvent getDerivedFrom() {
254
		return derivedFrom;
255
	}
256

    
257
	public void setDerivedFrom(DerivationEvent derivedFrom){
258
		if (getDerivedFrom() != null){
259
			getDerivedFrom().getDerivatives().remove(derivedFrom);
260
		}
261
		this.derivedFrom = derivedFrom;
262
		if (derivedFrom != null){
263
			derivedFrom.addDerivative(this);
264
		}
265
	}
266

    
267
	@Transient
268
	public Set<SpecimenOrObservationBase> getOriginals(){
269
		if(getDerivedFrom() != null){
270
			return getDerivedFrom().getOriginals();
271
		}
272
		return new HashSet<>();
273
	}
274

    
275
	public Collection getCollection(){
276
		return this.collection;
277
	}
278

    
279
	public void setCollection(Collection collection){
280
		this.collection = collection;
281
	}
282

    
283
	public String getCatalogNumber() {
284
		return catalogNumber;
285
	}
286

    
287
	public void setCatalogNumber(String catalogNumber) {
288
		this.catalogNumber = isBlank(catalogNumber)?null:catalogNumber;
289
	}
290

    
291
	public void setBarcode(String barcode) {
292
		this.barcode = isBlank(barcode)? null : barcode;
293
	}
294
	public String getBarcode() {
295
		return barcode;
296
	}
297

    
298
	public void setStoredUnder(TaxonName storedUnder) {
299
		this.storedUnder = storedUnder;
300
	}
301

    
302
	public String getAccessionNumber() {
303
		return accessionNumber;
304
	}
305
	public void setAccessionNumber(String accessionNumber) {
306
		this.accessionNumber = isBlank(accessionNumber)? null : accessionNumber;
307
	}
308

    
309
	/**
310
	 * Original label information may present the exact original text
311
	 * or any other text which fully or partly represents the text available
312
	 * on the specimens label. This information may differ from the information
313
	 * available in the derived unit itself.
314
	 * @return the original label information
315
	 */
316
	//#4218
317
	public String getOriginalLabelInfo() {
318
		return originalLabelInfo;
319
	}
320

    
321
	public void setOriginalLabelInfo(String originalLabelInfo) {
322
		this.originalLabelInfo = originalLabelInfo;
323
	}
324

    
325
	/**
326
	 * Will be removed in future versions as semantics is not clear.
327
	 * For accessing the collecting number use
328
	 * {@link FieldUnit#getFieldNumber()} instead.
329
	 * @return
330
	 */
331
	@Deprecated
332
	public String getCollectorsNumber() {
333
		return collectorsNumber;
334
	}
335

    
336
	/**
337
	 * Will be removed in future versions as semantics is not clear.
338
	 * For editing the collecting number use
339
	 * {@link FieldUnit#getFieldNumber()} instead.
340
	 * @return
341
	 */
342
	@Deprecated
343
	public void setCollectorsNumber(String collectorsNumber) {
344
		this.collectorsNumber = isBlank(collectorsNumber)? null : collectorsNumber;
345
	}
346

    
347
	public TaxonName getStoredUnder() {
348
		return storedUnder;
349
	}
350

    
351
	public void addSpecimenTypeDesignation(SpecimenTypeDesignation specimenTypeDesignation){
352
		if (specimenTypeDesignation.getTypeSpecimen() == this){
353
			return ;
354
		}else if (specimenTypeDesignation.getTypeSpecimen() != null){
355
			specimenTypeDesignation.getTypeSpecimen().removeSpecimenTypeDesignation(specimenTypeDesignation);
356

    
357
		}
358
		specimenTypeDesignations.add(specimenTypeDesignation);
359
		specimenTypeDesignation.setTypeSpecimen(this);
360
	}
361

    
362
	public void removeSpecimenTypeDesignation(SpecimenTypeDesignation specimenTypeDesignation){
363
		if (specimenTypeDesignation == null){
364
			return;
365
		}
366
		if (specimenTypeDesignations.contains(specimenTypeDesignation)){
367
			specimenTypeDesignations.remove(specimenTypeDesignation);
368
			specimenTypeDesignation.setTypeSpecimen(null);
369
		}
370
	}
371

    
372
    public String getMostSignificantIdentifier() {
373
        if (isNotBlank(getAccessionNumber())) {
374
            return getAccessionNumber();
375
        }
376
        else if(isNotBlank(getBarcode())){
377
            return getBarcode();
378
        }
379
        else if(isNotBlank(getCatalogNumber())){
380
            return getCatalogNumber();
381
        }
382
        return null;
383
    }
384

    
385
    /**
386
     * Collects all top most units (FieldUnits, DerivedUnits) in the parent branches of the derivation graph.
387
     * <p>
388
     * <b>NOTE:</b> As this method walks the derivation graph it should only be used in a transactional context or
389
     * it must be assured that the whole graph is initialized.
390
     *
391
     * @param typeRestriction
392
     *  Restricts the returned entities to a specific type of unit.
393
     */
394
    public <T extends SpecimenOrObservationBase> java.util.Collection<T> collectRootUnits(Class<T> typeRestriction) {
395
        return collectRootUnits(typeRestriction, new HashSet<>());
396
    }
397

    
398

    
399
    private <T extends SpecimenOrObservationBase> java.util.Collection<T> collectRootUnits(Class<T> typeRestriction, Set<SpecimenOrObservationBase<?>> cycleDetection) {
400

    
401
        if(typeRestriction == null) {
402
            typeRestriction = (Class<T>) SpecimenOrObservationBase.class;
403
        }
404

    
405
        cycleDetection.add(this);
406

    
407
        java.util.Collection<T> rootUnits = new HashSet<>();
408
        Set<SpecimenOrObservationBase> originals = getOriginals();
409

    
410
        if (originals != null && !originals.isEmpty()) {
411
            for (SpecimenOrObservationBase<?> original : originals) {
412
                if (original.isInstanceOf(FieldUnit.class)) {
413
                    if(typeRestriction.isAssignableFrom(FieldUnit.class)) {
414
                        if(logger.isTraceEnabled()) {logger.trace(" [" + original + "] <-- " + this );}
415
                        rootUnits.add(HibernateProxyHelper.deproxy(original, typeRestriction));
416
                    }
417
                    // otherwise this entity can be ignored
418
                } else if(original.isInstanceOf(DerivedUnit.class)){
419
                    DerivedUnit originalDerivedUnit = HibernateProxyHelper.deproxy(original, DerivedUnit.class);
420
                    if(!cycleDetection.contains(originalDerivedUnit)) {
421
                        rootUnits.addAll(originalDerivedUnit.collectRootUnits(typeRestriction, cycleDetection));
422
                    } else {
423
                        // circular graph path found, this should not exist but is not prevented by the cdmlib
424
                        // using this as rootNode
425
                        if(logger.isTraceEnabled()) {logger.trace("CYCLE! "+ originalDerivedUnit + " <-- [" + this + "] <-- ...");}
426
                        rootUnits.add((T)this);
427
                    }
428
                }
429
            }
430
        } else {
431
            // no more originals for this DerivedUnit.
432
            // Potential FieldUnits have been filtered out one call before in the recursion
433
            if(isInstanceOf(typeRestriction)) {
434
                rootUnits.add(HibernateProxyHelper.deproxy(this, typeRestriction));
435
            }
436
        }
437
        return rootUnits;
438
    }
439

    
440
// ******* GETTER / SETTER for preserved specimen only ******************/
441

    
442
	public Set<SpecimenTypeDesignation> getSpecimenTypeDesignations(){
443
		return specimenTypeDesignations;
444
	}
445

    
446
	public PreservationMethod getPreservation(){
447
		return this.preservation;
448
	}
449

    
450
	public void setPreservation(PreservationMethod preservation){
451
		this.preservation = preservation;
452
	}
453

    
454

    
455
	public void setExsiccatum(String exsiccatum) {
456
		this.exsiccatum = isBlank(exsiccatum)? null : exsiccatum;
457
	}
458

    
459
	public String getExsiccatum() {
460
		return exsiccatum;
461
	}
462

    
463
//*********** CLONE **********************************/
464

    
465
	/**
466
	 * Clones <i>this</i> derivedUnit. This is a shortcut that enables to
467
	 * create a new instance that differs only slightly from <i>this</i> specimen
468
	 * by modifying only some of the attributes.<BR>
469
	 * This method overrides the clone method from {@link SpecimenOrObservationBase SpecimenOrObservationBase}.
470
	 *
471
	 * @see SpecimenOrObservationBase#clone()
472
	 * @see java.lang.Object#clone()
473
	 */
474
	@Override
475
	public DerivedUnit clone() {
476
		try{
477
			DerivedUnit result = (DerivedUnit)super.clone();
478
			//collection
479
			result.setCollection(this.collection);
480
			//derivedFrom
481
			result.setDerivedFrom(this.derivedFrom);
482
			//storedUnder
483
			result.setStoredUnder(this.storedUnder);
484
			//preservation
485
			result.setPreservation(this.preservation);
486
			//no changes to: accessionNumber, catalogNumber, collectorsNumber
487
			return result;
488
		} catch (CloneNotSupportedException e) {
489
			logger.warn("Object does not implement cloneable");
490
			e.printStackTrace();
491
			return null;
492
		}
493
	}
494
}
(4-4/14)