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

    
10
package eu.etaxonomy.cdm.model.occurrence;
11

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

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

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

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

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

    
84
	private static final Logger logger = Logger.getLogger(DerivedUnit.class);
85

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

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

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

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

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

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

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

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

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

    
148

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

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

    
158

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

    
166
// ******************** FACTORY METHOD **********************************/
167

    
168

    
169
	public static DerivedUnit NewInstance(SpecimenOrObservationType type) {
170
		if (type.isMedia()){
171
			return MediaSpecimen.NewInstance(type);
172
		}else if (type.equals(SpecimenOrObservationType.DnaSample) || type.isKindOf(SpecimenOrObservationType.DnaSample)){
173
			return DnaSample.NewInstance();
174
		}else{
175
			return new DerivedUnit(type);
176
		}
177
	}
178

    
179
	public static DerivedUnit NewPreservedSpecimenInstance(){
180
		DerivedUnit result = new DerivedUnit(SpecimenOrObservationType.PreservedSpecimen);
181
		return result;
182
	}
183

    
184
//************************** CONSTRUCTOR *********************************/
185

    
186
	//Constructor: For hibernate use only
187
	protected DerivedUnit() {
188
	    super();
189
        initDefaultCacheStrategy();
190
	}
191

    
192

    
193
    /**
194
	 * Constructor
195
	 * @param recordBasis
196
	 */
197
	protected DerivedUnit(SpecimenOrObservationType recordBasis) {
198
		super(recordBasis);
199
        initDefaultCacheStrategy();
200
	}
201

    
202

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

    
217
	/**
218
	 * create new unit derived from an existing gathering event,
219
	 * thereby creating a new empty field unit
220
	 * @param gatheringEvent the gathering event this unit was collected at
221
	 */
222
	protected DerivedUnit(SpecimenOrObservationType recordBasis, GatheringEvent gatheringEvent) {
223
		this(recordBasis, new FieldUnit());
224
		FieldUnit field = (FieldUnit)this.getOriginalUnit();
225
		field.setGatheringEvent(gatheringEvent);
226
	}
227

    
228
    private static final String facadeStrategyClassName = "eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeCacheStrategy";
229
    /**
230
     * Sets the default cache strategy
231
     */
232
	@Override
233
    protected void initDefaultCacheStrategy() {
234
        try {
235
            String facadeClassName = facadeStrategyClassName;
236
            Class<?> facadeClass = Class.forName(facadeClassName);
237
            try {
238
                this.cacheStrategy = (IIdentifiableEntityCacheStrategy)facadeClass.newInstance();
239
            } catch (InstantiationException e) {
240
                e.printStackTrace();
241
            } catch (IllegalAccessException e) {
242
                e.printStackTrace();
243
            }
244
        } catch (ClassNotFoundException e) {
245
            this.cacheStrategy = new IdentifiableEntityDefaultCacheStrategy<>();
246
        }
247
    }
248
//
249
//    private static Class<?> facadeCacheStrategyClass;
250
//
251
//
252
//    @Override
253
//    protected void setFacadeCacheStrategyClass(Class<?> facadeCacheStrategyClass){
254
//        this.facadeCacheStrategyClass = facadeCacheStrategyClass;
255
//    }
256
//
257
//
258
//    @Override
259
//    protected Class<?> getFacadeCacheStrategyClass(){
260
//	    return facadeCacheStrategyClass;
261
//	}
262

    
263

    
264

    
265
// ******************** GETTER / SETTER *************************************/
266

    
267

    
268
	public DerivationEvent getDerivedFrom() {
269
		return derivedFrom;
270
	}
271

    
272
	public void setDerivedFrom(DerivationEvent derivedFrom){
273
		if (getDerivedFrom() != null){
274
			getDerivedFrom().getDerivatives().remove(derivedFrom);
275
		}
276
		this.derivedFrom = derivedFrom;
277
		if (derivedFrom != null){
278
			derivedFrom.addDerivative(this);
279
		}
280
	}
281

    
282
	@Transient
283
	public Set<SpecimenOrObservationBase> getOriginals(){
284
		if(getDerivedFrom() != null){
285
			return getDerivedFrom().getOriginals();
286
		}
287
		return null;
288
	}
289

    
290
	public Collection getCollection(){
291
		return this.collection;
292
	}
