Added Cascade.MERGE for some, not all relationships where Cascade.SAVE_UPDATE exists...
[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.lang.reflect.Method;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.Map;
16 import java.util.Set;
17
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.OneToMany;
25 import javax.persistence.Transient;
26 import javax.xml.bind.annotation.XmlAccessType;
27 import javax.xml.bind.annotation.XmlAccessorType;
28 import javax.xml.bind.annotation.XmlElement;
29 import javax.xml.bind.annotation.XmlElementWrapper;
30 import javax.xml.bind.annotation.XmlIDREF;
31 import javax.xml.bind.annotation.XmlRootElement;
32 import javax.xml.bind.annotation.XmlSchemaType;
33 import javax.xml.bind.annotation.XmlType;
34 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
35
36 import org.apache.log4j.Logger;
37 import org.hibernate.annotations.Cascade;
38 import org.hibernate.annotations.CascadeType;
39 import org.hibernate.annotations.Index;
40 import org.hibernate.annotations.Table;
41 import org.hibernate.envers.Audited;
42 import org.springframework.util.ReflectionUtils;
43
44 import eu.etaxonomy.cdm.jaxb.MultilanguageTextAdapter;
45 import eu.etaxonomy.cdm.model.common.Language;
46 import eu.etaxonomy.cdm.model.common.LanguageString;
47 import eu.etaxonomy.cdm.model.description.DescriptionBase;
48 import eu.etaxonomy.cdm.model.description.Sex;
49 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
50 import eu.etaxonomy.cdm.model.description.Stage;
51 import eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity;
52 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
53
54 /**
55 * type figures are observations with at least a figure object in media
56 * @author m.doering
57 * @version 1.0
58 * @created 08-Nov-2007 13:06:41
59 */
60 @XmlAccessorType(XmlAccessType.FIELD)
61 @XmlType(name = "SpecimenOrObservationBase", propOrder = {
62 "sex",
63 "individualCount",
64 "lifeStage",
65 "description",
66 "descriptions",
67 "determinations",
68 "derivationEvents"
69 })
70 @XmlRootElement(name = "SpecimenOrObservationBase")
71 @Entity
72 @Audited
73 @Inheritance(strategy=InheritanceType.SINGLE_TABLE)
74 @Table(appliesTo="SpecimenOrObservationBase", indexes = { @Index(name = "specimenOrObservationBaseTitleCacheIndex", columnNames = { "titleCache" }) })
75 public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCacheStrategy> extends IdentifiableMediaEntity<S> {
76
77 private static final Logger logger = Logger.getLogger(SpecimenOrObservationBase.class);
78
79 @XmlElementWrapper(name = "Descriptions")
80 @XmlElement(name = "Description")
81 @ManyToMany(fetch = FetchType.LAZY,mappedBy="describedSpecimenOrObservations",targetEntity=DescriptionBase.class)
82 private Set<SpecimenDescription> descriptions = new HashSet<SpecimenDescription>();
83
84 @XmlElementWrapper(name = "Determinations")
85 @XmlElement(name = "Determination")
86 @OneToMany(mappedBy="identifiedUnit")
87 @Cascade({CascadeType.SAVE_UPDATE, CascadeType.MERGE})
88 private Set<DeterminationEvent> determinations = new HashSet<DeterminationEvent>();
89
90 @XmlElement(name = "Sex")
91 @XmlIDREF
92 @XmlSchemaType(name = "IDREF")
93 @ManyToOne(fetch = FetchType.LAZY)
94 private Sex sex;
95
96 @XmlElement(name = "LifeStage")
97 @XmlIDREF
98 @XmlSchemaType(name = "IDREF")
99 @ManyToOne(fetch = FetchType.LAZY)
100 private Stage lifeStage;
101
102 @XmlElement(name = "IndividualCount")
103 private Integer individualCount;
104
105 // the verbatim description of this occurrence. Free text usable when no atomised data is available.
106 // in conjunction with titleCache which serves as the "citation" string for this object
107 @XmlElement(name = "Description")
108 @XmlJavaTypeAdapter(MultilanguageTextAdapter.class)
109 @OneToMany(fetch = FetchType.LAZY)
110 protected Map<Language,LanguageString> description = new HashMap<Language,LanguageString>();
111
112 // events that created derivedUnits from this unit
113 @XmlElementWrapper(name = "DerivationEvents")
114 @XmlElement(name = "DerivationEvent")
115 @XmlIDREF
116 @XmlSchemaType(name = "IDREF")
117 @ManyToMany(fetch=FetchType.LAZY)
118 protected Set<DerivationEvent> derivationEvents = new HashSet<DerivationEvent>();
119
120 /**
121 * Constructor
122 */
123 protected SpecimenOrObservationBase(){
124 super();
125 }
126
127 public Set<SpecimenDescription> getDescriptions() {
128 return this.descriptions;
129 }
130
131 public void addDescription(SpecimenDescription description) {
132 this.descriptions.add(description);
133 Method method = ReflectionUtils.findMethod(SpecimenDescription.class, "addDescribedSpecimenOrObservations", new Class[] {SpecimenOrObservationBase.class});
134 ReflectionUtils.makeAccessible(method);
135 ReflectionUtils.invokeMethod(method, description, new Object[] {this});
136 }
137
138 public void removeDescription(SpecimenDescription description) {
139 this.descriptions.remove(description);
140 Method method = ReflectionUtils.findMethod(SpecimenDescription.class, "removeDescribedSpecimenOrObservations", new Class[] {SpecimenOrObservationBase.class});
141 ReflectionUtils.makeAccessible(method);
142 ReflectionUtils.invokeMethod(method, description, new Object[] {this});
143 }
144
145 public Set<DerivationEvent> getDerivationEvents() {
146 return this.derivationEvents;
147 }
148
149 public void addDerivationEvent(DerivationEvent derivationEvent) {
150 if (! this.derivationEvents.contains(derivationEvent)){
151 this.derivationEvents.add(derivationEvent);
152 derivationEvent.addOriginal(this);
153 }
154 }
155
156 public void removeDerivationEvent(DerivationEvent derivationEvent) {
157 this.derivationEvents.remove(derivationEvent);
158 }
159
160 public Set<DeterminationEvent> getDeterminations() {
161 return this.determinations;
162 }
163
164 public void addDetermination(DeterminationEvent determination) {
165 // FIXME bidirectional integrity. Use protected Determination setter
166 this.determinations.add(determination);
167 }
168
169 public void removeDetermination(DeterminationEvent determination) {
170 // FIXME bidirectional integrity. Use protected Determination setter
171 this.determinations.remove(determination);
172 }
173
174 public Sex getSex() {
175 return sex;
176 }
177
178 public void setSex(Sex sex) {
179 this.sex = sex;
180 }
181
182 public Stage getLifeStage() {
183 return lifeStage;
184 }
185
186 public void setLifeStage(Stage lifeStage) {
187 this.lifeStage = lifeStage;
188 }
189
190 @Override
191 public String generateTitle(){
192 return "";
193 }
194
195 public Integer getIndividualCount() {
196 return individualCount;
197 }
198
199 public void setIndividualCount(Integer individualCount) {
200 this.individualCount = individualCount;
201 }
202
203 public Map<Language,LanguageString> getDefinition(){
204 return this.description;
205 }
206
207 public void addDefinition(LanguageString description){
208 this.description.put(description.getLanguage(),description);
209 }
210
211 public void addDefinition(String text, Language language){
212 this.description.put(language, LanguageString.NewInstance(text, language));
213 }
214 public void removeDefinition(Language lang){
215 this.description.remove(lang);
216 }
217
218 /**
219 * for derived units get the single next higher parental/original unit.
220 * If multiple original units exist throw error
221 * @return
222 */
223 @Transient
224 public SpecimenOrObservationBase getOriginalUnit(){
225 return null;
226 }
227
228 @Transient
229 public abstract GatheringEvent getGatheringEvent();
230
231
232 //******************** CLONE **********************************************/
233
234 /* (non-Javadoc)
235 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
236 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
237 * @see java.lang.Object#clone()
238 */
239 @Override
240 public Object clone() throws CloneNotSupportedException {
241 SpecimenOrObservationBase result = null;
242 result = (SpecimenOrObservationBase)super.clone();
243
244 //defininion (description, languageString)
245 result.description = new HashMap<Language,LanguageString>();
246 for(LanguageString languageString : this.description.values()) {
247 LanguageString newLanguageString = (LanguageString)languageString.clone();
248 result.addDefinition(newLanguageString);
249 }
250
251 //sex
252 result.setSex(this.sex);
253 //life stage
254 result.setLifeStage(this.lifeStage);
255
256 //Descriptions
257 for(DescriptionBase description : this.descriptions) {
258 result.addDescription((SpecimenDescription)description);
259 }
260
261 //DeterminationEvent FIXME should clone() the determination
262 // as the relationship is OneToMany
263 for(DeterminationEvent determination : this.determinations) {
264 result.addDetermination(determination);
265 }
266
267 //DerivationEvent
268 for(DerivationEvent derivationEvent : this.derivationEvents) {
269 result.addDerivationEvent(derivationEvent);
270 }
271
272 //no changes to: individualCount
273 return result;
274 }
275 }