minor
[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.text.ParseException;
15 import java.util.ArrayList;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Set;
21
22 import javax.persistence.Transient;
23
24 import org.apache.log4j.Logger;
25
26 import eu.etaxonomy.cdm.api.service.IOccurrenceService;
27 import eu.etaxonomy.cdm.model.agent.AgentBase;
28 import eu.etaxonomy.cdm.model.agent.Person;
29 import eu.etaxonomy.cdm.model.common.Annotation;
30 import eu.etaxonomy.cdm.model.common.CdmBase;
31 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
32 import eu.etaxonomy.cdm.model.common.Language;
33 import eu.etaxonomy.cdm.model.common.LanguageString;
34 import eu.etaxonomy.cdm.model.common.TimePeriod;
35 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
36 import eu.etaxonomy.cdm.model.description.Feature;
37 import eu.etaxonomy.cdm.model.description.Sex;
38 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
39 import eu.etaxonomy.cdm.model.description.Stage;
40 import eu.etaxonomy.cdm.model.description.TextData;
41 import eu.etaxonomy.cdm.model.location.NamedArea;
42 import eu.etaxonomy.cdm.model.location.Point;
43 import eu.etaxonomy.cdm.model.location.ReferenceSystem;
44 import eu.etaxonomy.cdm.model.media.Media;
45 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
46 import eu.etaxonomy.cdm.model.occurrence.Collection;
47 import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
48 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
49 import eu.etaxonomy.cdm.model.occurrence.DerivedUnitBase;
50 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
51 import eu.etaxonomy.cdm.model.occurrence.FieldObservation;
52 import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
53 import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;
54 import eu.etaxonomy.cdm.model.occurrence.Specimen;
55 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
56 import eu.etaxonomy.cdm.model.reference.Reference;
57
58 /**
59 * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
60 * a specimen based view. It does not support all functionality available in the
61 * occurrence package.<BR>
62 * The most significant restriction is that a specimen may derive only from one
63 * direct derivation event and there must be only one field observation
64 * (gathering event) it derives from.<BR>
65 *
66 * @author a.mueller
67 * @date 14.05.2010
68 */
69 public class DerivedUnitFacade {
70 @SuppressWarnings("unused")
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 private static final boolean CREATE = true;
76 private static final boolean CREATE_NOT = false;
77
78 /**
79 * Enum that defines the class the "Specimen" belongs to. Some methods of
80 * the facade are not available for certain classes and will throw an
81 * Exception when invoking them.
82 */
83 public enum DerivedUnitType {
84 Specimen("Specimen"),
85 Observation("Observation"),
86 LivingBeing("Living Being"),
87 Fossil("Fossil"),
88 DerivedUnit("Derived Unit"),
89 //Field Observation is experimental, please handle with care (it is the only type which does not
90 //have a derivedUnit and therefore throws exceptions for all method on derivedUnit attributes
91 FieldObservation("FieldObservation");
92
93 String representation;
94
95 private DerivedUnitType(String representation) {
96 this.representation = representation;
97 }
98
99 /**
100 * @return the representation
101 */
102 public String getRepresentation() {
103 return representation;
104 }
105
106 private DerivedUnitBase getNewDerivedUnitInstance() {
107 if (this == DerivedUnitType.Specimen) {
108 return eu.etaxonomy.cdm.model.occurrence.Specimen.NewInstance();
109 } else if (this == DerivedUnitType.Observation) {
110 return eu.etaxonomy.cdm.model.occurrence.Observation.NewInstance();
111 } else if (this == DerivedUnitType.LivingBeing) {
112 return eu.etaxonomy.cdm.model.occurrence.LivingBeing.NewInstance();
113 } else if (this == DerivedUnitType.Fossil) {
114 return eu.etaxonomy.cdm.model.occurrence.Fossil.NewInstance();
115 } else if (this == DerivedUnitType.DerivedUnit) {
116 return eu.etaxonomy.cdm.model.occurrence.DerivedUnit.NewInstance();
117 } else if (this == DerivedUnitType.FieldObservation) {
118 return null;
119 } else {
120 String message = "Unknown derived unit type %s";
121 message = String.format(message, this.getRepresentation());
122 throw new IllegalStateException(message);
123 }
124 }
125
126 public static DerivedUnitType valueOf2(String type) {
127 if (type == null) {
128 return null;
129 }
130 type = type.replace(" ", "").toLowerCase();
131 if (type.equals("specimen")) {
132 return Specimen;
133 } else if (type.equals("livingbeing")) {
134 return LivingBeing;
135 } else if (type.equals("observation")) {
136 return Observation;
137 } else if (type.equals("fossil")) {
138 return Fossil;
139 } else if (type.equals("fieldobservation")) {
140 return DerivedUnitType.FieldObservation;
141 } else if (type.equals("unknown")) {
142 return DerivedUnitType.DerivedUnit;
143 } else if (type.equals("derivedunit")) {
144 return DerivedUnitType.DerivedUnit;
145 }
146 return null;
147 }
148
149 public static DerivedUnitType valueOf2(Class<? extends SpecimenOrObservationBase> clazz) {
150 if (clazz == null) {
151 return null;
152 }
153 if (clazz.equals(Specimen.class)) {
154 return Specimen;
155 } else if (clazz.equals(eu.etaxonomy.cdm.model.occurrence.LivingBeing.class)) {
156 return LivingBeing;
157 } else if (clazz.equals(eu.etaxonomy.cdm.model.occurrence.Observation.class)) {
158 return Observation;
159 } else if (clazz.equals(eu.etaxonomy.cdm.model.occurrence.Fossil.class)) {
160 return Fossil;
161 } else if (clazz.equals(FieldObservation.class)) {
162 return DerivedUnitType.FieldObservation;
163 } else if (clazz.equals(DerivedUnit.class)) {
164 return DerivedUnitType.DerivedUnit;
165 }
166 return null;
167 }
168
169 }
170
171 private final DerivedUnitFacadeConfigurator config;
172
173 // private GatheringEvent gatheringEvent;
174 private DerivedUnitType type; // needed?
175
176 private FieldObservation fieldObservation;
177
178 private final DerivedUnitBase derivedUnit;
179
180 // media - the text data holding the media
181 private TextData derivedUnitMediaTextData;
182 private TextData fieldObjectMediaTextData;
183
184 private TextData ecology;
185 private TextData plantDescription;
186
187 /**
188 * Creates a derived unit facade for a new derived unit of type
189 * <code>type</code>.
190 *
191 * @param type
192 * @return
193 */
194 public static DerivedUnitFacade NewInstance(DerivedUnitType type) {
195 return new DerivedUnitFacade(type, null);
196 }
197
198 /**
199 * Creates a derived unit facade for a new derived unit of type
200 * <code>type</code>.
201 *
202 * @param type
203 * @return
204 */
205 public static DerivedUnitFacade NewInstance(DerivedUnitType type, FieldObservation fieldObservation) {
206 return new DerivedUnitFacade(type, fieldObservation);
207 }
208
209 /**
210 * Creates a derived unit facade for a given derived unit using the default
211 * configuration.
212 *
213 * @param derivedUnit
214 * @return
215 * @throws DerivedUnitFacadeNotSupportedException
216 */
217 public static DerivedUnitFacade NewInstance(DerivedUnitBase derivedUnit)
218 throws DerivedUnitFacadeNotSupportedException {
219 return new DerivedUnitFacade(derivedUnit, null);
220 }
221
222 public static DerivedUnitFacade NewInstance(DerivedUnitBase derivedUnit,
223 DerivedUnitFacadeConfigurator config)
224 throws DerivedUnitFacadeNotSupportedException {
225 return new DerivedUnitFacade(derivedUnit, config);
226 }
227
228 // ****************** CONSTRUCTOR ******************************************
229
230 private DerivedUnitFacade(DerivedUnitType type, FieldObservation fieldObservation) {
231 this.config = DerivedUnitFacadeConfigurator.NewInstance();
232 this.type = type;
233 // derivedUnit
234 derivedUnit = type.getNewDerivedUnitInstance();
235 setFieldObservation(fieldObservation);
236 if (derivedUnit != null){
237 setCacheStrategy();
238 }else{
239 setFieldObservationCacheStrategy();
240 }
241 }
242
243 private DerivedUnitFacade(DerivedUnitBase derivedUnit,
244 DerivedUnitFacadeConfigurator config)
245 throws DerivedUnitFacadeNotSupportedException {
246
247 if (config == null) {
248 config = DerivedUnitFacadeConfigurator.NewInstance();
249 }
250 this.config = config;
251
252 // derived unit
253 this.derivedUnit = derivedUnit;
254 this.type = DerivedUnitType.valueOf2(this.derivedUnit.getClass());
255
256 // derivation event
257 if (this.derivedUnit.getDerivedFrom() != null) {
258 DerivationEvent derivationEvent = getDerivationEvent(CREATE);
259 // fieldObservation
260 Set<FieldObservation> fieldOriginals = getFieldObservationsOriginals(
261 derivationEvent, null);
262 if (fieldOriginals.size() > 1) {
263 throw new DerivedUnitFacadeNotSupportedException(
264 "Specimen must not have more than 1 derivation event");
265 } else if (fieldOriginals.size() == 0) {
266 // fieldObservation = FieldObservation.NewInstance();
267 } else if (fieldOriginals.size() == 1) {
268 fieldObservation = fieldOriginals.iterator().next();
269 // ###fieldObservation =
270 // getInitializedFieldObservation(fieldObservation);
271 fieldObservation
272 .addPropertyChangeListener(getNewEventPropagationListener());
273 } else {
274 throw new IllegalStateException("Illegal state");
275 }
276 }
277
278 this.derivedUnitMediaTextData = inititializeTextDataWithSupportTest(
279 Feature.IMAGE(), this.derivedUnit, false, true);
280
281 fieldObjectMediaTextData = initializeFieldObjectTextDataWithSupportTest(
282 Feature.IMAGE(), false, true);
283
284 // handle derivedUnit.getMedia()
285 if (derivedUnit.getMedia().size() > 0) {
286 // TODO better changed model here to allow only one place for images
287 if (this.config.isMoveDerivedUnitMediaToGallery()) {
288 Set<Media> mediaSet = derivedUnit.getMedia();
289 for (Media media : mediaSet) {
290 this.addDerivedUnitMedia(media);
291 }
292 mediaSet.removeAll(getDerivedUnitMedia());
293 } else {
294 throw new DerivedUnitFacadeNotSupportedException(
295 "Specimen may not have direct media. Only (one) image gallery is allowed");
296 }
297 }
298
299 // handle fieldObservation.getMedia()
300 if (fieldObservation != null && fieldObservation.getMedia() != null
301 && fieldObservation.getMedia().size() > 0) {
302 // TODO better changed model here to allow only one place for images
303 if (this.config.isMoveFieldObjectMediaToGallery()) {
304 Set<Media> mediaSet = fieldObservation.getMedia();
305 for (Media media : mediaSet) {
306 this.addFieldObjectMedia(media);
307 }
308 mediaSet.removeAll(getFieldObjectMedia());
309 } else {
310 throw new DerivedUnitFacadeNotSupportedException(
311 "Field object may not have direct media. Only (one) image gallery is allowed");
312 }
313 }
314
315 // test if descriptions are supported
316 ecology = initializeFieldObjectTextDataWithSupportTest(
317 Feature.ECOLOGY(), false, false);
318 plantDescription = initializeFieldObjectTextDataWithSupportTest(
319 Feature.DESCRIPTION(), false, false);
320
321 setCacheStrategy();
322
323 }
324
325 private DerivedUnitBase getInitializedDerivedUnit(
326 DerivedUnitBase derivedUnit) {
327 IOccurrenceService occurrenceService = this.config
328 .getOccurrenceService();
329 if (occurrenceService == null) {
330 return derivedUnit;
331 }
332 List<String> propertyPaths = this.config.getPropertyPaths();
333 if (propertyPaths == null) {
334 return derivedUnit;
335 }
336 propertyPaths = getDerivedUnitPropertyPaths(propertyPaths);
337 DerivedUnitBase result = (DerivedUnitBase) occurrenceService.load(
338 derivedUnit.getUuid(), propertyPaths);
339 return result;
340 }
341
342 /**
343 * Initializes the derived unit according to the configuartions property
344 * path. If the property path is <code>null</code> or no occurrence service
345 * is given the returned object is the same as the input parameter.
346 *
347 * @param fieldObservation2
348 * @return
349 */
350 private FieldObservation getInitializedFieldObservation(
351 FieldObservation fieldObservation) {
352 IOccurrenceService occurrenceService = this.config
353 .getOccurrenceService();
354 if (occurrenceService == null) {
355 return fieldObservation;
356 }
357 List<String> propertyPaths = this.config.getPropertyPaths();
358 if (propertyPaths == null) {
359 return fieldObservation;
360 }
361 propertyPaths = getFieldObjectPropertyPaths(propertyPaths);
362 FieldObservation result = (FieldObservation) occurrenceService.load(
363 fieldObservation.getUuid(), propertyPaths);
364 return result;
365 }
366
367 /**
368 * Transforms the property paths in a way that the facade is handled just
369 * like an ordinary CdmBase object.<BR>
370 * E.g. a property path "collectinAreas" will be translated into
371 * gatheringEvent.collectingAreas
372 *
373 * @param propertyPaths
374 * @return
375 */
376 private List<String> getFieldObjectPropertyPaths(List<String> propertyPaths) {
377 List<String> result = new ArrayList<String>();
378 for (String facadePath : propertyPaths) {
379 // collecting areas (named area)
380 if (facadePath.startsWith("collectingAreas")) {
381 facadePath = "gatheringEvent." + facadePath;
382 result.add(facadePath);
383 }
384 // collector (agentBase)
385 else if (facadePath.startsWith("collector")) {
386 facadePath = facadePath.replace("collector",
387 "gatheringEvent.actor");
388 result.add(facadePath);
389 }
390 // exactLocation (agentBase)
391 else if (facadePath.startsWith("exactLocation")) {
392 facadePath = "gatheringEvent." + facadePath;
393 result.add(facadePath);
394 }
395 // gatheringPeriod (TimePeriod)
396 else if (facadePath.startsWith("gatheringPeriod")) {
397 facadePath = facadePath.replace("gatheringPeriod",
398 "gatheringEvent.timeperiod");
399 result.add(facadePath);
400 }
401 // (locality/ localityLanguage , LanguageString)
402 else if (facadePath.startsWith("locality")) {
403 facadePath = "gatheringEvent." + facadePath;
404 result.add(facadePath);
405 }
406
407 // *********** FIELD OBJECT ************
408 // fieldObjectDefinitions (Map<language, languageString)
409 else if (facadePath.startsWith("fieldObjectDefinitions")) {
410 // TODO or definition ???
411 facadePath = facadePath.replace("fieldObjectDefinitions",
412 "description");
413 result.add(facadePath);
414 }
415 // fieldObjectMedia (Media)
416 else if (facadePath.startsWith("fieldObjectMedia")) {
417 // TODO ???
418 facadePath = facadePath.replace("fieldObjectMedia",
419 "descriptions.elements.media");
420 result.add(facadePath);
421 }
422
423 // Gathering Event will always be added
424 result.add("gatheringEvent");
425
426 }
427
428 /*
429 * Gathering Event ==================== - gatheringEvent
430 * (GatheringEvent)
431 *
432 * Field Object ================= - ecology/ ecologyAll (String) ??? -
433 * plant description (like ecology)
434 *
435 * - fieldObjectImageGallery (SpecimenDescription) - is automatically
436 * initialized via fieldObjectMedia
437 */
438
439 return result;
440 }
441
442 /**
443 * Transforms the property paths in a way that the facade is handled just
444 * like an ordinary CdmBase object.<BR>
445 * E.g. a property path "collectinAreas" will be translated into
446 * gatheringEvent.collectingAreas
447 *
448 * Not needed (?) as the facade works with REST service property paths
449 * without using this method.
450 *
451 * @param propertyPaths
452 * @return
453 */
454 private List<String> getDerivedUnitPropertyPaths(List<String> propertyPaths) {
455 List<String> result = new ArrayList<String>();
456 for (String facadePath : propertyPaths) {
457 // determinations (DeterminationEvent)
458 if (facadePath.startsWith("determinations")) {
459 facadePath = "" + facadePath; // no change
460 result.add(facadePath);
461 }
462 // storedUnder (TaxonNameBase)
463 else if (facadePath.startsWith("storedUnder")) {
464 facadePath = "" + facadePath; // no change
465 result.add(facadePath);
466 }
467 // sources (IdentifiableSource)
468 else if (facadePath.startsWith("sources")) {
469 facadePath = "" + facadePath; // no change
470 result.add(facadePath);
471 }
472 // collection (Collection)
473 else if (facadePath.startsWith("collection")) {
474 facadePath = "" + facadePath; // no change
475 result.add(facadePath);
476 }
477 // (locality/ localityLanguage , LanguageString)
478 else if (facadePath.startsWith("locality")) {
479 facadePath = "gatheringEvent." + facadePath;
480 result.add(facadePath);
481 }
482
483 // *********** FIELD OBJECT ************
484 // derivedUnitDefinitions (Map<language, languageString)
485 else if (facadePath.startsWith("derivedUnitDefinitions")) {
486 // TODO or definition ???
487 facadePath = facadePath.replace("derivedUnitDefinitions",
488 "description");
489 result.add(facadePath);
490 }
491
492 // derivedUnitMedia (Media)
493 else if (facadePath.startsWith("derivedUnitMedia")) {
494 // TODO ???
495 facadePath = facadePath.replace("derivedUnitMedia",
496 "descriptions.elements.media");
497 result.add(facadePath);
498 }
499
500 }
501
502 /*
503 * //TODO Derived Unit =====================
504 *
505 * - derivedUnitImageGallery (SpecimenDescription) - is automatically
506 * initialized via derivedUnitMedia
507 *
508 * - derivationEvent (DerivationEvent) - will always be initialized -
509 * duplicates (??? Specimen???) ???
510 */
511
512 return result;
513 }
514
515 /**
516 *
517 */
518 private void setCacheStrategy() {
519 if (derivedUnit == null) {
520 throw new NullPointerException(
521 "Facade's derviedUnit must not be null to set cache strategy");
522 }else{
523 derivedUnit.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
524 setFieldObservationCacheStrategy();
525 }
526 }
527
528 private void setFieldObservationCacheStrategy() {
529 if (this.hasFieldObject()){
530 DerivedUnitFacadeFieldObservationCacheStrategy strategy = new DerivedUnitFacadeFieldObservationCacheStrategy();
531 this.fieldObservation.setCacheStrategy(strategy);
532 }
533 }
534
535 /**
536 * @param feature
537 * @param createIfNotExists
538 * @param isImageGallery
539 * @return
540 * @throws DerivedUnitFacadeNotSupportedException
541 */
542 private TextData initializeFieldObjectTextDataWithSupportTest(
543 Feature feature, boolean createIfNotExists, boolean isImageGallery)
544 throws DerivedUnitFacadeNotSupportedException {
545 // field object
546 FieldObservation fieldObject = getFieldObservation(createIfNotExists);
547 if (fieldObject == null) {
548 return null;
549 }
550 return inititializeTextDataWithSupportTest(feature, fieldObject,
551 createIfNotExists, isImageGallery);
552 }
553
554 /**
555 * @param feature
556 * @param specimen
557 * @param createIfNotExists
558 * @param isImageGallery
559 * @return
560 * @throws DerivedUnitFacadeNotSupportedException
561 */
562 private TextData inititializeTextDataWithSupportTest(Feature feature,
563 SpecimenOrObservationBase specimen, boolean createIfNotExists,
564 boolean isImageGallery)
565 throws DerivedUnitFacadeNotSupportedException {
566 if (feature == null) {
567 return null;
568 }
569 TextData textData = null;
570 if (createIfNotExists) {
571 textData = TextData.NewInstance(feature);
572 }
573
574 Set<SpecimenDescription> descriptions;
575 if (isImageGallery) {
576 descriptions = specimen.getSpecimenDescriptionImageGallery();
577 } else {
578 descriptions = specimen.getSpecimenDescriptions(false);
579 }
580 // no description exists yet for this specimen
581 if (descriptions.size() == 0) {
582 if (createIfNotExists) {
583 SpecimenDescription newSpecimenDescription = SpecimenDescription
584 .NewInstance(specimen);
585 newSpecimenDescription.addElement(textData);
586 newSpecimenDescription.setImageGallery(isImageGallery);
587 return textData;
588 } else {
589 return null;
590 }
591 }
592 // description already exists
593 Set<DescriptionElementBase> existingTextData = new HashSet<DescriptionElementBase>();
594 for (SpecimenDescription description : descriptions) {
595 // collect all existing text data
596 for (DescriptionElementBase element : description.getElements()) {
597 if (element.isInstanceOf(TextData.class)
598 && (feature.equals(element.getFeature()) || isImageGallery)) {
599 existingTextData.add(element);
600 }
601 }
602 }
603 // use existing text data if exactly one exists
604 if (existingTextData.size() > 1) {
605 throw new DerivedUnitFacadeNotSupportedException(
606 "Specimen facade does not support more than one description text data of type "
607 + feature.getLabel());
608
609 } else if (existingTextData.size() == 1) {
610 return CdmBase.deproxy(existingTextData.iterator().next(),
611 TextData.class);
612 } else {
613 if (createIfNotExists) {
614 SpecimenDescription description = descriptions.iterator()
615 .next();
616 description.addElement(textData);
617 }
618 return textData;
619 }
620 }
621
622 /**
623 * Tests if a given image gallery is supported by the derived unit facade.
624 * It returns the only text data attached to the given image gallery. If the
625 * given image gallery does not have text data attached, it is created and
626 * attached.
627 *
628 * @param imageGallery
629 * @return
630 * @throws DerivedUnitFacadeNotSupportedException
631 */
632 private TextData testImageGallery(SpecimenDescription imageGallery)
633 throws DerivedUnitFacadeNotSupportedException {
634 if (imageGallery.isImageGallery() == false) {
635 throw new DerivedUnitFacadeNotSupportedException(
636 "Image gallery needs to have image gallery flag set");
637 }
638 if (imageGallery.getElements().size() > 1) {
639 throw new DerivedUnitFacadeNotSupportedException(
640 "Image gallery must not have more then one description element");
641 }
642 TextData textData;
643 if (imageGallery.getElements().size() == 0) {
644 textData = TextData.NewInstance(Feature.IMAGE());
645 imageGallery.addElement(textData);
646 } else {
647 if (!imageGallery.getElements().iterator().next()
648 .isInstanceOf(TextData.class)) {
649 throw new DerivedUnitFacadeNotSupportedException(
650 "Image gallery must only have TextData as element");
651 } else {
652 textData = CdmBase.deproxy(imageGallery.getElements()
653 .iterator().next(), TextData.class);
654 }
655 }
656 return textData;
657 }
658
659 // ************************** METHODS
660 // *****************************************
661
662 private TextData getDerivedUnitImageGalleryTextData(
663 boolean createIfNotExists)
664 throws DerivedUnitFacadeNotSupportedException {
665 if (this.derivedUnitMediaTextData == null && createIfNotExists) {
666 this.derivedUnitMediaTextData = getImageGalleryTextData(
667 derivedUnit, "Specimen");
668 }
669 return this.derivedUnitMediaTextData;
670 }
671
672 private TextData getObservationImageGalleryTextData(
673 boolean createIfNotExists)
674 throws DerivedUnitFacadeNotSupportedException {
675 if (this.fieldObjectMediaTextData == null && createIfNotExists) {
676 this.fieldObjectMediaTextData = getImageGalleryTextData(
677 fieldObservation, "Field observation");
678 }
679 return this.fieldObjectMediaTextData;
680 }
681
682 /**
683 * @param derivationEvent2
684 * @return
685 * @throws DerivedUnitFacadeNotSupportedException
686 */
687 private Set<FieldObservation> getFieldObservationsOriginals(
688 DerivationEvent derivationEvent,
689 Set<SpecimenOrObservationBase> recursionAvoidSet)
690 throws DerivedUnitFacadeNotSupportedException {
691 if (recursionAvoidSet == null) {
692 recursionAvoidSet = new HashSet<SpecimenOrObservationBase>();
693 }
694 Set<FieldObservation> result = new HashSet<FieldObservation>();
695 Set<SpecimenOrObservationBase> originals = derivationEvent
696 .getOriginals();
697 for (SpecimenOrObservationBase original : originals) {
698 if (original.isInstanceOf(FieldObservation.class)) {
699 result.add(CdmBase.deproxy(original, FieldObservation.class));
700 } else if (original.isInstanceOf(DerivedUnitBase.class)) {
701 // if specimen has already been tested exclude it from further
702 // recursion
703 if (recursionAvoidSet.contains(original)) {
704 continue;
705 }
706 DerivedUnitBase derivedUnit = CdmBase.deproxy(original,
707 DerivedUnitBase.class);
708 DerivationEvent originalDerivation = derivedUnit
709 .getDerivedFrom();
710 // Set<DerivationEvent> derivationEvents =
711 // original.getDerivationEvents();
712 // for (DerivationEvent originalDerivation : derivationEvents){
713 Set<FieldObservation> fieldObservations = getFieldObservationsOriginals(
714 originalDerivation, recursionAvoidSet);
715 result.addAll(fieldObservations);
716 // }
717 } else {
718 throw new DerivedUnitFacadeNotSupportedException(
719 "Unhandled specimen or observation base type: "
720 + original.getClass().getName());
721 }
722
723 }
724 return result;
725 }
726
727 // *********** MEDIA METHODS ******************************
728
729 // /**
730 // * Returns the media list for a specimen. Throws an exception if the
731 // existing specimen descriptions
732 // * are not supported by this facade.
733 // * @param specimen the specimen the media belongs to
734 // * @param specimenExceptionText text describing the specimen for exception
735 // messages
736 // * @return
737 // * @throws DerivedUnitFacadeNotSupportedException
738 // */
739 // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase
740 // specimen, String specimenExceptionText) throws
741 // DerivedUnitFacadeNotSupportedException{
742 // List<Media> result;
743 // SpecimenDescription imageGallery =
744 // getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
745 // TextData textData = getImageTextDataWithSupportTest(imageGallery,
746 // specimenExceptionText);
747 // result = textData.getMedia();
748 // return result;
749 // }
750
751 /**
752 * Returns the media list for a specimen. Throws an exception if the
753 * existing specimen descriptions are not supported by this facade.
754 *
755 * @param specimen
756 * the specimen the media belongs to
757 * @param specimenExceptionText
758 * text describing the specimen for exception messages
759 * @return
760 * @throws DerivedUnitFacadeNotSupportedException
761 */
762 private TextData getImageGalleryTextData(SpecimenOrObservationBase specimen, String specimenExceptionText)
763 throws DerivedUnitFacadeNotSupportedException {
764 TextData result;
765 SpecimenDescription imageGallery = getImageGalleryWithSupportTest(
766 specimen, specimenExceptionText, true);
767 result = getImageTextDataWithSupportTest(imageGallery,
768 specimenExceptionText);
769 return result;
770 }
771
772 /**
773 * Returns the image gallery of the according specimen. Throws an exception
774 * if the attached image gallerie(s) are not supported by this facade. If no
775 * image gallery exists a new one is created if
776 * <code>createNewIfNotExists</code> is true and if specimen is not
777 * <code>null</code>.
778 *
779 * @param specimen
780 * @param specimenText
781 * @param createNewIfNotExists
782 * @return
783 * @throws DerivedUnitFacadeNotSupportedException
784 */
785 private SpecimenDescription getImageGalleryWithSupportTest(
786 SpecimenOrObservationBase<?> specimen, String specimenText,
787 boolean createNewIfNotExists)
788 throws DerivedUnitFacadeNotSupportedException {
789 if (specimen == null) {
790 return null;
791 }
792 SpecimenDescription imageGallery;
793 if (hasMultipleImageGalleries(specimen)) {
794 throw new DerivedUnitFacadeNotSupportedException(specimenText
795 + " must not have more than 1 image gallery");
796 } else {
797 imageGallery = getImageGallery(specimen, createNewIfNotExists);
798 getImageTextDataWithSupportTest(imageGallery, specimenText);
799 }
800 return imageGallery;
801 }
802
803 /**
804 * Returns the media holding text data element of the image gallery. Throws
805 * an exception if multiple such text data already exist. Creates a new text
806 * data if none exists and adds it to the image gallery. If image gallery is
807 * <code>null</code> nothing happens.
808 *
809 * @param imageGallery
810 * @param textData
811 * @return
812 * @throws DerivedUnitFacadeNotSupportedException
813 */
814 private TextData getImageTextDataWithSupportTest(
815 SpecimenDescription imageGallery, String specimenText)
816 throws DerivedUnitFacadeNotSupportedException {
817 if (imageGallery == null) {
818 return null;
819 }
820 TextData textData = null;
821 for (DescriptionElementBase element : imageGallery.getElements()) {
822 if (element.isInstanceOf(TextData.class)
823 && element.getFeature().equals(Feature.IMAGE())) {
824 if (textData != null) {
825 throw new DerivedUnitFacadeNotSupportedException(
826 specimenText
827 + " must not have more than 1 image text data element in image gallery");
828 }
829 textData = CdmBase.deproxy(element, TextData.class);
830 }
831 }
832 if (textData == null) {
833 textData = TextData.NewInstance(Feature.IMAGE());
834 imageGallery.addElement(textData);
835 }
836 return textData;
837 }
838
839 /**
840 * Checks, if a specimen belongs to more than one description that is an
841 * image gallery
842 *
843 * @param derivedUnit
844 * @return
845 */
846 private boolean hasMultipleImageGalleries(
847 SpecimenOrObservationBase<?> derivedUnit) {
848 int count = 0;
849 Set<SpecimenDescription> descriptions = derivedUnit
850 .getSpecimenDescriptions();
851 for (SpecimenDescription description : descriptions) {
852 if (description.isImageGallery()) {
853 count++;
854 }
855 }
856 return (count > 1);
857 }
858
859 /**
860 * Returns the image gallery for a specimen. If there are multiple specimen
861 * descriptions marked as image galleries an arbitrary one is chosen. If no
862 * image gallery exists, a new one is created if
863 * <code>createNewIfNotExists</code> is <code>true</code>.<Br>
864 * If specimen is <code>null</code> a null pointer exception is thrown.
865 *
866 * @param createNewIfNotExists
867 * @return
868 */
869 private SpecimenDescription getImageGallery(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) {
870 SpecimenDescription result = null;
871 Set<SpecimenDescription> descriptions = specimen.getSpecimenDescriptions();
872 for (SpecimenDescription description : descriptions) {
873 if (description.isImageGallery()) {
874 result = description;
875 break;
876 }
877 }
878 if (result == null && createIfNotExists) {
879 result = SpecimenDescription.NewInstance(specimen);
880 result.setImageGallery(true);
881 }
882 return result;
883 }
884
885 /**
886 * Adds a media to the specimens image gallery. If media is
887 * <code>null</code> nothing happens.
888 *
889 * @param media
890 * @param specimen
891 * @return true if media is not null (as specified by
892 * {@link java.util.Collection#add(Object) Collection.add(E e)}
893 * @throws DerivedUnitFacadeNotSupportedException
894 */
895 private boolean addMedia(Media media, SpecimenOrObservationBase<?> specimen)
896 throws DerivedUnitFacadeNotSupportedException {
897 if (media != null) {
898 List<Media> mediaList = getMedia(specimen, true);
899 return mediaList.add(media);
900 } else {
901 return false;
902 }
903 }
904
905 /**
906 * Removes a media from the specimens image gallery.
907 *
908 * @param media
909 * @param specimen
910 * @return true if an element was removed as a result of this call (as
911 * specified by {@link java.util.Collection#remove(Object)
912 * Collection.remove(E e)}
913 * @throws DerivedUnitFacadeNotSupportedException
914 */
915 private boolean removeMedia(Media media,
916 SpecimenOrObservationBase<?> specimen)
917 throws DerivedUnitFacadeNotSupportedException {
918 List<Media> mediaList = getMedia(specimen, true);
919 return mediaList == null ? null : mediaList.remove(media);
920 }
921
922 private List<Media> getMedia(SpecimenOrObservationBase<?> specimen,
923 boolean createIfNotExists)
924 throws DerivedUnitFacadeNotSupportedException {
925 TextData textData = getMediaTextData(specimen, createIfNotExists);
926 return textData == null ? null : textData.getMedia();
927 }
928
929 /**
930 * Returns the one media list of a specimen which is part of the only image
931 * gallery that this specimen is part of.<BR>
932 * If these conditions are not hold an exception is thrwon.
933 *
934 * @param specimen
935 * @return
936 * @throws DerivedUnitFacadeNotSupportedException
937 */
938 // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)
939 // throws DerivedUnitFacadeNotSupportedException {
940 // if (specimen == null){
941 // return null;
942 // }
943 // if (specimen == this.derivedUnit){
944 // return getDerivedUnitImageGalleryMedia();
945 // }else if (specimen == this.fieldObservation){
946 // return getObservationImageGalleryTextData();
947 // }else{
948 // return getImageGalleryMedia(specimen, "Undefined specimen ");
949 // }
950 // }
951
952 /**
953 * Returns the one media list of a specimen which is part of the only image
954 * gallery that this specimen is part of.<BR>
955 * If these conditions are not hold an exception is thrwon.
956 *
957 * @param specimen
958 * @return
959 * @throws DerivedUnitFacadeNotSupportedException
960 */
961 private TextData getMediaTextData(SpecimenOrObservationBase<?> specimen,
962 boolean createIfNotExists)
963 throws DerivedUnitFacadeNotSupportedException {
964 if (specimen == null) {
965 return null;
966 }
967 if (specimen == this.derivedUnit) {
968 return getDerivedUnitImageGalleryTextData(createIfNotExists);
969 } else if (specimen == this.fieldObservation) {
970 return getObservationImageGalleryTextData(createIfNotExists);
971 } else {
972 return getImageGalleryTextData(specimen, "Undefined specimen ");
973 }
974 }
975
976 // ****************** GETTER / SETTER / ADDER / REMOVER
977 // ***********************/
978
979 // ****************** Gathering Event *********************************/
980
981 // country
982 @Transient
983 public NamedArea getCountry() {
984 return (hasGatheringEvent() ? getGatheringEvent(true).getCountry()
985 : null);
986 }
987
988 public void setCountry(NamedArea country) {
989 getGatheringEvent(true).setCountry(country);
990 }
991
992 // Collecting area
993 public void addCollectingArea(NamedArea area) {
994 getGatheringEvent(true).addCollectingArea(area);
995 }
996
997 public void addCollectingAreas(java.util.Collection<NamedArea> areas) {
998 for (NamedArea area : areas) {
999 getGatheringEvent(true).addCollectingArea(area);
1000 }
1001 }
1002
1003 @Transient
1004 public Set<NamedArea> getCollectingAreas() {
1005 return (hasGatheringEvent() ? getGatheringEvent(true)
1006 .getCollectingAreas() : null);
1007 }
1008
1009 public void removeCollectingArea(NamedArea area) {
1010 if (hasGatheringEvent()) {
1011 getGatheringEvent(true).removeCollectingArea(area);
1012 }
1013 }
1014
1015 // absolute elevation
1016 /**
1017 * meter above/below sea level of the surface
1018 *
1019 * @see #getAbsoluteElevationError()
1020 * @see #getAbsoluteElevationRange()
1021 **/
1022 @Transient
1023 public Integer getAbsoluteElevation() {
1024 return (hasGatheringEvent() ? getGatheringEvent(true)
1025 .getAbsoluteElevation() : null);
1026 }
1027
1028 public void setAbsoluteElevation(Integer absoluteElevation) {
1029 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);
1030 }
1031
1032 // absolute elevation error
1033 @Transient
1034 public Integer getAbsoluteElevationError() {
1035 return (hasGatheringEvent() ? getGatheringEvent(true)
1036 .getAbsoluteElevationError() : null);
1037 }
1038
1039 public void setAbsoluteElevationError(Integer absoluteElevationError) {
1040 getGatheringEvent(true).setAbsoluteElevationError(
1041 absoluteElevationError);
1042 }
1043
1044 /**
1045 * @see #getAbsoluteElevation()
1046 * @see #getAbsoluteElevationError()
1047 * @see #setAbsoluteElevationRange(Integer, Integer)
1048 * @see #getAbsoluteElevationMaximum()
1049 */
1050 @Transient
1051 public Integer getAbsoluteElevationMinimum() {
1052 if (!hasGatheringEvent()) {
1053 return null;
1054 }
1055 Integer minimum = getGatheringEvent(true).getAbsoluteElevation();
1056 if (getGatheringEvent(true).getAbsoluteElevationError() != null) {
1057 minimum = minimum
1058 - getGatheringEvent(true).getAbsoluteElevationError();
1059 }
1060 return minimum;
1061 }
1062
1063 /**
1064 * @see #getAbsoluteElevation()
1065 * @see #getAbsoluteElevationError()
1066 * @see #setAbsoluteElevationRange(Integer, Integer)
1067 * @see #getAbsoluteElevationMinimum()
1068 */
1069 @Transient
1070 public Integer getAbsoluteElevationMaximum() {
1071 if (!hasGatheringEvent()) {
1072 return null;
1073 }
1074 Integer maximum = getGatheringEvent(true).getAbsoluteElevation();
1075 if (getGatheringEvent(true).getAbsoluteElevationError() != null) {
1076 maximum = maximum
1077 + getGatheringEvent(true).getAbsoluteElevationError();
1078 }
1079 return maximum;
1080 }
1081
1082 /**
1083 * This method replaces absoluteElevation and absoulteElevationError by
1084 * internally translating minimum and maximum values into average and error
1085 * values. As all these values are integer based it is necessary that the
1086 * distance is between minimum and maximum is <b>even</b>, otherwise we will
1087 * get a rounding error resulting in a maximum that is increased by 1.
1088 *
1089 * @see #setAbsoluteElevation(Integer)
1090 * @see #setAbsoluteElevationError(Integer)
1091 * @param minimumElevation
1092 * minimum of the range
1093 * @param maximumElevation
1094 * maximum of the range
1095 * @throws IllegalArgumentException
1096 */
1097 public void setAbsoluteElevationRange(Integer minimumElevation, Integer maximumElevation) throws IllegalArgumentException{
1098 if (minimumElevation == null || maximumElevation == null) {
1099 Integer elevation = minimumElevation;
1100 Integer error = 0;
1101 if (minimumElevation == null) {
1102 elevation = maximumElevation;
1103 if (elevation == null) {
1104 error = null;
1105 }
1106 }
1107 getGatheringEvent(true).setAbsoluteElevation(elevation);
1108 getGatheringEvent(true).setAbsoluteElevationError(error);
1109 } else {
1110 if (!isEvenDistance(minimumElevation, maximumElevation)) {
1111 throw new IllegalArgumentException(
1112 "Distance between minimum and maximum elevation must be even but was "
1113 + Math.abs(minimumElevation - maximumElevation));
1114 }
1115 Integer absoluteElevationError = Math.abs(maximumElevation
1116 - minimumElevation);
1117 absoluteElevationError = absoluteElevationError / 2;
1118 Integer absoluteElevation = minimumElevation
1119 + absoluteElevationError;
1120 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);
1121 getGatheringEvent(true).setAbsoluteElevationError(
1122 absoluteElevationError);
1123 }
1124 }
1125
1126 /**
1127 * @param minimumElevation
1128 * @param maximumElevation
1129 * @return
1130 */
1131 public boolean isEvenDistance(Integer minimumElevation,
1132 Integer maximumElevation) {
1133 Integer diff = (maximumElevation - minimumElevation);
1134 return diff % 2 == 0;
1135 }
1136
1137 // collector
1138 @Transient
1139 public AgentBase getCollector() {
1140 return (hasGatheringEvent() ? getGatheringEvent(true).getCollector()
1141 : null);
1142 }
1143
1144 public void setCollector(AgentBase collector) {
1145 getGatheringEvent(true).setCollector(collector);
1146 }
1147
1148 // collecting method
1149 @Transient
1150 public String getCollectingMethod() {
1151 return (hasGatheringEvent() ? getGatheringEvent(true)
1152 .getCollectingMethod() : null);
1153 }
1154
1155 public void setCollectingMethod(String collectingMethod) {
1156 getGatheringEvent(true).setCollectingMethod(collectingMethod);
1157 }
1158
1159 // distance to ground
1160 @Transient
1161 public Integer getDistanceToGround() {
1162 return (hasGatheringEvent() ? getGatheringEvent(true)
1163 .getDistanceToGround() : null);
1164 }
1165
1166 public void setDistanceToGround(Integer distanceToGround) {
1167 getGatheringEvent(true).setDistanceToGround(distanceToGround);
1168 }
1169
1170 // distance to water surface
1171 @Transient
1172 public Integer getDistanceToWaterSurface() {
1173 return (hasGatheringEvent() ? getGatheringEvent(true)
1174 .getDistanceToWaterSurface() : null);
1175 }
1176
1177 public void setDistanceToWaterSurface(Integer distanceToWaterSurface) {
1178 getGatheringEvent(true).setDistanceToWaterSurface(
1179 distanceToWaterSurface);
1180 }
1181
1182 // exact location
1183 @Transient
1184 public Point getExactLocation() {
1185 return (hasGatheringEvent() ? getGatheringEvent(true)
1186 .getExactLocation() : null);
1187 }
1188
1189 /**
1190 * Returns a sexagesimal representation of the exact location (e.g.
1191 * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty
1192 * string is returned.
1193 *
1194 * @param includeEmptySeconds
1195 * @param includeReferenceSystem
1196 * @return
1197 */
1198 public String getExactLocationText(boolean includeEmptySeconds,
1199 boolean includeReferenceSystem) {
1200 return (this.getExactLocation() == null ? "" : this.getExactLocation()
1201 .toSexagesimalString(includeEmptySeconds,
1202 includeReferenceSystem));
1203 }
1204
1205 public void setExactLocation(Point exactLocation) {
1206 getGatheringEvent(true).setExactLocation(exactLocation);
1207 }
1208
1209 public void setExactLocationByParsing(String longitudeToParse,
1210 String latitudeToParse, ReferenceSystem referenceSystem,
1211 Integer errorRadius) throws ParseException {
1212 Point point = Point.NewInstance(null, null, referenceSystem,
1213 errorRadius);
1214 point.setLongitudeByParsing(longitudeToParse);
1215 point.setLatitudeByParsing(latitudeToParse);
1216 setExactLocation(point);
1217 }
1218
1219 // gathering event description
1220 @Transient
1221 public String getGatheringEventDescription() {
1222 return (hasGatheringEvent() ? getGatheringEvent(true).getDescription()
1223 : null);
1224 }
1225
1226 public void setGatheringEventDescription(String description) {
1227 getGatheringEvent(true).setDescription(description);
1228 }
1229
1230 // gatering period
1231 @Transient
1232 public TimePeriod getGatheringPeriod() {
1233 return (hasGatheringEvent() ? getGatheringEvent(true).getTimeperiod()
1234 : null);
1235 }
1236
1237 public void setGatheringPeriod(TimePeriod timeperiod) {
1238 getGatheringEvent(true).setTimeperiod(timeperiod);
1239 }
1240
1241 // locality
1242 @Transient
1243 public LanguageString getLocality() {
1244 return (hasGatheringEvent() ? getGatheringEvent(true).getLocality()
1245 : null);
1246 }
1247
1248 /**
1249 * convienience method for {@link #getLocality()}.
1250 * {@link LanguageString#getText() getText()}
1251 *
1252 * @return
1253 */
1254 @Transient
1255 public String getLocalityText() {
1256 LanguageString locality = getLocality();
1257 if (locality != null) {
1258 return locality.getText();
1259 }
1260 return null;
1261 }
1262
1263 /**
1264 * convienience method for {@link #getLocality()}.
1265 * {@link LanguageString#getLanguage() getLanguage()}
1266 *
1267 * @return
1268 */
1269 @Transient
1270 public Language getLocalityLanguage() {
1271 LanguageString locality = getLocality();
1272 if (locality != null) {
1273 return locality.getLanguage();
1274 }
1275 return null;
1276 }
1277
1278 /**
1279 * Sets the locality string in the default language
1280 *
1281 * @param locality
1282 */
1283 public void setLocality(String locality) {
1284 Language language = Language.DEFAULT();
1285 setLocality(locality, language);
1286 }
1287
1288 public void setLocality(String locality, Language language) {
1289 LanguageString langString = LanguageString.NewInstance(locality,
1290 language);
1291 setLocality(langString);
1292 }
1293
1294 public void setLocality(LanguageString locality) {
1295 getGatheringEvent(true).setLocality(locality);
1296 }
1297
1298 /**
1299 * The gathering event will be used for the field object instead of the old
1300 * gathering event.<BR>
1301 * <B>This method will override all gathering values (see below).</B>
1302 *
1303 * @see #getAbsoluteElevation()
1304 * @see #getAbsoluteElevationError()
1305 * @see #getDistanceToGround()
1306 * @see #getDistanceToWaterSurface()
1307 * @see #getExactLocation()
1308 * @see #getGatheringEventDescription()
1309 * @see #getGatheringPeriod()
1310 * @see #getCollectingAreas()
1311 * @see #getCollectingMethod()
1312 * @see #getLocality()
1313 * @see #getCollector()
1314 * @param gatheringEvent
1315 */
1316 public void setGatheringEvent(GatheringEvent gatheringEvent) {
1317 getFieldObservation(true).setGatheringEvent(gatheringEvent);
1318 }
1319
1320 public boolean hasGatheringEvent() {
1321 return (getGatheringEvent(false) != null);
1322 }
1323
1324 public GatheringEvent innerGatheringEvent() {
1325 return getGatheringEvent(false);
1326 }
1327
1328 public GatheringEvent getGatheringEvent(boolean createIfNotExists) {
1329 if (!hasFieldObservation() && !createIfNotExists) {
1330 return null;
1331 }
1332 if (createIfNotExists
1333 && getFieldObservation(true).getGatheringEvent() == null) {
1334 GatheringEvent gatheringEvent = GatheringEvent.NewInstance();
1335 getFieldObservation(true).setGatheringEvent(gatheringEvent);
1336 }
1337 return getFieldObservation(true).getGatheringEvent();
1338 }
1339
1340 // ****************** Field Object ************************************/
1341
1342 /**
1343 * Returns true if a field observation exists (even if all attributes are
1344 * empty or <code>null<code>.
1345 *
1346 * @return
1347 */
1348 public boolean hasFieldObject() {
1349 return this.fieldObservation != null;
1350 }
1351
1352 // ecology
1353 @Transient
1354 public String getEcology() {
1355 return getEcology(Language.DEFAULT());
1356 }
1357
1358 public String getEcology(Language language) {
1359 LanguageString languageString = getEcologyAll().get(language);
1360 return (languageString == null ? null : languageString.getText());
1361 }
1362
1363 // public String getEcologyPreferred(List<Language> languages){
1364 // LanguageString languageString =
1365 // getEcologyAll().getPreferredLanguageString(languages);
1366 // return languageString.getText();
1367 // }
1368 /**
1369 * Returns a copy of the multilanguage text holding the ecology data.
1370 *
1371 * @see {@link TextData#getMultilanguageText()}
1372 * @return
1373 */
1374 @Transient
1375 public Map<Language, LanguageString> getEcologyAll() {
1376 if (ecology == null) {
1377 try {
1378 ecology = initializeFieldObjectTextDataWithSupportTest(
1379 Feature.ECOLOGY(), false, false);
1380 } catch (DerivedUnitFacadeNotSupportedException e) {
1381 throw new IllegalStateException(notSupportMessage, e);
1382 }
1383 if (ecology == null) {
1384 return new HashMap<Language, LanguageString>();
1385 }
1386 }
1387 return ecology.getMultilanguageText();
1388 }
1389
1390 public void setEcology(String ecology) {
1391 setEcology(ecology, null);
1392 }
1393
1394 public void setEcology(String ecologyText, Language language) {
1395 if (language == null) {
1396 language = Language.DEFAULT();
1397 }
1398 if (ecology == null) {
1399 try {
1400 ecology = initializeFieldObjectTextDataWithSupportTest(
1401 Feature.ECOLOGY(), true, false);
1402 } catch (DerivedUnitFacadeNotSupportedException e) {
1403 throw new IllegalStateException(notSupportMessage, e);
1404 }
1405 }
1406 if (ecologyText == null) {
1407 ecology.removeText(language);
1408 } else {
1409 ecology.putText(language, ecologyText);
1410 }
1411 }
1412
1413 public void removeEcology(Language language) {
1414 setEcology(null, language);
1415 }
1416
1417 /**
1418 * Removes ecology for the default language
1419 */
1420 public void removeEcology() {
1421 setEcology(null, null);
1422 }
1423
1424 public void removeEcologyAll() {
1425
1426 }
1427
1428 // plant description
1429 @Transient
1430 public String getPlantDescription() {
1431 return getPlantDescription(null);
1432 }
1433
1434 public String getPlantDescription(Language language) {
1435 if (language == null) {
1436 language = Language.DEFAULT();
1437 }
1438 LanguageString languageString = getPlantDescriptionAll().get(language);
1439 return (languageString == null ? null : languageString.getText());
1440 }
1441
1442 // public String getPlantDescriptionPreferred(List<Language> languages){
1443 // LanguageString languageString =
1444 // getPlantDescriptionAll().getPreferredLanguageString(languages);
1445 // return languageString.getText();
1446 // }
1447 /**
1448 * Returns a copy of the multilanguage text holding the description data.
1449 *
1450 * @see {@link TextData#getMultilanguageText()}
1451 * @return
1452 */
1453 @Transient
1454 public Map<Language, LanguageString> getPlantDescriptionAll() {
1455 if (plantDescription == null) {
1456 try {
1457 plantDescription = initializeFieldObjectTextDataWithSupportTest(
1458 Feature.DESCRIPTION(), false, false);
1459 } catch (DerivedUnitFacadeNotSupportedException e) {
1460 throw new IllegalStateException(notSupportMessage, e);
1461 }
1462 if (plantDescription == null) {
1463 return new HashMap<Language, LanguageString>();
1464 }
1465 }
1466 return plantDescription.getMultilanguageText();
1467 }
1468
1469 public void setPlantDescription(String plantDescription) {
1470 setPlantDescription(plantDescription, null);
1471 }
1472
1473 public void setPlantDescription(String plantDescriptionText,
1474 Language language) {
1475 if (language == null) {
1476 language = Language.DEFAULT();
1477 }
1478 if (plantDescription == null) {
1479 try {
1480 plantDescription = initializeFieldObjectTextDataWithSupportTest(
1481 Feature.DESCRIPTION(), true, false);
1482 } catch (DerivedUnitFacadeNotSupportedException e) {
1483 throw new IllegalStateException(notSupportMessage, e);
1484 }
1485 }
1486 if (plantDescriptionText == null) {
1487 plantDescription.removeText(language);
1488 } else {
1489 plantDescription.putText(language, plantDescriptionText);
1490 }
1491 }
1492
1493 public void removePlantDescription(Language language) {
1494 setPlantDescription(null, language);
1495 }
1496
1497 // field object definition
1498 public void addFieldObjectDefinition(String text, Language language) {
1499 getFieldObservation(true).addDefinition(text, language);
1500 }
1501
1502 @Transient
1503 public Map<Language, LanguageString> getFieldObjectDefinition() {
1504 if (!hasFieldObservation()) {
1505 return new HashMap<Language, LanguageString>();
1506 } else {
1507 return getFieldObservation(true).getDefinition();
1508 }
1509 }
1510
1511 public String getFieldObjectDefinition(Language language) {
1512 Map<Language, LanguageString> map = getFieldObjectDefinition();
1513 LanguageString languageString = (map == null ? null : map.get(language));
1514 if (languageString != null) {
1515 return languageString.getText();
1516 } else {
1517 return null;
1518 }
1519 }
1520
1521 public void removeFieldObjectDefinition(Language lang) {
1522 if (hasFieldObservation()) {
1523 getFieldObservation(true).removeDefinition(lang);
1524 }
1525 }
1526
1527 // media
1528 public boolean addFieldObjectMedia(Media media) {
1529 try {
1530 return addMedia(media, getFieldObservation(true));
1531 } catch (DerivedUnitFacadeNotSupportedException e) {
1532 throw new IllegalStateException(notSupportMessage, e);
1533 }
1534 }
1535
1536 /**
1537 * Returns true, if an image gallery for the field object exists.<BR>
1538 * Returns also <code>true</code> if the image gallery is empty.
1539 *
1540 * @return
1541 */
1542 public boolean hasFieldObjectImageGallery() {
1543 if (!hasFieldObject()) {
1544 return false;
1545 } else {
1546 return (getImageGallery(fieldObservation, false) != null);
1547 }
1548 }
1549
1550 public void setFieldObjectImageGallery(SpecimenDescription imageGallery)
1551 throws DerivedUnitFacadeNotSupportedException {
1552 SpecimenDescription existingGallery = getFieldObjectImageGallery(false);
1553
1554 // test attached specimens contain this.derivedUnit
1555 SpecimenOrObservationBase<?> facadeFieldObservation = innerFieldObservation();
1556 testSpecimenInImageGallery(imageGallery, facadeFieldObservation);
1557
1558 if (existingGallery != null) {
1559 if (existingGallery != imageGallery) {
1560 throw new DerivedUnitFacadeNotSupportedException(
1561 "DerivedUnitFacade does not allow more than one image gallery");
1562 } else {
1563 // do nothing
1564 }
1565 } else {
1566 TextData textData = testImageGallery(imageGallery);
1567 this.fieldObjectMediaTextData = textData;
1568 }
1569 }
1570
1571 /**
1572 * Returns the field object image gallery. If no such image gallery exists
1573 * and createIfNotExists is true an new one is created. Otherwise null is
1574 * returned.
1575 *
1576 * @param createIfNotExists
1577 * @return
1578 */
1579 public SpecimenDescription getFieldObjectImageGallery(
1580 boolean createIfNotExists) {
1581 TextData textData;
1582 try {
1583 textData = initializeFieldObjectTextDataWithSupportTest(
1584 Feature.IMAGE(), createIfNotExists, true);
1585 } catch (DerivedUnitFacadeNotSupportedException e) {
1586 throw new IllegalStateException(notSupportMessage, e);
1587 }
1588 if (textData != null) {
1589 return CdmBase.deproxy(textData.getInDescription(),
1590 SpecimenDescription.class);
1591 } else {
1592 return null;
1593 }
1594 }
1595
1596 /**
1597 * Returns the media for the field object.<BR>
1598 *
1599 * @return
1600 */
1601 @Transient
1602 public List<Media> getFieldObjectMedia() {
1603 try {
1604 List<Media> result = getMedia(getFieldObservation(false), false);
1605 return result == null ? new ArrayList<Media>() : result;
1606 } catch (DerivedUnitFacadeNotSupportedException e) {
1607 throw new IllegalStateException(notSupportMessage, e);
1608 }
1609 }
1610
1611 public boolean removeFieldObjectMedia(Media media) {
1612 try {
1613 return removeMedia(media, getFieldObservation(false));
1614 } catch (DerivedUnitFacadeNotSupportedException e) {
1615 throw new IllegalStateException(notSupportMessage, e);
1616 }
1617 }
1618
1619 // field number
1620 @Transient
1621 public String getFieldNumber() {
1622 if (!hasFieldObservation()) {
1623 return null;
1624 } else {
1625 return getFieldObservation(true).getFieldNumber();
1626 }
1627 }
1628
1629 public void setFieldNumber(String fieldNumber) {
1630 getFieldObservation(true).setFieldNumber(fieldNumber);
1631 }
1632
1633 // primary collector
1634 @Transient
1635 public Person getPrimaryCollector() {
1636 if (!hasFieldObservation()) {
1637 return null;
1638 } else {
1639 return getFieldObservation(true).getPrimaryCollector();
1640 }
1641 }
1642
1643 public void setPrimaryCollector(Person primaryCollector) {
1644 getFieldObservation(true).setPrimaryCollector(primaryCollector);
1645 }
1646
1647 // field notes
1648 @Transient
1649 public String getFieldNotes() {
1650 if (!hasFieldObservation()) {
1651 return null;
1652 } else {
1653 return getFieldObservation(true).getFieldNotes();
1654 }
1655 }
1656
1657 public void setFieldNotes(String fieldNotes) {
1658 getFieldObservation(true).setFieldNotes(fieldNotes);
1659 }
1660
1661 // individual counts
1662 @Transient
1663 public Integer getIndividualCount() {
1664 return (hasFieldObservation() ? getFieldObservation(true)
1665 .getIndividualCount() : null);
1666 }
1667
1668 public void setIndividualCount(Integer individualCount) {
1669 getFieldObservation(true).setIndividualCount(individualCount);
1670 }
1671
1672 // life stage
1673 @Transient
1674 public Stage getLifeStage() {
1675 return (hasFieldObservation() ? getFieldObservation(true)
1676 .getLifeStage() : null);
1677 }
1678
1679 public void setLifeStage(Stage lifeStage) {
1680 getFieldObservation(true).setLifeStage(lifeStage);
1681 }
1682
1683 // sex
1684 @Transient
1685 public Sex getSex() {
1686 return (hasFieldObservation() ? getFieldObservation(true).getSex()
1687 : null);
1688 }
1689
1690 public void setSex(Sex sex) {
1691 getFieldObservation(true).setSex(sex);
1692 }
1693
1694 // field observation
1695 public boolean hasFieldObservation() {
1696 return (getFieldObservation(false) != null);
1697 }
1698
1699 /**
1700 * Returns the field observation as an object.
1701 *
1702 * @return
1703 */
1704 public FieldObservation innerFieldObservation() {
1705 return getFieldObservation(false);
1706 }
1707
1708 /**
1709 * Returns the field observation as an object.
1710 *
1711 * @return
1712 */
1713 public FieldObservation getFieldObservation(boolean createIfNotExists) {
1714 if (fieldObservation == null && createIfNotExists) {
1715 setFieldObservation(FieldObservation.NewInstance());
1716 }
1717 return this.fieldObservation;
1718 }
1719
1720
1721 private void setFieldObservation(FieldObservation fieldObservation) {
1722 this.fieldObservation = fieldObservation;
1723 if (fieldObservation != null){
1724 fieldObservation.addPropertyChangeListener(getNewEventPropagationListener());
1725 if (derivedUnit != null){
1726 DerivationEvent derivationEvent = getDerivationEvent(CREATE);
1727 derivationEvent.addOriginal(fieldObservation);
1728 }
1729 setFieldObservationCacheStrategy();
1730 }
1731 }
1732
1733 // ****************** Specimen *******************************************
1734
1735 // Definition
1736 public void addDerivedUnitDefinition(String text, Language language) {
1737 innerDerivedUnit().putDefinition(language, text);
1738 }
1739
1740 @Transient
1741 public Map<Language, LanguageString> getDerivedUnitDefinitions() {
1742 testDerivedUnit();
1743 return this.derivedUnit.getDefinition();
1744 }
1745
1746
1747 public String getDerivedUnitDefinition(Language language) {
1748 testDerivedUnit();
1749 Map<Language, LanguageString> languageMap = derivedUnit.getDefinition();
1750 LanguageString languageString = languageMap.get(language);
1751 if (languageString != null) {
1752 return languageString.getText();
1753 } else {
1754 return null;
1755 }
1756 }
1757
1758 public void removeDerivedUnitDefinition(Language lang) {
1759 testDerivedUnit();
1760 derivedUnit.removeDefinition(lang);
1761 }
1762
1763 // Determination
1764 public void addDetermination(DeterminationEvent determination) {
1765 testDerivedUnit();
1766 determination.setIdentifiedUnit(derivedUnit);
1767 derivedUnit.addDetermination(determination);
1768 }
1769
1770 @Transient
1771 public DeterminationEvent getPreferredDetermination() {
1772 testDerivedUnit();
1773 Set<DeterminationEvent> events = derivedUnit.getDeterminations();
1774 for (DeterminationEvent event : events){
1775 if (event.getPreferredFlag() == true){
1776 return event;
1777 }
1778 }
1779 return null;
1780 }
1781
1782 /**
1783 * This method returns the preferred determination.
1784 * @see #getOtherDeterminations()
1785 * @see #getDeterminations()
1786 * @return
1787 */
1788 @Transient
1789 public void setPreferredDetermination(DeterminationEvent newEvent) {
1790 testDerivedUnit();
1791 Set<DeterminationEvent> events = derivedUnit.getDeterminations();
1792 for (DeterminationEvent event : events){
1793 if (event.getPreferredFlag() == true){
1794 event.setPreferredFlag(false);
1795 }
1796 }
1797 newEvent.setPreferredFlag(true);
1798 events.add(newEvent);
1799 }
1800
1801 /**
1802 * This method returns all determinations except for the preferred one.
1803 * @see #getPreferredDetermination()
1804 * @see #getDeterminations()
1805 * @return
1806 */
1807 @Transient
1808 public Set<DeterminationEvent> getOtherDeterminations() {
1809 testDerivedUnit();
1810 Set<DeterminationEvent> events = derivedUnit.getDeterminations();
1811 Set<DeterminationEvent> result = new HashSet<DeterminationEvent>();
1812 for (DeterminationEvent event : events){
1813 if (event.getPreferredFlag() != true){
1814 result.add(event);
1815 }
1816 }
1817 return result;
1818 }
1819
1820 /**
1821 * This method returns all determination events. The preferred one {@link #getPreferredDetermination()}
1822 * and all others {@link #getOtherDeterminations()}.
1823 * @return
1824 */
1825 @Transient
1826 public Set<DeterminationEvent> getDeterminations() {
1827 testDerivedUnit();
1828 return derivedUnit.getDeterminations();
1829 }
1830
1831 public void removeDetermination(DeterminationEvent determination) {
1832 testDerivedUnit();
1833 derivedUnit.removeDetermination(determination);
1834 }
1835
1836 // Media
1837 public boolean addDerivedUnitMedia(Media media) {
1838 testDerivedUnit();
1839 try {
1840 return addMedia(media, derivedUnit);
1841 } catch (DerivedUnitFacadeNotSupportedException e) {
1842 throw new IllegalStateException(notSupportMessage, e);
1843 }
1844 }
1845
1846 /**
1847 * Returns true, if an image gallery exists for the specimen.<BR>
1848 * Returns also <code>true</code> if the image gallery is empty.
1849 */
1850 public boolean hasDerivedUnitImageGallery() {
1851 return (getImageGallery(derivedUnit, false) != null);
1852 }
1853
1854 public SpecimenDescription getDerivedUnitImageGallery(boolean createIfNotExists) {
1855 testDerivedUnit();
1856 TextData textData;
1857 try {
1858 textData = inititializeTextDataWithSupportTest(Feature.IMAGE(),
1859 derivedUnit, createIfNotExists, true);
1860 } catch (DerivedUnitFacadeNotSupportedException e) {
1861 throw new IllegalStateException(notSupportMessage, e);
1862 }
1863 if (textData != null) {
1864 return CdmBase.deproxy(textData.getInDescription(),
1865 SpecimenDescription.class);
1866 } else {
1867 return null;
1868 }
1869 }
1870
1871 public void setDerivedUnitImageGallery(SpecimenDescription imageGallery)
1872 throws DerivedUnitFacadeNotSupportedException {
1873 testDerivedUnit();
1874 SpecimenDescription existingGallery = getDerivedUnitImageGallery(false);
1875
1876 // test attached specimens contain this.derivedUnit
1877 SpecimenOrObservationBase facadeDerivedUnit = innerDerivedUnit();
1878 testSpecimenInImageGallery(imageGallery, facadeDerivedUnit);
1879
1880 if (existingGallery != null) {
1881 if (existingGallery != imageGallery) {
1882 throw new DerivedUnitFacadeNotSupportedException(
1883 "DerivedUnitFacade does not allow more than one image gallery");
1884 } else {
1885 // do nothing
1886 }
1887 } else {
1888 TextData textData = testImageGallery(imageGallery);
1889 this.derivedUnitMediaTextData = textData;
1890 }
1891 }
1892
1893 /**
1894 * @param imageGallery
1895 * @throws DerivedUnitFacadeNotSupportedException
1896 */
1897 private void testSpecimenInImageGallery(SpecimenDescription imageGallery, SpecimenOrObservationBase specimen)
1898 throws DerivedUnitFacadeNotSupportedException {
1899 Set<SpecimenOrObservationBase> imageGallerySpecimens = imageGallery.getDescribedSpecimenOrObservations();
1900 if (imageGallerySpecimens.size() < 1) {
1901 throw new DerivedUnitFacadeNotSupportedException(
1902 "Image Gallery has no Specimen attached. Please attache according specimen or field observation.");
1903 }
1904 if (!imageGallerySpecimens.contains(specimen)) {
1905 throw new DerivedUnitFacadeNotSupportedException(
1906 "Image Gallery has not the facade's field object attached. Please add field object first to image gallery specimenOrObservation list.");
1907 }
1908 }
1909
1910 /**
1911 * Returns the media for the specimen.<BR>
1912 *
1913 * @return
1914 */
1915 @Transient
1916 public List<Media> getDerivedUnitMedia() {
1917 testDerivedUnit();
1918 try {
1919 List<Media> result = getMedia(derivedUnit, false);
1920 return result == null ? new ArrayList<Media>() : result;
1921 } catch (DerivedUnitFacadeNotSupportedException e) {
1922 throw new IllegalStateException(notSupportMessage, e);
1923 }
1924 }
1925
1926 public boolean removeDerivedUnitMedia(Media media) {
1927 testDerivedUnit();
1928 try {
1929 return removeMedia(media, derivedUnit);
1930 } catch (DerivedUnitFacadeNotSupportedException e) {
1931 throw new IllegalStateException(notSupportMessage, e);
1932 }
1933 }
1934
1935 // Accession Number
1936 @Transient
1937 public String getAccessionNumber() {
1938 testDerivedUnit();
1939 return derivedUnit.getAccessionNumber();
1940 }
1941
1942 public void setAccessionNumber(String accessionNumber) {
1943 testDerivedUnit();
1944 derivedUnit.setAccessionNumber(accessionNumber);
1945 }
1946
1947 @Transient
1948 public String getCatalogNumber() {
1949 testDerivedUnit();
1950 return derivedUnit.getCatalogNumber();
1951 }
1952
1953 public void setCatalogNumber(String catalogNumber) {
1954 testDerivedUnit();
1955 derivedUnit.setCatalogNumber(catalogNumber);
1956 }
1957
1958 @Transient
1959 public String getBarcode() {
1960 testDerivedUnit();
1961 return derivedUnit.getBarcode();
1962 }
1963
1964 public void setBarcode(String barcode) {
1965 testDerivedUnit();
1966 derivedUnit.setBarcode(barcode);
1967 }
1968
1969 // Preservation Method
1970
1971 /**
1972 * Only supported by specimen and fossils
1973 *
1974 * @see #DerivedUnitType
1975 * @return
1976 */
1977 @Transient
1978 public PreservationMethod getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException {
1979 testDerivedUnit();
1980 if (derivedUnit.isInstanceOf(Specimen.class)) {
1981 return CdmBase.deproxy(derivedUnit, Specimen.class)
1982 .getPreservation();
1983 } else {
1984 if (this.config
1985 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
1986 throw new MethodNotSupportedByDerivedUnitTypeException(
1987 "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
1988 } else {
1989 return null;
1990 }
1991 }
1992 }
1993
1994 /**
1995 * Only supported by specimen and fossils
1996 *
1997 * @see #DerivedUnitType
1998 * @return
1999 */
2000 public void setPreservationMethod(PreservationMethod preservation)
2001 throws MethodNotSupportedByDerivedUnitTypeException {
2002 testDerivedUnit();
2003 if (derivedUnit.isInstanceOf(Specimen.class)) {
2004 CdmBase.deproxy(derivedUnit, Specimen.class).setPreservation(
2005 preservation);
2006 } else {
2007 if (this.config
2008 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2009 throw new MethodNotSupportedByDerivedUnitTypeException(
2010 "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
2011 } else {
2012 return;
2013 }
2014 }
2015 }
2016
2017 // Stored under name
2018 @Transient
2019 public TaxonNameBase getStoredUnder() {
2020 testDerivedUnit();
2021 return derivedUnit.getStoredUnder();
2022 }
2023
2024 public void setStoredUnder(TaxonNameBase storedUnder) {
2025 testDerivedUnit();
2026 derivedUnit.setStoredUnder(storedUnder);
2027 }
2028
2029 // title cache
2030 public String getTitleCache() {
2031 SpecimenOrObservationBase<?> titledUnit = getTitledUnit();
2032
2033 if (!titledUnit.isProtectedTitleCache()) {
2034 // always compute title cache anew as long as there are no property
2035 // change listeners on
2036 // field observation, gathering event etc
2037 titledUnit.setTitleCache(null, false);
2038 }
2039 return titledUnit.getTitleCache();
2040 }
2041
2042 private SpecimenOrObservationBase<?> getTitledUnit(){
2043 return (derivedUnit != null )? derivedUnit : fieldObservation;
2044 }
2045
2046 public boolean isProtectedTitleCache() {
2047 return getTitledUnit().isProtectedTitleCache();
2048 }
2049
2050 public void setTitleCache(String titleCache, boolean isProtected) {
2051 this.getTitledUnit().setTitleCache(titleCache, isProtected);
2052 }
2053
2054 /**
2055 * Returns the derived unit itself.
2056 *
2057 * @return the derived unit
2058 */
2059 public DerivedUnitBase innerDerivedUnit() {
2060 return this.derivedUnit;
2061 }
2062
2063 // /**
2064 // * Returns the derived unit itself.
2065 // *
2066 // * @return the derived unit
2067 // */
2068 // public DerivedUnitBase innerDerivedUnit(boolean createIfNotExists) {
2069 // DerivedUnit result = this.derivedUnit;
2070 // if (result == null && createIfNotExists){
2071 // if (this.fieldObservation == null){
2072 // String message = "Field observation must exist to create derived unit.";
2073 // throw new IllegalStateException(message);
2074 // }else{
2075 // DerivedUnit =
2076 // DerivationEvent derivationEvent = getDerivationEvent(true);
2077 // derivationEvent.addOriginal(fieldObservation);
2078 // return this.derivedUnit;
2079 // }
2080 // }
2081 // }
2082
2083 private boolean hasDerivationEvent() {
2084 return getDerivationEvent() == null ? false : true;
2085 }
2086
2087 private DerivationEvent getDerivationEvent() {
2088 return getDerivationEvent(false);
2089 }
2090
2091 /**
2092 * Returns the derivation event. If no derivation event exists and <code>createIfNotExists</code>
2093 * is <code>true</code> a new derivation event is created and returned.
2094 * Otherwise <code>null</code> is returned.
2095 * @param createIfNotExists
2096 */
2097 private DerivationEvent getDerivationEvent(boolean createIfNotExists) {
2098 DerivationEvent result = null;
2099 if (derivedUnit != null){
2100 result = derivedUnit.getDerivedFrom();
2101 }else{
2102 return null;
2103 }
2104 if (result == null && createIfNotExists) {
2105 result = DerivationEvent.NewInstance();
2106 derivedUnit.setDerivedFrom(result);
2107 }
2108 return result;
2109 }
2110
2111 @Transient
2112 public String getExsiccatum()
2113 throws MethodNotSupportedByDerivedUnitTypeException {
2114 testDerivedUnit();
2115 if (derivedUnit.isInstanceOf(Specimen.class)) {
2116 return CdmBase.deproxy(derivedUnit, Specimen.class).getExsiccatum();
2117 } else {
2118 if (this.config
2119 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2120 throw new MethodNotSupportedByDerivedUnitTypeException(
2121 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2122 } else {
2123 return null;
2124 }
2125 }
2126 }
2127
2128 public void setExsiccatum(String exsiccatum) throws Exception {
2129 testDerivedUnit();
2130 if (derivedUnit.isInstanceOf(Specimen.class)) {
2131 CdmBase.deproxy(derivedUnit, Specimen.class).setExsiccatum(
2132 exsiccatum);
2133 } else {
2134 if (this.config
2135 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2136 throw new MethodNotSupportedByDerivedUnitTypeException(
2137 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2138 } else {
2139 return;
2140 }
2141 }
2142 }
2143
2144 // **** sources **/
2145 public void addSource(IdentifiableSource source) {
2146 testDerivedUnit();
2147 this.derivedUnit.addSource(source);
2148 }
2149
2150 /**
2151 * Creates an orignal source, adds it to the specimen and returns it.
2152 *
2153 * @param reference
2154 * @param microReference
2155 * @param originalNameString
2156 * @return
2157 */
2158 public IdentifiableSource addSource(Reference reference,
2159 String microReference, String originalNameString) {
2160 IdentifiableSource source = IdentifiableSource.NewInstance(reference, microReference);
2161 source.setOriginalNameString(originalNameString);
2162 addSource(source);
2163 return source;
2164 }
2165
2166 @Transient
2167 public Set<IdentifiableSource> getSources() {
2168 testDerivedUnit();
2169 return derivedUnit.getSources();
2170 }
2171
2172 public void removeSource(IdentifiableSource source) {
2173 testDerivedUnit();
2174 this.derivedUnit.removeSource(source);
2175 }
2176
2177 /**
2178 * @return the collection
2179 */
2180 @Transient
2181 public Collection getCollection() {
2182 testDerivedUnit();
2183 return derivedUnit.getCollection();
2184 }
2185
2186 /**
2187 * @param collection
2188 * the collection to set
2189 */
2190 public void setCollection(Collection collection) {
2191 testDerivedUnit();
2192 derivedUnit.setCollection(collection);
2193 }
2194
2195 // annotation
2196 public void addAnnotation(Annotation annotation) {
2197 testDerivedUnit();
2198 this.derivedUnit.addAnnotation(annotation);
2199 }
2200
2201 @Transient
2202 public void getAnnotations() {
2203 testDerivedUnit();
2204 this.derivedUnit.getAnnotations();
2205 }
2206
2207 public void removeAnnotation(Annotation annotation) {
2208 testDerivedUnit();
2209 this.derivedUnit.removeAnnotation(annotation);
2210 }
2211
2212 // ******************************* Events ***************************
2213
2214 /**
2215 * @return
2216 */
2217 private PropertyChangeListener getNewEventPropagationListener() {
2218 PropertyChangeListener listener = new PropertyChangeListener() {
2219 @Override
2220 public void propertyChange(PropertyChangeEvent event) {
2221 if (derivedUnit != null){
2222 derivedUnit.firePropertyChange(event);
2223 }else{
2224 if (! event.getSource().equals(fieldObservation)){
2225 fieldObservation.firePropertyChange(event);
2226 }
2227 }
2228 }
2229 };
2230 return listener;
2231 }
2232
2233 // **************** Other Collections
2234 // ***************************************************
2235
2236 /**
2237 * Creates a duplicate specimen which derives from the same derivation event
2238 * as the facade specimen and adds collection data to it (all data available
2239 * in DerivedUnitBase and Specimen. Data from SpecimenOrObservationBase and
2240 * above are not yet shared at the moment.
2241 *
2242 * @param collection
2243 * @param catalogNumber
2244 * @param accessionNumber
2245 * @param collectorsNumber
2246 * @param storedUnder
2247 * @param preservation
2248 * @return
2249 */
2250 public Specimen addDuplicate(Collection collection, String catalogNumber,
2251 String accessionNumber,
2252 TaxonNameBase storedUnder, PreservationMethod preservation) {
2253 testDerivedUnit();
2254 Specimen duplicate = Specimen.NewInstance();
2255 duplicate.setDerivedFrom(getDerivationEvent(CREATE));
2256 duplicate.setCollection(collection);
2257 duplicate.setCatalogNumber(catalogNumber);
2258 duplicate.setAccessionNumber(accessionNumber);
2259 duplicate.setStoredUnder(storedUnder);
2260 duplicate.setPreservation(preservation);
2261 return duplicate;
2262 }
2263
2264 public void addDuplicate(DerivedUnitBase duplicateSpecimen) {
2265 // TODO check derivedUnitType
2266 testDerivedUnit();
2267 getDerivationEvent(CREATE).addDerivative(duplicateSpecimen);
2268 }
2269
2270 @Transient
2271 public Set<Specimen> getDuplicates() {
2272 testDerivedUnit();
2273 Set<Specimen> result = new HashSet<Specimen>();
2274 if (hasDerivationEvent()) {
2275 for (DerivedUnitBase derivedUnit : getDerivationEvent(CREATE)
2276 .getDerivatives()) {
2277 if (derivedUnit.isInstanceOf(Specimen.class)
2278 && !derivedUnit.equals(this.derivedUnit)) {
2279 result.add(CdmBase.deproxy(derivedUnit, Specimen.class));
2280 }
2281 }
2282 }
2283 return result;
2284 }
2285
2286 public void removeDuplicate(Specimen duplicateSpecimen) {
2287 testDerivedUnit();
2288 if (hasDerivationEvent()) {
2289 getDerivationEvent(CREATE).removeDerivative(duplicateSpecimen);
2290 }
2291 }
2292
2293
2294
2295 private void testDerivedUnit() {
2296 if (derivedUnit == null){
2297 throw new IllegalStateException("This method is not allowed for this specimen or observation type. Probably you have tried to add specimen(derived unit) information to a field observation");
2298 }
2299 }
2300
2301 public void setType(DerivedUnitType type) {
2302 this.type = type;
2303 }
2304
2305 public DerivedUnitType getType() {
2306 return type;
2307 }
2308
2309 }