293

    
294
	public void setCollection(Collection collection){
295
		this.collection = collection;
296
	}
297

    
298
	public String getCatalogNumber() {
299
		return catalogNumber;
300
	}
301

    
302
	public void setCatalogNumber(String catalogNumber) {
303
		this.catalogNumber = isBlank(catalogNumber)?null:catalogNumber;
304
	}
305

    
306
	public void setBarcode(String barcode) {
307
		this.barcode = isBlank(barcode)? null : barcode;
308
	}
309
	public String getBarcode() {
310
		return barcode;
311
	}
312

    
313
	public void setStoredUnder(TaxonName storedUnder) {
314
		this.storedUnder = storedUnder;
315
	}
316

    
317
	public String getAccessionNumber() {
318
		return accessionNumber;
319
	}
320
	public void setAccessionNumber(String accessionNumber) {
321
		this.accessionNumber = isBlank(accessionNumber)? null : accessionNumber;
322
	}
323

    
324
	/**
325
	 * Original label information may present the exact original text
326
	 * or any other text which fully or partly represents the text available
327
	 * on the specimens label. This information may differ from the information
328
	 * available in the derived unit itself.
329
	 * @return the original label information
330
	 */
331
	//#4218
332
	public String getOriginalLabelInfo() {
333
		return originalLabelInfo;
334
	}
335

    
336
	public void setOriginalLabelInfo(String originalLabelInfo) {
337
		this.originalLabelInfo = originalLabelInfo;
338
	}
339

    
340
	/**
341
	 * Will be removed in future versions as semantics is not clear.
342
	 * For accessing the collecting number use
343
	 * {@link FieldUnit#getFieldNumber()} instead.
344
	 * @return
345
	 */
346
	@Deprecated
347
	public String getCollectorsNumber() {
348
		return collectorsNumber;
349
	}
350

    
351
	/**
352
	 * Will be removed in future versions as semantics is not clear.
353
	 * For editing the collecting number use
354
	 * {@link FieldUnit#getFieldNumber()} instead.
355
	 * @return
356
	 */
357
	@Deprecated
358
	public void setCollectorsNumber(String collectorsNumber) {
359
		this.collectorsNumber = isBlank(collectorsNumber)? null : collectorsNumber;
360
	}
361

    
362
	public TaxonName getStoredUnder() {
363
		return storedUnder;
364
	}
365

    
366
	public void addSpecimenTypeDesignation(SpecimenTypeDesignation specimenTypeDesignation){
367
		if (specimenTypeDesignation.getTypeSpecimen() == this){
368
			return ;
369
		}else if (specimenTypeDesignation.getTypeSpecimen() != null){
370
			specimenTypeDesignation.getTypeSpecimen().removeSpecimenTypeDesignation(specimenTypeDesignation);
371

    
372
		}
373
		specimenTypeDesignations.add(specimenTypeDesignation);
374
		specimenTypeDesignation.setTypeSpecimen(this);
375
	}
376

    
377
	public void removeSpecimenTypeDesignation(SpecimenTypeDesignation specimenTypeDesignation){
378
		if (specimenTypeDesignation == null){
379
			return;
380
		}
381
		if (specimenTypeDesignations.contains(specimenTypeDesignation)){
382
			specimenTypeDesignations.remove(specimenTypeDesignation);
383
			specimenTypeDesignation.setTypeSpecimen(null);
384
		}
385
	}
386

    
387
    public String getMostSignificantIdentifier() {
388
        if (isNotBlank(getAccessionNumber())) {
389
            return getAccessionNumber();
390
        }
391
        else if(isNotBlank(getBarcode())){
392
            return getBarcode();
393
        }
394
        else if(isNotBlank(getCatalogNumber())){
395
            return getCatalogNumber();
396
        }
397
        return null;
398
    }
399

    
400
    /**
401
     * Collects all top most units (FieldUnits, DerivedUnits) in the parent branches of the derivation graph.
402
     * <p>
403
     * <b>NOTE:</b> As this method walks the derivation graph it should only be used in a transactional context or
404
     * it must be assured that the whole graph is initialized.
405
     *
406
     * @param typeRestriction
407
     *  Restricts the returned entities to a specific type of unit.
408
     */
