Project

General

Profile

Download (15.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.occurrence;
11

    
12
import java.util.HashMap;
13
import java.util.HashSet;
14
import java.util.Map;
15
import java.util.Set;
16

    
17
import javax.persistence.Column;
18
import javax.persistence.Entity;
19
import javax.persistence.FetchType;
20
import javax.persistence.Inheritance;
21
import javax.persistence.InheritanceType;
22
import javax.persistence.ManyToMany;
23
import javax.persistence.ManyToOne;
24
import javax.persistence.MapKeyJoinColumn;
25
import javax.persistence.OneToMany;
26
import javax.persistence.Transient;
27
import javax.validation.constraints.Min;
28
import javax.validation.constraints.NotNull;
29
import javax.xml.bind.annotation.XmlAccessType;
30
import javax.xml.bind.annotation.XmlAccessorType;
31
import javax.xml.bind.annotation.XmlAttribute;
32
import javax.xml.bind.annotation.XmlElement;
33
import javax.xml.bind.annotation.XmlElementWrapper;
34
import javax.xml.bind.annotation.XmlIDREF;
35
import javax.xml.bind.annotation.XmlRootElement;
36
import javax.xml.bind.annotation.XmlSchemaType;
37
import javax.xml.bind.annotation.XmlType;
38
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
39

    
40
import org.apache.log4j.Logger;
41
import org.hibernate.annotations.Cascade;
42
import org.hibernate.annotations.CascadeType;
43
import org.hibernate.annotations.Index;
44
import org.hibernate.annotations.Table;
45
import org.hibernate.annotations.Type;
46
import org.hibernate.envers.Audited;
47
import org.hibernate.search.annotations.Analyze;
48
import org.hibernate.search.annotations.ContainedIn;
49
import org.hibernate.search.annotations.Field;
50
import org.hibernate.search.annotations.IndexedEmbedded;
51
import org.hibernate.search.annotations.NumericField;
52

    
53
import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
54
import eu.etaxonomy.cdm.model.common.DefinedTerm;
55
import eu.etaxonomy.cdm.model.common.IMultiLanguageTextHolder;
56
import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
57
import eu.etaxonomy.cdm.model.common.Language;
58
import eu.etaxonomy.cdm.model.common.LanguageString;
59
import eu.etaxonomy.cdm.model.common.MultilanguageText;
60
import eu.etaxonomy.cdm.model.common.TermType;
61
import eu.etaxonomy.cdm.model.description.DescriptionBase;
62
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
63
import eu.etaxonomy.cdm.model.description.TaxonDescription;
64
import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
65
import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
66

    
67
/**
68
 * type figures are observations with at least a figure object in media
69
 * @author m.doering
70
 * @created 08-Nov-2007 13:06:41
71
 */
72
@XmlAccessorType(XmlAccessType.FIELD)
73
@XmlType(name = "SpecimenOrObservationBase", propOrder = {
74
	"recordBasis",
75
	"publish",
76
	"sex",
77
	"lifeStage",
78
    "kindOfUnit",
79
	"individualCount",
80
    "definition",
81
    "descriptions",
82
    "determinations",
83
    "derivationEvents"
84
})
85
@XmlRootElement(name = "SpecimenOrObservationBase")
86
@Entity
87
@Audited
88
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
89
@Table(appliesTo="SpecimenOrObservationBase", indexes = { @Index(name = "specimenOrObservationBaseTitleCacheIndex", columnNames = { "titleCache" }) })
90
public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCacheStrategy> extends IdentifiableEntity<S> implements IMultiLanguageTextHolder{
91
	private static final long serialVersionUID = 6932680139334408031L;
92
	private static final Logger logger = Logger.getLogger(SpecimenOrObservationBase.class);
93

    
94
	/**
95
	 * An indication of what the unit record describes.
96
	 * 
97
	 * NOTE: The name of the attribute was chosen against the common naming conventions of the CDM
98
	 * as it is well known in common standards like ABCD and DarwinCore. According to CDM naming
99
	 * conventions it would specimenOrObservationType. 
100
	 * 
101
	 * @see ABCD: DataSets/DataSet/Units/Unit/RecordBasis
102
	 * @see Darwin Core: http://wiki.tdwg.org/twiki/bin/view/DarwinCore/BasisOfRecord 
103
	 */
104
	@XmlAttribute(name ="RecordBasis")
105
	@Column(name="recordBasis")
106
	@NotNull
107
    @Type(type = "eu.etaxonomy.cdm.hibernate.EnumUserType",
108
    	parameters = {@org.hibernate.annotations.Parameter(name  = "enumClass", value = "eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType")}
109
    )
110
	private SpecimenOrObservationType recordBasis;
111

    
112
	
113
	@XmlElementWrapper(name = "Descriptions")
114
	@XmlElement(name = "Description")
115
	@OneToMany(mappedBy="describedSpecimenOrObservation", fetch = FetchType.LAZY)
116
	@Cascade(CascadeType.SAVE_UPDATE)
117
    @ContainedIn
118
    @NotNull
119
	private Set<DescriptionBase> descriptions = new HashSet<DescriptionBase>();
120
	
121
	
122
	@XmlElementWrapper(name = "Determinations")
123
	@XmlElement(name = "Determination")
124
	@OneToMany(mappedBy="identifiedUnit", orphanRemoval=true)
125
	@Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE})
