toSexagesimalString for Point
[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 @Transient
172 public Set<SpecimenDescription> getSpecimenDescriptions(boolean includeImageGallery) {
173 Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
174 for (DescriptionBase descriptionBase : getDescriptions()){
175 if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
176 if (includeImageGallery || descriptionBase.isImageGallery() == false){
177 specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
178 }
179
180 }
181 }
182 return specimenDescriptions;
183 }
184 /**
185 * Returns the {@link SpecimenDescription specimen descriptions} this specimen is part of.
186 * @see #getDescriptions()
187 * @return
188 */
189 @Transient
190 public Set<SpecimenDescription> getSpecimenDescriptionImageGallery() {
191 Set<SpecimenDescription> specimenDescriptions = new HashSet<SpecimenDescription>();
192 for (DescriptionBase descriptionBase : getDescriptions()){
193 if (descriptionBase.isInstanceOf(SpecimenDescription.class)){
194 if (descriptionBase.isImageGallery() == true){
195 specimenDescriptions.add(descriptionBase.deproxy(descriptionBase, SpecimenDescription.class));
196 }
197 }
198 }
199 return specimenDescriptions;
200 }
201
202 /**
203 * Adds a new description to this specimen or observation
204 * @param description
205 */
206 public void addDescription(DescriptionBase description) {
207 this.descriptions.add(description);
208 if (! description.getDescribedSpecimenOrObservations().contains(this)){
209 description.addDescribedSpecimenOrObservation(this);
210 }
211 // Method method = ReflectionUtils.findMethod(SpecimenDescription.class, "addDescribedSpecimenOrObservation", new Class[] {SpecimenOrObservationBase.class});
212 // ReflectionUtils.makeAccessible(method);
213 // ReflectionUtils.invokeMethod(method, description, new Object[] {this});
214 }
215
216 /**
217 * Removes a specimen from a description (removes a description from this specimen)
218 * @param description
219 */
220 public void removeDescription(DescriptionBase description) {
221 this.descriptions.remove(description);
222 if (description.getDescribedSpecimenOrObservations().contains(this)){
223 description.removeDescribedSpecimenOrObservation(this);
224 }
225 // Method method = ReflectionUtils.findMethod(SpecimenDescription.class, "removeDescribedSpecimenOrObservations", new Class[] {SpecimenOrObservationBase.class});
226 // ReflectionUtils.makeAccessible(method);
227 // ReflectionUtils.invokeMethod(method, description, new Object[] {this});
228 }
229
230 public Set<DerivationEvent> getDerivationEvents() {
231 if(derivationEvents == null) {
232 this.derivationEvents = new HashSet<DerivationEvent>();
233 }
234 return this.derivationEvents;
235 }
236
237 public void addDerivationEvent(DerivationEvent derivationEvent) {
238 if (! this.derivationEvents.contains(derivationEvent)){
239 this.derivationEvents.add(derivationEvent);
240 derivationEvent.addOriginal(this);
241 }
242 }
243
244 public void removeDerivationEvent(DerivationEvent derivationEvent) {
245 this.derivationEvents.remove(derivationEvent);
246 }
247
248 public Set<DeterminationEvent> getDeterminations() {
249 if(determinations == null) {
250 this.determinations = new HashSet<DeterminationEvent>();
251 }
252 return this.determinations;
253 }
254
255 public void addDetermination(DeterminationEvent determination) {
256 // FIXME bidirectional integrity. Use protected Determination setter
257 this.determinations.add(determination);
258 }
259
260 public void removeDetermination(DeterminationEvent determination) {
261 // FIXME bidirectional integrity. Use protected Determination setter
262 this.determinations.remove(determination);
263 }
264
265 public Sex getSex() {
266 return sex;
267 }
268
269 public void setSex(Sex sex) {
270 this.sex = sex;
271 }
272
273 public Stage getLifeStage() {
274 return lifeStage;
275 }
276
277 public void setLifeStage(Stage lifeStage) {
278 this.lifeStage = lifeStage;
279 }
280
281 @Override
282 public String generateTitle(){
283 return getCacheStrategy().getTitleCache(this);
284 }
285
286 public Integer getIndividualCount() {
287 return individualCount;
288 }
289
290 public void setIndividualCount(Integer individualCount) {
291 this.individualCount = individualCount;
292 }
293
294 public Map<Language,LanguageString> getDefinition(){
295 return this.description;
296 }
297
298 public void addDefinition(LanguageString description){
299 this.description.put(description.getLanguage(),description);
300 }
301
302 public void addDefinition(String text, Language language){
303 this.description.put(language, LanguageString.NewInstance(text, language));
304 }
305 public void removeDefinition(Language lang){
306 this.description.remove(lang);
307 }
308
309 /**
310 * for derived units get the single next higher parental/original unit.
311 * If multiple original units exist throw error
312 * @return
313 */
314 @Transient
315 public SpecimenOrObservationBase getOriginalUnit(){
316 logger.warn("GetOriginalUnit not yet implemented");
317 return null;
318 }
319
320
321 //******************** CLONE **********************************************/
322
323 /* (non-Javadoc)
324 * @see eu.etaxonomy.cdm.model.media.IdentifiableMediaEntity#clone()
325 * @see eu.etaxonomy.cdm.model.common.IdentifiableEntity#clone()
326 * @see java.lang.Object#clone()
327 */
328 @Override
329 public Object clone() throws CloneNotSupportedException {
330 SpecimenOrObservationBase result = null;
331 result = (SpecimenOrObservationBase)super.clone();
332
333 //defininion (description, languageString)
334 result.description = new HashMap<Language,LanguageString>();
335 for(LanguageString languageString : this.description.values()) {
336 LanguageString newLanguageString = (LanguageString)languageString.clone();
337 result.addDefinition(newLanguageString);
338 }
339
340 //sex
341 result.setSex(this.sex);
342 //life stage
343 result.setLifeStage(this.lifeStage);
344
345 //Descriptions
346 for(DescriptionBase description : this.descriptions) {
347 result.addDescription((SpecimenDescription)description);
348 }
349
350 //DeterminationEvent FIXME should clone() the determination
351 // as the relationship is OneToMany
352 for(DeterminationEvent determination : this.determinations) {
353 result.addDetermination(determination);
354 }
355
356 //DerivationEvent
357 for(DerivationEvent derivationEvent : this.derivationEvents) {
358 result.addDerivationEvent(derivationEvent);
359 }
360
361 //no changes to: individualCount
362 return result;
363 }
364 }