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
.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
.DerivationEventType
;
49 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
50 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnitBase
;
51 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
52 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldObservation
;
53 import eu
.etaxonomy
.cdm
.model
.occurrence
.GatheringEvent
;
54 import eu
.etaxonomy
.cdm
.model
.occurrence
.Observation
;
55 import eu
.etaxonomy
.cdm
.model
.occurrence
.PreservationMethod
;
56 import eu
.etaxonomy
.cdm
.model
.occurrence
.Specimen
;
57 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
58 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
61 * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
62 * a specimen based view. It does not support all functionality available in the
63 * occurrence package.<BR>
64 * The most significant restriction is that a specimen may derive only from one
65 * direct derivation event and there must be only one field observation
66 * (gathering event) it derives from.<BR>
71 public class DerivedUnitFacade
{
72 @SuppressWarnings("unused")
73 private static final Logger logger
= Logger
.getLogger(DerivedUnitFacade
.class);
75 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 ";
77 private static final boolean CREATE
= true;
78 private static final boolean CREATE_NOT
= false;
81 * Enum that defines the class the "Specimen" belongs to. Some methods of
82 * the facade are not available for certain classes and will throw an
83 * Exception when invoking them.
85 public enum DerivedUnitType
{
87 Observation("Observation"),
88 LivingBeing("Living Being"),
90 DerivedUnit("Derived Unit"),
91 //Field Observation is experimental, please handle with care (it is the only type which does not
92 //have a derivedUnit and therefore throws exceptions for all method on derivedUnit attributes
93 FieldObservation("FieldObservation");
95 String representation
;
97 private DerivedUnitType(String representation
) {
98 this.representation
= representation
;
102 * @return the representation
104 public String
getRepresentation() {
105 return representation
;
108 public DerivedUnitBase
<?
> getNewDerivedUnitInstance() {
109 if (this == DerivedUnitType
.Specimen
) {
110 return eu
.etaxonomy
.cdm
.model
.occurrence
.Specimen
.NewInstance();
111 } else if (this == DerivedUnitType
.Observation
) {
112 return eu
.etaxonomy
.cdm
.model
.occurrence
.Observation
.NewInstance();
113 } else if (this == DerivedUnitType
.LivingBeing
) {
114 return eu
.etaxonomy
.cdm
.model
.occurrence
.LivingBeing
.NewInstance();
115 } else if (this == DerivedUnitType
.Fossil
) {
116 return eu
.etaxonomy
.cdm
.model
.occurrence
.Fossil
.NewInstance();
117 } else if (this == DerivedUnitType
.DerivedUnit
) {
118 return eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
.NewInstance();
119 } else if (this == DerivedUnitType
.FieldObservation
) {
122 String message
= "Unknown derived unit type %s";
123 message
= String
.format(message
, this.getRepresentation());
124 throw new IllegalStateException(message
);
128 public static DerivedUnitType
valueOf2(String type
) {
132 type
= type
.replace(" ", "").toLowerCase();
133 if (type
.equals("specimen")) {
135 } else if (type
.equals("livingbeing")) {
137 } else if (type
.equals("observation")) {
139 } else if (type
.equals("fossil")) {
141 } else if (type
.equals("fieldobservation")) {
142 return DerivedUnitType
.FieldObservation
;
143 } else if (type
.equals("unknown")) {
144 return DerivedUnitType
.DerivedUnit
;
145 } else if (type
.equals("derivedunit")) {
146 return DerivedUnitType
.DerivedUnit
;
151 public static DerivedUnitType
valueOf2(Class
<?
extends SpecimenOrObservationBase
> clazz
) {
155 if (clazz
.equals(Specimen
.class)) {
157 } else if (clazz
.equals(eu
.etaxonomy
.cdm
.model
.occurrence
.LivingBeing
.class)) {
159 } else if (clazz
.equals(eu
.etaxonomy
.cdm
.model
.occurrence
.Observation
.class)) {
161 } else if (clazz
.equals(eu
.etaxonomy
.cdm
.model
.occurrence
.Fossil
.class)) {
163 } else if (clazz
.equals(FieldObservation
.class)) {
164 return DerivedUnitType
.FieldObservation
;
165 } else if (clazz
.equals(DerivedUnit
.class)) {
166 return DerivedUnitType
.DerivedUnit
;
173 private final DerivedUnitFacadeConfigurator config
;
175 private Map
<PropertyChangeListener
, CdmBase
> listeners
= new HashMap
<PropertyChangeListener
, CdmBase
>();
177 // private GatheringEvent gatheringEvent;
178 private DerivedUnitType type
; // needed?
180 private FieldObservation fieldObservation
;
182 private final DerivedUnitBase derivedUnit
;
184 // media - the text data holding the media
185 private TextData derivedUnitMediaTextData
;
186 private TextData fieldObjectMediaTextData
;
188 private TextData ecology
;
189 private TextData plantDescription
;
192 * Creates a derived unit facade for a new derived unit of type
198 public static DerivedUnitFacade
NewInstance(DerivedUnitType type
) {
199 return new DerivedUnitFacade(type
, null, null);
203 * Creates a derived unit facade for a new derived unit of type
209 public static DerivedUnitFacade
NewInstance(DerivedUnitType type
, FieldObservation fieldObservation
) {
210 return new DerivedUnitFacade(type
, fieldObservation
, null);
214 * Creates a derived unit facade for a new derived unit of type
218 * @param fieldObservation the field observation to use
219 * @param config the facade configurator to use
220 * //TODO are there any ambiguities to solve with defining a field observation or a configurator
223 public static DerivedUnitFacade
NewInstance(DerivedUnitType type
, FieldObservation fieldObservation
, DerivedUnitFacadeConfigurator config
) {
224 return new DerivedUnitFacade(type
, fieldObservation
, config
);
229 * Creates a derived unit facade for a given derived unit using the default
234 * @throws DerivedUnitFacadeNotSupportedException
236 public static DerivedUnitFacade
NewInstance(DerivedUnitBase derivedUnit
)
237 throws DerivedUnitFacadeNotSupportedException
{
238 return new DerivedUnitFacade(derivedUnit
, null);
241 public static DerivedUnitFacade
NewInstance(DerivedUnitBase derivedUnit
,
242 DerivedUnitFacadeConfigurator config
)
243 throws DerivedUnitFacadeNotSupportedException
{
244 return new DerivedUnitFacade(derivedUnit
, config
);
247 // ****************** CONSTRUCTOR ******************************************
249 private DerivedUnitFacade(DerivedUnitType type
, FieldObservation fieldObservation
, DerivedUnitFacadeConfigurator config
) {
251 config
= DerivedUnitFacadeConfigurator
.NewInstance();
253 this.config
= config
;
256 derivedUnit
= type
.getNewDerivedUnitInstance();
257 setFieldObservation(fieldObservation
);
258 if (derivedUnit
!= null){
261 setFieldObservationCacheStrategy();
265 private DerivedUnitFacade(DerivedUnitBase derivedUnit
,
266 DerivedUnitFacadeConfigurator config
)
267 throws DerivedUnitFacadeNotSupportedException
{
269 if (config
== null) {
270 config
= DerivedUnitFacadeConfigurator
.NewInstance();
272 this.config
= config
;
275 this.derivedUnit
= derivedUnit
;
276 this.type
= DerivedUnitType
.valueOf2(this.derivedUnit
.getClass());
279 if (this.derivedUnit
.getDerivedFrom() != null) {
280 DerivationEvent derivationEvent
= getDerivationEvent(CREATE
);
282 Set
<FieldObservation
> fieldOriginals
= getFieldObservationsOriginals(
283 derivationEvent
, null);
284 if (fieldOriginals
.size() > 1) {
285 throw new DerivedUnitFacadeNotSupportedException(
286 "Specimen must not have more than 1 derivation event");
287 } else if (fieldOriginals
.size() == 0) {
288 // fieldObservation = FieldObservation.NewInstance();
289 } else if (fieldOriginals
.size() == 1) {
290 fieldObservation
= fieldOriginals
.iterator().next();
291 // ###fieldObservation =
292 // getInitializedFieldObservation(fieldObservation);
293 if (config
.isFirePropertyChangeEvents()){
294 addNewEventPropagationListener(fieldObservation
);
297 throw new IllegalStateException("Illegal state");
301 this.derivedUnitMediaTextData
= inititializeTextDataWithSupportTest(
302 Feature
.IMAGE(), this.derivedUnit
, false, true);
304 fieldObjectMediaTextData
= initializeFieldObjectTextDataWithSupportTest(
305 Feature
.IMAGE(), false, true);
307 // handle derivedUnit.getMedia()
308 if (derivedUnit
.getMedia().size() > 0) {
309 // TODO better changed model here to allow only one place for images
310 if (this.config
.isMoveDerivedUnitMediaToGallery()) {
311 Set
<Media
> mediaSet
= derivedUnit
.getMedia();
312 for (Media media
: mediaSet
) {
313 this.addDerivedUnitMedia(media
);
315 mediaSet
.removeAll(getDerivedUnitMedia());
317 throw new DerivedUnitFacadeNotSupportedException(
318 "Specimen may not have direct media. Only (one) image gallery is allowed");
322 // handle fieldObservation.getMedia()
323 if (fieldObservation
!= null && fieldObservation
.getMedia() != null
324 && fieldObservation
.getMedia().size() > 0) {
325 // TODO better changed model here to allow only one place for images
326 if (this.config
.isMoveFieldObjectMediaToGallery()) {
327 Set
<Media
> mediaSet
= fieldObservation
.getMedia();
328 for (Media media
: mediaSet
) {
329 this.addFieldObjectMedia(media
);
331 mediaSet
.removeAll(getFieldObjectMedia());
333 throw new DerivedUnitFacadeNotSupportedException(
334 "Field object may not have direct media. Only (one) image gallery is allowed");
338 // test if descriptions are supported
339 ecology
= initializeFieldObjectTextDataWithSupportTest(
340 Feature
.ECOLOGY(), false, false);
341 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
342 Feature
.DESCRIPTION(), false, false);
348 private DerivedUnitBase
getInitializedDerivedUnit(
349 DerivedUnitBase derivedUnit
) {
350 IOccurrenceService occurrenceService
= this.config
351 .getOccurrenceService();
352 if (occurrenceService
== null) {
355 List
<String
> propertyPaths
= this.config
.getPropertyPaths();
356 if (propertyPaths
== null) {
359 propertyPaths
= getDerivedUnitPropertyPaths(propertyPaths
);
360 DerivedUnitBase result
= (DerivedUnitBase
) occurrenceService
.load(
361 derivedUnit
.getUuid(), propertyPaths
);
366 * Initializes the derived unit according to the configuartions property
367 * path. If the property path is <code>null</code> or no occurrence service
368 * is given the returned object is the same as the input parameter.
370 * @param fieldObservation2
373 private FieldObservation
getInitializedFieldObservation(
374 FieldObservation fieldObservation
) {
375 IOccurrenceService occurrenceService
= this.config
376 .getOccurrenceService();
377 if (occurrenceService
== null) {
378 return fieldObservation
;
380 List
<String
> propertyPaths
= this.config
.getPropertyPaths();
381 if (propertyPaths
== null) {
382 return fieldObservation
;
384 propertyPaths
= getFieldObjectPropertyPaths(propertyPaths
);
385 FieldObservation result
= (FieldObservation
) occurrenceService
.load(
386 fieldObservation
.getUuid(), propertyPaths
);
391 * Transforms the property paths in a way that the facade is handled just
392 * like an ordinary CdmBase object.<BR>
393 * E.g. a property path "collectinAreas" will be translated into
394 * gatheringEvent.collectingAreas
396 * @param propertyPaths
399 private List
<String
> getFieldObjectPropertyPaths(List
<String
> propertyPaths
) {
400 List
<String
> result
= new ArrayList
<String
>();
401 for (String facadePath
: propertyPaths
) {
402 // collecting areas (named area)
403 if (facadePath
.startsWith("collectingAreas")) {
404 facadePath
= "gatheringEvent." + facadePath
;
405 result
.add(facadePath
);
407 // collector (agentBase)
408 else if (facadePath
.startsWith("collector")) {
409 facadePath
= facadePath
.replace("collector",
410 "gatheringEvent.actor");
411 result
.add(facadePath
);
413 // exactLocation (agentBase)
414 else if (facadePath
.startsWith("exactLocation")) {
415 facadePath
= "gatheringEvent." + facadePath
;
416 result
.add(facadePath
);
418 // gatheringPeriod (TimePeriod)
419 else if (facadePath
.startsWith("gatheringPeriod")) {
420 facadePath
= facadePath
.replace("gatheringPeriod",
421 "gatheringEvent.timeperiod");
422 result
.add(facadePath
);
424 // (locality/ localityLanguage , LanguageString)
425 else if (facadePath
.startsWith("locality")) {
426 facadePath
= "gatheringEvent." + facadePath
;
427 result
.add(facadePath
);
430 // *********** FIELD OBJECT ************
431 // fieldObjectDefinitions (Map<language, languageString)
432 else if (facadePath
.startsWith("fieldObjectDefinitions")) {
433 // TODO or definition ???
434 facadePath
= facadePath
.replace("fieldObjectDefinitions",
436 result
.add(facadePath
);
438 // fieldObjectMedia (Media)
439 else if (facadePath
.startsWith("fieldObjectMedia")) {
441 facadePath
= facadePath
.replace("fieldObjectMedia",
442 "descriptions.elements.media");
443 result
.add(facadePath
);
446 // Gathering Event will always be added
447 result
.add("gatheringEvent");
452 * Gathering Event ==================== - gatheringEvent
455 * Field Object ================= - ecology/ ecologyAll (String) ??? -
456 * plant description (like ecology)
458 * - fieldObjectImageGallery (SpecimenDescription) - is automatically
459 * initialized via fieldObjectMedia
466 * Transforms the property paths in a way that the facade is handled just
467 * like an ordinary CdmBase object.<BR>
468 * E.g. a property path "collectinAreas" will be translated into
469 * gatheringEvent.collectingAreas
471 * Not needed (?) as the facade works with REST service property paths
472 * without using this method.
474 * @param propertyPaths
477 private List
<String
> getDerivedUnitPropertyPaths(List
<String
> propertyPaths
) {
478 List
<String
> result
= new ArrayList
<String
>();
479 for (String facadePath
: propertyPaths
) {
480 // determinations (DeterminationEvent)
481 if (facadePath
.startsWith("determinations")) {
482 facadePath
= "" + facadePath
; // no change
483 result
.add(facadePath
);
485 // storedUnder (TaxonNameBase)
486 else if (facadePath
.startsWith("storedUnder")) {
487 facadePath
= "" + facadePath
; // no change
488 result
.add(facadePath
);
490 // sources (IdentifiableSource)
491 else if (facadePath
.startsWith("sources")) {
492 facadePath
= "" + facadePath
; // no change
493 result
.add(facadePath
);
495 // collection (Collection)
496 else if (facadePath
.startsWith("collection")) {
497 facadePath
= "" + facadePath
; // no change
498 result
.add(facadePath
);
500 // (locality/ localityLanguage , LanguageString)
501 else if (facadePath
.startsWith("locality")) {
502 facadePath
= "gatheringEvent." + facadePath
;
503 result
.add(facadePath
);
506 // *********** FIELD OBJECT ************
507 // derivedUnitDefinitions (Map<language, languageString)
508 else if (facadePath
.startsWith("derivedUnitDefinitions")) {
509 // TODO or definition ???
510 facadePath
= facadePath
.replace("derivedUnitDefinitions",
512 result
.add(facadePath
);
515 // derivedUnitMedia (Media)
516 else if (facadePath
.startsWith("derivedUnitMedia")) {
518 facadePath
= facadePath
.replace("derivedUnitMedia",
519 "descriptions.elements.media");
520 result
.add(facadePath
);
526 * //TODO Derived Unit =====================
528 * - derivedUnitImageGallery (SpecimenDescription) - is automatically
529 * initialized via derivedUnitMedia
531 * - derivationEvent (DerivationEvent) - will always be initialized -
532 * duplicates (??? Specimen???) ???
541 private void setCacheStrategy() {
542 if (derivedUnit
== null) {
543 throw new NullPointerException(
544 "Facade's derviedUnit must not be null to set cache strategy");
546 derivedUnit
.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
547 setFieldObservationCacheStrategy();
551 private void setFieldObservationCacheStrategy() {
552 if (this.hasFieldObject()){
553 DerivedUnitFacadeFieldObservationCacheStrategy strategy
= new DerivedUnitFacadeFieldObservationCacheStrategy();
554 this.fieldObservation
.setCacheStrategy(strategy
);
560 * @param createIfNotExists
561 * @param isImageGallery
563 * @throws DerivedUnitFacadeNotSupportedException
565 private TextData
initializeFieldObjectTextDataWithSupportTest(
566 Feature feature
, boolean createIfNotExists
, boolean isImageGallery
)
567 throws DerivedUnitFacadeNotSupportedException
{
569 FieldObservation fieldObject
= getFieldObservation(createIfNotExists
);
570 if (fieldObject
== null) {
573 return inititializeTextDataWithSupportTest(feature
, fieldObject
,
574 createIfNotExists
, isImageGallery
);
580 * @param createIfNotExists
581 * @param isImageGallery
583 * @throws DerivedUnitFacadeNotSupportedException
585 private TextData
inititializeTextDataWithSupportTest(Feature feature
,
586 SpecimenOrObservationBase specimen
, boolean createIfNotExists
,
587 boolean isImageGallery
)
588 throws DerivedUnitFacadeNotSupportedException
{
589 if (feature
== null) {
592 TextData textData
= null;
593 if (createIfNotExists
) {
594 textData
= TextData
.NewInstance(feature
);
597 Set
<SpecimenDescription
> descriptions
;
598 if (isImageGallery
) {
599 descriptions
= specimen
.getSpecimenDescriptionImageGallery();
601 descriptions
= specimen
.getSpecimenDescriptions(false);
603 // no description exists yet for this specimen
604 if (descriptions
.size() == 0) {
605 if (createIfNotExists
) {
606 SpecimenDescription newSpecimenDescription
= SpecimenDescription
607 .NewInstance(specimen
);
608 newSpecimenDescription
.addElement(textData
);
609 newSpecimenDescription
.setImageGallery(isImageGallery
);
615 // description already exists
616 Set
<DescriptionElementBase
> existingTextData
= new HashSet
<DescriptionElementBase
>();
617 for (SpecimenDescription description
: descriptions
) {
618 // collect all existing text data
619 for (DescriptionElementBase element
: description
.getElements()) {
620 if (element
.isInstanceOf(TextData
.class)
621 && (feature
.equals(element
.getFeature()) || isImageGallery
)) {
622 existingTextData
.add(element
);
626 // use existing text data if exactly one exists
627 if (existingTextData
.size() > 1) {
628 throw new DerivedUnitFacadeNotSupportedException(
629 "Specimen facade does not support more than one description text data of type "
630 + feature
.getLabel());
632 } else if (existingTextData
.size() == 1) {
633 return CdmBase
.deproxy(existingTextData
.iterator().next(),
636 if (createIfNotExists
) {
637 SpecimenDescription description
= descriptions
.iterator()
639 description
.addElement(textData
);
646 * Tests if a given image gallery is supported by the derived unit facade.
647 * It returns the only text data attached to the given image gallery. If the
648 * given image gallery does not have text data attached, it is created and
651 * @param imageGallery
653 * @throws DerivedUnitFacadeNotSupportedException
655 private TextData
testImageGallery(SpecimenDescription imageGallery
)
656 throws DerivedUnitFacadeNotSupportedException
{
657 if (imageGallery
.isImageGallery() == false) {
658 throw new DerivedUnitFacadeNotSupportedException(
659 "Image gallery needs to have image gallery flag set");
661 if (imageGallery
.getElements().size() > 1) {
662 throw new DerivedUnitFacadeNotSupportedException(
663 "Image gallery must not have more then one description element");
666 if (imageGallery
.getElements().size() == 0) {
667 textData
= TextData
.NewInstance(Feature
.IMAGE());
668 imageGallery
.addElement(textData
);
670 if (!imageGallery
.getElements().iterator().next()
671 .isInstanceOf(TextData
.class)) {
672 throw new DerivedUnitFacadeNotSupportedException(
673 "Image gallery must only have TextData as element");
675 textData
= CdmBase
.deproxy(imageGallery
.getElements()
676 .iterator().next(), TextData
.class);
682 // ************************** METHODS
683 // *****************************************
685 private TextData
getDerivedUnitImageGalleryTextData(
686 boolean createIfNotExists
)
687 throws DerivedUnitFacadeNotSupportedException
{
688 if (this.derivedUnitMediaTextData
== null && createIfNotExists
) {
689 this.derivedUnitMediaTextData
= getImageGalleryTextData(
690 derivedUnit
, "Specimen");
692 return this.derivedUnitMediaTextData
;
695 private TextData
getObservationImageGalleryTextData(
696 boolean createIfNotExists
)
697 throws DerivedUnitFacadeNotSupportedException
{
698 if (this.fieldObjectMediaTextData
== null && createIfNotExists
) {
699 this.fieldObjectMediaTextData
= getImageGalleryTextData(
700 fieldObservation
, "Field observation");
702 return this.fieldObjectMediaTextData
;
706 * @param derivationEvent
708 * @throws DerivedUnitFacadeNotSupportedException
710 private Set
<FieldObservation
> getFieldObservationsOriginals(
711 DerivationEvent derivationEvent
,
712 Set
<SpecimenOrObservationBase
> recursionAvoidSet
)
713 throws DerivedUnitFacadeNotSupportedException
{
714 if (recursionAvoidSet
== null) {
715 recursionAvoidSet
= new HashSet
<SpecimenOrObservationBase
>();
717 Set
<FieldObservation
> result
= new HashSet
<FieldObservation
>();
718 Set
<SpecimenOrObservationBase
> originals
= derivationEvent
.getOriginals();
719 for (SpecimenOrObservationBase original
: originals
) {
720 if (original
.isInstanceOf(FieldObservation
.class)) {
721 result
.add(CdmBase
.deproxy(original
, FieldObservation
.class));
722 } else if (original
.isInstanceOf(DerivedUnitBase
.class)) {
723 // if specimen has already been tested exclude it from further
725 if (recursionAvoidSet
.contains(original
)) {
728 DerivedUnitBase derivedUnit
= CdmBase
.deproxy(original
,
729 DerivedUnitBase
.class);
730 DerivationEvent originalDerivation
= derivedUnit
.getDerivedFrom();
731 // Set<DerivationEvent> derivationEvents =
732 // original.getDerivationEvents();
733 // for (DerivationEvent originalDerivation : derivationEvents){
734 Set
<FieldObservation
> fieldObservations
= getFieldObservationsOriginals(
735 originalDerivation
, recursionAvoidSet
);
736 result
.addAll(fieldObservations
);
739 throw new DerivedUnitFacadeNotSupportedException(
740 "Unhandled specimen or observation base type: "
741 + original
.getClass().getName());
748 // *********** MEDIA METHODS ******************************
751 // * Returns the media list for a specimen. Throws an exception if the
752 // existing specimen descriptions
753 // * are not supported by this facade.
754 // * @param specimen the specimen the media belongs to
755 // * @param specimenExceptionText text describing the specimen for exception
758 // * @throws DerivedUnitFacadeNotSupportedException
760 // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase
761 // specimen, String specimenExceptionText) throws
762 // DerivedUnitFacadeNotSupportedException{
763 // List<Media> result;
764 // SpecimenDescription imageGallery =
765 // getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
766 // TextData textData = getImageTextDataWithSupportTest(imageGallery,
767 // specimenExceptionText);
768 // result = textData.getMedia();
773 * Returns the media list for a specimen. Throws an exception if the
774 * existing specimen descriptions are not supported by this facade.
777 * the specimen the media belongs to
778 * @param specimenExceptionText
779 * text describing the specimen for exception messages
781 * @throws DerivedUnitFacadeNotSupportedException
783 private TextData
getImageGalleryTextData(SpecimenOrObservationBase specimen
, String specimenExceptionText
)
784 throws DerivedUnitFacadeNotSupportedException
{
786 SpecimenDescription imageGallery
= getImageGalleryWithSupportTest(
787 specimen
, specimenExceptionText
, true);
788 result
= getImageTextDataWithSupportTest(imageGallery
,
789 specimenExceptionText
);
794 * Returns the image gallery of the according specimen. Throws an exception
795 * if the attached image gallerie(s) are not supported by this facade. If no
796 * image gallery exists a new one is created if
797 * <code>createNewIfNotExists</code> is true and if specimen is not
801 * @param specimenText
802 * @param createNewIfNotExists
804 * @throws DerivedUnitFacadeNotSupportedException
806 private SpecimenDescription
getImageGalleryWithSupportTest(
807 SpecimenOrObservationBase
<?
> specimen
, String specimenText
,
808 boolean createNewIfNotExists
)
809 throws DerivedUnitFacadeNotSupportedException
{
810 if (specimen
== null) {
813 SpecimenDescription imageGallery
;
814 if (hasMultipleImageGalleries(specimen
)) {
815 throw new DerivedUnitFacadeNotSupportedException(specimenText
816 + " must not have more than 1 image gallery");
818 imageGallery
= getImageGallery(specimen
, createNewIfNotExists
);
819 getImageTextDataWithSupportTest(imageGallery
, specimenText
);
825 * Returns the media holding text data element of the image gallery. Throws
826 * an exception if multiple such text data already exist. Creates a new text
827 * data if none exists and adds it to the image gallery. If image gallery is
828 * <code>null</code> nothing happens.
830 * @param imageGallery
833 * @throws DerivedUnitFacadeNotSupportedException
835 private TextData
getImageTextDataWithSupportTest(
836 SpecimenDescription imageGallery
, String specimenText
)
837 throws DerivedUnitFacadeNotSupportedException
{
838 if (imageGallery
== null) {
841 TextData textData
= null;
842 for (DescriptionElementBase element
: imageGallery
.getElements()) {
843 if (element
.isInstanceOf(TextData
.class)
844 && element
.getFeature().equals(Feature
.IMAGE())) {
845 if (textData
!= null) {
846 throw new DerivedUnitFacadeNotSupportedException(
848 + " must not have more than 1 image text data element in image gallery");
850 textData
= CdmBase
.deproxy(element
, TextData
.class);
853 if (textData
== null) {
854 textData
= TextData
.NewInstance(Feature
.IMAGE());
855 imageGallery
.addElement(textData
);
861 * Checks, if a specimen belongs to more than one description that is an
867 private boolean hasMultipleImageGalleries(
868 SpecimenOrObservationBase
<?
> derivedUnit
) {
870 Set
<SpecimenDescription
> descriptions
= derivedUnit
871 .getSpecimenDescriptions();
872 for (SpecimenDescription description
: descriptions
) {
873 if (description
.isImageGallery()) {
881 * Returns the image gallery for a specimen. If there are multiple specimen
882 * descriptions marked as image galleries an arbitrary one is chosen. If no
883 * image gallery exists, a new one is created if
884 * <code>createNewIfNotExists</code> is <code>true</code>.<Br>
885 * If specimen is <code>null</code> a null pointer exception is thrown.
887 * @param createNewIfNotExists
890 private SpecimenDescription
getImageGallery(SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
) {
891 SpecimenDescription result
= null;
892 Set
<SpecimenDescription
> descriptions
= specimen
.getSpecimenDescriptions();
893 for (SpecimenDescription description
: descriptions
) {
894 if (description
.isImageGallery()) {
895 result
= description
;
899 if (result
== null && createIfNotExists
) {
900 result
= SpecimenDescription
.NewInstance(specimen
);
901 result
.setImageGallery(true);
907 * Adds a media to the specimens image gallery. If media is
908 * <code>null</code> nothing happens.
912 * @return true if media is not null (as specified by
913 * {@link java.util.Collection#add(Object) Collection.add(E e)}
914 * @throws DerivedUnitFacadeNotSupportedException
916 private boolean addMedia(Media media
, SpecimenOrObservationBase
<?
> specimen
) throws DerivedUnitFacadeNotSupportedException
{
918 List
<Media
> mediaList
= getMediaList(specimen
, true);
919 if (! mediaList
.contains(media
)){
920 return mediaList
.add(media
);
930 * Removes a media from the specimens image gallery.
934 * @return true if an element was removed as a result of this call (as
935 * specified by {@link java.util.Collection#remove(Object)
936 * Collection.remove(E e)}
937 * @throws DerivedUnitFacadeNotSupportedException
939 private boolean removeMedia(Media media
,
940 SpecimenOrObservationBase
<?
> specimen
)
941 throws DerivedUnitFacadeNotSupportedException
{
942 List
<Media
> mediaList
= getMediaList(specimen
, true);
943 return mediaList
== null ?
null : mediaList
.remove(media
);
946 private List
<Media
> getMediaList(SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
)
947 throws DerivedUnitFacadeNotSupportedException
{
948 TextData textData
= getMediaTextData(specimen
, createIfNotExists
);
949 return textData
== null ?
null : textData
.getMedia();
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.
959 * @throws DerivedUnitFacadeNotSupportedException
961 // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)
962 // throws DerivedUnitFacadeNotSupportedException {
963 // if (specimen == null){
966 // if (specimen == this.derivedUnit){
967 // return getDerivedUnitImageGalleryMedia();
968 // }else if (specimen == this.fieldObservation){
969 // return getObservationImageGalleryTextData();
971 // return getImageGalleryMedia(specimen, "Undefined specimen ");
976 * Returns the one media list of a specimen which is part of the only image
977 * gallery that this specimen is part of.<BR>
978 * If these conditions are not hold an exception is thrwon.
982 * @throws DerivedUnitFacadeNotSupportedException
984 private TextData
getMediaTextData(SpecimenOrObservationBase
<?
> specimen
,
985 boolean createIfNotExists
)
986 throws DerivedUnitFacadeNotSupportedException
{
987 if (specimen
== null) {
990 if (specimen
== this.derivedUnit
) {
991 return getDerivedUnitImageGalleryTextData(createIfNotExists
);
992 } else if (specimen
== this.fieldObservation
) {
993 return getObservationImageGalleryTextData(createIfNotExists
);
995 return getImageGalleryTextData(specimen
, "Undefined specimen ");
999 // ****************** GETTER / SETTER / ADDER / REMOVER
1000 // ***********************/
1002 // ****************** Gathering Event *********************************/
1006 public NamedArea
getCountry() {
1007 return (hasGatheringEvent() ?
getGatheringEvent(true).getCountry()
1011 public void setCountry(NamedArea country
) {
1012 getGatheringEvent(true).setCountry(country
);
1016 public void addCollectingArea(NamedArea area
) {
1017 getGatheringEvent(true).addCollectingArea(area
);
1020 public void addCollectingAreas(java
.util
.Collection
<NamedArea
> areas
) {
1021 for (NamedArea area
: areas
) {
1022 getGatheringEvent(true).addCollectingArea(area
);
1027 public Set
<NamedArea
> getCollectingAreas() {
1028 return (hasGatheringEvent() ?
getGatheringEvent(true)
1029 .getCollectingAreas() : null);
1032 public void removeCollectingArea(NamedArea area
) {
1033 if (hasGatheringEvent()) {
1034 getGatheringEvent(true).removeCollectingArea(area
);
1038 // absolute elevation
1040 * meter above/below sea level of the surface
1042 * @see #getAbsoluteElevationError()
1043 * @see #getAbsoluteElevationRange()
1046 public Integer
getAbsoluteElevation() {
1047 return (hasGatheringEvent() ?
getGatheringEvent(true)
1048 .getAbsoluteElevation() : null);
1051 public void setAbsoluteElevation(Integer absoluteElevation
) {
1052 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation
);
1055 // absolute elevation error
1057 public Integer
getAbsoluteElevationError() {
1058 return (hasGatheringEvent() ?
getGatheringEvent(true)
1059 .getAbsoluteElevationError() : null);
1062 public void setAbsoluteElevationError(Integer absoluteElevationError
) {
1063 getGatheringEvent(true).setAbsoluteElevationError(
1064 absoluteElevationError
);
1068 * @see #getAbsoluteElevation()
1069 * @see #getAbsoluteElevationError()
1070 * @see #setAbsoluteElevationRange(Integer, Integer)
1071 * @see #getAbsoluteElevationMaximum()
1074 public Integer
getAbsoluteElevationMinimum() {
1075 if (!hasGatheringEvent()) {
1078 Integer minimum
= getGatheringEvent(true).getAbsoluteElevation();
1079 if (getGatheringEvent(true).getAbsoluteElevationError() != null) {
1081 - getGatheringEvent(true).getAbsoluteElevationError();
1087 * @see #getAbsoluteElevation()
1088 * @see #getAbsoluteElevationError()
1089 * @see #setAbsoluteElevationRange(Integer, Integer)
1090 * @see #getAbsoluteElevationMinimum()
1093 public Integer
getAbsoluteElevationMaximum() {
1094 if (!hasGatheringEvent()) {
1097 Integer maximum
= getGatheringEvent(true).getAbsoluteElevation();
1098 if (getGatheringEvent(true).getAbsoluteElevationError() != null) {
1100 + getGatheringEvent(true).getAbsoluteElevationError();
1106 * This method replaces absoluteElevation and absoulteElevationError by
1107 * internally translating minimum and maximum values into average and error
1108 * values. As all these values are integer based it is necessary that the
1109 * distance is between minimum and maximum is <b>even</b>, otherwise we will
1110 * get a rounding error resulting in a maximum that is increased by 1.
1112 * @see #setAbsoluteElevation(Integer)
1113 * @see #setAbsoluteElevationError(Integer)
1114 * @param minimumElevation
1115 * minimum of the range
1116 * @param maximumElevation
1117 * maximum of the range
1118 * @throws IllegalArgumentException
1120 public void setAbsoluteElevationRange(Integer minimumElevation
, Integer maximumElevation
) throws IllegalArgumentException
{
1121 if (minimumElevation
== null || maximumElevation
== null) {
1122 Integer elevation
= minimumElevation
;
1124 if (minimumElevation
== null) {
1125 elevation
= maximumElevation
;
1126 if (elevation
== null) {
1130 getGatheringEvent(true).setAbsoluteElevation(elevation
);
1131 getGatheringEvent(true).setAbsoluteElevationError(error
);
1133 if (!isEvenDistance(minimumElevation
, maximumElevation
)) {
1134 throw new IllegalArgumentException(
1135 "Distance between minimum and maximum elevation must be even but was "
1136 + Math
.abs(minimumElevation
- maximumElevation
));
1138 Integer absoluteElevationError
= Math
.abs(maximumElevation
1139 - minimumElevation
);
1140 absoluteElevationError
= absoluteElevationError
/ 2;
1141 Integer absoluteElevation
= minimumElevation
1142 + absoluteElevationError
;
1143 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation
);
1144 getGatheringEvent(true).setAbsoluteElevationError(
1145 absoluteElevationError
);
1150 * @param minimumElevation
1151 * @param maximumElevation
1154 public boolean isEvenDistance(Integer minimumElevation
,
1155 Integer maximumElevation
) {
1156 Integer diff
= (maximumElevation
- minimumElevation
);
1157 return diff
% 2 == 0;
1162 public AgentBase
getCollector() {
1163 return (hasGatheringEvent() ?
getGatheringEvent(true).getCollector()
1167 public void setCollector(AgentBase collector
) {
1168 getGatheringEvent(true).setCollector(collector
);
1171 // collecting method
1173 public String
getCollectingMethod() {
1174 return (hasGatheringEvent() ?
getGatheringEvent(true)
1175 .getCollectingMethod() : null);
1178 public void setCollectingMethod(String collectingMethod
) {
1179 getGatheringEvent(true).setCollectingMethod(collectingMethod
);
1182 // distance to ground
1184 public Integer
getDistanceToGround() {
1185 return (hasGatheringEvent() ?
getGatheringEvent(true)
1186 .getDistanceToGround() : null);
1189 public void setDistanceToGround(Integer distanceToGround
) {
1190 getGatheringEvent(true).setDistanceToGround(distanceToGround
);
1193 // distance to water surface
1195 public Integer
getDistanceToWaterSurface() {
1196 return (hasGatheringEvent() ?
getGatheringEvent(true)
1197 .getDistanceToWaterSurface() : null);
1200 public void setDistanceToWaterSurface(Integer distanceToWaterSurface
) {
1201 getGatheringEvent(true).setDistanceToWaterSurface(
1202 distanceToWaterSurface
);
1207 public Point
getExactLocation() {
1208 return (hasGatheringEvent() ?
getGatheringEvent(true).getExactLocation() : null);
1212 * Returns a sexagesimal representation of the exact location (e.g.
1213 * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty
1214 * string is returned.
1216 * @param includeEmptySeconds
1217 * @param includeReferenceSystem
1220 public String
getExactLocationText(boolean includeEmptySeconds
,
1221 boolean includeReferenceSystem
) {
1222 return (this.getExactLocation() == null ?
"" : this.getExactLocation()
1223 .toSexagesimalString(includeEmptySeconds
,
1224 includeReferenceSystem
));
1227 public void setExactLocation(Point exactLocation
) {
1228 getGatheringEvent(true).setExactLocation(exactLocation
);
1231 public void setExactLocationByParsing(String longitudeToParse
,
1232 String latitudeToParse
, ReferenceSystem referenceSystem
,
1233 Integer errorRadius
) throws ParseException
{
1234 Point point
= Point
.NewInstance(null, null, referenceSystem
,
1236 point
.setLongitudeByParsing(longitudeToParse
);
1237 point
.setLatitudeByParsing(latitudeToParse
);
1238 setExactLocation(point
);
1241 // gathering event description
1243 public String
getGatheringEventDescription() {
1244 return (hasGatheringEvent() ?
getGatheringEvent(true).getDescription()
1248 public void setGatheringEventDescription(String description
) {
1249 getGatheringEvent(true).setDescription(description
);
1254 public TimePeriod
getGatheringPeriod() {
1255 return (hasGatheringEvent() ?
getGatheringEvent(true).getTimeperiod()
1259 public void setGatheringPeriod(TimePeriod timeperiod
) {
1260 getGatheringEvent(true).setTimeperiod(timeperiod
);
1265 public LanguageString
getLocality() {
1266 return (hasGatheringEvent() ?
getGatheringEvent(true).getLocality()
1271 * convienience method for {@link #getLocality()}.
1272 * {@link LanguageString#getText() getText()}
1277 public String
getLocalityText() {
1278 LanguageString locality
= getLocality();
1279 if (locality
!= null) {
1280 return locality
.getText();
1286 * convienience method for {@link #getLocality()}.
1287 * {@link LanguageString#getLanguage() getLanguage()}
1292 public Language
getLocalityLanguage() {
1293 LanguageString locality
= getLocality();
1294 if (locality
!= null) {
1295 return locality
.getLanguage();
1301 * Sets the locality string in the default language
1305 public void setLocality(String locality
) {
1306 Language language
= Language
.DEFAULT();
1307 setLocality(locality
, language
);
1310 public void setLocality(String locality
, Language language
) {
1311 LanguageString langString
= LanguageString
.NewInstance(locality
,
1313 setLocality(langString
);
1316 public void setLocality(LanguageString locality
) {
1317 getGatheringEvent(true).setLocality(locality
);
1321 * The gathering event will be used for the field object instead of the old
1322 * gathering event.<BR>
1323 * <B>This method will override all gathering values (see below).</B>
1325 * @see #getAbsoluteElevation()
1326 * @see #getAbsoluteElevationError()
1327 * @see #getDistanceToGround()
1328 * @see #getDistanceToWaterSurface()
1329 * @see #getExactLocation()
1330 * @see #getGatheringEventDescription()
1331 * @see #getGatheringPeriod()
1332 * @see #getCollectingAreas()
1333 * @see #getCollectingMethod()
1334 * @see #getLocality()
1335 * @see #getCollector()
1336 * @param gatheringEvent
1338 public void setGatheringEvent(GatheringEvent gatheringEvent
) {
1339 getFieldObservation(true).setGatheringEvent(gatheringEvent
);
1342 public boolean hasGatheringEvent() {
1343 return (getGatheringEvent(false) != null);
1346 public GatheringEvent
innerGatheringEvent() {
1347 return getGatheringEvent(false);
1350 public GatheringEvent
getGatheringEvent(boolean createIfNotExists
) {
1351 if (!hasFieldObservation() && !createIfNotExists
) {
1354 if (createIfNotExists
&& getFieldObservation(true).getGatheringEvent() == null) {
1355 GatheringEvent gatheringEvent
= GatheringEvent
.NewInstance();
1356 getFieldObservation(true).setGatheringEvent(gatheringEvent
);
1358 return getFieldObservation(true).getGatheringEvent();
1361 // ****************** Field Object ************************************/
1364 * Returns true if a field observation exists (even if all attributes are
1365 * empty or <code>null<code>.
1369 public boolean hasFieldObject() {
1370 return this.fieldObservation
!= null;
1375 public String
getEcology() {
1376 return getEcology(Language
.DEFAULT());
1379 public String
getEcology(Language language
) {
1380 LanguageString languageString
= getEcologyAll().get(language
);
1381 return (languageString
== null ?
null : languageString
.getText());
1384 // public String getEcologyPreferred(List<Language> languages){
1385 // LanguageString languageString =
1386 // getEcologyAll().getPreferredLanguageString(languages);
1387 // return languageString.getText();
1390 * Returns a copy of the multilanguage text holding the ecology data.
1392 * @see {@link TextData#getMultilanguageText()}
1396 public Map
<Language
, LanguageString
> getEcologyAll() {
1397 if (ecology
== null) {
1399 ecology
= initializeFieldObjectTextDataWithSupportTest(
1400 Feature
.ECOLOGY(), false, false);
1401 } catch (DerivedUnitFacadeNotSupportedException e
) {
1402 throw new IllegalStateException(notSupportMessage
, e
);
1404 if (ecology
== null) {
1405 return new HashMap
<Language
, LanguageString
>();
1408 return ecology
.getMultilanguageText();
1411 public void setEcology(String ecology
) {
1412 setEcology(ecology
, null);
1415 public void setEcology(String ecologyText
, Language language
) {
1416 if (language
== null) {
1417 language
= Language
.DEFAULT();
1419 if (ecology
== null) {
1421 ecology
= initializeFieldObjectTextDataWithSupportTest(
1422 Feature
.ECOLOGY(), true, false);
1423 } catch (DerivedUnitFacadeNotSupportedException e
) {
1424 throw new IllegalStateException(notSupportMessage
, e
);
1427 if (ecologyText
== null) {
1428 ecology
.removeText(language
);
1430 ecology
.putText(language
, ecologyText
);
1434 public void removeEcology(Language language
) {
1435 setEcology(null, language
);
1439 * Removes ecology for the default language
1441 public void removeEcology() {
1442 setEcology(null, null);
1445 public void removeEcologyAll() {
1449 // plant description
1451 public String
getPlantDescription() {
1452 return getPlantDescription(null);
1455 public String
getPlantDescription(Language language
) {
1456 if (language
== null) {
1457 language
= Language
.DEFAULT();
1459 LanguageString languageString
= getPlantDescriptionAll().get(language
);
1460 return (languageString
== null ?
null : languageString
.getText());
1463 // public String getPlantDescriptionPreferred(List<Language> languages){
1464 // LanguageString languageString =
1465 // getPlantDescriptionAll().getPreferredLanguageString(languages);
1466 // return languageString.getText();
1469 * Returns a copy of the multilanguage text holding the description data.
1471 * @see {@link TextData#getMultilanguageText()}
1475 public Map
<Language
, LanguageString
> getPlantDescriptionAll() {
1476 if (plantDescription
== null) {
1478 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
1479 Feature
.DESCRIPTION(), false, false);
1480 } catch (DerivedUnitFacadeNotSupportedException e
) {
1481 throw new IllegalStateException(notSupportMessage
, e
);
1483 if (plantDescription
== null) {
1484 return new HashMap
<Language
, LanguageString
>();
1487 return plantDescription
.getMultilanguageText();
1490 public void setPlantDescription(String plantDescription
) {
1491 setPlantDescription(plantDescription
, null);
1494 public void setPlantDescription(String plantDescriptionText
,
1495 Language language
) {
1496 if (language
== null) {
1497 language
= Language
.DEFAULT();
1499 if (plantDescription
== null) {
1501 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
1502 Feature
.DESCRIPTION(), true, false);
1503 } catch (DerivedUnitFacadeNotSupportedException e
) {
1504 throw new IllegalStateException(notSupportMessage
, e
);
1507 if (plantDescriptionText
== null) {
1508 plantDescription
.removeText(language
);
1510 plantDescription
.putText(language
, plantDescriptionText
);
1514 public void removePlantDescription(Language language
) {
1515 setPlantDescription(null, language
);
1518 // field object definition
1519 public void addFieldObjectDefinition(String text
, Language language
) {
1520 getFieldObservation(true).putDefinition(language
, text
);
1524 public Map
<Language
, LanguageString
> getFieldObjectDefinition() {
1525 if (!hasFieldObservation()) {
1526 return new HashMap
<Language
, LanguageString
>();
1528 return getFieldObservation(true).getDefinition();
1532 public String
getFieldObjectDefinition(Language language
) {
1533 Map
<Language
, LanguageString
> map
= getFieldObjectDefinition();
1534 LanguageString languageString
= (map
== null ?
null : map
.get(language
));
1535 if (languageString
!= null) {
1536 return languageString
.getText();
1542 public void removeFieldObjectDefinition(Language lang
) {
1543 if (hasFieldObservation()) {
1544 getFieldObservation(true).removeDefinition(lang
);
1549 public boolean addFieldObjectMedia(Media media
) {
1551 return addMedia(media
, getFieldObservation(true));
1552 } catch (DerivedUnitFacadeNotSupportedException e
) {
1553 throw new IllegalStateException(notSupportMessage
, e
);
1558 * Returns true, if an image gallery for the field object exists.<BR>
1559 * Returns also <code>true</code> if the image gallery is empty.
1563 public boolean hasFieldObjectImageGallery() {
1564 if (!hasFieldObject()) {
1567 return (getImageGallery(fieldObservation
, false) != null);
1571 public void setFieldObjectImageGallery(SpecimenDescription imageGallery
)
1572 throws DerivedUnitFacadeNotSupportedException
{
1573 SpecimenDescription existingGallery
= getFieldObjectImageGallery(false);
1575 // test attached specimens contain this.derivedUnit
1576 SpecimenOrObservationBase
<?
> facadeFieldObservation
= innerFieldObservation();
1577 testSpecimenInImageGallery(imageGallery
, facadeFieldObservation
);
1579 if (existingGallery
!= null) {
1580 if (existingGallery
!= imageGallery
) {
1581 throw new DerivedUnitFacadeNotSupportedException(
1582 "DerivedUnitFacade does not allow more than one image gallery");
1587 TextData textData
= testImageGallery(imageGallery
);
1588 this.fieldObjectMediaTextData
= textData
;
1593 * Returns the field object image gallery. If no such image gallery exists
1594 * and createIfNotExists is true an new one is created. Otherwise null is
1597 * @param createIfNotExists
1600 public SpecimenDescription
getFieldObjectImageGallery(
1601 boolean createIfNotExists
) {
1604 textData
= initializeFieldObjectTextDataWithSupportTest(
1605 Feature
.IMAGE(), createIfNotExists
, true);
1606 } catch (DerivedUnitFacadeNotSupportedException e
) {
1607 throw new IllegalStateException(notSupportMessage
, e
);
1609 if (textData
!= null) {
1610 return CdmBase
.deproxy(textData
.getInDescription(),
1611 SpecimenDescription
.class);
1618 * Returns the media for the field object.<BR>
1623 public List
<Media
> getFieldObjectMedia() {
1625 List
<Media
> result
= getMediaList(getFieldObservation(false), false);
1626 return result
== null ?
new ArrayList
<Media
>() : result
;
1627 } catch (DerivedUnitFacadeNotSupportedException e
) {
1628 throw new IllegalStateException(notSupportMessage
, e
);
1632 public boolean removeFieldObjectMedia(Media media
) {
1634 return removeMedia(media
, getFieldObservation(false));
1635 } catch (DerivedUnitFacadeNotSupportedException e
) {
1636 throw new IllegalStateException(notSupportMessage
, e
);
1642 public String
getFieldNumber() {
1643 if (!hasFieldObservation()) {
1646 return getFieldObservation(true).getFieldNumber();
1650 public void setFieldNumber(String fieldNumber
) {
1651 getFieldObservation(true).setFieldNumber(fieldNumber
);
1654 // primary collector
1656 public Person
getPrimaryCollector() {
1657 if (!hasFieldObservation()) {
1660 return getFieldObservation(true).getPrimaryCollector();
1664 public void setPrimaryCollector(Person primaryCollector
) {
1665 getFieldObservation(true).setPrimaryCollector(primaryCollector
);
1670 public String
getFieldNotes() {
1671 if (!hasFieldObservation()) {
1674 return getFieldObservation(true).getFieldNotes();
1678 public void setFieldNotes(String fieldNotes
) {
1679 getFieldObservation(true).setFieldNotes(fieldNotes
);
1682 // individual counts
1684 public Integer
getIndividualCount() {
1685 return (hasFieldObservation() ?
getFieldObservation(true)
1686 .getIndividualCount() : null);
1689 public void setIndividualCount(Integer individualCount
) {
1690 getFieldObservation(true).setIndividualCount(individualCount
);
1695 public Stage
getLifeStage() {
1696 return (hasFieldObservation() ?
getFieldObservation(true)
1697 .getLifeStage() : null);
1700 public void setLifeStage(Stage lifeStage
) {
1701 getFieldObservation(true).setLifeStage(lifeStage
);
1706 public Sex
getSex() {
1707 return (hasFieldObservation() ?
getFieldObservation(true).getSex()
1711 public void setSex(Sex sex
) {
1712 getFieldObservation(true).setSex(sex
);
1715 // field observation
1716 public boolean hasFieldObservation() {
1717 return (getFieldObservation(false) != null);
1721 * Returns the field observation as an object.
1725 public FieldObservation
innerFieldObservation() {
1726 return getFieldObservation(false);
1730 * Returns the field observation as an object.
1734 public FieldObservation
getFieldObservation(boolean createIfNotExists
) {
1735 if (fieldObservation
== null && createIfNotExists
) {
1736 setFieldObservation(FieldObservation
.NewInstance());
1738 return this.fieldObservation
;
1742 private void setFieldObservation(FieldObservation fieldObservation
) {
1743 this.fieldObservation
= fieldObservation
;
1744 if (fieldObservation
!= null){
1745 if (config
.isFirePropertyChangeEvents()){
1746 addNewEventPropagationListener(fieldObservation
);
1748 if (derivedUnit
!= null){
1749 DerivationEvent derivationEvent
= getDerivationEvent(CREATE
);
1750 derivationEvent
.addOriginal(fieldObservation
);
1752 setFieldObservationCacheStrategy();
1756 // ****************** Specimen *******************************************
1759 public void addDerivedUnitDefinition(String text
, Language language
) {
1760 innerDerivedUnit().putDefinition(language
, text
);
1764 public Map
<Language
, LanguageString
> getDerivedUnitDefinitions() {
1766 return this.derivedUnit
.getDefinition();
1770 public String
getDerivedUnitDefinition(Language language
) {
1772 Map
<Language
, LanguageString
> languageMap
= derivedUnit
.getDefinition();
1773 LanguageString languageString
= languageMap
.get(language
);
1774 if (languageString
!= null) {
1775 return languageString
.getText();
1781 public void removeDerivedUnitDefinition(Language lang
) {
1783 derivedUnit
.removeDefinition(lang
);
1787 public void addDetermination(DeterminationEvent determination
) {
1789 //TODO implement correct bidirectional mapping in model classes
1790 determination
.setIdentifiedUnit(derivedUnit
);
1791 derivedUnit
.addDetermination(determination
);
1795 public DeterminationEvent
getPreferredDetermination() {
1797 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1798 for (DeterminationEvent event
: events
){
1799 if (event
.getPreferredFlag() == true){
1807 * This method returns the preferred determination.
1808 * @see #getOtherDeterminations()
1809 * @see #getDeterminations()
1813 public void setPreferredDetermination(DeterminationEvent newEvent
) {
1815 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1816 for (DeterminationEvent event
: events
){
1817 if (event
.getPreferredFlag() == true){
1818 event
.setPreferredFlag(false);
1821 newEvent
.setPreferredFlag(true);
1822 events
.add(newEvent
);
1826 * This method returns all determinations except for the preferred one.
1827 * @see #getPreferredDetermination()
1828 * @see #getDeterminations()
1832 public Set
<DeterminationEvent
> getOtherDeterminations() {
1834 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1835 Set
<DeterminationEvent
> result
= new HashSet
<DeterminationEvent
>();
1836 for (DeterminationEvent event
: events
){
1837 if (event
.getPreferredFlag() != true){
1845 * This method returns all determination events. The preferred one {@link #getPreferredDetermination()}
1846 * and all others {@link #getOtherDeterminations()}.
1850 public Set
<DeterminationEvent
> getDeterminations() {
1852 return derivedUnit
.getDeterminations();
1855 public void removeDetermination(DeterminationEvent determination
) {
1857 derivedUnit
.removeDetermination(determination
);
1861 public boolean addDerivedUnitMedia(Media media
) {
1864 return addMedia(media
, derivedUnit
);
1865 } catch (DerivedUnitFacadeNotSupportedException e
) {
1866 throw new IllegalStateException(notSupportMessage
, e
);
1871 * Returns true, if an image gallery exists for the specimen.<BR>
1872 * Returns also <code>true</code> if the image gallery is empty.
1874 public boolean hasDerivedUnitImageGallery() {
1875 return (getImageGallery(derivedUnit
, false) != null);
1878 public SpecimenDescription
getDerivedUnitImageGallery(boolean createIfNotExists
) {
1882 textData
= inititializeTextDataWithSupportTest(Feature
.IMAGE(),
1883 derivedUnit
, createIfNotExists
, true);
1884 } catch (DerivedUnitFacadeNotSupportedException e
) {
1885 throw new IllegalStateException(notSupportMessage
, e
);
1887 if (textData
!= null) {
1888 return CdmBase
.deproxy(textData
.getInDescription(),
1889 SpecimenDescription
.class);
1895 public void setDerivedUnitImageGallery(SpecimenDescription imageGallery
)
1896 throws DerivedUnitFacadeNotSupportedException
{
1898 SpecimenDescription existingGallery
= getDerivedUnitImageGallery(false);
1900 // test attached specimens contain this.derivedUnit
1901 SpecimenOrObservationBase facadeDerivedUnit
= innerDerivedUnit();
1902 testSpecimenInImageGallery(imageGallery
, facadeDerivedUnit
);
1904 if (existingGallery
!= null) {
1905 if (existingGallery
!= imageGallery
) {
1906 throw new DerivedUnitFacadeNotSupportedException(
1907 "DerivedUnitFacade does not allow more than one image gallery");
1912 TextData textData
= testImageGallery(imageGallery
);
1913 this.derivedUnitMediaTextData
= textData
;
1918 * @param imageGallery
1919 * @throws DerivedUnitFacadeNotSupportedException
1921 private void testSpecimenInImageGallery(SpecimenDescription imageGallery
, SpecimenOrObservationBase specimen
)
1922 throws DerivedUnitFacadeNotSupportedException
{
1923 Set
<SpecimenOrObservationBase
> imageGallerySpecimens
= imageGallery
.getDescribedSpecimenOrObservations();
1924 if (imageGallerySpecimens
.size() < 1) {
1925 throw new DerivedUnitFacadeNotSupportedException(
1926 "Image Gallery has no Specimen attached. Please attache according specimen or field observation.");
1928 if (!imageGallerySpecimens
.contains(specimen
)) {
1929 throw new DerivedUnitFacadeNotSupportedException(
1930 "Image Gallery has not the facade's field object attached. Please add field object first to image gallery specimenOrObservation list.");
1935 * Returns the media for the specimen.<BR>
1940 public List
<Media
> getDerivedUnitMedia() {
1943 List
<Media
> result
= getMediaList(derivedUnit
, false);
1944 return result
== null ?
new ArrayList
<Media
>() : result
;
1945 } catch (DerivedUnitFacadeNotSupportedException e
) {
1946 throw new IllegalStateException(notSupportMessage
, e
);
1950 public boolean removeDerivedUnitMedia(Media media
) {
1953 return removeMedia(media
, derivedUnit
);
1954 } catch (DerivedUnitFacadeNotSupportedException e
) {
1955 throw new IllegalStateException(notSupportMessage
, e
);
1961 public String
getAccessionNumber() {
1963 return derivedUnit
.getAccessionNumber();
1966 public void setAccessionNumber(String accessionNumber
) {
1968 derivedUnit
.setAccessionNumber(accessionNumber
);
1972 public String
getCatalogNumber() {
1974 return derivedUnit
.getCatalogNumber();
1977 public void setCatalogNumber(String catalogNumber
) {
1979 derivedUnit
.setCatalogNumber(catalogNumber
);
1983 public String
getBarcode() {
1985 return derivedUnit
.getBarcode();
1988 public void setBarcode(String barcode
) {
1990 derivedUnit
.setBarcode(barcode
);
1993 // Preservation Method
1996 * Only supported by specimen and fossils
1998 * @see #DerivedUnitType
2002 public PreservationMethod
getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException
{
2004 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2005 return CdmBase
.deproxy(derivedUnit
, Specimen
.class)
2009 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2010 throw new MethodNotSupportedByDerivedUnitTypeException(
2011 "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
2019 * Only supported by specimen and fossils
2021 * @see #DerivedUnitType
2024 public void setPreservationMethod(PreservationMethod preservation
)
2025 throws MethodNotSupportedByDerivedUnitTypeException
{
2027 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2028 CdmBase
.deproxy(derivedUnit
, Specimen
.class).setPreservation(
2032 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2033 throw new MethodNotSupportedByDerivedUnitTypeException(
2034 "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
2041 // Stored under name
2043 public TaxonNameBase
getStoredUnder() {
2045 return derivedUnit
.getStoredUnder();
2048 public void setStoredUnder(TaxonNameBase storedUnder
) {
2050 derivedUnit
.setStoredUnder(storedUnder
);
2054 public String
getTitleCache() {
2055 SpecimenOrObservationBase
<?
> titledUnit
= getTitledUnit();
2057 if (!titledUnit
.isProtectedTitleCache()) {
2058 // always compute title cache anew as long as there are no property
2059 // change listeners on
2060 // field observation, gathering event etc
2061 titledUnit
.setTitleCache(null, false);
2063 return titledUnit
.getTitleCache();
2066 private SpecimenOrObservationBase
<?
> getTitledUnit(){
2067 return (derivedUnit
!= null )? derivedUnit
: fieldObservation
;
2070 public boolean isProtectedTitleCache() {
2071 return getTitledUnit().isProtectedTitleCache();
2074 public void setTitleCache(String titleCache
, boolean isProtected
) {
2075 this.getTitledUnit().setTitleCache(titleCache
, isProtected
);
2079 * Returns the derived unit itself.
2081 * @return the derived unit
2083 public DerivedUnitBase
innerDerivedUnit() {
2084 return this.derivedUnit
;
2088 // * Returns the derived unit itself.
2090 // * @return the derived unit
2092 // public DerivedUnitBase innerDerivedUnit(boolean createIfNotExists) {
2093 // DerivedUnit result = this.derivedUnit;
2094 // if (result == null && createIfNotExists){
2095 // if (this.fieldObservation == null){
2096 // String message = "Field observation must exist to create derived unit.";
2097 // throw new IllegalStateException(message);
2100 // DerivationEvent derivationEvent = getDerivationEvent(true);
2101 // derivationEvent.addOriginal(fieldObservation);
2102 // return this.derivedUnit;
2107 private boolean hasDerivationEvent() {
2108 return getDerivationEvent() == null ?
false : true;
2111 private DerivationEvent
getDerivationEvent() {
2112 return getDerivationEvent(false);
2116 * Returns the derivation event. If no derivation event exists and <code>createIfNotExists</code>
2117 * is <code>true</code> a new derivation event is created and returned.
2118 * Otherwise <code>null</code> is returned.
2119 * @param createIfNotExists
2121 private DerivationEvent
getDerivationEvent(boolean createIfNotExists
) {
2122 DerivationEvent result
= null;
2123 if (derivedUnit
!= null){
2124 result
= derivedUnit
.getDerivedFrom();
2128 if (result
== null && createIfNotExists
) {
2129 DerivationEventType type
= null;
2130 if (isAccessioned(derivedUnit
)){
2131 type
= DerivationEventType
.ACCESSIONING();
2134 result
= DerivationEvent
.NewInstance(type
);
2135 derivedUnit
.setDerivedFrom(result
);
2141 * TODO still unclear which classes do definetly require accessioning.
2142 * Only return true for those classes which are clear.
2143 * @param derivedUnit
2146 private boolean isAccessioned(DerivedUnitBase
<?
> derivedUnit
) {
2147 if (derivedUnit
.isInstanceOf(Specimen
.class) ){
2148 return CdmBase
.deproxy(derivedUnit
, Specimen
.class).getClass().equals(Specimen
.class);
2155 public String
getExsiccatum()
2156 throws MethodNotSupportedByDerivedUnitTypeException
{
2158 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2159 return CdmBase
.deproxy(derivedUnit
, Specimen
.class).getExsiccatum();
2162 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2163 throw new MethodNotSupportedByDerivedUnitTypeException(
2164 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2171 public void setExsiccatum(String exsiccatum
) throws Exception
{
2173 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2174 CdmBase
.deproxy(derivedUnit
, Specimen
.class).setExsiccatum(
2178 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2179 throw new MethodNotSupportedByDerivedUnitTypeException(
2180 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2188 public void addSource(IdentifiableSource source
) {
2190 this.derivedUnit
.addSource(source
);
2194 * Creates an orignal source, adds it to the specimen and returns it.
2197 * @param microReference
2198 * @param originalNameString
2201 public IdentifiableSource
addSource(Reference reference
, String microReference
, String originalNameString
) {
2202 IdentifiableSource source
= IdentifiableSource
.NewInstance(reference
, microReference
);
2203 source
.setOriginalNameString(originalNameString
);
2209 public Set
<IdentifiableSource
> getSources() {
2211 return derivedUnit
.getSources();
2214 public void removeSource(IdentifiableSource source
) {
2216 this.derivedUnit
.removeSource(source
);
2220 * @return the collection
2223 public Collection
getCollection() {
2225 return derivedUnit
.getCollection();
2230 * the collection to set
2232 public void setCollection(Collection collection
) {
2234 derivedUnit
.setCollection(collection
);
2238 public void addAnnotation(Annotation annotation
) {
2240 this.derivedUnit
.addAnnotation(annotation
);
2244 public void getAnnotations() {
2246 this.derivedUnit
.getAnnotations();
2249 public void removeAnnotation(Annotation annotation
) {
2251 this.derivedUnit
.removeAnnotation(annotation
);
2254 // ******************************* Events ***************************
2256 //set of events that were currently fired by this facades field observation
2257 //to avoid recursive fireing of the same event
2258 private Set
<PropertyChangeEvent
> fireingEvents
= new HashSet
<PropertyChangeEvent
>();
2263 private void addNewEventPropagationListener(CdmBase listeningObject
) {
2264 //if there is already a listener, don't do anything
2265 for (PropertyChangeListener listener
: this.listeners
.keySet()){
2266 if (listeners
.get(listener
) == listeningObject
){
2270 //create new listener
2271 PropertyChangeListener listener
= new PropertyChangeListener() {
2273 public void propertyChange(PropertyChangeEvent event
) {
2274 if (derivedUnit
!= null){
2275 derivedUnit
.firePropertyChange(event
);
2277 if (! event
.getSource().equals(fieldObservation
) && ! fireingEvents
.contains(event
) ){
2278 fireingEvents
.add(event
);
2279 fieldObservation
.firePropertyChange(event
);
2280 fireingEvents
.remove(event
);
2285 //add listener to listening object and to list of listeners
2286 listeningObject
.addPropertyChangeListener(listener
);
2287 listeners
.put(listener
, listeningObject
);
2290 // **************** Other Collections ********************************
2293 * Creates a duplicate specimen which derives from the same derivation event
2294 * as the facade specimen and adds collection data to it (all data available
2295 * in DerivedUnitBase and Specimen. Data from SpecimenOrObservationBase and
2296 * above are not yet shared at the moment.
2299 * @param catalogNumber
2300 * @param accessionNumber
2301 * @param collectorsNumber
2302 * @param storedUnder
2303 * @param preservation
2306 public Specimen
addDuplicate(Collection collection
, String catalogNumber
,
2307 String accessionNumber
,
2308 TaxonNameBase storedUnder
, PreservationMethod preservation
) {
2310 Specimen duplicate
= Specimen
.NewInstance();
2311 duplicate
.setDerivedFrom(getDerivationEvent(CREATE
));
2312 duplicate
.setCollection(collection
);
2313 duplicate
.setCatalogNumber(catalogNumber
);
2314 duplicate
.setAccessionNumber(accessionNumber
);
2315 duplicate
.setStoredUnder(storedUnder
);
2316 duplicate
.setPreservation(preservation
);
2320 public void addDuplicate(DerivedUnitBase duplicateSpecimen
) {
2321 // TODO check derivedUnitType
2323 getDerivationEvent(CREATE
).addDerivative(duplicateSpecimen
);
2327 public Set
<Specimen
> getDuplicates() {
2329 Set
<Specimen
> result
= new HashSet
<Specimen
>();
2330 if (hasDerivationEvent()) {
2331 for (DerivedUnitBase derivedUnit
: getDerivationEvent(CREATE
)
2332 .getDerivatives()) {
2333 if (derivedUnit
.isInstanceOf(Specimen
.class)
2334 && !derivedUnit
.equals(this.derivedUnit
)) {
2335 result
.add(CdmBase
.deproxy(derivedUnit
, Specimen
.class));
2342 public void removeDuplicate(Specimen duplicateSpecimen
) {
2344 if (hasDerivationEvent()) {
2345 getDerivationEvent(CREATE
).removeDerivative(duplicateSpecimen
);
2351 private void testDerivedUnit() {
2352 if (derivedUnit
== null){
2353 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");
2357 public void setType(DerivedUnitType type
) {
2361 public DerivedUnitType
getType() {
2367 * Closes this facade. As a minimum this method removes all listeners created by this facade from their
2368 * listening objects.
2370 public void close(){
2371 for (PropertyChangeListener listener
: this.listeners
.keySet()){
2372 CdmBase listeningObject
= listeners
.get(listener
);
2373 listeningObject
.removePropertyChangeListener(listener
);