126
	@IndexedEmbedded(depth = 2)
127
	@NotNull
128
	private Set<DeterminationEvent> determinations = new HashSet<DeterminationEvent>();
129

    
130
	@XmlElement(name = "Sex")
131
	@XmlIDREF
132
	@XmlSchemaType(name = "IDREF")
133
	@ManyToOne(fetch = FetchType.LAZY)
134
	private DefinedTerm sex;
135

    
136
	@XmlElement(name = "LifeStage")
137
	@XmlIDREF
138
	@XmlSchemaType(name = "IDREF")
139
	@ManyToOne(fetch = FetchType.LAZY)
140
	private DefinedTerm lifeStage;
141
	
142
	/**
143
	 * Part(s) of organism or class of materials represented by this unit.
144
	 * Example: fruits, seeds, tissue, gDNA, leaves
145
	 * 
146
	 * @see ABCD: DataSets/DataSet/Units/Unit/KindOfUnit
147
	 * @see TermType#KindOfUnit
148
	 */
149
	@XmlElement(name = "KindOfUnit")
150
    @XmlIDREF
151
    @XmlSchemaType(name = "IDREF")
152
    @ManyToOne(fetch = FetchType.LAZY)
153
//    @IndexedEmbedded(depth=1)
154
	private DefinedTerm kindOfUnit;
155

    
156
	@XmlElement(name = "IndividualCount")
157
	@Field(analyze = Analyze.NO)
158
	@NumericField
159
	@Min(0)
160
	private Integer individualCount;
161

    
162
	// the verbatim description of this occurrence. Free text usable when no atomised data is available.
163
	// in conjunction with titleCache which serves as the "citation" string for this object
164
	@XmlElement(name = "Description")
165
	@XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
166
	@OneToMany(fetch = FetchType.LAZY, orphanRemoval=true)
167
	@MapKeyJoinColumn(name="definition_mapkey_id")
168
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
169
	@IndexedEmbedded
170
	@NotNull
171
	protected Map<Language,LanguageString> definition = new HashMap<Language,LanguageString>();
172

    
173
	// events that created derivedUnits from this unit
174
	@XmlElementWrapper(name = "DerivationEvents")
175
	@XmlElement(name = "DerivationEvent")
176
    @XmlIDREF
177
    @XmlSchemaType(name = "IDREF")
178
    @ManyToMany(fetch=FetchType.LAZY)
179
    @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE})
180
    @NotNull
181
	protected Set<DerivationEvent> derivationEvents = new HashSet<DerivationEvent>();
182
    
183
    @XmlAttribute(name = "publish")
184
    private boolean publish = true;
185

    
186

    
187
//********************************** CONSTRUCTOR *********************************/	
188

    
189
  	//for hibernate use only
190
  	@Deprecated
191
  	protected SpecimenOrObservationBase(){super();}
192

    
193
	protected SpecimenOrObservationBase(SpecimenOrObservationType recordBasis) {
194
		super();
195
		if (recordBasis == null){ throw new IllegalArgumentException("RecordBasis must not be null");}
196
		this.recordBasis = recordBasis;
197
	}
198
	
199
//************************* GETTER / SETTER ***********************/	
200

    
201
	/**
202
	 * @see #recordBasis
203
	 * @return
204
	 */
205
	public SpecimenOrObservationType getRecordBasis() {
206
		return recordBasis;
207
	}
208

    
209
	/**
210
     * @see #recordBasis
211
	 * @param recordBasis
212
	 */
213
	public void setRecordBasis(SpecimenOrObservationType recordBasis) {
214
		this.recordBasis = recordBasis;
215
	}
216
	
