merging /branches/cdmlib/SPRINT-Chichorieae1/ to trunk
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / model / occurrence / SpecimenOrObservationBase.java
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.Entity;
18 import javax.persistence.FetchType;
19 import javax.persistence.Inheritance;
20 import javax.persistence.InheritanceType;
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.Min;
26 import javax.validation.constraints.NotNull;
27 import javax.xml.bind.annotation.XmlAccessType;
28 import javax.xml.bind.annotation.XmlAccessorType;
29 import javax.xml.bind.annotation.XmlElement;
30 import javax.xml.bind.annotation.XmlElementWrapper;
31 import javax.xml.bind.annotation.XmlIDREF;
32 import javax.xml.bind.annotation.XmlRootElement;
33 import javax.xml.bind.annotation.XmlSchemaType;
34 import javax.xml.bind.annotation.XmlType;
35 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
36
37 import org.apache.log4j.Logger;
38 import org.hibernate.annotations.Cascade;
39 import org.hibernate.annotations.CascadeType;
40 import org.hibernate.annotations.Index;
41 import org.hibernate.annotations.Table;
42 import org.hibernate.envers.Audited;
43 import org.hibernate.search.annotations.Field;
44 import org.hibernate.search.annotations.IndexedEmbedded;
45
46 import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
47 import eu.etaxonomy.cdm.model.common.Language;
48 import eu.etaxonomy.cdm.model.common.LanguageString;
49 import eu.etaxonomy.cdm.model.description.DescriptionBase;
50 import eu.etaxonomy.cdm.model.description.Sex;
51 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
52 import eu.etaxonomy.cdm.model.description.Stage;
53 import eu.etaxonomy.cdm.model.description.TaxonDescription;
54 import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
55 import eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity;
56 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
57
58 /**
59 * type figures are observations with at least a figure object in media
60 * @author m.doering
61 * @version 1.0
62 * @created 08-Nov-2007 13:06:41
63 */
64 @XmlAccessorType(XmlAccessType.FIELD)
65 @XmlType(name = "SpecimenOrObservationBase", propOrder = {
66 "sex",
67 "individualCount",
68 "lifeStage",
69 "description",
70 "descriptions",
71 "determinations",
72 "derivationEvents"
73 })
74 @XmlRootElement(name = "SpecimenOrObservationBase")
75 @Entity
76 @Audited
77 @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
78 @Table(appliesTo="SpecimenOrObservationBase", indexes = { @Index(name = "specimenOrObservationBaseTitleCacheIndex", columnNames = { "titleCache" }) })
79 public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCacheStrategy> extends IdentifiableMediaEntity<S> {
80
81 private static final Logger logger = Logger.getLogger(SpecimenOrObservationBase.class);
82
83 @XmlElementWrapper(name = "Descriptions")
84 @XmlElement(name = "Description")
85 @ManyToMany(fetch = FetchType.LAZY,mappedBy="describedSpecimenOrObservations",targetEntity=DescriptionBase.class)
86 @Cascade(CascadeType.SAVE_UPDATE)
87 @NotNull
88 private Set<DescriptionBase> descriptions = new HashSet<DescriptionBase>();
89
90 @XmlElementWrapper(name = "Determinations")
91 @XmlElement(name = "Determination")
92 @OneToMany(mappedBy="identifiedUnit")
93 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
94 @IndexedEmbedded(depth = 2)
95 @NotNull
96 private Set<DeterminationEvent> determinations = new HashSet<DeterminationEvent>();
97
98 @XmlElement(name = "Sex")
99 @XmlIDREF
100 @XmlSchemaType(name = "IDREF")
101 @ManyToOne(fetch = FetchType.LAZY)
102 private Sex sex;
103
104 @XmlElement(name = "LifeStage")
105 @XmlIDREF
106 @XmlSchemaType(name = "IDREF")
107 @ManyToOne(fetch = FetchType.LAZY)
108 private Stage lifeStage;
109
110 @XmlElement(name = "IndividualCount")
111 @Field(index=org.hibernate.search.annotations.Index.UN_TOKENIZED)
112 @Min(0)
113 private Integer individualCount;
114
115 // the verbatim description of this occurrence. Free text usable when no atomised data is available.
116 // in conjunction with titleCache which serves as the "citation" string for this object
117 @XmlElement(name = "Description")
118 @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
119 @OneToMany(fetch = FetchType.LAZY)
120 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
121 @IndexedEmbedded
122 @NotNull
123 protected Map<Language,LanguageString> description = new HashMap<Language,LanguageString>();
124
125 // events that created derivedUnits from this unit
126 @XmlElementWrapper(name = "DerivationEvents")
127 @XmlElement(name = "DerivationEvent")
128 @XmlIDREF
129 @XmlSchemaType(name = "IDREF")
130 @ManyToMany(fetch=FetchType.LAZY)
131 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.DELETE, CascadeType.DELETE_ORPHAN})
132 @NotNull
133 protected Set<DerivationEvent> derivationEvents = new HashSet<DerivationEvent>();
134
135 /**
136 * Constructor
137 */
138 protected SpecimenOrObservationBase(){
139 super();
140 }
141
142 /**
143 * The descriptions this specimen or observation is part of.<BR>
144 * A specimen can not only have it's own {@link SpecimenDescription specimen description }
145 * but can also be part of a {@link TaxonDescription taxon description} or a
146 * {@link TaxonNameDescription taxon name description}.<BR>
147 * @see #getSpecimenDescriptions()
148 * @return
149 */
150 public Set<DescriptionBase> getDescriptions() {
151 if(descriptions == null) {
152 this.descriptions = new HashSet<DescriptionBase>();
153 }
154 return this.descriptions;
155 }
156
157 /**
158 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
159 * @see #getDescriptions()
160 * @return
161 */
162 public Set<SpecimenDescription> getSpecimenDescriptions() {
163 return getSpecimenDescriptions(true);
164 }
165
166 /**
167 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
168 * @see #getDescriptions()
169 * @return
170 */
171 public Set<SpecimenDescription> getSpecimenDescriptions(boolean includeImageGallery) {
172 Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
173 for (DescriptionBase descriptionBase : getDescriptions()){
174 if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
175 if (includeImageGallery || descriptionBase.isImageGallery() == false){
176 specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
177 }
178
179 }
180 }
181 return specimenDescriptions;
182 }
183
184 /**
185 * Adds a new description to this specimen or observation
186 * @param description
187 */
188 public void addDescription(DescriptionBase description) {
189 this.descriptions.add(description);
190 if (! description.getDescribedSpecimenOrObservations().contains(this)){
191 description.addDescribedSpecimenOrObservation(this);
192 }
193 // Method method = ReflectionUtils.findMethod(SpecimenDescription.class, "addDescribedSpecimenOrObservation", new Class[] {SpecimenOrObservationBase.class});
194 // ReflectionUtils.makeAccessible(method);
195 // ReflectionUtils.invokeMethod(method, description, new Object[] {this});
196 }
197
198 /**
199 * Removes a specimen from a description (removes a description from this specimen)
200 * @param description
201 */
202 public void removeDescription(DescriptionBase description) {
203 this.descriptions.remove(description);
204 if (description.getDescribedSpecimenOrObservations().contains(this)){
205 description.removeDescribedSpecimenOrObservation(this);
206 }
207 // Method method = ReflectionUtils.findMethod(SpecimenDescription.class, "removeDescribedSpecimenOrObservations", new Class[] {SpecimenOrObservationBase.class});
208 // ReflectionUtils.makeAccessible(method);
209 // ReflectionUtils.invokeMethod(method, description, new Object[] {this});
210 }
211
212 public Set<DerivationEvent> getDerivationEvents() {
213 if(derivationEvents == null) {
214 this.derivationEvents = new HashSet<DerivationEvent>();
215 }
216 return this.derivationEvents;
217 }
218
219 public void addDerivationEvent(DerivationEvent derivationEvent) {
220 if (! this.derivationEvents.contains(derivationEvent)){
221 this.derivationEvents.add(derivationEvent);
222 derivationEvent.addOriginal(this);
223 }
224 }
225
226 public void removeDerivationEvent(DerivationEvent derivationEvent) {
227 this.derivationEvents.remove(derivationEvent);
228 }
229
230 public Set<DeterminationEvent> getDeterminations() {
231 if(determinations == null) {
232 this.determinations = new HashSet<DeterminationEvent>();
233 }
234 return this.determinations;
235 }
236
237 public void addDetermination(DeterminationEvent determination) {
238 // FIXME bidirectional integrity. Use protected Determination setter
239 this.determinations.add(determination);
240 }
241
242 public void removeDetermination(DeterminationEvent determination) {
243 // FIXME bidirectional integrity. Use protected Determination setter
244 this.determinations.remove(determination);
245 }
246
247 public Sex getSex() {
248 return sex;
249 }
250
251 public void setSex(Sex sex) {
252 this.sex = sex;
253 }
254
255 public Stage getLifeStage() {
256 return lifeStage;
257 }
258
259 public void setLifeStage(Stage lifeStage) {
260 this.lifeStage = lifeStage;
261 }
262
263 @Override
264 public String generateTitle(){
265 return getCacheStrategy().getTitleCache(this);
266 }
267
268 public Integer getIndividualCount() {
269 return individualCount;
270 }
271
272 public void setIndividualCount(Integer individualCount) {
273 this.individualCount = individualCount;
274 }
275
276 public Map<Language,LanguageString> getDefinition(){
277 return this.description;
278 }
279
280 public void addDefinition(LanguageString description){
281 this.description.put(description.getLanguage(),description);
282 }
283
284 public void addDefinition(String text, Language language){
285 this.description.put(language, LanguageString.NewInstance(text, language));
286 }
287 public void removeDefinition(Language lang){
288 this.description.remove(lang);
289 }
290
291 /**
292 * for derived units get the single next higher parental/original unit.
293 * If multiple original units exist throw error
294 * @return
295 */
296 @Transient
297 public SpecimenOrObservationBase getOriginalUnit(){
298 logger.warn("GetOriginalUnit not yet implemented");
299 return null;
300 }
301
302 @Transient
303 public abstract GatheringEvent getGatheringEvent();
304
305
306 //******************** CLONE **********************************************/
307
308 /* (non-Javadoc)
309 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
310 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
311 * @see java.lang.Object#clone()
312 */
313 @Override
314 public Object clone() throws CloneNotSupportedException {
315 SpecimenOrObservationBase result = null;
316 result = (SpecimenOrObservationBase)super.clone();
317
318 //defininion (description, languageString)
319 result.description = new HashMap<Language,LanguageString>();
320 for(LanguageString languageString : this.description.values()) {
321 LanguageString newLanguageString = (LanguageString)languageString.clone();
322 result.addDefinition(newLanguageString);
323 }
324
325 //sex
326 result.setSex(this.sex);
327 //life stage
328 result.setLifeStage(this.lifeStage);
329
330 //Descriptions
331 for(DescriptionBase description : this.descriptions) {
332 result.addDescription((SpecimenDescription)description);
333 }
334
335 //DeterminationEvent FIXME should clone() the determination
336 // as the relationship is OneToMany
337 for(DeterminationEvent determination : this.determinations) {
338 result.addDetermination(determination);
339 }
340
341 //DerivationEvent
342 for(DerivationEvent derivationEvent : this.derivationEvents) {
343 result.addDerivationEvent(derivationEvent);
344 }
345
346 //no changes to: individualCount
347 return result;
348 }
349 }