409
    public <T extends SpecimenOrObservationBase> java.util.Collection<T> collectRootUnits(Class<T> typeRestriction) {
410
        return collectRootUnits(typeRestriction, new HashSet<>());
411
    }
412

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

    
415
        if(typeRestriction == null) {
416
            typeRestriction = (Class<T>) SpecimenOrObservationBase.class;
417
        }
418

    
419
        cycleDetection.add(this);
420

    
421
        java.util.Collection<T> rootUnits = new HashSet<>();
422
        Set<SpecimenOrObservationBase> originals = getOriginals();
423

    
424
        if (originals != null && !originals.isEmpty()) {
425
            for (SpecimenOrObservationBase<?> original : originals) {
426
                if (original.isInstanceOf(FieldUnit.class)) {
427
                    if(typeRestriction.isAssignableFrom(FieldUnit.class)) {
428
                        if(logger.isTraceEnabled()) {logger.trace(" [" + original + "] <-- " + this );}
429
                        rootUnits.add(HibernateProxyHelper.deproxy(original, typeRestriction));
430
                    }
431
                    // otherwise this entity can be ignored
432
                } else if(original.isInstanceOf(DerivedUnit.class)){
433
                    DerivedUnit originalDerivedUnit = HibernateProxyHelper.deproxy(original, DerivedUnit.class);
434
                    if(!cycleDetection.contains(originalDerivedUnit)) {
435
                        rootUnits.addAll(originalDerivedUnit.collectRootUnits(typeRestriction, cycleDetection));
436
                    } else {
437
                        // circular graph path found, this should not exist but is not prevented by the cdmlib
438
                        // using this as rootNode
439
                        if(logger.isTraceEnabled()) {logger.trace("CYCLE! "+ originalDerivedUnit + " <-- [" + this + "] <-- ...");}
440
                        rootUnits.add((T)this);
441
                    }
442
                }
443
            }
444
        } else {
445
            // no more originals for this DerivedUnit.
446
            // Potential FieldUnits have been filtered out one call before in the recursion
447
            if(isInstanceOf(typeRestriction)) {
448
                rootUnits.add(HibernateProxyHelper.deproxy(this, typeRestriction));
449
            }
450
        }
451
        return rootUnits;
452
    }
453

    
454
// ******* GETTER / SETTER for preserved specimen only ******************/
455

    
456
	public Set<SpecimenTypeDesignation> getSpecimenTypeDesignations(){
457
		return specimenTypeDesignations;
458
	}
459

    
460
	public PreservationMethod getPreservation(){
461
		return this.preservation;
462
	}
463

    
464
	public void setPreservation(PreservationMethod preservation){
465
		this.preservation = preservation;
466
	}
467

    
468

    
469
	public void setExsiccatum(String exsiccatum) {
470
		this.exsiccatum = isBlank(exsiccatum)? null : exsiccatum;
471
	}
472

    
473
	public String getExsiccatum() {
474
		return exsiccatum;
475
	}
476

    
477
//*********** CLONE **********************************/
478

    
479
	/**
480
	 * Clones <i>this</i> derivedUnit. This is a shortcut that enables to
481
	 * create a new instance that differs only slightly from <i>this</i> specimen
482
	 * by modifying only some of the attributes.<BR>
483
	 * This method overrides the clone method from {@link SpecimenOrObservationBase SpecimenOrObservationBase}.
484
	 *
485
	 * @see SpecimenOrObservationBase#clone()
486
	 * @see java.lang.Object#clone()
487
	 */
488
	@Override
489
	public DerivedUnit clone() {
490
		try{
491
			DerivedUnit result = (DerivedUnit)super.clone();
492
			//collection
493
			result.setCollection(this.collection);
494
			//derivedFrom
495
			result.setDerivedFrom(this.derivedFrom);
496
			//storedUnder
497
			result.setStoredUnder(this.storedUnder);
498
			//preservation
499
			result.setPreservation(this.preservation);
500
			//no changes to: accessionNumber, catalogNumber, collectorsNumber
501
			return result;
502
		} catch (CloneNotSupportedException e) {
503
			logger.warn("Object does not implement cloneable");
504
			e.printStackTrace();
505
			return null;
506
		}
507
	}
508

    
509

    
510

    
511
}
(4-4/14)