217

    
218
	/**
219
	 * Returns the boolean value indicating if this specimen or observation should be withheld 
220
	 * (<code>publish=false</code>) or not (<code>publish=true</code>) during any publication
221
	 * process to the general public.
222
	 * This publish flag implementation is preliminary and may be replaced by a more general 
223
	 * implementation of READ rights in future.<BR>
224
	 * The default value is <code>true</code>.
225
	 */
226
	public boolean isPublish() {
227
		return publish;
228
	}
229

    
230
	/**
231
	 * @see #isPublish()
232
	 * @param publish
233
	 */
234
	public void setPublish(boolean publish) {
235
		this.publish = publish;
236
	}
237
	
238
	/**
239
	 * The descriptions this specimen or observation is part of.<BR>
240
	 * A specimen can not only have it's own {@link SpecimenDescription specimen description }
241
	 * but can also be part of a {@link TaxonDescription taxon description} or a
242
	 * {@link TaxonNameDescription taxon name description}.<BR>
243
	 * @see #getSpecimenDescriptions()
244
	 * @return
245
	 */
246
	public Set<DescriptionBase> getDescriptions() {
247
		if(descriptions == null) {
248
			this.descriptions = new HashSet<DescriptionBase>();
249
		}
250
		return this.descriptions;
251
	}
252

    
253
	/**
254
	 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
255
	 * @see #getDescriptions()
256
	 * @return
257
	 */
258
	@Transient
259
	public Set<SpecimenDescription> getSpecimenDescriptions() {
260
		return getSpecimenDescriptions(true);
261
	}
262

    
263
	/**
264
	 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
265
	 * @see #getDescriptions()
266
	 * @return
267
	 */
268
	@Transient
269
	public Set<SpecimenDescription> getSpecimenDescriptions(boolean includeImageGallery) {
270
		Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
271
		for (DescriptionBase descriptionBase : getDescriptions()){
272
			if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
273
				if (includeImageGallery || descriptionBase.isImageGallery() == false){
274
					specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
275
				}
276

    
277
			}
278
		}
279
		return specimenDescriptions;
280
	}
281
	/**
282
	 * Returns the {@link SpecimenDescription specimen descriptions} which act as an image gallery
283
	 * and which this specimen is part of.
284
	 * @see #getDescriptions()
285
	 * @return
286
	 */
287
	@Transient
288
	public Set<SpecimenDescription> getSpecimenDescriptionImageGallery() {
289
		Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
290
		for (DescriptionBase descriptionBase : getDescriptions()){
291
			if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
292
				if (descriptionBase.isImageGallery() == true){
293
					specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
294
				}
295
			}
296
		}
297
		return specimenDescriptions;
298
	}
299

    
300
	
301
	/**
302
	 * Adds a new description to this specimen or observation
303
	 * @param description
304
	 */
305
	public void addDescription(DescriptionBase description) {
306
		if (description.getDescribedSpecimenOrObservation() != null){
307
			description.getDescribedSpecimenOrObservation().removeDescription(description);
308
		}
309
		descriptions.add(description);
310
		description.setDescribedSpecimenOrObservation(this);
311
	}
312

    
313
	/**
314
	 * Removes a specimen from a description (removes a description from this specimen)
315
	 * @param description
316
	 */
317
	public void removeDescription(DescriptionBase description) {
318
        boolean existed = descriptions.remove(description);
319
        if (existed){
320
        	description.setDescribedSpecimenOrObservation(null);
321
        }
322
	}
323

    
324
	public Set<DerivationEvent> getDerivationEvents() {
325
		if(derivationEvents == null) {
326
			this.derivationEvents = new HashSet<DerivationEvent>();
327
		}
328
		return this.derivationEvents;
329
	}
330

    
331
	public void addDerivationEvent(DerivationEvent derivationEvent) {
332
		if (! this.derivationEvents.contains(derivationEvent)){
333
			this.derivationEvents.add(derivationEvent);
334
			derivationEvent.addOriginal(this);
335
		}
336
	}
337

    
338
	public void removeDerivationEvent(DerivationEvent derivationEvent) {
339
		this.derivationEvents.remove(derivationEvent);
340
	}
341

    
342
	public Set<DeterminationEvent> getDeterminations() {
343
		if(determinations == null) {
344
			this.determinations = new HashSet<DeterminationEvent>();
345
		}
346
		return this.determinations;
347
	}
348

    
349
	public void addDetermination(DeterminationEvent determination) {
350
		// FIXME bidirectional integrity. Use protected Determination setter
351
		this.determinations.add(determination);
352
	}
