2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
10 package eu
.etaxonomy
.cdm
.api
.facade
;
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
;
22 import javax
.persistence
.Transient
;
24 import org
.apache
.log4j
.Logger
;
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
.IOriginalSource
;
32 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
33 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
34 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
35 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceBase
;
36 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
37 import eu
.etaxonomy
.cdm
.model
.common
.TimePeriod
;
38 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
39 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
40 import eu
.etaxonomy
.cdm
.model
.description
.Sex
;
41 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
42 import eu
.etaxonomy
.cdm
.model
.description
.Stage
;
43 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
44 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
45 import eu
.etaxonomy
.cdm
.model
.location
.Point
;
46 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
47 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
48 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
49 import eu
.etaxonomy
.cdm
.model
.occurrence
.Collection
;
50 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEvent
;
51 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEventType
;
52 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
53 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnitBase
;
54 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
55 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldObservation
;
56 import eu
.etaxonomy
.cdm
.model
.occurrence
.GatheringEvent
;
57 import eu
.etaxonomy
.cdm
.model
.occurrence
.PreservationMethod
;
58 import eu
.etaxonomy
.cdm
.model
.occurrence
.Specimen
;
59 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
60 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
63 * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
64 * a specimen based view. It does not support all functionality available in the
65 * occurrence package.<BR>
66 * The most significant restriction is that a specimen may derive only from one
67 * direct derivation event and there must be only one field observation
68 * (gathering event) it derives from.<BR>
73 public class DerivedUnitFacade
{
74 @SuppressWarnings("unused")
75 private static final Logger logger
= Logger
.getLogger(DerivedUnitFacade
.class);
77 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 ";
79 private static final boolean CREATE
= true;
80 private static final boolean CREATE_NOT
= false;
83 * Enum that defines the class the "Specimen" belongs to. Some methods of
84 * the facade are not available for certain classes and will throw an
85 * Exception when invoking them.
87 public enum DerivedUnitType
{
89 Observation("Observation"),
90 LivingBeing("Living Being"),
92 DerivedUnit("Derived Unit"),
93 //Field Observation is experimental, please handle with care (it is the only type which does not
94 //have a derivedUnit and therefore throws exceptions for all method on derivedUnit attributes
95 FieldObservation("FieldObservation");
97 String representation
;
99 private DerivedUnitType(String representation
) {
100 this.representation
= representation
;
104 * @return the representation
106 public String
getRepresentation() {
107 return representation
;
110 public DerivedUnitBase
<?
> getNewDerivedUnitInstance() {
111 if (this == DerivedUnitType
.Specimen
) {
112 return eu
.etaxonomy
.cdm
.model
.occurrence
.Specimen
.NewInstance();
113 } else if (this == DerivedUnitType
.Observation
) {
114 return eu
.etaxonomy
.cdm
.model
.occurrence
.Observation
.NewInstance();
115 } else if (this == DerivedUnitType
.LivingBeing
) {
116 return eu
.etaxonomy
.cdm
.model
.occurrence
.LivingBeing
.NewInstance();
117 } else if (this == DerivedUnitType
.Fossil
) {
118 return eu
.etaxonomy
.cdm
.model
.occurrence
.Fossil
.NewInstance();
119 } else if (this == DerivedUnitType
.DerivedUnit
) {
120 return eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
.NewInstance();
121 } else if (this == DerivedUnitType
.FieldObservation
) {
124 String message
= "Unknown derived unit type %s";
125 message
= String
.format(message
, this.getRepresentation());
126 throw new IllegalStateException(message
);
130 public static DerivedUnitType
valueOf2(String type
) {
134 type
= type
.replace(" ", "").toLowerCase();
135 if (type
.equals("specimen")) {
137 } else if (type
.equals("livingbeing")) {
139 } else if (type
.equals("observation")) {
141 } else if (type
.equals("fossil")) {
143 } else if (type
.equals("fieldobservation")) {
144 return DerivedUnitType
.FieldObservation
;
145 } else if (type
.equals("unknown")) {
146 return DerivedUnitType
.DerivedUnit
;
147 } else if (type
.equals("derivedunit")) {
148 return DerivedUnitType
.DerivedUnit
;
153 public static DerivedUnitType
valueOf2(Class
<?
extends SpecimenOrObservationBase
> clazz
) {
157 if (clazz
.equals(Specimen
.class)) {
159 } else if (clazz
.equals(eu
.etaxonomy
.cdm
.model
.occurrence
.LivingBeing
.class)) {
161 } else if (clazz
.equals(eu
.etaxonomy
.cdm
.model
.occurrence
.Observation
.class)) {
163 } else if (clazz
.equals(eu
.etaxonomy
.cdm
.model
.occurrence
.Fossil
.class)) {
165 } else if (clazz
.equals(FieldObservation
.class)) {
166 return DerivedUnitType
.FieldObservation
;
167 } else if (clazz
.equals(DerivedUnit
.class)) {
168 return DerivedUnitType
.DerivedUnit
;
175 private final DerivedUnitFacadeConfigurator config
;
177 private Map
<PropertyChangeListener
, CdmBase
> listeners
= new HashMap
<PropertyChangeListener
, CdmBase
>();
179 // private GatheringEvent gatheringEvent;
180 private DerivedUnitType type
; // needed?
182 private FieldObservation fieldObservation
;
184 private final DerivedUnitBase derivedUnit
;
186 // media - the text data holding the media
187 private TextData derivedUnitMediaTextData
;
188 private TextData fieldObjectMediaTextData
;
190 private TextData ecology
;
191 private TextData plantDescription
;
194 * Creates a derived unit facade for a new derived unit of type
200 public static DerivedUnitFacade
NewInstance(DerivedUnitType type
) {
201 return new DerivedUnitFacade(type
, null, null);
205 * Creates a derived unit facade for a new derived unit of type
211 public static DerivedUnitFacade
NewInstance(DerivedUnitType type
, FieldObservation fieldObservation
) {
212 return new DerivedUnitFacade(type
, fieldObservation
, null);
216 * Creates a derived unit facade for a new derived unit of type
220 * @param fieldObservation the field observation to use
221 * @param config the facade configurator to use
222 * //TODO are there any ambiguities to solve with defining a field observation or a configurator
225 public static DerivedUnitFacade
NewInstance(DerivedUnitType type
, FieldObservation fieldObservation
, DerivedUnitFacadeConfigurator config
) {
226 return new DerivedUnitFacade(type
, fieldObservation
, config
);
231 * Creates a derived unit facade for a given derived unit using the default
236 * @throws DerivedUnitFacadeNotSupportedException
238 public static DerivedUnitFacade
NewInstance(DerivedUnitBase derivedUnit
)
239 throws DerivedUnitFacadeNotSupportedException
{
240 return new DerivedUnitFacade(derivedUnit
, null);
243 public static DerivedUnitFacade
NewInstance(DerivedUnitBase derivedUnit
,
244 DerivedUnitFacadeConfigurator config
)
245 throws DerivedUnitFacadeNotSupportedException
{
246 return new DerivedUnitFacade(derivedUnit
, config
);
249 // ****************** CONSTRUCTOR ******************************************
251 private DerivedUnitFacade(DerivedUnitType type
, FieldObservation fieldObservation
, DerivedUnitFacadeConfigurator config
) {
253 config
= DerivedUnitFacadeConfigurator
.NewInstance();
255 this.config
= config
;
258 derivedUnit
= type
.getNewDerivedUnitInstance();
259 setFieldObservation(fieldObservation
);
260 if (derivedUnit
!= null){
263 setFieldObservationCacheStrategy();
267 private DerivedUnitFacade(DerivedUnitBase derivedUnit
,
268 DerivedUnitFacadeConfigurator config
)
269 throws DerivedUnitFacadeNotSupportedException
{
271 if (config
== null) {
272 config
= DerivedUnitFacadeConfigurator
.NewInstance();
274 this.config
= config
;
277 this.derivedUnit
= derivedUnit
;
278 this.type
= DerivedUnitType
.valueOf2(this.derivedUnit
.getClass());
281 if (this.derivedUnit
.getDerivedFrom() != null) {
282 DerivationEvent derivationEvent
= getDerivationEvent(CREATE
);
284 Set
<FieldObservation
> fieldOriginals
= getFieldObservationsOriginals(
285 derivationEvent
, null);
286 if (fieldOriginals
.size() > 1) {
287 throw new DerivedUnitFacadeNotSupportedException(
288 "Specimen must not have more than 1 derivation event");
289 } else if (fieldOriginals
.size() == 0) {
290 // fieldObservation = FieldObservation.NewInstance();
291 } else if (fieldOriginals
.size() == 1) {
292 fieldObservation
= fieldOriginals
.iterator().next();
293 // ###fieldObservation =
294 // getInitializedFieldObservation(fieldObservation);
295 if (config
.isFirePropertyChangeEvents()){
296 addNewEventPropagationListener(fieldObservation
);
299 throw new IllegalStateException("Illegal state");
303 this.derivedUnitMediaTextData
= inititializeTextDataWithSupportTest(
304 Feature
.IMAGE(), this.derivedUnit
, false, true);
306 fieldObjectMediaTextData
= initializeFieldObjectTextDataWithSupportTest(
307 Feature
.IMAGE(), false, true);
309 // handle derivedUnit.getMedia()
310 if (derivedUnit
.getMedia().size() > 0) {
311 // TODO better changed model here to allow only one place for images
312 if (this.config
.isMoveDerivedUnitMediaToGallery()) {
313 Set
<Media
> mediaSet
= derivedUnit
.getMedia();
314 for (Media media
: mediaSet
) {
315 this.addDerivedUnitMedia(media
);
317 mediaSet
.removeAll(getDerivedUnitMedia());
319 throw new DerivedUnitFacadeNotSupportedException(
320 "Specimen may not have direct media. Only (one) image gallery is allowed");
324 // handle fieldObservation.getMedia()
325 if (fieldObservation
!= null && fieldObservation
.getMedia() != null
326 && fieldObservation
.getMedia().size() > 0) {
327 // TODO better changed model here to allow only one place for images
328 if (this.config
.isMoveFieldObjectMediaToGallery()) {
329 Set
<Media
> mediaSet
= fieldObservation
.getMedia();
330 for (Media media
: mediaSet
) {
331 this.addFieldObjectMedia(media
);
333 mediaSet
.removeAll(getFieldObjectMedia());
335 throw new DerivedUnitFacadeNotSupportedException(
336 "Field object may not have direct media. Only (one) image gallery is allowed");
340 // test if descriptions are supported
341 ecology
= initializeFieldObjectTextDataWithSupportTest(
342 Feature
.ECOLOGY(), false, false);
343 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
344 Feature
.DESCRIPTION(), false, false);
350 private DerivedUnitBase
getInitializedDerivedUnit(
351 DerivedUnitBase derivedUnit
) {
352 IOccurrenceService occurrenceService
= this.config
353 .getOccurrenceService();
354 if (occurrenceService
== null) {
357 List
<String
> propertyPaths
= this.config
.getPropertyPaths();
358 if (propertyPaths
== null) {
361 propertyPaths
= getDerivedUnitPropertyPaths(propertyPaths
);
362 DerivedUnitBase result
= (DerivedUnitBase
) occurrenceService
.load(
363 derivedUnit
.getUuid(), propertyPaths
);
368 * Initializes the derived unit according to the configuartions property
369 * path. If the property path is <code>null</code> or no occurrence service
370 * is given the returned object is the same as the input parameter.
372 * @param fieldObservation2
375 private FieldObservation
getInitializedFieldObservation(
376 FieldObservation fieldObservation
) {
377 IOccurrenceService occurrenceService
= this.config
378 .getOccurrenceService();
379 if (occurrenceService
== null) {
380 return fieldObservation
;
382 List
<String
> propertyPaths
= this.config
.getPropertyPaths();
383 if (propertyPaths
== null) {
384 return fieldObservation
;
386 propertyPaths
= getFieldObjectPropertyPaths(propertyPaths
);
387 FieldObservation result
= (FieldObservation
) occurrenceService
.load(
388 fieldObservation
.getUuid(), propertyPaths
);
393 * Transforms the property paths in a way that the facade is handled just
394 * like an ordinary CdmBase object.<BR>
395 * E.g. a property path "collectinAreas" will be translated into
396 * gatheringEvent.collectingAreas
398 * @param propertyPaths
401 private List
<String
> getFieldObjectPropertyPaths(List
<String
> propertyPaths
) {
402 List
<String
> result
= new ArrayList
<String
>();
403 for (String facadePath
: propertyPaths
) {
404 // collecting areas (named area)
405 if (facadePath
.startsWith("collectingAreas")) {
406 facadePath
= "gatheringEvent." + facadePath
;
407 result
.add(facadePath
);
409 // collector (agentBase)
410 else if (facadePath
.startsWith("collector")) {
411 facadePath
= facadePath
.replace("collector",
412 "gatheringEvent.actor");
413 result
.add(facadePath
);
415 // exactLocation (agentBase)
416 else if (facadePath
.startsWith("exactLocation")) {
417 facadePath
= "gatheringEvent." + facadePath
;
418 result
.add(facadePath
);
420 // gatheringPeriod (TimePeriod)
421 else if (facadePath
.startsWith("gatheringPeriod")) {
422 facadePath
= facadePath
.replace("gatheringPeriod",
423 "gatheringEvent.timeperiod");
424 result
.add(facadePath
);
426 // (locality/ localityLanguage , LanguageString)
427 else if (facadePath
.startsWith("locality")) {
428 facadePath
= "gatheringEvent." + facadePath
;
429 result
.add(facadePath
);
432 // *********** FIELD OBJECT ************
433 // fieldObjectDefinitions (Map<language, languageString)
434 else if (facadePath
.startsWith("fieldObjectDefinitions")) {
435 // TODO or definition ???
436 facadePath
= facadePath
.replace("fieldObjectDefinitions",
438 result
.add(facadePath
);
440 // fieldObjectMedia (Media)
441 else if (facadePath
.startsWith("fieldObjectMedia")) {
443 facadePath
= facadePath
.replace("fieldObjectMedia",
444 "descriptions.elements.media");
445 result
.add(facadePath
);
448 // Gathering Event will always be added
449 result
.add("gatheringEvent");
454 * Gathering Event ==================== - gatheringEvent
457 * Field Object ================= - ecology/ ecologyAll (String) ??? -
458 * plant description (like ecology)
460 * - fieldObjectImageGallery (SpecimenDescription) - is automatically
461 * initialized via fieldObjectMedia
468 * Transforms the property paths in a way that the facade is handled just
469 * like an ordinary CdmBase object.<BR>
470 * E.g. a property path "collectinAreas" will be translated into
471 * gatheringEvent.collectingAreas
473 * Not needed (?) as the facade works with REST service property paths
474 * without using this method.
476 * @param propertyPaths
479 private List
<String
> getDerivedUnitPropertyPaths(List
<String
> propertyPaths
) {
480 List
<String
> result
= new ArrayList
<String
>();
481 for (String facadePath
: propertyPaths
) {
482 // determinations (DeterminationEvent)
483 if (facadePath
.startsWith("determinations")) {
484 facadePath
= "" + facadePath
; // no change
485 result
.add(facadePath
);
487 // storedUnder (TaxonNameBase)
488 else if (facadePath
.startsWith("storedUnder")) {
489 facadePath
= "" + facadePath
; // no change
490 result
.add(facadePath
);
492 // sources (IdentifiableSource)
493 else if (facadePath
.startsWith("sources")) {
494 facadePath
= "" + facadePath
; // no change
495 result
.add(facadePath
);
497 // collection (Collection)
498 else if (facadePath
.startsWith("collection")) {
499 facadePath
= "" + facadePath
; // no change
500 result
.add(facadePath
);
502 // (locality/ localityLanguage , LanguageString)
503 else if (facadePath
.startsWith("locality")) {
504 facadePath
= "gatheringEvent." + facadePath
;
505 result
.add(facadePath
);
508 // *********** FIELD OBJECT ************
509 // derivedUnitDefinitions (Map<language, languageString)
510 else if (facadePath
.startsWith("derivedUnitDefinitions")) {
511 // TODO or definition ???
512 facadePath
= facadePath
.replace("derivedUnitDefinitions",
514 result
.add(facadePath
);
517 // derivedUnitMedia (Media)
518 else if (facadePath
.startsWith("derivedUnitMedia")) {
520 facadePath
= facadePath
.replace("derivedUnitMedia",
521 "descriptions.elements.media");
522 result
.add(facadePath
);
528 * //TODO Derived Unit =====================
530 * - derivedUnitImageGallery (SpecimenDescription) - is automatically
531 * initialized via derivedUnitMedia
533 * - derivationEvent (DerivationEvent) - will always be initialized -
534 * duplicates (??? Specimen???) ???
543 private void setCacheStrategy() {
544 if (derivedUnit
== null) {
545 throw new NullPointerException(
546 "Facade's derviedUnit must not be null to set cache strategy");
548 derivedUnit
.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
549 setFieldObservationCacheStrategy();
553 private void setFieldObservationCacheStrategy() {
554 if (this.hasFieldObject()){
555 DerivedUnitFacadeFieldObservationCacheStrategy strategy
= new DerivedUnitFacadeFieldObservationCacheStrategy();
556 this.fieldObservation
.setCacheStrategy(strategy
);
562 * @param createIfNotExists
563 * @param isImageGallery
565 * @throws DerivedUnitFacadeNotSupportedException
567 private TextData
initializeFieldObjectTextDataWithSupportTest(
568 Feature feature
, boolean createIfNotExists
, boolean isImageGallery
)
569 throws DerivedUnitFacadeNotSupportedException
{
571 FieldObservation fieldObject
= getFieldObservation(createIfNotExists
);
572 if (fieldObject
== null) {
575 return inititializeTextDataWithSupportTest(feature
, fieldObject
,
576 createIfNotExists
, isImageGallery
);
582 * @param createIfNotExists
583 * @param isImageGallery
585 * @throws DerivedUnitFacadeNotSupportedException
587 private TextData
inititializeTextDataWithSupportTest(Feature feature
,
588 SpecimenOrObservationBase specimen
, boolean createIfNotExists
,
589 boolean isImageGallery
)
590 throws DerivedUnitFacadeNotSupportedException
{
591 if (feature
== null) {
594 TextData textData
= null;
595 if (createIfNotExists
) {
596 textData
= TextData
.NewInstance(feature
);
599 Set
<SpecimenDescription
> descriptions
;
600 if (isImageGallery
) {
601 descriptions
= specimen
.getSpecimenDescriptionImageGallery();
603 descriptions
= specimen
.getSpecimenDescriptions(false);
605 // no description exists yet for this specimen
606 if (descriptions
.size() == 0) {
607 if (createIfNotExists
) {
608 SpecimenDescription newSpecimenDescription
= SpecimenDescription
609 .NewInstance(specimen
);
610 newSpecimenDescription
.addElement(textData
);
611 newSpecimenDescription
.setImageGallery(isImageGallery
);
617 // description already exists
618 Set
<DescriptionElementBase
> existingTextData
= new HashSet
<DescriptionElementBase
>();
619 for (SpecimenDescription description
: descriptions
) {
620 // collect all existing text data
621 for (DescriptionElementBase element
: description
.getElements()) {
622 if (element
.isInstanceOf(TextData
.class)
623 && (feature
.equals(element
.getFeature()) || isImageGallery
)) {
624 existingTextData
.add(element
);
628 // use existing text data if exactly one exists
629 if (existingTextData
.size() > 1) {
630 throw new DerivedUnitFacadeNotSupportedException(
631 "Specimen facade does not support more than one description text data of type "
632 + feature
.getLabel());
634 } else if (existingTextData
.size() == 1) {
635 return CdmBase
.deproxy(existingTextData
.iterator().next(),
638 if (createIfNotExists
) {
639 SpecimenDescription description
= descriptions
.iterator()
641 description
.addElement(textData
);
648 * Tests if a given image gallery is supported by the derived unit facade.
649 * It returns the only text data attached to the given image gallery. If the
650 * given image gallery does not have text data attached, it is created and
653 * @param imageGallery
655 * @throws DerivedUnitFacadeNotSupportedException
657 private TextData
testImageGallery(SpecimenDescription imageGallery
)
658 throws DerivedUnitFacadeNotSupportedException
{
659 if (imageGallery
.isImageGallery() == false) {
660 throw new DerivedUnitFacadeNotSupportedException(
661 "Image gallery needs to have image gallery flag set");
663 if (imageGallery
.getElements().size() > 1) {
664 throw new DerivedUnitFacadeNotSupportedException(
665 "Image gallery must not have more then one description element");
668 if (imageGallery
.getElements().size() == 0) {
669 textData
= TextData
.NewInstance(Feature
.IMAGE());
670 imageGallery
.addElement(textData
);
672 if (!imageGallery
.getElements().iterator().next()
673 .isInstanceOf(TextData
.class)) {
674 throw new DerivedUnitFacadeNotSupportedException(
675 "Image gallery must only have TextData as element");
677 textData
= CdmBase
.deproxy(imageGallery
.getElements()
678 .iterator().next(), TextData
.class);
684 // ************************** METHODS
685 // *****************************************
687 private TextData
getDerivedUnitImageGalleryTextData(
688 boolean createIfNotExists
)
689 throws DerivedUnitFacadeNotSupportedException
{
690 if (this.derivedUnitMediaTextData
== null && createIfNotExists
) {
691 this.derivedUnitMediaTextData
= getImageGalleryTextData(
692 derivedUnit
, "Specimen");
694 return this.derivedUnitMediaTextData
;
697 private TextData
getObservationImageGalleryTextData(
698 boolean createIfNotExists
)
699 throws DerivedUnitFacadeNotSupportedException
{
700 if (this.fieldObjectMediaTextData
== null && createIfNotExists
) {
701 this.fieldObjectMediaTextData
= getImageGalleryTextData(
702 fieldObservation
, "Field observation");
704 return this.fieldObjectMediaTextData
;
708 * @param derivationEvent
710 * @throws DerivedUnitFacadeNotSupportedException
712 private Set
<FieldObservation
> getFieldObservationsOriginals(
713 DerivationEvent derivationEvent
,
714 Set
<SpecimenOrObservationBase
> recursionAvoidSet
)
715 throws DerivedUnitFacadeNotSupportedException
{
716 if (recursionAvoidSet
== null) {
717 recursionAvoidSet
= new HashSet
<SpecimenOrObservationBase
>();
719 Set
<FieldObservation
> result
= new HashSet
<FieldObservation
>();
720 Set
<SpecimenOrObservationBase
> originals
= derivationEvent
.getOriginals();
721 for (SpecimenOrObservationBase original
: originals
) {
722 if (original
.isInstanceOf(FieldObservation
.class)) {
723 result
.add(CdmBase
.deproxy(original
, FieldObservation
.class));
724 } else if (original
.isInstanceOf(DerivedUnitBase
.class)) {
725 // if specimen has already been tested exclude it from further
727 if (recursionAvoidSet
.contains(original
)) {
730 DerivedUnitBase derivedUnit
= CdmBase
.deproxy(original
,
731 DerivedUnitBase
.class);
732 DerivationEvent originalDerivation
= derivedUnit
.getDerivedFrom();
733 // Set<DerivationEvent> derivationEvents =
734 // original.getDerivationEvents();
735 // for (DerivationEvent originalDerivation : derivationEvents){
736 Set
<FieldObservation
> fieldObservations
= getFieldObservationsOriginals(
737 originalDerivation
, recursionAvoidSet
);
738 result
.addAll(fieldObservations
);
741 throw new DerivedUnitFacadeNotSupportedException(
742 "Unhandled specimen or observation base type: "
743 + original
.getClass().getName());
750 // *********** MEDIA METHODS ******************************
753 // * Returns the media list for a specimen. Throws an exception if the
754 // existing specimen descriptions
755 // * are not supported by this facade.
756 // * @param specimen the specimen the media belongs to
757 // * @param specimenExceptionText text describing the specimen for exception
760 // * @throws DerivedUnitFacadeNotSupportedException
762 // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase
763 // specimen, String specimenExceptionText) throws
764 // DerivedUnitFacadeNotSupportedException{
765 // List<Media> result;
766 // SpecimenDescription imageGallery =
767 // getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
768 // TextData textData = getImageTextDataWithSupportTest(imageGallery,
769 // specimenExceptionText);
770 // result = textData.getMedia();
775 * Returns the media list for a specimen. Throws an exception if the
776 * existing specimen descriptions are not supported by this facade.
779 * the specimen the media belongs to
780 * @param specimenExceptionText
781 * text describing the specimen for exception messages
783 * @throws DerivedUnitFacadeNotSupportedException
785 private TextData
getImageGalleryTextData(SpecimenOrObservationBase specimen
, String specimenExceptionText
)
786 throws DerivedUnitFacadeNotSupportedException
{
788 SpecimenDescription imageGallery
= getImageGalleryWithSupportTest(
789 specimen
, specimenExceptionText
, true);
790 result
= getImageTextDataWithSupportTest(imageGallery
,
791 specimenExceptionText
);
796 * Returns the image gallery of the according specimen. Throws an exception
797 * if the attached image gallerie(s) are not supported by this facade. If no
798 * image gallery exists a new one is created if
799 * <code>createNewIfNotExists</code> is true and if specimen is not
803 * @param specimenText
804 * @param createNewIfNotExists
806 * @throws DerivedUnitFacadeNotSupportedException
808 private SpecimenDescription
getImageGalleryWithSupportTest(
809 SpecimenOrObservationBase
<?
> specimen
, String specimenText
,
810 boolean createNewIfNotExists
)
811 throws DerivedUnitFacadeNotSupportedException
{
812 if (specimen
== null) {
815 SpecimenDescription imageGallery
;
816 if (hasMultipleImageGalleries(specimen
)) {
817 throw new DerivedUnitFacadeNotSupportedException(specimenText
818 + " must not have more than 1 image gallery");
820 imageGallery
= getImageGallery(specimen
, createNewIfNotExists
);
821 getImageTextDataWithSupportTest(imageGallery
, specimenText
);
827 * Returns the media holding text data element of the image gallery. Throws
828 * an exception if multiple such text data already exist. Creates a new text
829 * data if none exists and adds it to the image gallery. If image gallery is
830 * <code>null</code> nothing happens.
832 * @param imageGallery
835 * @throws DerivedUnitFacadeNotSupportedException
837 private TextData
getImageTextDataWithSupportTest(
838 SpecimenDescription imageGallery
, String specimenText
)
839 throws DerivedUnitFacadeNotSupportedException
{
840 if (imageGallery
== null) {
843 TextData textData
= null;
844 for (DescriptionElementBase element
: imageGallery
.getElements()) {
845 if (element
.isInstanceOf(TextData
.class)
846 && element
.getFeature().equals(Feature
.IMAGE())) {
847 if (textData
!= null) {
848 throw new DerivedUnitFacadeNotSupportedException(
850 + " must not have more than 1 image text data element in image gallery");
852 textData
= CdmBase
.deproxy(element
, TextData
.class);
855 if (textData
== null) {
856 textData
= TextData
.NewInstance(Feature
.IMAGE());
857 imageGallery
.addElement(textData
);
863 * Checks, if a specimen belongs to more than one description that is an
869 private boolean hasMultipleImageGalleries(
870 SpecimenOrObservationBase
<?
> derivedUnit
) {
872 Set
<SpecimenDescription
> descriptions
= derivedUnit
873 .getSpecimenDescriptions();
874 for (SpecimenDescription description
: descriptions
) {
875 if (description
.isImageGallery()) {
883 * Returns the image gallery for a specimen. If there are multiple specimen
884 * descriptions marked as image galleries an arbitrary one is chosen. If no
885 * image gallery exists, a new one is created if
886 * <code>createNewIfNotExists</code> is <code>true</code>.<Br>
887 * If specimen is <code>null</code> a null pointer exception is thrown.
889 * @param createNewIfNotExists
892 private SpecimenDescription
getImageGallery(SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
) {
893 SpecimenDescription result
= null;
894 Set
<SpecimenDescription
> descriptions
= specimen
.getSpecimenDescriptions();
895 for (SpecimenDescription description
: descriptions
) {
896 if (description
.isImageGallery()) {
897 result
= description
;
901 if (result
== null && createIfNotExists
) {
902 result
= SpecimenDescription
.NewInstance(specimen
);
903 result
.setImageGallery(true);
909 * Adds a media to the specimens image gallery. If media is
910 * <code>null</code> nothing happens.
914 * @return true if media is not null (as specified by
915 * {@link java.util.Collection#add(Object) Collection.add(E e)}
916 * @throws DerivedUnitFacadeNotSupportedException
918 private boolean addMedia(Media media
, SpecimenOrObservationBase
<?
> specimen
) throws DerivedUnitFacadeNotSupportedException
{
920 List
<Media
> mediaList
= getMediaList(specimen
, true);
921 if (! mediaList
.contains(media
)){
922 return mediaList
.add(media
);
932 * Removes a media from the specimens image gallery.
936 * @return true if an element was removed as a result of this call (as
937 * specified by {@link java.util.Collection#remove(Object)
938 * Collection.remove(E e)}
939 * @throws DerivedUnitFacadeNotSupportedException
941 private boolean removeMedia(Media media
,
942 SpecimenOrObservationBase
<?
> specimen
)
943 throws DerivedUnitFacadeNotSupportedException
{
944 List
<Media
> mediaList
= getMediaList(specimen
, true);
945 return mediaList
== null ?
null : mediaList
.remove(media
);
948 private List
<Media
> getMediaList(SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
)
949 throws DerivedUnitFacadeNotSupportedException
{
950 TextData textData
= getMediaTextData(specimen
, createIfNotExists
);
951 return textData
== null ?
null : textData
.getMedia();
955 * Returns the one media list of a specimen which is part of the only image
956 * gallery that this specimen is part of.<BR>
957 * If these conditions are not hold an exception is thrwon.
961 * @throws DerivedUnitFacadeNotSupportedException
963 // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)
964 // throws DerivedUnitFacadeNotSupportedException {
965 // if (specimen == null){
968 // if (specimen == this.derivedUnit){
969 // return getDerivedUnitImageGalleryMedia();
970 // }else if (specimen == this.fieldObservation){
971 // return getObservationImageGalleryTextData();
973 // return getImageGalleryMedia(specimen, "Undefined specimen ");
978 * Returns the one media list of a specimen which is part of the only image
979 * gallery that this specimen is part of.<BR>
980 * If these conditions are not hold an exception is thrwon.
984 * @throws DerivedUnitFacadeNotSupportedException
986 private TextData
getMediaTextData(SpecimenOrObservationBase
<?
> specimen
,
987 boolean createIfNotExists
)
988 throws DerivedUnitFacadeNotSupportedException
{
989 if (specimen
== null) {
992 if (specimen
== this.derivedUnit
) {
993 return getDerivedUnitImageGalleryTextData(createIfNotExists
);
994 } else if (specimen
== this.fieldObservation
) {
995 return getObservationImageGalleryTextData(createIfNotExists
);
997 return getImageGalleryTextData(specimen
, "Undefined specimen ");
1001 // ****************** GETTER / SETTER / ADDER / REMOVER
1002 // ***********************/
1004 // ****************** Gathering Event *********************************/
1008 public NamedArea
getCountry() {
1009 return (hasGatheringEvent() ?
getGatheringEvent(true).getCountry()
1013 public void setCountry(NamedArea country
) {
1014 getGatheringEvent(true).setCountry(country
);
1018 public void addCollectingArea(NamedArea area
) {
1019 getGatheringEvent(true).addCollectingArea(area
);
1022 public void addCollectingAreas(java
.util
.Collection
<NamedArea
> areas
) {
1023 for (NamedArea area
: areas
) {
1024 getGatheringEvent(true).addCollectingArea(area
);
1029 public Set
<NamedArea
> getCollectingAreas() {
1030 return (hasGatheringEvent() ?
getGatheringEvent(true)
1031 .getCollectingAreas() : null);
1034 public void removeCollectingArea(NamedArea area
) {
1035 if (hasGatheringEvent()) {
1036 getGatheringEvent(true).removeCollectingArea(area
);
1040 // absolute elevation
1042 * meter above/below sea level of the surface
1044 * @see #getAbsoluteElevationError()
1045 * @see #getAbsoluteElevationRange()
1048 public Integer
getAbsoluteElevation() {
1049 return (hasGatheringEvent() ?
getGatheringEvent(true)
1050 .getAbsoluteElevation() : null);
1053 public void setAbsoluteElevation(Integer absoluteElevation
) {
1054 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation
);
1057 // absolute elevation error
1059 public Integer
getAbsoluteElevationError() {
1060 return (hasGatheringEvent() ?
getGatheringEvent(true)
1061 .getAbsoluteElevationError() : null);
1064 public void setAbsoluteElevationError(Integer absoluteElevationError
) {
1065 getGatheringEvent(true).setAbsoluteElevationError(
1066 absoluteElevationError
);
1070 * @see #getAbsoluteElevation()
1071 * @see #getAbsoluteElevationError()
1072 * @see #setAbsoluteElevationRange(Integer, Integer)
1073 * @see #getAbsoluteElevationMaximum()
1076 public Integer
getAbsoluteElevationMinimum() {
1077 if (!hasGatheringEvent()) {
1080 Integer minimum
= getGatheringEvent(true).getAbsoluteElevation();
1081 if (getGatheringEvent(true).getAbsoluteElevationError() != null) {
1083 - getGatheringEvent(true).getAbsoluteElevationError();
1089 * @see #getAbsoluteElevation()
1090 * @see #getAbsoluteElevationError()
1091 * @see #setAbsoluteElevationRange(Integer, Integer)
1092 * @see #getAbsoluteElevationMinimum()
1095 public Integer
getAbsoluteElevationMaximum() {
1096 if (!hasGatheringEvent()) {
1099 Integer maximum
= getGatheringEvent(true).getAbsoluteElevation();
1100 if (getGatheringEvent(true).getAbsoluteElevationError() != null) {
1102 + getGatheringEvent(true).getAbsoluteElevationError();
1108 * This method replaces absoluteElevation and absoulteElevationError by
1109 * internally translating minimum and maximum values into average and error
1110 * values. As all these values are integer based it is necessary that the
1111 * distance is between minimum and maximum is <b>even</b>, otherwise we will
1112 * get a rounding error resulting in a maximum that is increased by 1.
1114 * @see #setAbsoluteElevation(Integer)
1115 * @see #setAbsoluteElevationError(Integer)
1116 * @param minimumElevation
1117 * minimum of the range
1118 * @param maximumElevation
1119 * maximum of the range
1120 * @throws IllegalArgumentException
1122 public void setAbsoluteElevationRange(Integer minimumElevation
, Integer maximumElevation
) throws IllegalArgumentException
{
1123 if (minimumElevation
== null || maximumElevation
== null) {
1124 Integer elevation
= minimumElevation
;
1126 if (minimumElevation
== null) {
1127 elevation
= maximumElevation
;
1128 if (elevation
== null) {
1132 getGatheringEvent(true).setAbsoluteElevation(elevation
);
1133 getGatheringEvent(true).setAbsoluteElevationError(error
);
1135 if (!isEvenDistance(minimumElevation
, maximumElevation
)) {
1136 throw new IllegalArgumentException(
1137 "Distance between minimum and maximum elevation must be even but was "
1138 + Math
.abs(minimumElevation
- maximumElevation
));
1140 Integer absoluteElevationError
= Math
.abs(maximumElevation
1141 - minimumElevation
);
1142 absoluteElevationError
= absoluteElevationError
/ 2;
1143 Integer absoluteElevation
= minimumElevation
1144 + absoluteElevationError
;
1145 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation
);
1146 getGatheringEvent(true).setAbsoluteElevationError(
1147 absoluteElevationError
);
1152 * @param minimumElevation
1153 * @param maximumElevation
1156 public boolean isEvenDistance(Integer minimumElevation
,
1157 Integer maximumElevation
) {
1158 Integer diff
= (maximumElevation
- minimumElevation
);
1159 return diff
% 2 == 0;
1164 public AgentBase
getCollector() {
1165 return (hasGatheringEvent() ?
getGatheringEvent(true).getCollector()
1169 public void setCollector(AgentBase collector
) {
1170 getGatheringEvent(true).setCollector(collector
);
1173 // collecting method
1175 public String
getCollectingMethod() {
1176 return (hasGatheringEvent() ?
getGatheringEvent(true)
1177 .getCollectingMethod() : null);
1180 public void setCollectingMethod(String collectingMethod
) {
1181 getGatheringEvent(true).setCollectingMethod(collectingMethod
);
1184 // distance to ground
1186 public Integer
getDistanceToGround() {
1187 return (hasGatheringEvent() ?
getGatheringEvent(true)
1188 .getDistanceToGround() : null);
1191 public void setDistanceToGround(Integer distanceToGround
) {
1192 getGatheringEvent(true).setDistanceToGround(distanceToGround
);
1195 // distance to water surface
1197 public Integer
getDistanceToWaterSurface() {
1198 return (hasGatheringEvent() ?
getGatheringEvent(true)
1199 .getDistanceToWaterSurface() : null);
1202 public void setDistanceToWaterSurface(Integer distanceToWaterSurface
) {
1203 getGatheringEvent(true).setDistanceToWaterSurface(
1204 distanceToWaterSurface
);
1209 public Point
getExactLocation() {
1210 return (hasGatheringEvent() ?
getGatheringEvent(true).getExactLocation() : null);
1214 * Returns a sexagesimal representation of the exact location (e.g.
1215 * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty
1216 * string is returned.
1218 * @param includeEmptySeconds
1219 * @param includeReferenceSystem
1222 public String
getExactLocationText(boolean includeEmptySeconds
,
1223 boolean includeReferenceSystem
) {
1224 return (this.getExactLocation() == null ?
"" : this.getExactLocation()
1225 .toSexagesimalString(includeEmptySeconds
,
1226 includeReferenceSystem
));
1229 public void setExactLocation(Point exactLocation
) {
1230 getGatheringEvent(true).setExactLocation(exactLocation
);
1233 public void setExactLocationByParsing(String longitudeToParse
,
1234 String latitudeToParse
, ReferenceSystem referenceSystem
,
1235 Integer errorRadius
) throws ParseException
{
1236 Point point
= Point
.NewInstance(null, null, referenceSystem
,
1238 point
.setLongitudeByParsing(longitudeToParse
);
1239 point
.setLatitudeByParsing(latitudeToParse
);
1240 setExactLocation(point
);
1243 // gathering event description
1245 public String
getGatheringEventDescription() {
1246 return (hasGatheringEvent() ?
getGatheringEvent(true).getDescription()
1250 public void setGatheringEventDescription(String description
) {
1251 getGatheringEvent(true).setDescription(description
);
1256 public TimePeriod
getGatheringPeriod() {
1257 return (hasGatheringEvent() ?
getGatheringEvent(true).getTimeperiod()
1261 public void setGatheringPeriod(TimePeriod timeperiod
) {
1262 getGatheringEvent(true).setTimeperiod(timeperiod
);
1267 public LanguageString
getLocality() {
1268 return (hasGatheringEvent() ?
getGatheringEvent(true).getLocality()
1273 * convienience method for {@link #getLocality()}.
1274 * {@link LanguageString#getText() getText()}
1279 public String
getLocalityText() {
1280 LanguageString locality
= getLocality();
1281 if (locality
!= null) {
1282 return locality
.getText();
1288 * convienience method for {@link #getLocality()}.
1289 * {@link LanguageString#getLanguage() getLanguage()}
1294 public Language
getLocalityLanguage() {
1295 LanguageString locality
= getLocality();
1296 if (locality
!= null) {
1297 return locality
.getLanguage();
1303 * Sets the locality string in the default language
1307 public void setLocality(String locality
) {
1308 Language language
= Language
.DEFAULT();
1309 setLocality(locality
, language
);
1312 public void setLocality(String locality
, Language language
) {
1313 LanguageString langString
= LanguageString
.NewInstance(locality
,
1315 setLocality(langString
);
1318 public void setLocality(LanguageString locality
) {
1319 getGatheringEvent(true).setLocality(locality
);
1323 * The gathering event will be used for the field object instead of the old
1324 * gathering event.<BR>
1325 * <B>This method will override all gathering values (see below).</B>
1327 * @see #getAbsoluteElevation()
1328 * @see #getAbsoluteElevationError()
1329 * @see #getDistanceToGround()
1330 * @see #getDistanceToWaterSurface()
1331 * @see #getExactLocation()
1332 * @see #getGatheringEventDescription()
1333 * @see #getGatheringPeriod()
1334 * @see #getCollectingAreas()
1335 * @see #getCollectingMethod()
1336 * @see #getLocality()
1337 * @see #getCollector()
1338 * @param gatheringEvent
1340 public void setGatheringEvent(GatheringEvent gatheringEvent
) {
1341 getFieldObservation(true).setGatheringEvent(gatheringEvent
);
1344 public boolean hasGatheringEvent() {
1345 return (getGatheringEvent(false) != null);
1348 public GatheringEvent
innerGatheringEvent() {
1349 return getGatheringEvent(false);
1352 public GatheringEvent
getGatheringEvent(boolean createIfNotExists
) {
1353 if (!hasFieldObservation() && !createIfNotExists
) {
1356 if (createIfNotExists
&& getFieldObservation(true).getGatheringEvent() == null) {
1357 GatheringEvent gatheringEvent
= GatheringEvent
.NewInstance();
1358 getFieldObservation(true).setGatheringEvent(gatheringEvent
);
1360 return getFieldObservation(true).getGatheringEvent();
1363 // ****************** Field Object ************************************/
1366 * Returns true if a field observation exists (even if all attributes are
1367 * empty or <code>null<code>.
1371 public boolean hasFieldObject() {
1372 return this.fieldObservation
!= null;
1377 public String
getEcology() {
1378 return getEcology(Language
.DEFAULT());
1381 public String
getEcology(Language language
) {
1382 LanguageString languageString
= getEcologyAll().get(language
);
1383 return (languageString
== null ?
null : languageString
.getText());
1386 // public String getEcologyPreferred(List<Language> languages){
1387 // LanguageString languageString =
1388 // getEcologyAll().getPreferredLanguageString(languages);
1389 // return languageString.getText();
1392 * Returns a copy of the multilanguage text holding the ecology data.
1394 * @see {@link TextData#getMultilanguageText()}
1398 public Map
<Language
, LanguageString
> getEcologyAll() {
1399 if (ecology
== null) {
1401 ecology
= initializeFieldObjectTextDataWithSupportTest(
1402 Feature
.ECOLOGY(), false, false);
1403 } catch (DerivedUnitFacadeNotSupportedException e
) {
1404 throw new IllegalStateException(notSupportMessage
, e
);
1406 if (ecology
== null) {
1407 return new HashMap
<Language
, LanguageString
>();
1410 return ecology
.getMultilanguageText();
1413 public void setEcology(String ecology
) {
1414 setEcology(ecology
, null);
1417 public void setEcology(String ecologyText
, Language language
) {
1418 if (language
== null) {
1419 language
= Language
.DEFAULT();
1421 if (ecology
== null) {
1423 ecology
= initializeFieldObjectTextDataWithSupportTest(
1424 Feature
.ECOLOGY(), true, false);
1425 } catch (DerivedUnitFacadeNotSupportedException e
) {
1426 throw new IllegalStateException(notSupportMessage
, e
);
1429 if (ecologyText
== null) {
1430 ecology
.removeText(language
);
1432 ecology
.putText(language
, ecologyText
);
1436 public void removeEcology(Language language
) {
1437 setEcology(null, language
);
1441 * Removes ecology for the default language
1443 public void removeEcology() {
1444 setEcology(null, null);
1447 public void removeEcologyAll() {
1451 // plant description
1453 public String
getPlantDescription() {
1454 return getPlantDescription(null);
1457 public String
getPlantDescription(Language language
) {
1458 if (language
== null) {
1459 language
= Language
.DEFAULT();
1461 LanguageString languageString
= getPlantDescriptionAll().get(language
);
1462 return (languageString
== null ?
null : languageString
.getText());
1465 // public String getPlantDescriptionPreferred(List<Language> languages){
1466 // LanguageString languageString =
1467 // getPlantDescriptionAll().getPreferredLanguageString(languages);
1468 // return languageString.getText();
1471 * Returns a copy of the multilanguage text holding the description data.
1473 * @see {@link TextData#getMultilanguageText()}
1477 public Map
<Language
, LanguageString
> getPlantDescriptionAll() {
1478 if (plantDescription
== null) {
1480 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
1481 Feature
.DESCRIPTION(), false, false);
1482 } catch (DerivedUnitFacadeNotSupportedException e
) {
1483 throw new IllegalStateException(notSupportMessage
, e
);
1485 if (plantDescription
== null) {
1486 return new HashMap
<Language
, LanguageString
>();
1489 return plantDescription
.getMultilanguageText();
1492 public void setPlantDescription(String plantDescription
) {
1493 setPlantDescription(plantDescription
, null);
1496 public void setPlantDescription(String plantDescriptionText
,
1497 Language language
) {
1498 if (language
== null) {
1499 language
= Language
.DEFAULT();
1501 if (plantDescription
== null) {
1503 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
1504 Feature
.DESCRIPTION(), true, false);
1505 } catch (DerivedUnitFacadeNotSupportedException e
) {
1506 throw new IllegalStateException(notSupportMessage
, e
);
1509 if (plantDescriptionText
== null) {
1510 plantDescription
.removeText(language
);
1512 plantDescription
.putText(language
, plantDescriptionText
);
1516 public void removePlantDescription(Language language
) {
1517 setPlantDescription(null, language
);
1520 // field object definition
1521 public void addFieldObjectDefinition(String text
, Language language
) {
1522 getFieldObservation(true).putDefinition(language
, text
);
1526 public Map
<Language
, LanguageString
> getFieldObjectDefinition() {
1527 if (!hasFieldObservation()) {
1528 return new HashMap
<Language
, LanguageString
>();
1530 return getFieldObservation(true).getDefinition();
1534 public String
getFieldObjectDefinition(Language language
) {
1535 Map
<Language
, LanguageString
> map
= getFieldObjectDefinition();
1536 LanguageString languageString
= (map
== null ?
null : map
.get(language
));
1537 if (languageString
!= null) {
1538 return languageString
.getText();
1544 public void removeFieldObjectDefinition(Language lang
) {
1545 if (hasFieldObservation()) {
1546 getFieldObservation(true).removeDefinition(lang
);
1551 public boolean addFieldObjectMedia(Media media
) {
1553 return addMedia(media
, getFieldObservation(true));
1554 } catch (DerivedUnitFacadeNotSupportedException e
) {
1555 throw new IllegalStateException(notSupportMessage
, e
);
1560 * Returns true, if an image gallery for the field object exists.<BR>
1561 * Returns also <code>true</code> if the image gallery is empty.
1565 public boolean hasFieldObjectImageGallery() {
1566 if (!hasFieldObject()) {
1569 return (getImageGallery(fieldObservation
, false) != null);
1573 public void setFieldObjectImageGallery(SpecimenDescription imageGallery
)
1574 throws DerivedUnitFacadeNotSupportedException
{
1575 SpecimenDescription existingGallery
= getFieldObjectImageGallery(false);
1577 // test attached specimens contain this.derivedUnit
1578 SpecimenOrObservationBase
<?
> facadeFieldObservation
= innerFieldObservation();
1579 testSpecimenInImageGallery(imageGallery
, facadeFieldObservation
);
1581 if (existingGallery
!= null) {
1582 if (existingGallery
!= imageGallery
) {
1583 throw new DerivedUnitFacadeNotSupportedException(
1584 "DerivedUnitFacade does not allow more than one image gallery");
1589 TextData textData
= testImageGallery(imageGallery
);
1590 this.fieldObjectMediaTextData
= textData
;
1595 * Returns the field object image gallery. If no such image gallery exists
1596 * and createIfNotExists is true an new one is created. Otherwise null is
1599 * @param createIfNotExists
1602 public SpecimenDescription
getFieldObjectImageGallery(
1603 boolean createIfNotExists
) {
1606 textData
= initializeFieldObjectTextDataWithSupportTest(
1607 Feature
.IMAGE(), createIfNotExists
, true);
1608 } catch (DerivedUnitFacadeNotSupportedException e
) {
1609 throw new IllegalStateException(notSupportMessage
, e
);
1611 if (textData
!= null) {
1612 return CdmBase
.deproxy(textData
.getInDescription(),
1613 SpecimenDescription
.class);
1620 * Returns the media for the field object.<BR>
1625 public List
<Media
> getFieldObjectMedia() {
1627 List
<Media
> result
= getMediaList(getFieldObservation(false), false);
1628 return result
== null ?
new ArrayList
<Media
>() : result
;
1629 } catch (DerivedUnitFacadeNotSupportedException e
) {
1630 throw new IllegalStateException(notSupportMessage
, e
);
1634 public boolean removeFieldObjectMedia(Media media
) {
1636 return removeMedia(media
, getFieldObservation(false));
1637 } catch (DerivedUnitFacadeNotSupportedException e
) {
1638 throw new IllegalStateException(notSupportMessage
, e
);
1644 public String
getFieldNumber() {
1645 if (!hasFieldObservation()) {
1648 return getFieldObservation(true).getFieldNumber();
1652 public void setFieldNumber(String fieldNumber
) {
1653 getFieldObservation(true).setFieldNumber(fieldNumber
);
1656 // primary collector
1658 public Person
getPrimaryCollector() {
1659 if (!hasFieldObservation()) {
1662 return getFieldObservation(true).getPrimaryCollector();
1666 public void setPrimaryCollector(Person primaryCollector
) {
1667 getFieldObservation(true).setPrimaryCollector(primaryCollector
);
1672 public String
getFieldNotes() {
1673 if (!hasFieldObservation()) {
1676 return getFieldObservation(true).getFieldNotes();
1680 public void setFieldNotes(String fieldNotes
) {
1681 getFieldObservation(true).setFieldNotes(fieldNotes
);
1684 // individual counts
1686 public Integer
getIndividualCount() {
1687 return (hasFieldObservation() ?
getFieldObservation(true)
1688 .getIndividualCount() : null);
1691 public void setIndividualCount(Integer individualCount
) {
1692 getFieldObservation(true).setIndividualCount(individualCount
);
1697 public Stage
getLifeStage() {
1698 return (hasFieldObservation() ?
getFieldObservation(true)
1699 .getLifeStage() : null);
1702 public void setLifeStage(Stage lifeStage
) {
1703 getFieldObservation(true).setLifeStage(lifeStage
);
1708 public Sex
getSex() {
1709 return (hasFieldObservation() ?
getFieldObservation(true).getSex()
1713 public void setSex(Sex sex
) {
1714 getFieldObservation(true).setSex(sex
);
1717 // field observation
1718 public boolean hasFieldObservation() {
1719 return (getFieldObservation(false) != null);
1723 * Returns the field observation as an object.
1727 public FieldObservation
innerFieldObservation() {
1728 return getFieldObservation(false);
1732 * Returns the field observation as an object.
1736 public FieldObservation
getFieldObservation(boolean createIfNotExists
) {
1737 if (fieldObservation
== null && createIfNotExists
) {
1738 setFieldObservation(FieldObservation
.NewInstance());
1740 return this.fieldObservation
;
1744 private void setFieldObservation(FieldObservation fieldObservation
) {
1745 this.fieldObservation
= fieldObservation
;
1746 if (fieldObservation
!= null){
1747 if (config
.isFirePropertyChangeEvents()){
1748 addNewEventPropagationListener(fieldObservation
);
1750 if (derivedUnit
!= null){
1751 DerivationEvent derivationEvent
= getDerivationEvent(CREATE
);
1752 derivationEvent
.addOriginal(fieldObservation
);
1754 setFieldObservationCacheStrategy();
1758 // ****************** Specimen *******************************************
1761 public void addDerivedUnitDefinition(String text
, Language language
) {
1762 innerDerivedUnit().putDefinition(language
, text
);
1766 public Map
<Language
, LanguageString
> getDerivedUnitDefinitions() {
1768 return this.derivedUnit
.getDefinition();
1772 public String
getDerivedUnitDefinition(Language language
) {
1774 Map
<Language
, LanguageString
> languageMap
= derivedUnit
.getDefinition();
1775 LanguageString languageString
= languageMap
.get(language
);
1776 if (languageString
!= null) {
1777 return languageString
.getText();
1783 public void removeDerivedUnitDefinition(Language lang
) {
1785 derivedUnit
.removeDefinition(lang
);
1789 public void addDetermination(DeterminationEvent determination
) {
1791 //TODO implement correct bidirectional mapping in model classes
1792 determination
.setIdentifiedUnit(derivedUnit
);
1793 derivedUnit
.addDetermination(determination
);
1797 public DeterminationEvent
getPreferredDetermination() {
1799 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1800 for (DeterminationEvent event
: events
){
1801 if (event
.getPreferredFlag() == true){
1809 * This method returns the preferred determination.
1810 * @see #getOtherDeterminations()
1811 * @see #getDeterminations()
1815 public void setPreferredDetermination(DeterminationEvent newEvent
) {
1817 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1818 for (DeterminationEvent event
: events
){
1819 if (event
.getPreferredFlag() == true){
1820 event
.setPreferredFlag(false);
1823 newEvent
.setPreferredFlag(true);
1824 events
.add(newEvent
);
1828 * This method returns all determinations except for the preferred one.
1829 * @see #getPreferredDetermination()
1830 * @see #getDeterminations()
1834 public Set
<DeterminationEvent
> getOtherDeterminations() {
1836 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1837 Set
<DeterminationEvent
> result
= new HashSet
<DeterminationEvent
>();
1838 for (DeterminationEvent event
: events
){
1839 if (event
.getPreferredFlag() != true){
1847 * This method returns all determination events. The preferred one {@link #getPreferredDetermination()}
1848 * and all others {@link #getOtherDeterminations()}.
1852 public Set
<DeterminationEvent
> getDeterminations() {
1854 return derivedUnit
.getDeterminations();
1857 public void removeDetermination(DeterminationEvent determination
) {
1859 derivedUnit
.removeDetermination(determination
);
1863 public boolean addDerivedUnitMedia(Media media
) {
1866 return addMedia(media
, derivedUnit
);
1867 } catch (DerivedUnitFacadeNotSupportedException e
) {
1868 throw new IllegalStateException(notSupportMessage
, e
);
1873 * Returns true, if an image gallery exists for the specimen.<BR>
1874 * Returns also <code>true</code> if the image gallery is empty.
1876 public boolean hasDerivedUnitImageGallery() {
1877 return (getImageGallery(derivedUnit
, false) != null);
1880 public SpecimenDescription
getDerivedUnitImageGallery(boolean createIfNotExists
) {
1884 textData
= inititializeTextDataWithSupportTest(Feature
.IMAGE(),
1885 derivedUnit
, createIfNotExists
, true);
1886 } catch (DerivedUnitFacadeNotSupportedException e
) {
1887 throw new IllegalStateException(notSupportMessage
, e
);
1889 if (textData
!= null) {
1890 return CdmBase
.deproxy(textData
.getInDescription(),
1891 SpecimenDescription
.class);
1897 public void setDerivedUnitImageGallery(SpecimenDescription imageGallery
)
1898 throws DerivedUnitFacadeNotSupportedException
{
1900 SpecimenDescription existingGallery
= getDerivedUnitImageGallery(false);
1902 // test attached specimens contain this.derivedUnit
1903 SpecimenOrObservationBase facadeDerivedUnit
= innerDerivedUnit();
1904 testSpecimenInImageGallery(imageGallery
, facadeDerivedUnit
);
1906 if (existingGallery
!= null) {
1907 if (existingGallery
!= imageGallery
) {
1908 throw new DerivedUnitFacadeNotSupportedException(
1909 "DerivedUnitFacade does not allow more than one image gallery");
1914 TextData textData
= testImageGallery(imageGallery
);
1915 this.derivedUnitMediaTextData
= textData
;
1920 * @param imageGallery
1921 * @throws DerivedUnitFacadeNotSupportedException
1923 private void testSpecimenInImageGallery(SpecimenDescription imageGallery
, SpecimenOrObservationBase specimen
)
1924 throws DerivedUnitFacadeNotSupportedException
{
1925 Set
<SpecimenOrObservationBase
> imageGallerySpecimens
= imageGallery
.getDescribedSpecimenOrObservations();
1926 if (imageGallerySpecimens
.size() < 1) {
1927 throw new DerivedUnitFacadeNotSupportedException(
1928 "Image Gallery has no Specimen attached. Please attache according specimen or field observation.");
1930 if (!imageGallerySpecimens
.contains(specimen
)) {
1931 throw new DerivedUnitFacadeNotSupportedException(
1932 "Image Gallery has not the facade's field object attached. Please add field object first to image gallery specimenOrObservation list.");
1937 * Returns the media for the specimen.<BR>
1942 public List
<Media
> getDerivedUnitMedia() {
1945 List
<Media
> result
= getMediaList(derivedUnit
, false);
1946 return result
== null ?
new ArrayList
<Media
>() : result
;
1947 } catch (DerivedUnitFacadeNotSupportedException e
) {
1948 throw new IllegalStateException(notSupportMessage
, e
);
1952 public boolean removeDerivedUnitMedia(Media media
) {
1955 return removeMedia(media
, derivedUnit
);
1956 } catch (DerivedUnitFacadeNotSupportedException e
) {
1957 throw new IllegalStateException(notSupportMessage
, e
);
1963 public String
getAccessionNumber() {
1965 return derivedUnit
.getAccessionNumber();
1968 public void setAccessionNumber(String accessionNumber
) {
1970 derivedUnit
.setAccessionNumber(accessionNumber
);
1974 public String
getCatalogNumber() {
1976 return derivedUnit
.getCatalogNumber();
1979 public void setCatalogNumber(String catalogNumber
) {
1981 derivedUnit
.setCatalogNumber(catalogNumber
);
1985 public String
getBarcode() {
1987 return derivedUnit
.getBarcode();
1990 public void setBarcode(String barcode
) {
1992 derivedUnit
.setBarcode(barcode
);
1995 // Preservation Method
1998 * Only supported by specimen and fossils
2000 * @see #DerivedUnitType
2004 public PreservationMethod
getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException
{
2006 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2007 return CdmBase
.deproxy(derivedUnit
, Specimen
.class)
2011 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2012 throw new MethodNotSupportedByDerivedUnitTypeException(
2013 "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
2021 * Only supported by specimen and fossils
2023 * @see #DerivedUnitType
2026 public void setPreservationMethod(PreservationMethod preservation
)
2027 throws MethodNotSupportedByDerivedUnitTypeException
{
2029 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2030 CdmBase
.deproxy(derivedUnit
, Specimen
.class).setPreservation(
2034 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2035 throw new MethodNotSupportedByDerivedUnitTypeException(
2036 "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
2043 // Stored under name
2045 public TaxonNameBase
getStoredUnder() {
2047 return derivedUnit
.getStoredUnder();
2050 public void setStoredUnder(TaxonNameBase storedUnder
) {
2052 derivedUnit
.setStoredUnder(storedUnder
);
2056 public String
getTitleCache() {
2057 SpecimenOrObservationBase
<?
> titledUnit
= getTitledUnit();
2059 if (!titledUnit
.isProtectedTitleCache()) {
2060 // always compute title cache anew as long as there are no property
2061 // change listeners on
2062 // field observation, gathering event etc
2063 titledUnit
.setTitleCache(null, false);
2065 return titledUnit
.getTitleCache();
2068 private SpecimenOrObservationBase
<?
> getTitledUnit(){
2069 return (derivedUnit
!= null )? derivedUnit
: fieldObservation
;
2072 public boolean isProtectedTitleCache() {
2073 return getTitledUnit().isProtectedTitleCache();
2076 public void setTitleCache(String titleCache
, boolean isProtected
) {
2077 this.getTitledUnit().setTitleCache(titleCache
, isProtected
);
2081 * Returns the derived unit itself.
2083 * @return the derived unit
2085 public DerivedUnitBase
innerDerivedUnit() {
2086 return this.derivedUnit
;
2090 // * Returns the derived unit itself.
2092 // * @return the derived unit
2094 // public DerivedUnitBase innerDerivedUnit(boolean createIfNotExists) {
2095 // DerivedUnit result = this.derivedUnit;
2096 // if (result == null && createIfNotExists){
2097 // if (this.fieldObservation == null){
2098 // String message = "Field observation must exist to create derived unit.";
2099 // throw new IllegalStateException(message);
2102 // DerivationEvent derivationEvent = getDerivationEvent(true);
2103 // derivationEvent.addOriginal(fieldObservation);
2104 // return this.derivedUnit;
2109 private boolean hasDerivationEvent() {
2110 return getDerivationEvent() == null ?
false : true;
2113 private DerivationEvent
getDerivationEvent() {
2114 return getDerivationEvent(false);
2118 * Returns the derivation event. If no derivation event exists and <code>createIfNotExists</code>
2119 * is <code>true</code> a new derivation event is created and returned.
2120 * Otherwise <code>null</code> is returned.
2121 * @param createIfNotExists
2123 private DerivationEvent
getDerivationEvent(boolean createIfNotExists
) {
2124 DerivationEvent result
= null;
2125 if (derivedUnit
!= null){
2126 result
= derivedUnit
.getDerivedFrom();
2130 if (result
== null && createIfNotExists
) {
2131 DerivationEventType type
= null;
2132 if (isAccessioned(derivedUnit
)){
2133 type
= DerivationEventType
.ACCESSIONING();
2136 result
= DerivationEvent
.NewInstance(type
);
2137 derivedUnit
.setDerivedFrom(result
);
2143 * TODO still unclear which classes do definetly require accessioning.
2144 * Only return true for those classes which are clear.
2145 * @param derivedUnit
2148 private boolean isAccessioned(DerivedUnitBase
<?
> derivedUnit
) {
2149 if (derivedUnit
.isInstanceOf(Specimen
.class) ){
2150 return CdmBase
.deproxy(derivedUnit
, Specimen
.class).getClass().equals(Specimen
.class);
2157 public String
getExsiccatum()
2158 throws MethodNotSupportedByDerivedUnitTypeException
{
2160 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2161 return CdmBase
.deproxy(derivedUnit
, Specimen
.class).getExsiccatum();
2164 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2165 throw new MethodNotSupportedByDerivedUnitTypeException(
2166 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2173 public void setExsiccatum(String exsiccatum
) throws Exception
{
2175 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2176 CdmBase
.deproxy(derivedUnit
, Specimen
.class).setExsiccatum(
2180 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2181 throw new MethodNotSupportedByDerivedUnitTypeException(
2182 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2190 public void addSource(IdentifiableSource source
) {
2192 this.derivedUnit
.addSource(source
);
2196 * Creates an {@link IOriginalSource orignal source} or type ,
2197 * adds it to the specimen and returns it.
2200 * @param microReference
2201 * @param originalNameString
2204 public IdentifiableSource
addSource(OriginalSourceType type
, Reference reference
, String microReference
, String originalNameString
) {
2205 IdentifiableSource source
= IdentifiableSource
.NewInstance(type
, null, null, reference
, microReference
);
2206 source
.setOriginalNameString(originalNameString
);
2212 public Set
<IdentifiableSource
> getSources() {
2214 return derivedUnit
.getSources();
2217 public void removeSource(IdentifiableSource source
) {
2219 this.derivedUnit
.removeSource(source
);
2223 * @return the collection
2226 public Collection
getCollection() {
2228 return derivedUnit
.getCollection();
2233 * the collection to set
2235 public void setCollection(Collection collection
) {
2237 derivedUnit
.setCollection(collection
);
2241 public void addAnnotation(Annotation annotation
) {
2243 this.derivedUnit
.addAnnotation(annotation
);
2247 public void getAnnotations() {
2249 this.derivedUnit
.getAnnotations();
2252 public void removeAnnotation(Annotation annotation
) {
2254 this.derivedUnit
.removeAnnotation(annotation
);
2257 // ******************************* Events ***************************
2259 //set of events that were currently fired by this facades field observation
2260 //to avoid recursive fireing of the same event
2261 private Set
<PropertyChangeEvent
> fireingEvents
= new HashSet
<PropertyChangeEvent
>();
2266 private void addNewEventPropagationListener(CdmBase listeningObject
) {
2267 //if there is already a listener, don't do anything
2268 for (PropertyChangeListener listener
: this.listeners
.keySet()){
2269 if (listeners
.get(listener
) == listeningObject
){
2273 //create new listener
2274 PropertyChangeListener listener
= new PropertyChangeListener() {
2276 public void propertyChange(PropertyChangeEvent event
) {
2277 if (derivedUnit
!= null){
2278 derivedUnit
.firePropertyChange(event
);
2280 if (! event
.getSource().equals(fieldObservation
) && ! fireingEvents
.contains(event
) ){
2281 fireingEvents
.add(event
);
2282 fieldObservation
.firePropertyChange(event
);
2283 fireingEvents
.remove(event
);
2288 //add listener to listening object and to list of listeners
2289 listeningObject
.addPropertyChangeListener(listener
);
2290 listeners
.put(listener
, listeningObject
);
2293 // **************** Other Collections ********************************
2296 * Creates a duplicate specimen which derives from the same derivation event
2297 * as the facade specimen and adds collection data to it (all data available
2298 * in DerivedUnitBase and Specimen. Data from SpecimenOrObservationBase and
2299 * above are not yet shared at the moment.
2302 * @param catalogNumber
2303 * @param accessionNumber
2304 * @param collectorsNumber
2305 * @param storedUnder
2306 * @param preservation
2309 public Specimen
addDuplicate(Collection collection
, String catalogNumber
,
2310 String accessionNumber
,
2311 TaxonNameBase storedUnder
, PreservationMethod preservation
) {
2313 Specimen duplicate
= Specimen
.NewInstance();
2314 duplicate
.setDerivedFrom(getDerivationEvent(CREATE
));
2315 duplicate
.setCollection(collection
);
2316 duplicate
.setCatalogNumber(catalogNumber
);
2317 duplicate
.setAccessionNumber(accessionNumber
);
2318 duplicate
.setStoredUnder(storedUnder
);
2319 duplicate
.setPreservation(preservation
);
2323 public void addDuplicate(DerivedUnitBase duplicateSpecimen
) {
2324 // TODO check derivedUnitType
2326 getDerivationEvent(CREATE
).addDerivative(duplicateSpecimen
);
2330 public Set
<Specimen
> getDuplicates() {
2332 Set
<Specimen
> result
= new HashSet
<Specimen
>();
2333 if (hasDerivationEvent()) {
2334 for (DerivedUnitBase derivedUnit
: getDerivationEvent(CREATE
)
2335 .getDerivatives()) {
2336 if (derivedUnit
.isInstanceOf(Specimen
.class)
2337 && !derivedUnit
.equals(this.derivedUnit
)) {
2338 result
.add(CdmBase
.deproxy(derivedUnit
, Specimen
.class));
2345 public void removeDuplicate(Specimen duplicateSpecimen
) {
2347 if (hasDerivationEvent()) {
2348 getDerivationEvent(CREATE
).removeDerivative(duplicateSpecimen
);
2354 private void testDerivedUnit() {
2355 if (derivedUnit
== null){
2356 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");
2360 public void setType(DerivedUnitType type
) {
2364 public DerivedUnitType
getType() {
2370 * Closes this facade. As a minimum this method removes all listeners created by this facade from their
2371 * listening objects.
2373 public void close(){
2374 for (PropertyChangeListener listener
: this.listeners
.keySet()){
2375 CdmBase listeningObject
= listeners
.get(listener
);
2376 listeningObject
.removePropertyChangeListener(listener
);