add country to gathering event
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / facade / DerivedUnitFacade.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.api.facade;
11
12 import java.beans.PropertyChangeEvent;
13 import java.beans.PropertyChangeListener;
14 import java.beans.PropertyChangeSupport;
15 import java.text.ParseException;
16 import java.util.ArrayList;
17 import java.util.HashMap;
18 import java.util.HashSet;
19 import java.util.List;
20 import java.util.Map;
21 import java.util.Set;
22
23 import javax.mail.MethodNotSupportedException; //FIMXE use other execption class
24 import javax.persistence.Transient;
25
26
27 import org.apache.log4j.Logger;
28
29 import eu.etaxonomy.cdm.api.service.IOccurrenceService;
30 import eu.etaxonomy.cdm.model.agent.AgentBase;
31 import eu.etaxonomy.cdm.model.common.Annotation;
32 import eu.etaxonomy.cdm.model.common.CdmBase;
33 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
34 import eu.etaxonomy.cdm.model.common.Language;
35 import eu.etaxonomy.cdm.model.common.LanguageString;
36 import eu.etaxonomy.cdm.model.common.TimePeriod;
37 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
38 import eu.etaxonomy.cdm.model.description.Feature;
39 import eu.etaxonomy.cdm.model.description.Sex;
40 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
41 import eu.etaxonomy.cdm.model.description.Stage;
42 import eu.etaxonomy.cdm.model.description.TextData;
43 import eu.etaxonomy.cdm.model.location.NamedArea;
44 import eu.etaxonomy.cdm.model.location.Point;
45 import eu.etaxonomy.cdm.model.location.ReferenceSystem;
46 import eu.etaxonomy.cdm.model.media.Media;
47 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
48 import eu.etaxonomy.cdm.model.occurrence.Collection;
49 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
50 import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
51 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
52 import eu.etaxonomy.cdm.model.occurrence.FieldObservation;
53 import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
54 import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;
55 import eu.etaxonomy.cdm.model.occurrence.Specimen;
56 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
57 import eu.etaxonomy.cdm.model.reference.ReferenceBase;
58
59 /**
60 * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
61 * a specimen based view. It does not support all functionality available in the
62 * occurrence package.<BR>
63 * The most significant restriction is that a specimen may derive only from
64 * one direct derivation event and there must be only one field observation (gathering event)
65 * it derives from.<BR>
66 *
67 * @author a.mueller
68 * @date 14.05.2010
69 */
70 public class DerivedUnitFacade {
71 private static final Logger logger = Logger.getLogger(DerivedUnitFacade.class);
72
73 private static final String notSupportMessage = "A specimen facade not supported exception has occurred at a place where this should not have happened. The developer should implement not support check properly during class initialization ";
74
75 /**
76 * Enum that defines the class the "Specimen" belongs to.
77 * Some methods of the facade are not available for certain classes
78 * and will throw an Exception when invoking them.
79 */
80 public enum DerivedUnitType{
81 Specimen ("Specimen"),
82 Observation("Observation"),
83 LivingBeing("Living Being"),
84 Fossil("Fossil"),
85 DerivedUnit("Derived Unit");
86
87 String representation;
88 private DerivedUnitType(String representation){
89 this.representation = representation;
90 }
91
92 /**
93 * @return the representation
94 */
95 public String getRepresentation() {
96 return representation;
97 }
98
99 private DerivedUnitBase getNewDerivedUnitInstance(){
100 if (this == DerivedUnitType.Specimen){
101 return eu.etaxonomy.cdm.model.occurrence.Specimen.NewInstance();
102 }else if (this == DerivedUnitType.Observation){
103 return eu.etaxonomy.cdm.model.occurrence.Observation.NewInstance();
104 }else if (this == DerivedUnitType.LivingBeing){
105 return eu.etaxonomy.cdm.model.occurrence.LivingBeing.NewInstance();
106 }else if (this == DerivedUnitType.Fossil){
107 return eu.etaxonomy.cdm.model.occurrence.Fossil.NewInstance();
108 }else if (this == DerivedUnitType.DerivedUnit){
109 return eu.etaxonomy.cdm.model.occurrence.DerivedUnit.NewInstance();
110 }else{
111 throw new IllegalStateException("Unknown derived unit type " + this.getRepresentation());
112 }
113 }
114
115 }
116
117
118 private DerivedUnitFacadeConfigurator config;
119
120 //private GatheringEvent gatheringEvent;
121 private DerivedUnitType type; //needed?
122
123 private FieldObservation fieldObservation;
124
125 private DerivedUnitBase derivedUnit;
126
127 //media - the text data holding the media
128 private TextData derivedUnitMediaTextData;
129 private TextData fieldObjectMediaTextData;
130
131
132 private TextData ecology;
133 private TextData plantDescription;
134
135
136 /**
137 * Creates a derived unit facade for a new derived unit of type <code>type</code>.
138 * @param type
139 * @return
140 */
141 public static DerivedUnitFacade NewInstance(DerivedUnitType type){
142 return new DerivedUnitFacade(type);
143 }
144
145 /**
146 * Creates a derived unit facade for a given derived unit using the default configuation.
147 * @param derivedUnit
148 * @return
149 * @throws DerivedUnitFacadeNotSupportedException
150 */
151 public static DerivedUnitFacade NewInstance(DerivedUnitBase derivedUnit) throws DerivedUnitFacadeNotSupportedException{
152 return new DerivedUnitFacade(derivedUnit, null);
153 }
154
155 public static DerivedUnitFacade NewInstance(DerivedUnitBase derivedUnit, DerivedUnitFacadeConfigurator config) throws DerivedUnitFacadeNotSupportedException{
156 return new DerivedUnitFacade(derivedUnit, config);
157 }
158
159
160
161 // ****************** CONSTRUCTOR ****************************************************
162
163 private DerivedUnitFacade(DerivedUnitType type){
164 this.config = DerivedUnitFacadeConfigurator.NewInstance();
165
166 //derivedUnit
167 derivedUnit = type.getNewDerivedUnitInstance();
168 setCacheStrategy();
169 }
170
171 private DerivedUnitFacade(DerivedUnitBase derivedUnit, DerivedUnitFacadeConfigurator config) throws DerivedUnitFacadeNotSupportedException{
172
173 if (config == null){
174 config = DerivedUnitFacadeConfigurator.NewInstance();
175 }
176 this.config = config;
177
178 //derived unit
179 this.derivedUnit = derivedUnit;
180 setCacheStrategy();
181
182 //derivation event
183 if (this.derivedUnit.getDerivedFrom() != null){
184 DerivationEvent derivationEvent = getDerivationEvent(true);
185 //fieldObservation
186 Set<FieldObservation> fieldOriginals = getFieldObservationsOriginals(derivationEvent, null);
187 if (fieldOriginals.size() > 1){
188 throw new DerivedUnitFacadeNotSupportedException("Specimen must not have more than 1 derivation event");
189 }else if (fieldOriginals.size() == 0){
190 //fieldObservation = FieldObservation.NewInstance();
191 }else if (fieldOriginals.size() == 1){
192 fieldObservation = fieldOriginals.iterator().next();
193 //###fieldObservation = getInitializedFieldObservation(fieldObservation);
194 fieldObservation.addPropertyChangeListener(getNewEventPropagationListener());
195 }else{
196 throw new IllegalStateException("Illegal state");
197 }
198 }
199 // #### derivedUnit = getInitializedDerivedUnit(derivedUnit);
200
201 //test if unsupported
202
203 //media
204 //specimen
205 // String objectTypeExceptionText = "Specimen";
206 // SpecimenDescription imageGallery = getImageGalleryWithSupportTest(derivedUnit, objectTypeExceptionText, false);
207 // getImageTextDataWithSupportTest(imageGallery, objectTypeExceptionText);
208 this.derivedUnitMediaTextData = inititialzeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);
209
210 //field observation
211 // objectTypeExceptionText = "Field observation";
212 // imageGallery = getImageGalleryWithSupportTest(fieldObservation, objectTypeExceptionText, false);
213 // getImageTextDataWithSupportTest(imageGallery, objectTypeExceptionText);
214 fieldObjectMediaTextData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), false, true);
215
216 //handle derivedUnit.getMedia()
217 if (derivedUnit.getMedia().size() > 0){
218 //TODO better changed model here to allow only one place for images
219 if (this.config.isMoveDerivedUnitMediaToGallery()){
220 Set<Media> mediaSet = derivedUnit.getMedia();
221 for (Media media : mediaSet){
222 this.addDerivedUnitMedia(media);
223 }
224 mediaSet.removeAll(getDerivedUnitMedia());
225 }else{
226 throw new DerivedUnitFacadeNotSupportedException("Specimen may not have direct media. Only (one) image gallery is allowed");
227 }
228 }
229
230 //handle fieldObservation.getMedia()
231 if (fieldObservation != null && fieldObservation.getMedia() != null && fieldObservation.getMedia().size() > 0){
232 //TODO better changed model here to allow only one place for images
233 if (this.config.isMoveFieldObjectMediaToGallery()){
234 Set<Media> mediaSet = fieldObservation.getMedia();
235 for (Media media : mediaSet){
236 this.addFieldObjectMedia(media);
237 }
238 mediaSet.removeAll(getFieldObjectMedia());
239 }else{
240 throw new DerivedUnitFacadeNotSupportedException("Field object may not have direct media. Only (one) image gallery is allowed");
241 }
242 }
243
244 //test if descriptions are supported
245 ecology = initializeFieldObjectTextDataWithSupportTest(Feature.ECOLOGY(), false, false);
246 plantDescription = initializeFieldObjectTextDataWithSupportTest(Feature.DESCRIPTION(), false, false);
247 }
248
249
250 private DerivedUnitBase getInitializedDerivedUnit(DerivedUnitBase derivedUnit) {
251 IOccurrenceService occurrenceService = this.config.getOccurrenceService();
252 if (occurrenceService == null){
253 return derivedUnit;
254 }
255 List<String> propertyPaths = this.config.getPropertyPaths();
256 if (propertyPaths == null){
257 return derivedUnit;
258 }
259 propertyPaths = getDerivedUnitPropertyPaths(propertyPaths);
260 DerivedUnitBase result = (DerivedUnitBase)occurrenceService.load(derivedUnit.getUuid(), propertyPaths);
261 return result;
262 }
263
264 /**
265 * Initializes the derived unit according to the configuartions property path.
266 * If the property path is <code>null</code> or no occurrence service is given the
267 * returned object is the same as the input parameter.
268 * @param fieldObservation2
269 * @return
270 */
271 private FieldObservation getInitializedFieldObservation(FieldObservation fieldObservation) {
272 IOccurrenceService occurrenceService = this.config.getOccurrenceService();
273 if (occurrenceService == null){
274 return fieldObservation;
275 }
276 List<String> propertyPaths = this.config.getPropertyPaths();
277 if (propertyPaths == null){
278 return fieldObservation;
279 }
280 propertyPaths = getFieldObjectPropertyPaths(propertyPaths);
281 FieldObservation result = (FieldObservation)occurrenceService.load(fieldObservation.getUuid(), propertyPaths);
282 return result;
283 }
284
285 /**
286 * Transforms the property paths in a way that the facade is handled just like an
287 * ordinary CdmBase object.<BR>
288 * E.g. a property path "collectinAreas" will be translated into gatheringEvent.collectingAreas
289 * @param propertyPaths
290 * @return
291 */
292 private List<String> getFieldObjectPropertyPaths(List<String> propertyPaths) {
293 List<String> result = new ArrayList<String>();
294 for (String facadePath : propertyPaths){
295 // collecting areas (named area)
296 if (facadePath.startsWith("collectingAreas")){
297 facadePath = "gatheringEvent." + facadePath;
298 result.add(facadePath);
299 }
300 // collector (agentBase)
301 else if (facadePath.startsWith("collector")){
302 facadePath = facadePath.replace("collector", "gatheringEvent.actor");
303 result.add(facadePath);
304 }
305 // exactLocation (agentBase)
306 else if (facadePath.startsWith("exactLocation")){
307 facadePath = "gatheringEvent." + facadePath;
308 result.add(facadePath);
309 }
310 // gatheringPeriod (TimePeriod)
311 else if (facadePath.startsWith("gatheringPeriod")){
312 facadePath = facadePath.replace("gatheringPeriod", "gatheringEvent.timeperiod");
313 result.add(facadePath);
314 }
315 // (locality/ localityLanguage , LanguageString)
316 else if (facadePath.startsWith("locality")){
317 facadePath = "gatheringEvent." + facadePath;
318 result.add(facadePath);
319 }
320
321 //*********** FIELD OBJECT ************
322 // fieldObjectDefinitions (Map<language, languageString)
323 else if (facadePath.startsWith("fieldObjectDefinitions")){
324 // TODO or definition ???
325 facadePath = facadePath.replace("fieldObjectDefinitions", "description");
326 result.add(facadePath);
327 }
328 // fieldObjectMedia (Media)
329 else if (facadePath.startsWith("fieldObjectMedia")){
330 // TODO ???
331 facadePath = facadePath.replace("fieldObjectMedia", "descriptions.elements.media");
332 result.add(facadePath);
333 }
334
335 //Gathering Event will always be added
336 result.add("gatheringEvent");
337
338 }
339
340 /*
341 Gathering Event
342 ====================
343 - gatheringEvent (GatheringEvent)
344
345 Field Object
346 =================
347 - ecology/ ecologyAll (String) ???
348 - plant description (like ecology)
349
350 - fieldObjectImageGallery (SpecimenDescription) - is automatically initialized via fieldObjectMedia
351
352 */
353
354 return result;
355 }
356
357 /**
358 * Transforms the property paths in a way that the facade is handled just like an
359 * ordinary CdmBase object.<BR>
360 * E.g. a property path "collectinAreas" will be translated into gatheringEvent.collectingAreas
361 * @param propertyPaths
362 * @return
363 */
364 private List<String> getDerivedUnitPropertyPaths(List<String> propertyPaths) {
365 List<String> result = new ArrayList<String>();
366 for (String facadePath : propertyPaths){
367 // determinations (DeterminationEvent)
368 if (facadePath.startsWith("determinations")){
369 facadePath = "" + facadePath; //no change
370 result.add(facadePath);
371 }
372 // storedUnder (TaxonNameBase)
373 else if (facadePath.startsWith("storedUnder")){
374 facadePath = "" + facadePath; //no change
375 result.add(facadePath);
376 }
377 // sources (IdentifiableSource)
378 else if (facadePath.startsWith("sources")){
379 facadePath = "" + facadePath; //no change
380 result.add(facadePath);
381 }
382 // collection (Collection)
383 else if (facadePath.startsWith("collection")){
384 facadePath = "" + facadePath; //no change
385 result.add(facadePath);
386 }
387 // (locality/ localityLanguage , LanguageString)
388 else if (facadePath.startsWith("locality")){
389 facadePath = "gatheringEvent." + facadePath;
390 result.add(facadePath);
391 }
392
393 //*********** FIELD OBJECT ************
394 // derivedUnitDefinitions (Map<language, languageString)
395 else if (facadePath.startsWith("derivedUnitDefinitions")){
396 // TODO or definition ???
397 facadePath = facadePath.replace("derivedUnitDefinitions", "description");
398 result.add(facadePath);
399 }
400
401 // derivedUnitMedia (Media)
402 else if (facadePath.startsWith("derivedUnitMedia")){
403 // TODO ???
404 facadePath = facadePath.replace("derivedUnitMedia", "descriptions.elements.media");
405 result.add(facadePath);
406 }
407
408 }
409
410 /*
411 //TODO
412 Derived Unit
413 =====================
414
415 - derivedUnitImageGallery (SpecimenDescription) - is automatically initialized via derivedUnitMedia
416
417 - derivationEvent (DerivationEvent) - will always be initialized
418 - duplicates (??? Specimen???) ???
419 */
420
421 return result;
422 }
423
424 /**
425 *
426 */
427 private void setCacheStrategy() {
428 derivedUnit.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
429 }
430
431
432 /**
433 * @param feature
434 * @param createIfNotExists
435 * @param isImageGallery
436 * @return
437 * @throws DerivedUnitFacadeNotSupportedException
438 */
439 private TextData initializeFieldObjectTextDataWithSupportTest(Feature feature, boolean createIfNotExists, boolean isImageGallery) throws DerivedUnitFacadeNotSupportedException {
440 //field object
441 FieldObservation fieldObject = getFieldObservation(createIfNotExists) ;
442 if (fieldObject == null){
443 return null;
444 }
445 return inititialzeTextDataWithSupportTest(feature, fieldObject, createIfNotExists, isImageGallery);
446 }
447
448
449 /**
450 * @param feature
451 * @param specimen
452 * @param createIfNotExists
453 * @param isImageGallery
454 * @return
455 * @throws DerivedUnitFacadeNotSupportedException
456 */
457 private TextData inititialzeTextDataWithSupportTest(Feature feature, SpecimenOrObservationBase specimen, boolean createIfNotExists,
458 boolean isImageGallery) throws DerivedUnitFacadeNotSupportedException {
459 if (feature == null ){
460 return null;
461 }
462 TextData textData = null;
463 if (createIfNotExists){
464 textData = TextData.NewInstance(feature);
465 }
466
467 Set<SpecimenDescription> descriptions;
468 if (isImageGallery){
469 descriptions = specimen.getSpecimenDescriptionImageGallery();
470 }else{
471 descriptions = specimen.getSpecimenDescriptions(false);
472 }
473 if (descriptions.size() == 0){
474 if (createIfNotExists){
475 SpecimenDescription newSpecimenDescription = SpecimenDescription.NewInstance(specimen);
476 newSpecimenDescription.addElement(textData);
477 return textData;
478 }else{
479 return null;
480 }
481 }
482 Set<DescriptionElementBase> existingTextData = new HashSet<DescriptionElementBase>();
483 for (SpecimenDescription description : descriptions){
484 for (DescriptionElementBase element: description.getElements()){
485 if (element.isInstanceOf(TextData.class) && ( feature.equals(element.getFeature() )|| isImageGallery ) ){
486 existingTextData.add(element);
487 }
488 }
489 }
490 if (existingTextData.size() > 1){
491 throw new DerivedUnitFacadeNotSupportedException("Specimen facade does not support more than one description text data of type " + feature.getLabel());
492
493 }else if (existingTextData.size() == 1){
494 return CdmBase.deproxy(existingTextData.iterator().next(), TextData.class);
495 }else{
496 SpecimenDescription description = descriptions.iterator().next();
497 description.addElement(textData);
498 return textData;
499 }
500 }
501
502 //************************** METHODS *****************************************
503
504 private TextData getDerivedUnitImageGalleryTextData(boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException{
505 if (this.derivedUnitMediaTextData == null && createIfNotExists){
506 this.derivedUnitMediaTextData = getImageGalleryTextData(derivedUnit, "Specimen");
507 }
508 return this.derivedUnitMediaTextData;
509 }
510
511 private TextData getObservationImageGalleryTextData(boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException{
512 if (this.fieldObjectMediaTextData == null && createIfNotExists){
513 this.fieldObjectMediaTextData = getImageGalleryTextData(fieldObservation, "Field observation");
514 }
515 return this.fieldObjectMediaTextData;
516 }
517
518
519
520 /**
521 * @param derivationEvent2
522 * @return
523 * @throws DerivedUnitFacadeNotSupportedException
524 */
525 private Set<FieldObservation> getFieldObservationsOriginals(DerivationEvent derivationEvent, Set<SpecimenOrObservationBase> recursionAvoidSet) throws DerivedUnitFacadeNotSupportedException {
526 if (recursionAvoidSet == null){
527 recursionAvoidSet = new HashSet<SpecimenOrObservationBase>();
528 }
529 Set<FieldObservation> result = new HashSet<FieldObservation>();
530 Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
531 for (SpecimenOrObservationBase original : originals){
532 if (original.isInstanceOf(FieldObservation.class)){
533 result.add(CdmBase.deproxy(original, FieldObservation.class));
534 }else if (original.isInstanceOf(DerivedUnitBase.class)){
535 //if specimen has already been tested exclude it from further recursion
536 if (recursionAvoidSet.contains(original)){
537 continue;
538 }
539 DerivedUnitBase derivedUnit = CdmBase.deproxy(original, DerivedUnitBase.class);
540 DerivationEvent originalDerivation = derivedUnit.getDerivedFrom();
541 // Set<DerivationEvent> derivationEvents = original.getDerivationEvents();
542 // for (DerivationEvent originalDerivation : derivationEvents){
543 Set<FieldObservation> fieldObservations = getFieldObservationsOriginals(originalDerivation, recursionAvoidSet);
544 result.addAll(fieldObservations);
545 // }
546 }else{
547 throw new DerivedUnitFacadeNotSupportedException("Unhandled specimen or observation base type: " + original.getClass().getName() );
548 }
549
550 }
551 return result;
552 }
553
554 //*********** MEDIA METHODS ******************************
555
556 // /**
557 // * Returns the media list for a specimen. Throws an exception if the existing specimen descriptions
558 // * are not supported by this facade.
559 // * @param specimen the specimen the media belongs to
560 // * @param specimenExceptionText text describing the specimen for exception messages
561 // * @return
562 // * @throws DerivedUnitFacadeNotSupportedException
563 // */
564 // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase specimen, String specimenExceptionText) throws DerivedUnitFacadeNotSupportedException{
565 // List<Media> result;
566 // SpecimenDescription imageGallery = getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
567 // TextData textData = getImageTextDataWithSupportTest(imageGallery, specimenExceptionText);
568 // result = textData.getMedia();
569 // return result;
570 // }
571
572 /**
573 * Returns the media list for a specimen. Throws an exception if the existing specimen descriptions
574 * are not supported by this facade.
575 * @param specimen the specimen the media belongs to
576 * @param specimenExceptionText text describing the specimen for exception messages
577 * @return
578 * @throws DerivedUnitFacadeNotSupportedException
579 */
580 private TextData getImageGalleryTextData(SpecimenOrObservationBase specimen, String specimenExceptionText) throws DerivedUnitFacadeNotSupportedException{
581 TextData result;
582 SpecimenDescription imageGallery = getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
583 result = getImageTextDataWithSupportTest(imageGallery, specimenExceptionText);
584 return result;
585 }
586
587
588 /**
589 * Returns the image gallery of the according specimen. Throws an exception if the attached
590 * image gallerie(s) are not supported by this facade.
591 * If no image gallery exists a new one is created if <code>createNewIfNotExists</code> is true and
592 * if specimen is not <code>null</code>.
593 * @param specimen
594 * @param specimenText
595 * @param createNewIfNotExists
596 * @return
597 * @throws DerivedUnitFacadeNotSupportedException
598 */
599 private SpecimenDescription getImageGalleryWithSupportTest(SpecimenOrObservationBase<?> specimen, String specimenText, boolean createNewIfNotExists) throws DerivedUnitFacadeNotSupportedException{
600 if (specimen == null){
601 return null;
602 }
603 SpecimenDescription imageGallery;
604 if (hasMultipleImageGalleries(specimen)){
605 throw new DerivedUnitFacadeNotSupportedException( specimenText + " must not have more than 1 image gallery");
606 }else{
607 imageGallery = getImageGallery(specimen, createNewIfNotExists);
608 getImageTextDataWithSupportTest(imageGallery, specimenText);
609 }
610 return imageGallery;
611 }
612
613 /**
614 * Returns the media holding text data element of the image gallery. Throws an exception if multiple
615 * such text data already exist.
616 * Creates a new text data if none exists and adds it to the image gallery.
617 * If image gallery is <code>null</code> nothing happens.
618 * @param imageGallery
619 * @param textData
620 * @return
621 * @throws DerivedUnitFacadeNotSupportedException
622 */
623 private TextData getImageTextDataWithSupportTest(SpecimenDescription imageGallery, String specimenText) throws DerivedUnitFacadeNotSupportedException {
624 if (imageGallery == null){
625 return null;
626 }
627 TextData textData = null;
628 for (DescriptionElementBase element: imageGallery.getElements()){
629 if (element.isInstanceOf(TextData.class) && element.getFeature().equals(Feature.IMAGE())){
630 if (textData != null){
631 throw new DerivedUnitFacadeNotSupportedException( specimenText + " must not have more than 1 image text data element in image gallery");
632 }
633 textData = CdmBase.deproxy(element, TextData.class);
634 }
635 }
636 if (textData == null){
637 textData = TextData.NewInstance(Feature.IMAGE());
638 imageGallery.addElement(textData);
639 }
640 return textData;
641 }
642
643 /**
644 * Checks, if a specimen belongs to more than one description that is an image gallery
645 * @param derivedUnit
646 * @return
647 */
648 private boolean hasMultipleImageGalleries(SpecimenOrObservationBase<?> derivedUnit){
649 int count = 0;
650 Set<SpecimenDescription> descriptions= derivedUnit.getSpecimenDescriptions();
651 for (SpecimenDescription description : descriptions){
652 if (description.isImageGallery()){
653 count++;
654 }
655 }
656 return (count > 1);
657 }
658
659
660 /**
661 * Returns the image gallery for a specimen. If there are multiple specimen descriptions
662 * marked as image galleries an arbitrary one is chosen.
663 * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>
664 * is <code>true</code>.<Br>
665 * If specimen is <code>null</code> a null pointer exception is thrown.
666 * @param createNewIfNotExists
667 * @return
668 */
669 private SpecimenDescription getImageGallery(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) {
670 SpecimenDescription result = null;
671 Set<SpecimenDescription> descriptions= specimen.getSpecimenDescriptions();
672 for (SpecimenDescription description : descriptions){
673 if (description.isImageGallery()){
674 result = description;
675 break;
676 }
677 }
678 if (result == null && createIfNotExists){
679 result = SpecimenDescription.NewInstance(specimen);
680 result.setImageGallery(true);
681 }
682 return result;
683 }
684
685 /**
686 * Adds a media to the specimens image gallery. If media is <code>null</code> nothing happens.
687 * @param media
688 * @param specimen
689 * @return true if media is not null (as specified by {@link java.util.Collection#add(Object) Collection.add(E e)}
690 * @throws DerivedUnitFacadeNotSupportedException
691 */
692 private boolean addMedia(Media media, SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {
693 if (media != null){
694 List<Media> mediaList = getMedia(specimen, true);
695 return mediaList.add(media);
696 }else{
697 return false;
698 }
699 }
700
701 /**
702 * Removes a media from the specimens image gallery.
703 * @param media
704 * @param specimen
705 * @return true if an element was removed as a result of this call (as specified by {@link java.util.Collection#remove(Object) Collection.remove(E e)}
706 * @throws DerivedUnitFacadeNotSupportedException
707 */
708 private boolean removeMedia(Media media, SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {
709 List<Media> mediaList = getMedia(specimen, true);
710 return mediaList == null ? null : mediaList.remove(media);
711 }
712
713 private List<Media> getMedia(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException {
714 TextData textData = getMediaTextData(specimen, createIfNotExists);
715 return textData == null ? null : textData.getMedia();
716 }
717
718 /**
719 * Returns the one media list of a specimen which is part of the only image gallery that
720 * this specimen is part of.<BR>
721 * If these conditions are not hold an exception is thrwon.
722 * @param specimen
723 * @return
724 * @throws DerivedUnitFacadeNotSupportedException
725 */
726 // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {
727 // if (specimen == null){
728 // return null;
729 // }
730 // if (specimen == this.derivedUnit){
731 // return getDerivedUnitImageGalleryMedia();
732 // }else if (specimen == this.fieldObservation){
733 // return getObservationImageGalleryTextData();
734 // }else{
735 // return getImageGalleryMedia(specimen, "Undefined specimen ");
736 // }
737 // }
738
739 /**
740 * Returns the one media list of a specimen which is part of the only image gallery that
741 * this specimen is part of.<BR>
742 * If these conditions are not hold an exception is thrwon.
743 * @param specimen
744 * @return
745 * @throws DerivedUnitFacadeNotSupportedException
746 */
747 private TextData getMediaTextData(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException {
748 if (specimen == null){
749 return null;
750 }
751 if (specimen == this.derivedUnit){
752 return getDerivedUnitImageGalleryTextData(createIfNotExists);
753 }else if (specimen == this.fieldObservation){
754 return getObservationImageGalleryTextData(createIfNotExists);
755 }else{
756 return getImageGalleryTextData(specimen, "Undefined specimen ");
757 }
758 }
759
760
761 //****************** GETTER / SETTER / ADDER / REMOVER ***********************/
762
763 // ****************** Gathering Event *********************************/
764
765 //country
766 public NamedArea getCountry(){
767 return (hasGatheringEvent() ? getGatheringEvent(true).getCountry() : null);
768 }
769
770 public void setCountry(NamedArea country){
771 getGatheringEvent(true).setCountry(country);
772 }
773
774
775 //Collecting area
776 public void addCollectingArea(NamedArea area) {
777 getGatheringEvent(true).addCollectingArea(area);
778 }
779 public void addCollectingAreas(java.util.Collection<NamedArea> areas) {
780 for (NamedArea area : areas){
781 getGatheringEvent(true).addCollectingArea(area);
782 }
783 }
784 public Set<NamedArea> getCollectingAreas() {
785 return (hasGatheringEvent() ? getGatheringEvent(true).getCollectingAreas() : null);
786 }
787 public void removeCollectingArea(NamedArea area) {
788 if (hasGatheringEvent()){
789 getGatheringEvent(true).removeCollectingArea(area);
790 }
791 }
792
793 //absolute elevation
794 /** meter above/below sea level of the surface
795 * @see #getAbsoluteElevationError()
796 * @see #getAbsoluteElevationRange()
797 **/
798 public Integer getAbsoluteElevation() {
799 return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevation() : null);
800 }
801 public void setAbsoluteElevation(Integer absoluteElevation) {
802 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);
803 }
804
805 //absolute elevation error
806 public Integer getAbsoluteElevationError() {
807 return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevationError() : null);
808 }
809 public void setAbsoluteElevationError(Integer absoluteElevationError) {
810 getGatheringEvent(true).setAbsoluteElevationError(absoluteElevationError);
811 }
812
813 /**
814 * @see #getAbsoluteElevation()
815 * @see #getAbsoluteElevationError()
816 * @see #setAbsoluteElevationRange(Integer, Integer)
817 * @see #getAbsoluteElevationMaximum()
818 */
819 public Integer getAbsoluteElevationMinimum(){
820 if ( ! hasGatheringEvent() ){
821 return null;
822 }
823 Integer minimum = getGatheringEvent(true).getAbsoluteElevation();
824 if (getGatheringEvent(true).getAbsoluteElevationError() != null){
825 minimum = minimum - getGatheringEvent(true).getAbsoluteElevationError();
826 }
827 return minimum;
828 }
829 /**
830 * @see #getAbsoluteElevation()
831 * @see #getAbsoluteElevationError()
832 * @see #setAbsoluteElevationRange(Integer, Integer)
833 * @see #getAbsoluteElevationMinimum()
834 */
835 public Integer getAbsoluteElevationMaximum(){
836 if ( ! hasGatheringEvent() ){
837 return null;
838 }
839 Integer maximum = getGatheringEvent(true).getAbsoluteElevation();
840 if (getGatheringEvent(true).getAbsoluteElevationError() != null){
841 maximum = maximum + getGatheringEvent(true).getAbsoluteElevationError();
842 }
843 return maximum;
844 }
845
846
847 /**
848 * This method replaces absoluteElevation and absoulteElevationError by
849 * internally translating minimum and maximum values into
850 * average and error values. As all these values are integer based
851 * it is necessary that the distance is between minimum and maximum is <b>even</b>,
852 * otherwise we will get a rounding error resulting in a maximum that is increased
853 * by 1.
854 * @see #setAbsoluteElevation(Integer)
855 * @see #setAbsoluteElevationError(Integer)
856 * @param minimumElevation minimum of the range
857 * @param maximumElevation maximum of the range
858 */
859 public void setAbsoluteElevationRange(Integer minimumElevation, Integer maximumElevation){
860 if (minimumElevation == null || maximumElevation == null){
861 Integer elevation = minimumElevation;
862 Integer error = 0;
863 if (minimumElevation == null){
864 elevation = maximumElevation;
865 if (elevation == null){
866 error = null;
867 }
868 }
869 getGatheringEvent(true).setAbsoluteElevation(elevation);
870 getGatheringEvent(true).setAbsoluteElevationError(error);
871 }else{
872 if (! isEvenDistance(minimumElevation, maximumElevation) ){
873 throw new IllegalArgumentException("Distance between minimum and maximum elevation must be even but was " + Math.abs(minimumElevation - maximumElevation));
874 }
875 Integer absoluteElevationError = Math.abs(maximumElevation - minimumElevation);
876 absoluteElevationError = absoluteElevationError / 2;
877 Integer absoluteElevation = minimumElevation + absoluteElevationError;
878 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);
879 getGatheringEvent(true).setAbsoluteElevationError(absoluteElevationError);
880 }
881 }
882
883 /**
884 * @param minimumElevation
885 * @param maximumElevation
886 * @return
887 */
888 private boolean isEvenDistance(Integer minimumElevation, Integer maximumElevation) {
889 Integer diff = ( maximumElevation - minimumElevation);
890 Integer testDiff = (diff /2) *2 ;
891 return (testDiff == diff);
892 }
893
894 //collector
895 public AgentBase getCollector() {
896 return (hasGatheringEvent() ? getGatheringEvent(true).getCollector() : null);
897 }
898 public void setCollector(AgentBase collector){
899 getGatheringEvent(true).setCollector(collector);
900 }
901
902 //collecting method
903 public String getCollectingMethod() {
904 return (hasGatheringEvent() ? getGatheringEvent(true).getCollectingMethod() : null);
905 }
906 public void setCollectingMethod(String collectingMethod) {
907 getGatheringEvent(true).setCollectingMethod(collectingMethod);
908 }
909
910 //distance to ground
911 public Integer getDistanceToGround() {
912 return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToGround() : null);
913 }
914 public void setDistanceToGround(Integer distanceToGround) {
915 getGatheringEvent(true).setDistanceToGround(distanceToGround);
916 }
917
918 //distance to water surface
919 public Integer getDistanceToWaterSurface() {
920 return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToWaterSurface() : null);
921 }
922 public void setDistanceToWaterSurface(Integer distanceToWaterSurface) {
923 getGatheringEvent(true).setDistanceToWaterSurface(distanceToWaterSurface);
924 }
925
926 //exact location
927 public Point getExactLocation() {
928 return (hasGatheringEvent() ? getGatheringEvent(true).getExactLocation() : null );
929 }
930
931 /**
932 * Returns a sexagesimal representation of the exact location (e.g. 12°59'N, 35°23E).
933 * If the exact location is <code>null</code> the empty string is returned.
934 * @param includeEmptySeconds
935 * @param includeReferenceSystem
936 * @return
937 */
938 public String getExactLocationText(boolean includeEmptySeconds, boolean includeReferenceSystem){
939 return (this.getExactLocation() == null ? "" : this.getExactLocation().toSexagesimalString(includeEmptySeconds, includeReferenceSystem));
940 }
941 public void setExactLocation(Point exactLocation) {
942 getGatheringEvent(true).setExactLocation(exactLocation);
943 }
944 public void setExactLocationByParsing(String longitudeToParse, String latitudeToParse, ReferenceSystem referenceSystem, Integer errorRadius) throws ParseException{
945 Point point = Point.NewInstance(null, null, referenceSystem, errorRadius);
946 point.setLongitudeByParsing(longitudeToParse);
947 point.setLatitudeByParsing(latitudeToParse);
948 setExactLocation(point);
949 }
950
951 //gathering event description
952 public String getGatheringEventDescription() {
953 return (hasGatheringEvent() ? getGatheringEvent(true).getDescription() : null);
954 }
955 public void setGatheringEventDescription(String description) {
956 getGatheringEvent(true).setDescription(description);
957 }
958
959 //gatering period
960 public TimePeriod getGatheringPeriod() {
961 return (hasGatheringEvent() ? getGatheringEvent(true).getTimeperiod() : null);
962 }
963 public void setGatheringPeriod(TimePeriod timeperiod) {
964 getGatheringEvent(true).setTimeperiod(timeperiod);
965 }
966
967 //locality
968 public LanguageString getLocality(){
969 return (hasGatheringEvent() ? getGatheringEvent(true).getLocality() : null);
970 }
971 @Transient
972 public String getLocalityText(){
973 LanguageString locality = getLocality();
974 if(locality != null){
975 return locality.getText();
976 }
977 return null;
978 }
979 @Transient
980 public Language getLocalityLanguage(){
981 LanguageString locality = getLocality();
982 if(locality != null){
983 return locality.getLanguage();
984 }
985 return null;
986 }
987
988 /**
989 * Sets the locality string in the default language
990 * @param locality
991 */
992 public void setLocality(String locality){
993 Language language = Language.DEFAULT();
994 setLocality(locality, language);
995 }
996 public void setLocality(String locality, Language language){
997 LanguageString langString = LanguageString.NewInstance(locality, language);
998 setLocality(langString);
999 }
1000 public void setLocality(LanguageString locality){
1001 getGatheringEvent(true).setLocality(locality);
1002 }
1003
1004 /**
1005 * The gathering event will be used for the field object instead of the old gathering event.<BR>
1006 * <B>This method will override all gathering values (see below).</B>
1007 * @see #getAbsoluteElevation()
1008 * @see #getAbsoluteElevationError()
1009 * @see #getDistanceToGround()
1010 * @see #getDistanceToWaterSurface()
1011 * @see #getExactLocation()
1012 * @see #getGatheringEventDescription()
1013 * @see #getGatheringPeriod()
1014 * @see #getCollectingAreas()
1015 * @see #getCollectingMethod()
1016 * @see #getLocality()
1017 * @see #getCollector()
1018 * @param gatheringEvent
1019 */
1020 public void setGatheringEvent(GatheringEvent gatheringEvent) {
1021 getFieldObservation(true).setGatheringEvent(gatheringEvent);
1022 }
1023 public boolean hasGatheringEvent(){
1024 return (getGatheringEvent(false) != null);
1025 }
1026 @Transient
1027 public GatheringEvent getGatheringEvent() {
1028 return getGatheringEvent(false);
1029 }
1030
1031 public GatheringEvent getGatheringEvent(boolean createIfNotExists) {
1032 if (! hasFieldObservation() && ! createIfNotExists){
1033 return null;
1034 }
1035 if (createIfNotExists && getFieldObservation(true).getGatheringEvent() == null ){
1036 GatheringEvent gatheringEvent = GatheringEvent.NewInstance();
1037 getFieldObservation(true).setGatheringEvent(gatheringEvent);
1038 }
1039 return getFieldObservation(true).getGatheringEvent();
1040 }
1041
1042 // ****************** Field Object ************************************/
1043
1044 /**
1045 * Returns true if a field observation exists (even if all attributes are empty or <code>null<code>.
1046 * @return
1047 */
1048 public boolean hasFieldObject(){
1049 return this.fieldObservation != null;
1050 }
1051
1052 //ecology
1053 @Transient
1054 public String getEcology(){
1055 return getEcology(Language.DEFAULT());
1056 }
1057 public String getEcology(Language language){
1058 LanguageString languageString = getEcologyAll().get(language);
1059 return (languageString == null ? null : languageString.getText());
1060 }
1061 // public String getEcologyPreferred(List<Language> languages){
1062 // LanguageString languageString = getEcologyAll().getPreferredLanguageString(languages);
1063 // return languageString.getText();
1064 // }
1065 public Map<Language, LanguageString> getEcologyAll(){
1066 if (ecology == null){
1067 try {
1068 ecology = initializeFieldObjectTextDataWithSupportTest(Feature.ECOLOGY(), false, false);
1069 } catch (DerivedUnitFacadeNotSupportedException e) {
1070 throw new IllegalStateException(notSupportMessage, e);
1071 }
1072 if (ecology == null){
1073 return new HashMap<Language, LanguageString>();
1074 }
1075 }
1076 return ecology.getMultilanguageText();
1077 }
1078
1079 public void setEcology(String ecology){
1080 setEcology(ecology, null);
1081 }
1082 public void setEcology(String ecologyText, Language language){
1083 if (language == null){
1084 language = Language.DEFAULT();
1085 }
1086 if (ecology == null){
1087 try {
1088 ecology = initializeFieldObjectTextDataWithSupportTest(Feature.ECOLOGY(), true, false);
1089 } catch (DerivedUnitFacadeNotSupportedException e) {
1090 throw new IllegalStateException(notSupportMessage, e);
1091 }
1092 }
1093 if (ecologyText == null){
1094 ecology.removeText(language);
1095 }else{
1096 ecology.putText(ecologyText, language);
1097 }
1098 }
1099 public void removeEcology(Language language){
1100 setEcology(null, language);
1101 }
1102 /**
1103 * Removes ecology for the default language
1104 */
1105 public void removeEcology(){
1106 setEcology(null, null);
1107 }
1108 public void removeEcologyAll(){
1109
1110 }
1111
1112
1113 //plant description
1114 @Transient
1115 public String getPlantDescription(){
1116 return getPlantDescription(null);
1117 }
1118 public String getPlantDescription(Language language){
1119 if (language == null){
1120 language = Language.DEFAULT();
1121 }
1122 LanguageString languageString = getPlantDescriptionAll().get(language);
1123 return (languageString == null ? null : languageString.getText());
1124 }
1125 // public String getPlantDescriptionPreferred(List<Language> languages){
1126 // LanguageString languageString = getPlantDescriptionAll().getPreferredLanguageString(languages);
1127 // return languageString.getText();
1128 // }
1129 public Map<Language, LanguageString> getPlantDescriptionAll(){
1130 if (plantDescription == null){
1131 try {
1132 plantDescription = initializeFieldObjectTextDataWithSupportTest(Feature.DESCRIPTION(), false, false);
1133 } catch (DerivedUnitFacadeNotSupportedException e) {
1134 throw new IllegalStateException(notSupportMessage, e);
1135 }
1136 if (plantDescription == null){
1137 return new HashMap<Language, LanguageString>();
1138 }
1139 }
1140 return plantDescription.getMultilanguageText();
1141 }
1142 public void setPlantDescription(String plantDescription){
1143 setPlantDescription(plantDescription, null);
1144 }
1145 public void setPlantDescription(String plantDescriptionText, Language language){
1146 if (language == null){
1147 language = Language.DEFAULT();
1148 }
1149 if (plantDescription == null){
1150 try {
1151 plantDescription = initializeFieldObjectTextDataWithSupportTest(Feature.DESCRIPTION(), true, false);
1152 } catch (DerivedUnitFacadeNotSupportedException e) {
1153 throw new IllegalStateException(notSupportMessage, e);
1154 }
1155 }
1156 if (plantDescriptionText == null){
1157 plantDescription.removeText(language);
1158 }else{
1159 plantDescription.putText(plantDescriptionText, language);
1160 }
1161 }
1162 public void removePlantDescription(Language language){
1163 setPlantDescription(null, language);
1164 }
1165
1166 //field object definition
1167 public void addFieldObjectDefinition(String text, Language language) {
1168 getFieldObservation(true).addDefinition(text, language);
1169 }
1170
1171 public Map<Language, LanguageString> getFieldObjectDefinition() {
1172 if (! hasFieldObservation()){
1173 return new HashMap<Language, LanguageString>();
1174 }else{
1175 return getFieldObservation(true).getDefinition();
1176 }
1177 }
1178 public String getFieldObjectDefinition(Language language) {
1179 Map<Language, LanguageString> map = getFieldObjectDefinition();
1180 LanguageString languageString = (map == null? null : map.get(language));
1181 if (languageString != null){
1182 return languageString.getText();
1183 }else {
1184 return null;
1185 }
1186 }
1187 public void removeFieldObjectDefinition(Language lang) {
1188 if (hasFieldObservation()){
1189 getFieldObservation(true).removeDefinition(lang);
1190 }
1191 }
1192
1193
1194 //media
1195 public boolean addFieldObjectMedia(Media media) {
1196 try {
1197 return addMedia(media, getFieldObservation(true));
1198 } catch (DerivedUnitFacadeNotSupportedException e) {
1199 throw new IllegalStateException(notSupportMessage, e);
1200 }
1201 }
1202 /**
1203 * Returns true, if an image gallery for the field object exists.<BR>
1204 * Returns also <code>true</code> if the image gallery is empty.
1205 * @return
1206 */
1207 public boolean hasFieldObjectImageGallery(){
1208 if (! hasFieldObject()){
1209 return false;
1210 }else{
1211 return (getImageGallery(fieldObservation, false) != null);
1212 }
1213 }
1214
1215 /**
1216 * @param createIfNotExists
1217 * @return
1218 */
1219 public SpecimenDescription getFieldObjectImageGallery(boolean createIfNotExists){
1220 TextData textData;
1221 try {
1222 textData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), createIfNotExists, true);
1223 } catch (DerivedUnitFacadeNotSupportedException e) {
1224 throw new IllegalStateException(notSupportMessage, e);
1225 }
1226 if (textData != null){
1227 return CdmBase.deproxy(textData.getInDescription(), SpecimenDescription.class);
1228 }else{
1229 return null;
1230 }
1231 }
1232 /**
1233 * Returns the media for the field object.<BR>
1234 * @return
1235 */
1236 public List<Media> getFieldObjectMedia() {
1237 try {
1238 List<Media> result = getMedia(getFieldObservation(false), false);
1239 return result == null ? new ArrayList<Media>() : result;
1240 } catch (DerivedUnitFacadeNotSupportedException e) {
1241 throw new IllegalStateException(notSupportMessage, e);
1242 }
1243 }
1244 public boolean removeFieldObjectMedia(Media media) {
1245 try {
1246 return removeMedia(media, getFieldObservation(false));
1247 } catch (DerivedUnitFacadeNotSupportedException e) {
1248 throw new IllegalStateException(notSupportMessage, e);
1249 }
1250 }
1251
1252 //field number
1253 public String getFieldNumber() {
1254 if (! hasFieldObservation()){
1255 return null;
1256 }else{
1257 return getFieldObservation(true).getFieldNumber();
1258 }
1259 }
1260 public void setFieldNumber(String fieldNumber) {
1261 getFieldObservation(true).setFieldNumber(fieldNumber);
1262 }
1263
1264
1265 //field notes
1266 public String getFieldNotes() {
1267 if (! hasFieldObservation()){
1268 return null;
1269 }else{
1270 return getFieldObservation(true).getFieldNotes();
1271 }
1272 }
1273 public void setFieldNotes(String fieldNotes) {
1274 getFieldObservation(true).setFieldNotes(fieldNotes);
1275 }
1276
1277
1278 //individual counts
1279 public Integer getIndividualCount() {
1280 return (hasFieldObservation()? getFieldObservation(true).getIndividualCount() : null );
1281 }
1282 public void setIndividualCount(Integer individualCount) {
1283 getFieldObservation(true).setIndividualCount(individualCount);
1284 }
1285
1286 //life stage
1287 public Stage getLifeStage() {
1288 return (hasFieldObservation()? getFieldObservation(true).getLifeStage() : null );
1289 }
1290 public void setLifeStage(Stage lifeStage) {
1291 getFieldObservation(true).setLifeStage(lifeStage);
1292 }
1293
1294 //sex
1295 public Sex getSex() {
1296 return (hasFieldObservation()? getFieldObservation(true).getSex() : null );
1297 }
1298 public void setSex(Sex sex) {
1299 getFieldObservation(true).setSex(sex);
1300 }
1301
1302
1303 //field observation
1304 public boolean hasFieldObservation(){
1305 return (getFieldObservation(false) != null);
1306 }
1307
1308 /**
1309 * Returns the field observation as an object.
1310 * @return
1311 */
1312 @Transient
1313 public FieldObservation getFieldObservation(){
1314 return getFieldObservation(false);
1315 }
1316
1317 /**
1318 * Returns the field observation as an object.
1319 * @return
1320 */
1321 public FieldObservation getFieldObservation(boolean createIfNotExists){
1322 if (fieldObservation == null && createIfNotExists){
1323 fieldObservation = FieldObservation.NewInstance();
1324 fieldObservation.addPropertyChangeListener(getNewEventPropagationListener());
1325 DerivationEvent derivationEvent = getDerivationEvent(true);
1326 derivationEvent.addOriginal(fieldObservation);
1327 }
1328 return this.fieldObservation;
1329 }
1330
1331
1332
1333
1334
1335 //****************** Specimen **************************************************
1336
1337 //Definition
1338 public void addDerivedUnitDefinition(String text, Language language) {
1339 derivedUnit.addDefinition(text, language);
1340 }
1341 public Map<Language, LanguageString> getDerivedUnitDefinitions(){
1342 return this.derivedUnit.getDefinition();
1343 }
1344 public String getDerivedUnitDefinition(Language language) {
1345 Map<Language,LanguageString> languageMap = derivedUnit.getDefinition();
1346 LanguageString languageString = languageMap.get(language);
1347 if (languageString != null){
1348 return languageString.getText();
1349 }else {
1350 return null;
1351 }
1352 }
1353 public void removeDerivedUnitDefinition(Language lang) {
1354 derivedUnit.removeDefinition(lang);
1355 }
1356
1357 //Determination
1358 public void addDetermination(DeterminationEvent determination) {
1359 derivedUnit.addDetermination(determination);
1360 }
1361 public Set<DeterminationEvent> getDeterminations() {
1362 return derivedUnit.getDeterminations();
1363 }
1364 public void removeDetermination(DeterminationEvent determination) {
1365 derivedUnit.removeDetermination(determination);
1366 }
1367
1368 //Media
1369 public boolean addDerivedUnitMedia(Media media) {
1370 try {
1371 return addMedia(media, derivedUnit);
1372 } catch (DerivedUnitFacadeNotSupportedException e) {
1373 throw new IllegalStateException(notSupportMessage, e);
1374 }
1375 }
1376 /**
1377 * Returns true, if an image gallery exists for the specimen.<BR>
1378 * Returns also <code>true</code> if the image gallery is empty.
1379 */
1380 public boolean hasDerivedUnitImageGallery(){
1381 return (getImageGallery(derivedUnit, false) != null);
1382 }
1383
1384 public SpecimenDescription getDerivedUnitImageGallery(boolean createIfNotExists){
1385 TextData textData;
1386 try {
1387 textData = inititialzeTextDataWithSupportTest(Feature.IMAGE(), derivedUnit, createIfNotExists, true);
1388 } catch (DerivedUnitFacadeNotSupportedException e) {
1389 throw new IllegalStateException(notSupportMessage, e);
1390 }
1391 if (textData != null){
1392 return CdmBase.deproxy(textData.getInDescription(), SpecimenDescription.class);
1393 }else{
1394 return null;
1395 }
1396 }
1397
1398 /**
1399 * Returns the media for the specimen.<BR>
1400 * @return
1401 */
1402 public List<Media> getDerivedUnitMedia() {
1403 try {
1404 List<Media> result = getMedia(derivedUnit, false);
1405 return result == null ? new ArrayList<Media>() : result;
1406 } catch (DerivedUnitFacadeNotSupportedException e) {
1407 throw new IllegalStateException(notSupportMessage, e);
1408 }
1409 }
1410 public boolean removeDerivedUnitMedia(Media media) {
1411 try {
1412 return removeMedia(media, derivedUnit);
1413 } catch (DerivedUnitFacadeNotSupportedException e) {
1414 throw new IllegalStateException(notSupportMessage, e);
1415 }
1416 }
1417
1418
1419 //Accession Number
1420 public String getAccessionNumber() {
1421 return derivedUnit.getAccessionNumber();
1422 }
1423 public void setAccessionNumber(String accessionNumber) {
1424 derivedUnit.setAccessionNumber(accessionNumber);
1425 }
1426
1427 //Catalog Number
1428 public String getCatalogNumber() {
1429 return derivedUnit.getCatalogNumber();
1430 }
1431 public void setCatalogNumber(String catalogNumber) {
1432 derivedUnit.setCatalogNumber(catalogNumber);
1433 }
1434
1435 //Preservation Method
1436
1437 /**
1438 * Only supported by specimen and fossils
1439 * @see #DerivedUnitType
1440 * @return
1441 */
1442 public PreservationMethod getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException {
1443 if (derivedUnit.isInstanceOf(Specimen.class)){
1444 return CdmBase.deproxy(derivedUnit, Specimen.class).getPreservation();
1445 }else{
1446 if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()){
1447 throw new MethodNotSupportedByDerivedUnitTypeException("A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
1448 }else{
1449 return null;
1450 }
1451 }
1452 }
1453 /**
1454 * Only supported by specimen and fossils
1455 * @see #DerivedUnitType
1456 * @return
1457 */
1458 public void setPreservationMethod(PreservationMethod preservation)throws MethodNotSupportedByDerivedUnitTypeException {
1459 if (derivedUnit.isInstanceOf(Specimen.class)){
1460 CdmBase.deproxy(derivedUnit, Specimen.class).setPreservation(preservation);
1461 }else{
1462 if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()){
1463 throw new MethodNotSupportedByDerivedUnitTypeException("A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
1464 }else{
1465 return;
1466 }
1467 }
1468 }
1469
1470 //Stored under name
1471 public TaxonNameBase getStoredUnder() {
1472 return derivedUnit.getStoredUnder();
1473 }
1474 public void setStoredUnder(TaxonNameBase storedUnder) {
1475 derivedUnit.setStoredUnder(storedUnder);
1476 }
1477
1478 //colletors number
1479 public String getCollectorsNumber() {
1480 return derivedUnit.getCollectorsNumber();
1481 }
1482 public void setCollectorsNumber(String collectorsNumber) {
1483 this.derivedUnit.setCollectorsNumber(collectorsNumber);
1484 }
1485
1486 //title cache
1487 public String getTitleCache() {
1488 if (! derivedUnit.isProtectedTitleCache()){
1489 //always compute title cache anew as long as there are no property change listeners on
1490 //field observation, gathering event etc
1491 derivedUnit.setTitleCache(null, false);
1492 }
1493 return this.derivedUnit.getTitleCache();
1494 }
1495 public void setTitleCache(String titleCache, boolean isProtected) {
1496 this.derivedUnit.setTitleCache(titleCache, isProtected);
1497 }
1498
1499
1500 /**
1501 * Returns the derived unit itself.
1502 * @return the derived unit
1503 */
1504 @Transient
1505 public DerivedUnitBase getDerivedUnit() {
1506 return this.derivedUnit;
1507 }
1508
1509 private boolean hasDerivationEvent(){
1510 return getDerivationEvent() == null ? false : true;
1511 }
1512 private DerivationEvent getDerivationEvent(){
1513 return getDerivationEvent(false);
1514 }
1515 private DerivationEvent getDerivationEvent(boolean createIfNotExists){
1516 DerivationEvent result = derivedUnit.getDerivedFrom();
1517 if (result == null){
1518 result = DerivationEvent.NewInstance();
1519 derivedUnit.setDerivedFrom(result);
1520 }
1521 return result;
1522 }
1523
1524 public String getExsiccatum() {
1525 logger.warn("Exsiccatum method not yet supported. Needs model change");
1526 return null;
1527 }
1528
1529 public String setExsiccatum() throws MethodNotSupportedException{
1530 throw new MethodNotSupportedException("Exsiccatum method not yet supported. Needs model change");
1531 }
1532
1533
1534 // **** sources **/
1535 public void addSource(IdentifiableSource source){
1536 this.derivedUnit.addSource(source);
1537 }
1538 /**
1539 * Creates an orignal source, adds it to the specimen and returns it.
1540 * @param reference
1541 * @param microReference
1542 * @param originalNameString
1543 * @return
1544 */
1545 public IdentifiableSource addSource(ReferenceBase reference, String microReference, String originalNameString){
1546 IdentifiableSource source = IdentifiableSource.NewInstance(reference, microReference);
1547 source.setOriginalNameString(originalNameString);
1548 derivedUnit.addSource(source);
1549 return source;
1550 }
1551
1552 public Set<IdentifiableSource> getSources(){
1553 return derivedUnit.getSources();
1554 }
1555
1556 public void removeSource(IdentifiableSource source){
1557 this.derivedUnit.removeSource(source);
1558 }
1559
1560
1561 /**
1562 * @return the collection
1563 */
1564 public Collection getCollection() {
1565 return derivedUnit.getCollection();
1566 }
1567
1568
1569 /**
1570 * @param collection the collection to set
1571 */
1572 public void setCollection(Collection collection) {
1573 derivedUnit.setCollection(collection);
1574 }
1575
1576 //annotation
1577 public void addAnnotation(Annotation annotation){
1578 this.derivedUnit.addAnnotation(annotation);
1579 }
1580
1581 public void getAnnotations(){
1582 this.derivedUnit.getAnnotations();
1583 }
1584
1585 public void removeAnnotation(Annotation annotation){
1586 this.derivedUnit.removeAnnotation(annotation);
1587 }
1588
1589
1590 // ******************************* Events *********************************************
1591
1592 /**
1593 * @return
1594 */
1595 private PropertyChangeListener getNewEventPropagationListener() {
1596 PropertyChangeListener listener = new PropertyChangeListener(){
1597 @Override
1598 public void propertyChange(PropertyChangeEvent event) {
1599 derivedUnit.firePropertyChange(event);
1600 }
1601
1602 };
1603 return listener;
1604 }
1605
1606
1607
1608
1609 //**************** Other Collections ***************************************************
1610
1611 /**
1612 * Creates a duplicate specimen which derives from the same derivation event
1613 * as the facade specimen and adds collection data to it (all data available in
1614 * DerivedUnitBase and Specimen. Data from SpecimenOrObservationBase and above
1615 * are not yet shared at the moment.
1616 * @param collection
1617 * @param catalogNumber
1618 * @param accessionNumber
1619 * @param collectorsNumber
1620 * @param storedUnder
1621 * @param preservation
1622 * @return
1623 */
1624 public Specimen addDuplicate(Collection collection, String catalogNumber, String accessionNumber,
1625 String collectorsNumber, TaxonNameBase storedUnder, PreservationMethod preservation){
1626 Specimen duplicate = Specimen.NewInstance();
1627 duplicate.setDerivedFrom(getDerivationEvent(true));
1628 duplicate.setCollection(collection);
1629 duplicate.setCatalogNumber(catalogNumber);
1630 duplicate.setAccessionNumber(accessionNumber);
1631 duplicate.setCollectorsNumber(collectorsNumber);
1632 duplicate.setStoredUnder(storedUnder);
1633 duplicate.setPreservation(preservation);
1634 return duplicate;
1635 }
1636
1637 public void addDuplicate(DerivedUnitBase duplicateSpecimen){
1638 //TODO check derivedUnitType
1639 getDerivationEvent(true).addDerivative(duplicateSpecimen);
1640 }
1641
1642 @Transient
1643 public Set<Specimen> getDuplicates(){
1644 Set<Specimen> result = new HashSet<Specimen>();
1645 if (hasDerivationEvent()){
1646 for (DerivedUnitBase derivedUnit: getDerivationEvent(true).getDerivatives()){
1647 if (derivedUnit.isInstanceOf(Specimen.class) && ! derivedUnit.equals(this.derivedUnit)){
1648 result.add(CdmBase.deproxy(derivedUnit, Specimen.class));
1649 }
1650 }
1651 }
1652 return result;
1653 }
1654 public void removeDuplicate(Specimen duplicateSpecimen){
1655 if (hasDerivationEvent()){
1656 getDerivationEvent(true).removeDerivative(duplicateSpecimen);
1657 }
1658 }
1659
1660
1661
1662
1663 }