353

    
354
	public void removeDetermination(DeterminationEvent determination) {
355
		// FIXME bidirectional integrity. Use protected Determination setter
356
		this.determinations.remove(determination);
357
	}
358

    
359
	public DefinedTerm getSex() {
360
		return sex;
361
	}
362

    
363
	public void setSex(DefinedTerm sex) {
364
		this.sex = sex;
365
	}
366

    
367
	public DefinedTerm getLifeStage() {
368
		return lifeStage;
369
	}
370

    
371
	public void setLifeStage(DefinedTerm lifeStage) {
372
		this.lifeStage = lifeStage;
373
	}
374
	
375

    
376
	/**
377
	 * @see #kindOfUnit
378
	 * @return
379
	 */
380
	public DefinedTerm getKindOfUnit() {
381
		return kindOfUnit;
382
	}
383

    
384
	/**
385
	 * @see #kindOfUnit
386
	 * @param kindOfUnit
387
	 */
388
	public void setKindOfUnit(DefinedTerm kindOfUnit) {
389
		this.kindOfUnit = kindOfUnit;
390
	}
391

    
392
	public Integer getIndividualCount() {
393
		return individualCount;
394
	}
395

    
396
	public void setIndividualCount(Integer individualCount) {
397
		this.individualCount = individualCount;
398
	}
399

    
400
	public Map<Language,LanguageString> getDefinition(){
401
		return this.definition;
402
	}
403

    
404
	/**
405
	 * adds the {@link LanguageString description} to the {@link MultilanguageText multilanguage text}
406
	 * used to define <i>this</i> specimen or observation.
407
	 *
408
	 * @param description	the languageString in with the title string and the given language
409
	 *
410
	 * @see    	   		#getDefinition()
411
	 * @see    	   		#putDefinition(Language, String)
412
	 */
413
	public void putDefinition(LanguageString description){
414
		this.definition.put(description.getLanguage(),description);
415
	}
416

    
417
	/**
418
	 * Creates a {@link LanguageString language string} based on the given text string
419
	 * and the given {@link Language language} and adds it to the {@link MultilanguageText multilanguage text}
420
	 * used to define <i>this</i> specimen or observation.
421
	 *
422
	 * @param language	the language in which the title string is formulated
423
	 * @param text		the definition in a particular language
424
	 *
425
	 * @see    	   		#getDefinition()
426
	 * @see    	   		#putDefinition(LanguageString)
427
	 */
428
	public void putDefinition(Language language, String text){
429
		this.definition.put(language, LanguageString.NewInstance(text, language));
430
	}
431

    
432

    
433
	public void removeDefinition(Language lang){
434
		this.definition.remove(lang);
435
	}
436

    
437
	/**
438
	 * for derived units get the single next higher parental/original unit.
439
	 * If multiple original units exist throw error
440
	 * @return
441
	 */
442
	@Transient
443
	public SpecimenOrObservationBase getOriginalUnit(){
444
		logger.warn("GetOriginalUnit not yet implemented");
445
		return null;
446
	}
447

    
448

    
449
//******************** CLONE **********************************************/
450

    
451
	/* (non-Javadoc)
452
	 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
453
	 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
454
	 * @see java.lang.Object#clone()
455
	 */
456
	@Override
457
	public Object clone() throws CloneNotSupportedException {
458
		SpecimenOrObservationBase result = null;
459
		result = (SpecimenOrObservationBase)super.clone();
460

    
461
		//defininion (description, languageString)
462
		result.definition = new HashMap<Language,LanguageString>();
463
		for(LanguageString languageString : this.definition.values()) {
464
			LanguageString newLanguageString = (LanguageString)languageString.clone();
465
			result.putDefinition(newLanguageString);
466
		}
467

    
468
		//sex
469
		result.setSex(this.sex);
470
		//life stage
471
		result.setLifeStage(this.lifeStage);
472

    
473
		//Descriptions
474
		for(DescriptionBase description : this.descriptions) {
475
			result.addDescription(description);
476
		}
477

    
478
		//DeterminationEvent FIXME should clone() the determination
479
		// as the relationship is OneToMany
480
		for(DeterminationEvent determination : this.determinations) {
481
			result.addDetermination(determination);
482
		}
483

    
484
		//DerivationEvent
485
		for(DerivationEvent derivationEvent : this.derivationEvents) {
486
			result.addDerivationEvent(derivationEvent);
487
		}
488

    
489
		//no changes to: individualCount
490
		return result;
491
	}
492

    
493

    
494
}
(11-11/14)