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.
9 package eu
.etaxonomy
.cdm
.api
.facade
;
11 import java
.beans
.PropertyChangeEvent
;
12 import java
.beans
.PropertyChangeListener
;
13 import java
.text
.ParseException
;
14 import java
.util
.ArrayList
;
15 import java
.util
.HashMap
;
16 import java
.util
.HashSet
;
17 import java
.util
.List
;
21 import javax
.persistence
.Transient
;
23 import org
.apache
.commons
.lang
.StringUtils
;
24 import org
.apache
.log4j
.Logger
;
26 import eu
.etaxonomy
.cdm
.api
.service
.IOccurrenceService
;
27 import eu
.etaxonomy
.cdm
.common
.URI
;
28 import eu
.etaxonomy
.cdm
.format
.occurrences
.DistanceStringFormatter
;
29 import eu
.etaxonomy
.cdm
.model
.agent
.AgentBase
;
30 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
31 import eu
.etaxonomy
.cdm
.model
.common
.Annotation
;
32 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
33 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
34 import eu
.etaxonomy
.cdm
.model
.common
.Identifier
;
35 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
36 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
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
.SpecimenDescription
;
41 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
42 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
43 import eu
.etaxonomy
.cdm
.model
.location
.Point
;
44 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
45 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
46 import eu
.etaxonomy
.cdm
.model
.media
.Rights
;
47 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
48 import eu
.etaxonomy
.cdm
.model
.occurrence
.Collection
;
49 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEvent
;
50 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEventType
;
51 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
52 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
53 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
54 import eu
.etaxonomy
.cdm
.model
.occurrence
.GatheringEvent
;
55 import eu
.etaxonomy
.cdm
.model
.occurrence
.PreservationMethod
;
56 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
57 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationType
;
58 import eu
.etaxonomy
.cdm
.model
.reference
.IOriginalSource
;
59 import eu
.etaxonomy
.cdm
.model
.reference
.OriginalSourceType
;
60 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
61 import eu
.etaxonomy
.cdm
.model
.term
.DefinedTerm
;
62 import eu
.etaxonomy
.cdm
.ref
.TypedEntityReference
;
63 import eu
.etaxonomy
.cdm
.strategy
.cache
.occurrence
.DerivedUnitDefaultCacheStrategy
;
66 * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
67 * a specimen based view. It does not support all functionality available in the
68 * occurrence package.<BR>
69 * The most significant restriction is that a specimen may derive only from one
70 * direct derivation event and there must be only one field unit
71 * (gathering event) it derives from.<BR>
76 public class DerivedUnitFacade
{
77 private static final String METER
= "m";
79 @SuppressWarnings("unused")
80 private static final Logger logger
= Logger
.getLogger(DerivedUnitFacade
.class);
82 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 ";
84 private static final boolean CREATE
= true;
85 private static final boolean CREATE_NOT
= false;
87 private final DerivedUnitFacadeConfigurator config
;
89 private final Map
<PropertyChangeListener
, CdmBase
> listeners
= new HashMap
<>();
91 // Either fieldUnit or derivedUnit must not be null.
92 private FieldUnit fieldUnit
;
93 private final DerivedUnit derivedUnit
;
95 // media - the text data holding the media
96 private TextData derivedUnitMediaTextData
;
97 private TextData fieldObjectMediaTextData
;
99 private TextData ecology
;
100 private TextData plantDescription
;
101 private TextData lifeform
;
104 * Creates a derived unit facade for a new derived unit of type
110 public static DerivedUnitFacade
NewInstance(SpecimenOrObservationType type
) {
111 return new DerivedUnitFacade(type
, null, null);
115 * Creates a derived unit facade for a new derived unit of type
116 * {@link SpecimenOrObservationType#PreservedSpecimen}.
118 * @return the derived unit facade
120 public static DerivedUnitFacade
NewPreservedSpecimenInstance() {
121 return new DerivedUnitFacade(SpecimenOrObservationType
.PreservedSpecimen
, null, null);
125 * Creates a derived unit facade for a new derived unit of type
131 public static DerivedUnitFacade
NewInstance(SpecimenOrObservationType type
, FieldUnit fieldUnit
) {
132 return new DerivedUnitFacade(type
, fieldUnit
, null);
136 * Creates a derived unit facade for a new derived unit of type
140 * @param fieldUnit the field unit to use
141 * @param config the facade configurator to use
142 * //TODO are there any ambiguities to solve with defining a field unit or a configurator
145 public static DerivedUnitFacade
NewInstance(SpecimenOrObservationType type
, FieldUnit fieldUnit
, DerivedUnitFacadeConfigurator config
) {
146 return new DerivedUnitFacade(type
, fieldUnit
, config
);
151 * Creates a derived unit facade for a given derived unit using the default
156 * @throws DerivedUnitFacadeNotSupportedException
158 public static DerivedUnitFacade
NewInstance(DerivedUnit derivedUnit
)
159 throws DerivedUnitFacadeNotSupportedException
{
160 return new DerivedUnitFacade(derivedUnit
, null);
163 public static DerivedUnitFacade
NewInstance(DerivedUnit derivedUnit
,
164 DerivedUnitFacadeConfigurator config
)
165 throws DerivedUnitFacadeNotSupportedException
{
166 return new DerivedUnitFacade(derivedUnit
, config
);
169 // ****************** CONSTRUCTOR ******************************************
171 private DerivedUnitFacade(SpecimenOrObservationType type
, FieldUnit fieldUnit
, DerivedUnitFacadeConfigurator config
) {
173 config
= DerivedUnitFacadeConfigurator
.NewInstance();
175 this.config
= config
;
177 derivedUnit
= getNewDerivedUnitInstance(type
);
178 //TODO parameter checking should be solved in a more generic way if we start using other entity facades
179 if(derivedUnit
==null && fieldUnit
==null && type
.isFieldUnit()){
180 this.fieldUnit
= getFieldUnit(CREATE
);
182 setFieldUnit(fieldUnit
);
183 if (derivedUnit
!= null){
186 setFieldUnitCacheStrategy();
190 private DerivedUnit
getNewDerivedUnitInstance(SpecimenOrObservationType type
) {
191 if (type
.isFieldUnit()){
193 }else if(type
.isAnyDerivedUnit()){
194 return DerivedUnit
.NewInstance(type
);
196 String message
= "Unknown specimen or observation type %s";
197 message
= String
.format(message
, type
.getLabel());
198 throw new IllegalStateException(message
);
202 private DerivedUnitFacade(DerivedUnit derivedUnit
, DerivedUnitFacadeConfigurator config
)
203 throws DerivedUnitFacadeNotSupportedException
{
205 if(derivedUnit
==null){
206 throw new IllegalArgumentException("DerivedUnit must not be null");
209 if (config
== null) {
210 config
= DerivedUnitFacadeConfigurator
.NewInstance();
212 this.config
= config
;
215 this.derivedUnit
= derivedUnit
;
218 if (this.derivedUnit
.getDerivedFrom() != null) {
219 DerivationEvent derivationEvent
= getDerivationEvent(CREATE
);
221 Set
<FieldUnit
> fieldOriginals
= getFieldUnitOriginals(derivationEvent
, null);
222 if (fieldOriginals
.size() > 1) {
223 throw new DerivedUnitFacadeNotSupportedException(
224 "Specimen must not have more than 1 derivation event");
225 } else if (fieldOriginals
.size() == 0) {
226 // fieldUnit = FieldUnit.NewInstance();
227 } else if (fieldOriginals
.size() == 1) {
228 fieldUnit
= fieldOriginals
.iterator().next();
230 // getInitializedFieldUnit(fieldUnit);
231 if (config
.isFirePropertyChangeEvents()){
232 addNewEventPropagationListener(fieldUnit
);
235 throw new IllegalStateException("Illegal state");
239 this.derivedUnitMediaTextData
= inititializeTextDataWithSupportTest(Feature
.IMAGE(), this.derivedUnit
, false, true);
241 fieldObjectMediaTextData
= initializeFieldObjectTextDataWithSupportTest(Feature
.IMAGE(), false, true);
244 //direct media have been removed from specimenorobservationbase #3597
245 // // handle derivedUnit.getMedia()
246 // if (derivedUnit.getMedia().size() > 0) {
247 // // TODO better changed model here to allow only one place for images
248 // if (this.config.isMoveDerivedUnitMediaToGallery()) {
249 // Set<Media> mediaSet = derivedUnit.getMedia();
250 // for (Media media : mediaSet) {
251 // this.addDerivedUnitMedia(media);
253 // mediaSet.removeAll(getDerivedUnitMedia());
255 // throw new DerivedUnitFacadeNotSupportedException(
256 // "Specimen may not have direct media. Only (one) image gallery is allowed");
260 // // handle fieldUnit.getMedia()
261 // if (fieldUnit != null && fieldUnit.getMedia() != null
262 // && fieldUnit.getMedia().size() > 0) {
263 // // TODO better changed model here to allow only one place for images
264 // if (this.config.isMoveFieldObjectMediaToGallery()) {
265 // Set<Media> mediaSet = fieldUnit.getMedia();
266 // for (Media media : mediaSet) {
267 // this.addFieldObjectMedia(media);
269 // mediaSet.removeAll(getFieldObjectMedia());
271 // throw new DerivedUnitFacadeNotSupportedException(
272 // "Field object may not have direct media. Only (one) image gallery is allowed");
276 // test if descriptions are supported
277 ecology
= initializeFieldObjectTextDataWithSupportTest(
278 Feature
.ECOLOGY(), false, false);
279 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
280 Feature
.DESCRIPTION(), false, false);
286 private DerivedUnit
getInitializedDerivedUnit(
287 DerivedUnit derivedUnit
) {
288 IOccurrenceService occurrenceService
= this.config
289 .getOccurrenceService();
290 if (occurrenceService
== null) {
293 List
<String
> propertyPaths
= this.config
.getPropertyPaths();
294 if (propertyPaths
== null) {
297 propertyPaths
= getDerivedUnitPropertyPaths(propertyPaths
);
298 DerivedUnit result
= (DerivedUnit
) occurrenceService
.load(
299 derivedUnit
.getUuid(), propertyPaths
);
304 * Initializes the derived unit according to the configuartions property
305 * path. If the property path is <code>null</code> or no occurrence service
306 * is given the returned object is the same as the input parameter.
311 private FieldUnit
getInitializedFieldUnit(FieldUnit fieldUnit
) {
312 IOccurrenceService occurrenceService
= this.config
313 .getOccurrenceService();
314 if (occurrenceService
== null) {
317 List
<String
> propertyPaths
= this.config
.getPropertyPaths();
318 if (propertyPaths
== null) {
321 propertyPaths
= getFieldObjectPropertyPaths(propertyPaths
);
322 FieldUnit result
= (FieldUnit
) occurrenceService
.load(
323 fieldUnit
.getUuid(), propertyPaths
);
328 * Transforms the property paths in a way that the facade is handled just
329 * like an ordinary CdmBase object.<BR>
330 * E.g. a property path "collectinAreas" will be translated into
331 * gatheringEvent.collectingAreas
333 * @param propertyPaths
336 private List
<String
> getFieldObjectPropertyPaths(List
<String
> propertyPaths
) {
337 List
<String
> result
= new ArrayList
<String
>();
338 for (String facadePath
: propertyPaths
) {
339 // collecting areas (named area)
340 if (facadePath
.startsWith("collectingAreas")) {
341 facadePath
= "gatheringEvent." + facadePath
;
342 result
.add(facadePath
);
344 // collector (agentBase)
345 else if (facadePath
.startsWith("collector")) {
346 facadePath
= facadePath
.replace("collector",
347 "gatheringEvent.actor");
348 result
.add(facadePath
);
350 // exactLocation (agentBase)
351 else if (facadePath
.startsWith("exactLocation")) {
352 facadePath
= "gatheringEvent." + facadePath
;
353 result
.add(facadePath
);
355 // gatheringPeriod (TimePeriod)
356 else if (facadePath
.startsWith("gatheringPeriod")) {
357 facadePath
= facadePath
.replace("gatheringPeriod",
358 "gatheringEvent.timeperiod");
359 result
.add(facadePath
);
361 // (locality/ localityLanguage , LanguageString)
362 else if (facadePath
.startsWith("locality")) {
363 facadePath
= "gatheringEvent." + facadePath
;
364 result
.add(facadePath
);
367 // *********** FIELD OBJECT ************
368 // fieldObjectDefinitions (Map<language, languageString)
369 else if (facadePath
.startsWith("fieldObjectDefinitions")) {
370 // TODO or definition ???
371 facadePath
= facadePath
.replace("fieldObjectDefinitions",
373 result
.add(facadePath
);
375 // fieldObjectMedia (Media)
376 else if (facadePath
.startsWith("fieldObjectMedia")) {
378 facadePath
= facadePath
.replace("fieldObjectMedia",
379 "descriptions.elements.media");
380 result
.add(facadePath
);
383 // Gathering Event will always be added
384 result
.add("gatheringEvent");
389 * Gathering Event ==================== - gatheringEvent
392 * Field Object ================= - ecology/ ecologyAll (String) ??? -
393 * plant description (like ecology)
395 * - fieldObjectImageGallery (SpecimenDescription) - is automatically
396 * initialized via fieldObjectMedia
403 * Transforms the property paths in a way that the facade is handled just
404 * like an ordinary CdmBase object.<BR>
405 * E.g. a property path "collectinAreas" will be translated into
406 * gatheringEvent.collectingAreas
408 * Not needed (?) as the facade works with REST service property paths
409 * without using this method.
411 * @param propertyPaths
414 private List
<String
> getDerivedUnitPropertyPaths(List
<String
> propertyPaths
) {
415 List
<String
> result
= new ArrayList
<String
>();
416 for (String facadePath
: propertyPaths
) {
417 // determinations (DeterminationEvent)
418 if (facadePath
.startsWith("determinations")) {
419 facadePath
= "" + facadePath
; // no change
420 result
.add(facadePath
);
422 // storedUnder (TaxonName)
423 else if (facadePath
.startsWith("storedUnder")) {
424 facadePath
= "" + facadePath
; // no change
425 result
.add(facadePath
);
427 // sources (IdentifiableSource)
428 else if (facadePath
.startsWith("sources")) {
429 facadePath
= "" + facadePath
; // no change
430 result
.add(facadePath
);
432 // collection (Collection)
433 else if (facadePath
.startsWith("collection")) {
434 facadePath
= "" + facadePath
; // no change
435 result
.add(facadePath
);
437 // (locality/ localityLanguage , LanguageString)
438 else if (facadePath
.startsWith("locality")) {
439 facadePath
= "gatheringEvent." + facadePath
;
440 result
.add(facadePath
);
443 // *********** FIELD OBJECT ************
444 // derivedUnitDefinitions (Map<language, languageString)
445 else if (facadePath
.startsWith("derivedUnitDefinitions")) {
446 // TODO or definition ???
447 facadePath
= facadePath
.replace("derivedUnitDefinitions",
449 result
.add(facadePath
);
452 // derivedUnitMedia (Media)
453 else if (facadePath
.startsWith("derivedUnitMedia")) {
455 facadePath
= facadePath
.replace("derivedUnitMedia",
456 "descriptions.elements.media");
457 result
.add(facadePath
);
463 * //TODO Derived Unit =====================
465 * - derivedUnitImageGallery (SpecimenDescription) - is automatically
466 * initialized via derivedUnitMedia
468 * - derivationEvent (DerivationEvent) - will always be initialized -
469 * duplicates (??? Specimen???) ???
475 private void setCacheStrategy() {
476 if (derivedUnit
== null) {
477 throw new NullPointerException(
478 "Facade's derviedUnit must not be null to set cache strategy");
480 derivedUnit
.setCacheStrategy(DerivedUnitFacadeCacheStrategy
.NewInstance(false));
481 setFieldUnitCacheStrategy();
485 private void setFieldUnitCacheStrategy() {
486 if (this.hasFieldObject()){
487 DerivedUnitFacadeFieldUnitCacheStrategy strategy
= new DerivedUnitFacadeFieldUnitCacheStrategy();
488 this.fieldUnit
.setCacheStrategy(strategy
);
492 private TextData
initializeFieldObjectTextDataWithSupportTest(
493 Feature feature
, boolean createIfNotExists
, boolean isImageGallery
)
494 throws DerivedUnitFacadeNotSupportedException
{
496 FieldUnit fieldObject
= getFieldUnit(createIfNotExists
);
497 if (fieldObject
== null) {
500 return inititializeTextDataWithSupportTest(feature
, fieldObject
,
501 createIfNotExists
, isImageGallery
);
507 * @param createIfNotExists
508 * @param isImageGallery
510 * @throws DerivedUnitFacadeNotSupportedException
512 private TextData
inititializeTextDataWithSupportTest(Feature feature
,
513 SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
,
514 boolean isImageGallery
)
515 throws DerivedUnitFacadeNotSupportedException
{
516 if (feature
== null) {
519 TextData textData
= null;
520 if (createIfNotExists
) {
521 textData
= TextData
.NewInstance(feature
);
524 Set
<SpecimenDescription
> descriptions
;
525 if (isImageGallery
) {
526 descriptions
= specimen
.getSpecimenDescriptionImageGallery();
528 descriptions
= specimen
.getSpecimenDescriptions(false);
530 // no description exists yet for this specimen
531 if (descriptions
.size() == 0) {
532 if (createIfNotExists
) {
533 SpecimenDescription newSpecimenDescription
= SpecimenDescription
534 .NewInstance(specimen
);
535 newSpecimenDescription
.addElement(textData
);
536 newSpecimenDescription
.setImageGallery(isImageGallery
);
542 // description already exists
543 Set
<DescriptionElementBase
> existingTextData
= new HashSet
<>();
544 for (SpecimenDescription description
: descriptions
) {
545 // collect all existing text data
546 for (DescriptionElementBase element
: description
.getElements()) {
547 if (element
.isInstanceOf(TextData
.class)
548 && (feature
.equals(element
.getFeature()) || isImageGallery
)) {
549 existingTextData
.add(element
);
553 // use existing text data if exactly one exists
554 if (existingTextData
.size() > 1) {
555 throw new DerivedUnitFacadeNotSupportedException(
556 "Specimen facade does not support more than one description text data of type "
557 + feature
.getLabel());
559 } else if (existingTextData
.size() == 1) {
560 return CdmBase
.deproxy(existingTextData
.iterator().next(),
563 if (createIfNotExists
) {
564 SpecimenDescription description
= descriptions
.iterator()
566 description
.addElement(textData
);
573 * Tests if a given image gallery is supported by the derived unit facade.
574 * It returns the only text data attached to the given image gallery. If the
575 * given image gallery does not have text data attached, it is created and
578 * @param imageGallery
580 * @throws DerivedUnitFacadeNotSupportedException
582 private TextData
testImageGallery(SpecimenDescription imageGallery
)
583 throws DerivedUnitFacadeNotSupportedException
{
584 if (imageGallery
.isImageGallery() == false) {
585 throw new DerivedUnitFacadeNotSupportedException(
586 "Image gallery needs to have image gallery flag set");
588 if (imageGallery
.getElements().size() > 1) {
589 throw new DerivedUnitFacadeNotSupportedException(
590 "Image gallery must not have more then one description element");
593 if (imageGallery
.getElements().size() == 0) {
594 textData
= TextData
.NewInstance(Feature
.IMAGE());
595 imageGallery
.addElement(textData
);
597 if (!imageGallery
.getElements().iterator().next()
598 .isInstanceOf(TextData
.class)) {
599 throw new DerivedUnitFacadeNotSupportedException(
600 "Image gallery must only have TextData as element");
602 textData
= CdmBase
.deproxy(imageGallery
.getElements()
603 .iterator().next(), TextData
.class);
609 // ************************** METHODS
610 // *****************************************
612 private TextData
getDerivedUnitImageGalleryTextData(
613 boolean createIfNotExists
)
614 throws DerivedUnitFacadeNotSupportedException
{
615 if (this.derivedUnitMediaTextData
== null && createIfNotExists
) {
616 this.derivedUnitMediaTextData
= getImageGalleryTextData(
617 derivedUnit
, "Specimen");
619 return this.derivedUnitMediaTextData
;
622 private TextData
getObservationImageGalleryTextData(
623 boolean createIfNotExists
)
624 throws DerivedUnitFacadeNotSupportedException
{
625 if (this.fieldObjectMediaTextData
== null && createIfNotExists
) {
626 this.fieldObjectMediaTextData
= getImageGalleryTextData(fieldUnit
, "Field unit");
628 return this.fieldObjectMediaTextData
;
632 * @param derivationEvent
634 * @throws DerivedUnitFacadeNotSupportedException
636 private Set
<FieldUnit
> getFieldUnitOriginals(
637 DerivationEvent derivationEvent
,
638 Set
<SpecimenOrObservationBase
> recursionAvoidSet
)
639 throws DerivedUnitFacadeNotSupportedException
{
640 if (recursionAvoidSet
== null) {
641 recursionAvoidSet
= new HashSet
<>();
643 Set
<FieldUnit
> result
= new HashSet
<>();
644 Set
<SpecimenOrObservationBase
> originals
= derivationEvent
.getOriginals();
645 for (SpecimenOrObservationBase original
: originals
) {
646 if (original
.isInstanceOf(FieldUnit
.class)) {
647 result
.add(CdmBase
.deproxy(original
, FieldUnit
.class));
648 } else if (original
.isInstanceOf(DerivedUnit
.class)) {
649 // if specimen has already been tested exclude it from further
651 if (recursionAvoidSet
.contains(original
)) {
654 DerivedUnit derivedUnit
= CdmBase
.deproxy(original
, DerivedUnit
.class);
655 DerivationEvent originalDerivation
= derivedUnit
.getDerivedFrom();
656 // Set<DerivationEvent> derivationEvents =
657 // original.getDerivationEvents();
658 // for (DerivationEvent originalDerivation : derivationEvents){
659 if(originalDerivation
!=null){
660 Set
<FieldUnit
> fieldUnits
= getFieldUnitOriginals(
661 originalDerivation
, recursionAvoidSet
);
662 result
.addAll(fieldUnits
);
666 throw new DerivedUnitFacadeNotSupportedException(
667 "Unhandled specimen or observation base type: "
668 + original
.getClass().getName());
675 // *********** MEDIA METHODS ******************************
678 // * Returns the media list for a specimen. Throws an exception if the
679 // existing specimen descriptions
680 // * are not supported by this facade.
681 // * @param specimen the specimen the media belongs to
682 // * @param specimenExceptionText text describing the specimen for exception
685 // * @throws DerivedUnitFacadeNotSupportedException
687 // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase
688 // specimen, String specimenExceptionText) throws
689 // DerivedUnitFacadeNotSupportedException{
690 // List<Media> result;
691 // SpecimenDescription imageGallery =
692 // getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
693 // TextData textData = getImageTextDataWithSupportTest(imageGallery,
694 // specimenExceptionText);
695 // result = textData.getMedia();
700 * Returns the media list for a specimen. Throws an exception if the
701 * existing specimen descriptions are not supported by this facade.
704 * the specimen the media belongs to
705 * @param specimenExceptionText
706 * text describing the specimen for exception messages
708 * @throws DerivedUnitFacadeNotSupportedException
710 private TextData
getImageGalleryTextData(SpecimenOrObservationBase specimen
, String specimenExceptionText
)
711 throws DerivedUnitFacadeNotSupportedException
{
713 SpecimenDescription imageGallery
= getImageGalleryWithSupportTest(
714 specimen
, specimenExceptionText
, true);
715 result
= getImageTextDataWithSupportTest(imageGallery
,
716 specimenExceptionText
);
721 * Returns the image gallery of the according specimen. Throws an exception
722 * if the attached image gallerie(s) are not supported by this facade. If no
723 * image gallery exists a new one is created if
724 * <code>createNewIfNotExists</code> is true and if specimen is not
728 * @param specimenText
729 * @param createNewIfNotExists
731 * @throws DerivedUnitFacadeNotSupportedException
733 private SpecimenDescription
getImageGalleryWithSupportTest(
734 SpecimenOrObservationBase
<?
> specimen
, String specimenText
,
735 boolean createNewIfNotExists
)
736 throws DerivedUnitFacadeNotSupportedException
{
737 if (specimen
== null) {
740 SpecimenDescription imageGallery
;
741 if (hasMultipleImageGalleries(specimen
)) {
742 throw new DerivedUnitFacadeNotSupportedException(specimenText
743 + " must not have more than 1 image gallery");
745 imageGallery
= getImageGallery(specimen
, createNewIfNotExists
);
746 getImageTextDataWithSupportTest(imageGallery
, specimenText
);
752 * Returns the media holding text data element of the image gallery. Throws
753 * an exception if multiple such text data already exist. Creates a new text
754 * data if none exists and adds it to the image gallery. If image gallery is
755 * <code>null</code> nothing happens.
757 * @param imageGallery
760 * @throws DerivedUnitFacadeNotSupportedException
762 private TextData
getImageTextDataWithSupportTest(
763 SpecimenDescription imageGallery
, String specimenText
)
764 throws DerivedUnitFacadeNotSupportedException
{
765 if (imageGallery
== null) {
768 TextData textData
= null;
769 for (DescriptionElementBase element
: imageGallery
.getElements()) {
770 if (element
.isInstanceOf(TextData
.class)
771 && element
.getFeature().equals(Feature
.IMAGE())) {
772 if (textData
!= null) {
773 throw new DerivedUnitFacadeNotSupportedException(
775 + " must not have more than 1 image text data element in image gallery");
777 textData
= CdmBase
.deproxy(element
, TextData
.class);
780 if (textData
== null) {
781 textData
= TextData
.NewInstance(Feature
.IMAGE());
782 imageGallery
.addElement(textData
);
788 * Checks, if a specimen belongs to more than one description that is an
794 private boolean hasMultipleImageGalleries(
795 SpecimenOrObservationBase
<?
> derivedUnit
) {
797 Set
<SpecimenDescription
> descriptions
= derivedUnit
798 .getSpecimenDescriptions();
799 for (SpecimenDescription description
: descriptions
) {
800 if (description
.isImageGallery()) {
808 * Returns the image gallery for a specimen. If there are multiple specimen
809 * descriptions marked as image galleries an arbitrary one is chosen. If no
810 * image gallery exists, a new one is created if
811 * <code>createNewIfNotExists</code> is <code>true</code>.<Br>
812 * If specimen is <code>null</code> a null pointer exception is thrown.
814 * @param createNewIfNotExists
817 private SpecimenDescription
getImageGallery(SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
) {
818 SpecimenDescription result
= null;
819 Set
<SpecimenDescription
> descriptions
= specimen
.getSpecimenDescriptions();
820 for (SpecimenDescription description
: descriptions
) {
821 if (description
.isImageGallery()) {
822 result
= description
;
826 if (result
== null && createIfNotExists
) {
827 result
= SpecimenDescription
.NewInstance(specimen
);
828 result
.setImageGallery(true);
834 * Adds a media to the specimens image gallery. If media is
835 * <code>null</code> nothing happens.
839 * @return true if media is not null (as specified by
840 * {@link java.util.Collection#add(Object) Collection.add(E e)}
841 * @throws DerivedUnitFacadeNotSupportedException
843 private boolean addMedia(Media media
, SpecimenOrObservationBase
<?
> specimen
) throws DerivedUnitFacadeNotSupportedException
{
845 List
<Media
> mediaList
= getMediaList(specimen
, true);
846 if (! mediaList
.contains(media
)){
847 return mediaList
.add(media
);
857 * Removes a media from the specimens image gallery.
861 * @return true if an element was removed as a result of this call (as
862 * specified by {@link java.util.Collection#remove(Object)
863 * Collection.remove(E e)}
864 * @throws DerivedUnitFacadeNotSupportedException
866 private boolean removeMedia(Media media
,
867 SpecimenOrObservationBase
<?
> specimen
)
868 throws DerivedUnitFacadeNotSupportedException
{
869 List
<Media
> mediaList
= getMediaList(specimen
, true);
870 return mediaList
== null ?
null : mediaList
.remove(media
);
873 private List
<Media
> getMediaList(SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
)
874 throws DerivedUnitFacadeNotSupportedException
{
875 TextData textData
= getMediaTextData(specimen
, createIfNotExists
);
876 return textData
== null ?
null : textData
.getMedia();
880 * Returns the one media list of a specimen which is part of the only image
881 * gallery that this specimen is part of.<BR>
882 * If these conditions are not hold an exception is thrwon.
886 * @throws DerivedUnitFacadeNotSupportedException
888 // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)
889 // throws DerivedUnitFacadeNotSupportedException {
890 // if (specimen == null){
893 // if (specimen == this.derivedUnit){
894 // return getDerivedUnitImageGalleryMedia();
895 // }else if (specimen == this.fieldUnit){
896 // return getObservationImageGalleryTextData();
898 // return getImageGalleryMedia(specimen, "Undefined specimen ");
903 * Returns the one media list of a specimen which is part of the only image
904 * gallery that this specimen is part of.<BR>
905 * If these conditions are not hold an exception is thrown.
909 * @throws DerivedUnitFacadeNotSupportedException
911 private TextData
getMediaTextData(SpecimenOrObservationBase
<?
> specimen
,
912 boolean createIfNotExists
)
913 throws DerivedUnitFacadeNotSupportedException
{
914 if (specimen
== null) {
917 if (specimen
== this.derivedUnit
) {
918 return getDerivedUnitImageGalleryTextData(createIfNotExists
);
919 } else if (specimen
== this.fieldUnit
) {
920 return getObservationImageGalleryTextData(createIfNotExists
);
922 return getImageGalleryTextData(specimen
, "Undefined specimen ");
926 // ****************** GETTER / SETTER / ADDER / REMOVER
927 // ***********************/
929 // ****************** Gathering Event *********************************/
933 public NamedArea
getCountry() {
934 return (hasGatheringEvent() ?
getGatheringEvent(true).getCountry()
938 public void setCountry(NamedArea country
) {
939 getGatheringEvent(true).setCountry(country
);
943 public void addCollectingArea(NamedArea area
) {
944 getGatheringEvent(true).addCollectingArea(area
);
947 public void addCollectingAreas(java
.util
.Collection
<NamedArea
> areas
) {
948 for (NamedArea area
: areas
) {
949 getGatheringEvent(true).addCollectingArea(area
);
954 public Set
<NamedArea
> getCollectingAreas() {
955 return (hasGatheringEvent() ?
getGatheringEvent(true)
956 .getCollectingAreas() : null);
959 public void removeCollectingArea(NamedArea area
) {
960 if (hasGatheringEvent()) {
961 getGatheringEvent(true).removeCollectingArea(area
);
965 static final String ALTITUDE_POSTFIX
= " m";
968 * Returns the correctly formatted <code>absolute elevation</code> information.
969 * If absoluteElevationText is set, this will be returned,
970 * otherwise we absoluteElevation will be returned, followed by absoluteElevationMax
971 * if existing, separated by " - "
975 public String
absoluteElevationToString() {
976 if (! hasGatheringEvent()){
979 GatheringEvent ev
= getGatheringEvent(true);
980 if (StringUtils
.isNotBlank(ev
.getAbsoluteElevationText())){
981 return ev
.getAbsoluteElevationText();
983 String text
= ev
.getAbsoluteElevationText();
984 Integer min
= getAbsoluteElevation();
985 Integer max
= getAbsoluteElevationMaximum();
986 return DistanceStringFormatter
.distanceString(min
, max
, text
, METER
);
993 * meter above/below sea level of the surface
995 * @see #getAbsoluteElevationError()
996 * @see #getAbsoluteElevationRange()
999 public Integer
getAbsoluteElevation() {
1000 return (hasGatheringEvent() ?
getGatheringEvent(true).getAbsoluteElevation() : null);
1003 public void setAbsoluteElevation(Integer absoluteElevation
) {
1004 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation
);
1007 public void setAbsoluteElevationMax(Integer absoluteElevationMax
) {
1008 getGatheringEvent(true).setAbsoluteElevationMax(absoluteElevationMax
);
1011 public void setAbsoluteElevationText(String absoluteElevationText
) {
1012 getGatheringEvent(true).setAbsoluteElevationText(absoluteElevationText
);
1016 * @see #getAbsoluteElevation()
1017 * @see #getAbsoluteElevationError()
1018 * @see #setAbsoluteElevationRange(Integer, Integer)
1019 * @see #getAbsoluteElevationMinimum()
1022 public Integer
getAbsoluteElevationMaximum() {
1023 if (!hasGatheringEvent()) {
1026 return getGatheringEvent(true).getAbsoluteElevationMax();
1031 * @see #getAbsoluteElevation()
1032 * @see #getAbsoluteElevationError()
1033 * @see #setAbsoluteElevationRange(Integer, Integer)
1034 * @see #getAbsoluteElevationMinimum()
1037 public String
getAbsoluteElevationText() {
1038 if (!hasGatheringEvent()) {
1041 return getGatheringEvent(true).getAbsoluteElevationText();
1046 * Convenience method to set absolute elevation minimum and maximum.
1048 * @see #setAbsoluteElevation(Integer)
1049 * @see #setAbsoluteElevationMax(Integer)
1050 * @param minimumElevation minimum of the range
1051 * @param maximumElevation maximum of the range
1053 public void setAbsoluteElevationRange(Integer minimumElevation
, Integer maximumElevation
) {
1054 getGatheringEvent(true).setAbsoluteElevation(minimumElevation
);
1055 getGatheringEvent(true).setAbsoluteElevationMax(maximumElevation
);
1060 public AgentBase
getCollector() {
1061 return (hasGatheringEvent() ?
getGatheringEvent(true).getCollector()
1065 public void setCollector(AgentBase collector
) {
1066 getGatheringEvent(true).setCollector(collector
);
1069 // collecting method
1071 public String
getCollectingMethod() {
1072 return (hasGatheringEvent() ?
getGatheringEvent(true).getCollectingMethod() : null);
1075 public void setCollectingMethod(String collectingMethod
) {
1076 getGatheringEvent(true).setCollectingMethod(collectingMethod
);
1079 // distance to ground
1082 * Returns the correctly formatted <code>distance to ground</code> information.
1083 * If distanceToGroundText is not blank, it will be returned,
1084 * otherwise distanceToGround will be returned, followed by distanceToGroundMax
1085 * if existing, separated by " - "
1089 public String
distanceToGroundToString() {
1090 if (! hasGatheringEvent()){
1093 GatheringEvent ev
= getGatheringEvent(true);
1094 String text
= ev
.getDistanceToGroundText();
1095 Double min
= getDistanceToGround();
1096 Double max
= getDistanceToGroundMax();
1097 return DistanceStringFormatter
.distanceString(min
, max
, text
, METER
);
1102 public Double
getDistanceToGround() {
1103 return (hasGatheringEvent() ?
getGatheringEvent(true).getDistanceToGround() : null);
1106 public void setDistanceToGround(Double distanceToGround
) {
1107 getGatheringEvent(true).setDistanceToGround(distanceToGround
);
1111 * @see #getDistanceToGround()
1112 * @see #getDistanceToGroundRange(Integer, Integer)
1115 public Double
getDistanceToGroundMax() {
1116 if (!hasGatheringEvent()) {
1119 return getGatheringEvent(true).getDistanceToGroundMax();
1123 public void setDistanceToGroundMax(Double distanceToGroundMax
) {
1124 getGatheringEvent(true).setDistanceToGroundMax(distanceToGroundMax
);
1128 * @see #getDistanceToGround()
1129 * @see #setDistanceToGroundRange(Integer, Integer)
1132 public String
getDistanceToGroundText() {
1133 if (!hasGatheringEvent()) {
1136 return getGatheringEvent(true).getDistanceToGroundText();
1139 public void setDistanceToGroundText(String distanceToGroundText
) {
1140 getGatheringEvent(true).setDistanceToGroundText(distanceToGroundText
);
1144 * Convenience method to set distance to ground minimum and maximum.
1146 * @see #getDistanceToGround()
1147 * @see #getDistanceToGroundMax()
1148 * @param minimumDistance minimum of the range
1149 * @param maximumDistance maximum of the range
1151 public void setDistanceToGroundRange(Double minimumDistance
, Double maximumDistance
) throws IllegalArgumentException
{
1152 getGatheringEvent(true).setDistanceToGround(minimumDistance
);
1153 getGatheringEvent(true).setDistanceToGroundMax(maximumDistance
);
1158 * Returns the correctly formatted <code>distance to water surface</code> information.
1159 * If distanceToWaterSurfaceText is not blank, it will be returned,
1160 * otherwise distanceToWaterSurface will be returned, followed by distanceToWatersurfaceMax
1161 * if existing, separated by " - "
1165 public String
distanceToWaterSurfaceToString() {
1166 if (! hasGatheringEvent()){
1169 GatheringEvent ev
= getGatheringEvent(true);
1170 String text
= ev
.getDistanceToWaterSurfaceText();
1171 Double min
= getDistanceToWaterSurface();
1172 Double max
= getDistanceToWaterSurfaceMax();
1173 return DistanceStringFormatter
.distanceString(min
, max
, text
, METER
);
1177 // distance to water surface
1179 public Double
getDistanceToWaterSurface() {
1180 return (hasGatheringEvent() ?
getGatheringEvent(true).getDistanceToWaterSurface() : null);
1183 public void setDistanceToWaterSurface(Double distanceToWaterSurface
) {
1184 getGatheringEvent(true).setDistanceToWaterSurface(distanceToWaterSurface
);
1188 * @see #getDistanceToWaterSurface()
1189 * @see #getDistanceToWaterSurfaceRange(Double, Double)
1192 public Double
getDistanceToWaterSurfaceMax() {
1193 if (!hasGatheringEvent()) {
1196 return getGatheringEvent(true).getDistanceToWaterSurfaceMax();
1200 public void setDistanceToWaterSurfaceMax(Double distanceToWaterSurfaceMax
) {
1201 getGatheringEvent(true).setDistanceToWaterSurfaceMax(distanceToWaterSurfaceMax
);
1205 * @see #getDistanceToWaterSurface()
1206 * @see #getDistanceToWaterSurfaceRange(Double, Double)
1209 public String
getDistanceToWaterSurfaceText() {
1210 if (!hasGatheringEvent()) {
1213 return getGatheringEvent(true).getDistanceToWaterSurfaceText();
1216 public void setDistanceToWaterSurfaceText(String distanceToWaterSurfaceText
) {
1217 getGatheringEvent(true).setDistanceToWaterSurfaceText(distanceToWaterSurfaceText
);
1221 * Convenience method to set distance to ground minimum and maximum.
1223 * @see #getDistanceToWaterSurface()
1224 * @see #getDistanceToWaterSurfaceMax()
1225 * @param minimumDistance minimum of the range, this is the distance which is closer to the water surface
1226 * @param maximumDistance maximum of the range, this is the distance which is farer to the water surface
1228 public void setDistanceToWaterSurfaceRange(Double minimumDistance
, Double maximumDistance
) throws IllegalArgumentException
{
1229 getGatheringEvent(true).setDistanceToWaterSurface(minimumDistance
);
1230 getGatheringEvent(true).setDistanceToWaterSurfaceMax(maximumDistance
);
1236 public Point
getExactLocation() {
1237 return (hasGatheringEvent() ?
getGatheringEvent(true).getExactLocation() : null);
1241 * Returns a sexagesimal representation of the exact location (e.g.
1242 * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty
1243 * string is returned.
1245 * @param includeEmptySeconds
1246 * @param includeReferenceSystem
1249 public String
getExactLocationText(boolean includeEmptySeconds
,
1250 boolean includeReferenceSystem
) {
1251 return (this.getExactLocation() == null ?
"" : this.getExactLocation()
1252 .toSexagesimalString(includeEmptySeconds
,
1253 includeReferenceSystem
));
1256 public void setExactLocation(Point exactLocation
) {
1257 getGatheringEvent(true).setExactLocation(exactLocation
);
1260 public void setExactLocationByParsing(String longitudeToParse
,
1261 String latitudeToParse
, ReferenceSystem referenceSystem
,
1262 Integer errorRadius
) throws ParseException
{
1263 Point point
= Point
.NewInstance(null, null, referenceSystem
,
1265 point
.setLongitudeByParsing(longitudeToParse
);
1266 point
.setLatitudeByParsing(latitudeToParse
);
1267 setExactLocation(point
);
1270 // gathering event description
1272 public String
getGatheringEventDescription() {
1273 return (hasGatheringEvent() ?
getGatheringEvent(true).getDescription()
1277 public void setGatheringEventDescription(String description
) {
1278 getGatheringEvent(true).setDescription(description
);
1283 public TimePeriod
getGatheringPeriod() {
1284 return (hasGatheringEvent() ?
getGatheringEvent(true).getTimeperiod()
1288 public void setGatheringPeriod(TimePeriod timeperiod
) {
1289 getGatheringEvent(true).setTimeperiod(timeperiod
);
1294 public LanguageString
getLocality() {
1295 return (hasGatheringEvent() ?
getGatheringEvent(true).getLocality()
1300 * convienience method for {@link #getLocality()}.
1301 * {@link LanguageString#getText() getText()}
1304 public String
getLocalityText() {
1305 LanguageString locality
= getLocality();
1306 if (locality
!= null) {
1307 return locality
.getText();
1313 * Convenience method for {@link #getLocality()}.
1314 * {@link LanguageString#getLanguage() getLanguage()}
1319 public Language
getLocalityLanguage() {
1320 LanguageString locality
= getLocality();
1321 if (locality
!= null) {
1322 return locality
.getLanguage();
1328 * Sets the locality string in the default language
1332 public void setLocality(String locality
) {
1333 Language language
= Language
.DEFAULT();
1334 setLocality(locality
, language
);
1337 public void setLocality(String locality
, Language language
) {
1338 LanguageString langString
= LanguageString
.NewInstance(locality
, language
);
1339 setLocality(langString
);
1342 public void setLocality(LanguageString locality
) {
1343 getGatheringEvent(true).setLocality(locality
);
1347 * The gathering event will be used for the field object instead of the old
1348 * gathering event.<BR>
1349 * <B>This method will override all gathering values (see below).</B>
1351 * @see #getAbsoluteElevation()
1352 * @see #getAbsoluteElevationError()
1353 * @see #getDistanceToGround()
1354 * @see #getDistanceToWaterSurface()
1355 * @see #getExactLocation()
1356 * @see #getGatheringEventDescription()
1357 * @see #getGatheringPeriod()
1358 * @see #getCollectingAreas()
1359 * @see #getCollectingMethod()
1360 * @see #getLocality()
1361 * @see #getCollector()
1362 * @param gatheringEvent
1364 public void setGatheringEvent(GatheringEvent gatheringEvent
) {
1365 getFieldUnit(true).setGatheringEvent(gatheringEvent
);
1368 public boolean hasGatheringEvent() {
1369 return (getGatheringEvent(false) != null);
1372 public GatheringEvent
innerGatheringEvent() {
1373 return getGatheringEvent(false);
1376 public GatheringEvent
getGatheringEvent(boolean createIfNotExists
) {
1377 if (!hasFieldUnit() && !createIfNotExists
) {
1380 if (createIfNotExists
&& getFieldUnit(true).getGatheringEvent() == null) {
1381 GatheringEvent gatheringEvent
= GatheringEvent
.NewInstance();
1382 getFieldUnit(true).setGatheringEvent(gatheringEvent
);
1384 return getFieldUnit(true).getGatheringEvent();
1387 // ****************** Field Object ************************************/
1390 * Returns true if a field unit exists (even if all attributes are
1391 * empty or <code>null<code>.
1395 public boolean hasFieldObject() {
1396 return this.fieldUnit
!= null;
1401 public String
getEcology() {
1402 return getEcology(Language
.DEFAULT());
1405 public String
getEcology(Language language
) {
1406 LanguageString languageString
= getEcologyAll().get(language
);
1407 return (languageString
== null ?
null : languageString
.getText());
1410 // public String getEcologyPreferred(List<Language> languages){
1411 // LanguageString languageString =
1412 // getEcologyAll().getPreferredLanguageString(languages);
1413 // return languageString.getText();
1416 * Returns a copy of the multilanguage text holding the ecology data.
1418 * @see {@link TextData#getMultilanguageText()}
1422 public Map
<Language
, LanguageString
> getEcologyAll() {
1423 if (ecology
== null) {
1425 ecology
= initializeFieldObjectTextDataWithSupportTest(
1426 Feature
.ECOLOGY(), false, false);
1427 } catch (DerivedUnitFacadeNotSupportedException e
) {
1428 throw new IllegalStateException(notSupportMessage
, e
);
1430 if (ecology
== null) {
1431 return new HashMap
<>();
1434 return ecology
.getMultilanguageText();
1437 public void setEcology(String ecology
) {
1438 setEcology(ecology
, null);
1441 public void setEcology(String ecologyText
, Language language
) {
1442 if (language
== null) {
1443 language
= Language
.DEFAULT();
1445 boolean isEmpty
= StringUtils
.isBlank(ecologyText
);
1446 if (ecology
== null) {
1448 ecology
= initializeFieldObjectTextDataWithSupportTest(
1449 Feature
.ECOLOGY(), !isEmpty
, false);
1450 } catch (DerivedUnitFacadeNotSupportedException e
) {
1451 throw new IllegalStateException(notSupportMessage
, e
);
1454 if (ecology
!= null){
1455 if (ecologyText
== null) {
1456 ecology
.removeText(language
);
1458 ecology
.putText(language
, ecologyText
);
1463 public void removeEcology(Language language
) {
1464 setEcology(null, language
);
1468 * Removes ecology for the default language
1470 public void removeEcology() {
1471 setEcology(null, null);
1474 // plant description
1476 public String
getPlantDescription() {
1477 return getPlantDescription(null);
1480 public String
getPlantDescription(Language language
) {
1481 if (language
== null) {
1482 language
= Language
.DEFAULT();
1484 LanguageString languageString
= getPlantDescriptionAll().get(language
);
1485 return (languageString
== null ?
null : languageString
.getText());
1488 // public String getPlantDescriptionPreferred(List<Language> languages){
1489 // LanguageString languageString =
1490 // getPlantDescriptionAll().getPreferredLanguageString(languages);
1491 // return languageString.getText();
1494 * Returns a copy of the multilanguage text holding the description data.
1496 * @see {@link TextData#getMultilanguageText()}
1500 public Map
<Language
, LanguageString
> getPlantDescriptionAll() {
1501 if (plantDescription
== null) {
1503 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
1504 Feature
.DESCRIPTION(), false, false);
1505 } catch (DerivedUnitFacadeNotSupportedException e
) {
1506 throw new IllegalStateException(notSupportMessage
, e
);
1508 if (plantDescription
== null) {
1509 return new HashMap
<>();
1512 return plantDescription
.getMultilanguageText();
1515 public void setPlantDescription(String plantDescription
) {
1516 setPlantDescription(plantDescription
, null);
1519 public void setPlantDescription(String plantDescriptionText
, Language language
) {
1520 if (language
== null) {
1521 language
= Language
.DEFAULT();
1523 boolean isEmpty
= StringUtils
.isBlank(plantDescriptionText
);
1524 if (plantDescription
== null) {
1526 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
1527 Feature
.DESCRIPTION(), !isEmpty
, false);
1528 } catch (DerivedUnitFacadeNotSupportedException e
) {
1529 throw new IllegalStateException(notSupportMessage
, e
);
1532 if (plantDescription
!= null){
1533 if (plantDescriptionText
== null) {
1534 plantDescription
.removeText(language
);
1536 plantDescription
.putText(language
, plantDescriptionText
);
1541 public void removePlantDescription(Language language
) {
1542 setPlantDescription(null, language
);
1547 public String
getLifeform() {
1548 return getLifeform(Language
.DEFAULT());
1551 public String
getLifeform(Language language
) {
1552 LanguageString languageString
= getLifeformAll().get(language
);
1553 return (languageString
== null ?
null : languageString
.getText());
1556 // public String getLifeformPreferred(List<Language> languages){
1557 // LanguageString languageString =
1558 // getLifeformAll().getPreferredLanguageString(languages);
1559 // return languageString.getText();
1562 * Returns a copy of the multi language text holding the life-form data.
1564 * @see {@link TextData#getMultilanguageText()}
1568 public Map
<Language
, LanguageString
> getLifeformAll() {
1569 if (lifeform
== null) {
1571 lifeform
= initializeFieldObjectTextDataWithSupportTest(
1572 Feature
.LIFEFORM(), false, false);
1573 } catch (DerivedUnitFacadeNotSupportedException e
) {
1574 throw new IllegalStateException(notSupportMessage
, e
);
1576 if (lifeform
== null) {
1577 return new HashMap
<>();
1580 return lifeform
.getMultilanguageText();
1583 public void setLifeform(String lifeform
) {
1584 setLifeform(lifeform
, null);
1587 public void setLifeform(String lifeformText
, Language language
) {
1588 if (language
== null) {
1589 language
= Language
.DEFAULT();
1591 boolean isEmpty
= StringUtils
.isBlank(lifeformText
);
1592 if (lifeform
== null) {
1594 lifeform
= initializeFieldObjectTextDataWithSupportTest(
1595 Feature
.LIFEFORM(), !isEmpty
, false);
1596 } catch (DerivedUnitFacadeNotSupportedException e
) {
1597 throw new IllegalStateException(notSupportMessage
, e
);
1600 if (lifeform
!= null){
1601 if (lifeformText
== null) {
1602 lifeform
.removeText(language
);
1604 lifeform
.putText(language
, lifeformText
);
1609 public void removeLifeform(Language language
) {
1610 setLifeform(null, language
);
1614 * Removes life-form for the default language
1616 public void removeLifeform() {
1617 setLifeform(null, null);
1620 // field object definition
1621 public void addFieldObjectDefinition(String text
, Language language
) {
1622 getFieldUnit(true).putDefinition(language
, text
);
1626 public Map
<Language
, LanguageString
> getFieldObjectDefinition() {
1627 if (!hasFieldUnit()) {
1628 return new HashMap
<>();
1630 return getFieldUnit(true).getDefinition();
1634 public String
getFieldObjectDefinition(Language language
) {
1635 Map
<Language
, LanguageString
> map
= getFieldObjectDefinition();
1636 LanguageString languageString
= (map
== null ?
null : map
.get(language
));
1637 if (languageString
!= null) {
1638 return languageString
.getText();
1644 public void removeFieldObjectDefinition(Language lang
) {
1645 if (hasFieldUnit()) {
1646 getFieldUnit(true).removeDefinition(lang
);
1651 public boolean addFieldObjectMedia(Media media
) {
1653 return addMedia(media
, getFieldUnit(true));
1654 } catch (DerivedUnitFacadeNotSupportedException e
) {
1655 throw new IllegalStateException(notSupportMessage
, e
);
1660 * Returns true, if an image gallery for the field object exists.<BR>
1661 * Returns also <code>true</code> if the image gallery is empty.
1665 public boolean hasFieldObjectImageGallery() {
1666 if (!hasFieldObject()) {
1669 return (getImageGallery(fieldUnit
, false) != null);
1673 public void setFieldObjectImageGallery(SpecimenDescription imageGallery
)
1674 throws DerivedUnitFacadeNotSupportedException
{
1675 SpecimenDescription existingGallery
= getFieldObjectImageGallery(false);
1677 // test attached specimens contain this.derivedUnit
1678 SpecimenOrObservationBase
<?
> facadeFieldUnit
= innerFieldUnit();
1679 testSpecimenInImageGallery(imageGallery
, facadeFieldUnit
);
1681 if (existingGallery
!= null) {
1682 if (existingGallery
!= imageGallery
) {
1683 throw new DerivedUnitFacadeNotSupportedException(
1684 "DerivedUnitFacade does not allow more than one image gallery");
1689 TextData textData
= testImageGallery(imageGallery
);
1690 this.fieldObjectMediaTextData
= textData
;
1695 * Returns the field object image gallery. If no such image gallery exists
1696 * and createIfNotExists is true an new one is created. Otherwise null is
1699 * @param createIfNotExists
1702 public SpecimenDescription
getFieldObjectImageGallery(
1703 boolean createIfNotExists
) {
1706 textData
= initializeFieldObjectTextDataWithSupportTest(
1707 Feature
.IMAGE(), createIfNotExists
, true);
1708 } catch (DerivedUnitFacadeNotSupportedException e
) {
1709 throw new IllegalStateException(notSupportMessage
, e
);
1711 if (textData
!= null) {
1712 return CdmBase
.deproxy(textData
.getInDescription(),
1713 SpecimenDescription
.class);
1720 * Returns the media for the field object.<BR>
1725 public List
<Media
> getFieldObjectMedia() {
1727 List
<Media
> result
= getMediaList(getFieldUnit(false), false);
1728 return result
== null ?
new ArrayList
<>() : result
;
1729 } catch (DerivedUnitFacadeNotSupportedException e
) {
1730 throw new IllegalStateException(notSupportMessage
, e
);
1734 public boolean removeFieldObjectMedia(Media media
) {
1736 return removeMedia(media
, getFieldUnit(false));
1737 } catch (DerivedUnitFacadeNotSupportedException e
) {
1738 throw new IllegalStateException(notSupportMessage
, e
);
1744 public String
getFieldNumber() {
1745 if (!hasFieldUnit()) {
1748 return getFieldUnit(true).getFieldNumber();
1752 public void setFieldNumber(String fieldNumber
) {
1753 getFieldUnit(true).setFieldNumber(fieldNumber
);
1756 // primary collector
1758 public Person
getPrimaryCollector() {
1759 if (!hasFieldUnit()) {
1762 return getFieldUnit(true).getPrimaryCollector();
1766 public void setPrimaryCollector(Person primaryCollector
) {
1767 getFieldUnit(true).setPrimaryCollector(primaryCollector
);
1772 public String
getFieldNotes() {
1773 if (!hasFieldUnit()) {
1776 return getFieldUnit(true).getFieldNotes();
1780 public void setFieldNotes(String fieldNotes
) {
1781 getFieldUnit(true).setFieldNotes(fieldNotes
);
1784 // individual counts
1786 public String
getIndividualCount() {
1787 return (hasFieldUnit() ?
getFieldUnit(true).getIndividualCount() : null);
1790 public void setIndividualCount(String individualCount
) {
1791 getFieldUnit(true).setIndividualCount(individualCount
);
1796 public DefinedTerm
getLifeStage() {
1797 return (hasFieldUnit() ?
getFieldUnit(true).getLifeStage() : null);
1800 public void setLifeStage(DefinedTerm lifeStage
) {
1801 FieldUnit fieldUnit
= getFieldUnit(lifeStage
!= null);
1802 if (fieldUnit
!= null){
1803 fieldUnit
.setLifeStage(lifeStage
);
1809 public DefinedTerm
getSex() {
1810 return (hasFieldUnit() ?
getFieldUnit(true).getSex(): null);
1813 public void setSex(DefinedTerm sex
) {
1814 FieldUnit fieldUnit
= getFieldUnit(sex
!= null);
1815 if (fieldUnit
!= null){
1816 fieldUnit
.setSex(sex
);
1822 public DefinedTerm
getFieldUnitKindOfUnit() {
1823 return (hasFieldUnit() ? fieldUnit
.getKindOfUnit() : null);
1827 public DefinedTerm
getDerivedUnitKindOfUnit() {
1829 return checkDerivedUnit() ? derivedUnit
.getKindOfUnit() : null;
1834 * Sets the kind-of-unit
1837 public void setFieldUnitKindOfUnit(DefinedTerm kindOfUnit
) {
1838 FieldUnit fieldUnit
= getFieldUnit(kindOfUnit
!= null);
1839 if (fieldUnit
!= null){
1840 fieldUnit
.setKindOfUnit(kindOfUnit
);
1844 public void setDerivedUnitKindOfUnit(DefinedTerm kindOfUnit
) {
1847 baseUnit().setKindOfUnit(kindOfUnit
);
1852 public boolean hasFieldUnit() {
1853 return (getFieldUnit(CREATE_NOT
) != null);
1857 * Returns the field unit as an object.
1861 public FieldUnit
innerFieldUnit() {
1862 return getFieldUnit(CREATE_NOT
);
1866 * Returns the field unit as an object.
1870 public FieldUnit
getFieldUnit(boolean createIfNotExists
) {
1871 if (fieldUnit
== null && createIfNotExists
) {
1872 setFieldUnit(FieldUnit
.NewInstance());
1874 return this.fieldUnit
;
1878 public void setFieldUnit(FieldUnit fieldUnit
) {
1879 this.fieldUnit
= fieldUnit
;
1880 if (fieldUnit
!= null){
1881 if (config
.isFirePropertyChangeEvents()){
1882 addNewEventPropagationListener(fieldUnit
);
1884 if (derivedUnit
!= null){
1885 DerivationEvent derivationEvent
= getDerivationEvent(CREATE
);
1886 derivationEvent
.addOriginal(fieldUnit
);
1888 setFieldUnitCacheStrategy();
1892 // ****************** Specimen *******************************************
1896 public void addDerivedUnitDefinition(String text
, Language language
) {
1897 innerDerivedUnit().putDefinition(language
, text
);
1901 public Map
<Language
, LanguageString
> getDerivedUnitDefinitions() {
1902 return ! checkDerivedUnit()?
null : this.derivedUnit
.getDefinition();
1905 public TypedEntityReference
<DerivedUnit
> getDerivedUnitEntityReference() {
1906 return new TypedEntityReference(derivedUnit
.getClass(), derivedUnit
.getUuid());
1909 public TypedEntityReference
<FieldUnit
> getFieldUnitEntityReference() {
1910 if(fieldUnit
== null) {
1913 return new TypedEntityReference(FieldUnit
.class, fieldUnit
.getUuid());
1917 public String
getDerivedUnitDefinition(Language language
) {
1918 if (! checkDerivedUnit()){
1921 Map
<Language
, LanguageString
> languageMap
= derivedUnit
.getDefinition();
1922 LanguageString languageString
= languageMap
.get(language
);
1923 if (languageString
!= null) {
1924 return languageString
.getText();
1930 public void removeDerivedUnitDefinition(Language lang
) {
1932 derivedUnit
.removeDefinition(lang
);
1936 public void addDetermination(DeterminationEvent determination
) {
1937 //TODO implement correct bidirectional mapping in model classes
1938 determination
.setIdentifiedUnit(baseUnit());
1939 baseUnit().addDetermination(determination
);
1943 public DeterminationEvent
getPreferredDetermination() {
1944 return baseUnit().getPreferredDetermination();
1948 * This method returns the preferred determination.
1949 * @see #getOtherDeterminations()
1950 * @see #getDeterminations()
1953 public void setPreferredDetermination(DeterminationEvent newEvent
) {
1954 baseUnit().setPreferredDetermination(newEvent
);
1958 * This method returns all determinations except for the preferred one.
1959 * @see #getPreferredDetermination()
1960 * @see #getDeterminations()
1964 public Set
<DeterminationEvent
> getOtherDeterminations() {
1965 return baseUnit().getOtherDeterminations();
1969 * This method returns all determination events. The preferred one {@link #getPreferredDetermination()}
1970 * and all others {@link #getOtherDeterminations()}.
1974 public Set
<DeterminationEvent
> getDeterminations() {
1975 return baseUnit().getDeterminations();
1978 public void removeDetermination(DeterminationEvent determination
) {
1979 baseUnit().removeDetermination(determination
);
1983 public boolean addDerivedUnitMedia(Media media
) {
1986 return addMedia(media
, derivedUnit
);
1987 } catch (DerivedUnitFacadeNotSupportedException e
) {
1988 throw new IllegalStateException(notSupportMessage
, e
);
1993 * Returns true, if an image gallery exists for the specimen.<BR>
1994 * Returns also <code>true</code> if the image gallery is empty.
1996 public boolean hasDerivedUnitImageGallery() {
1997 return (getImageGallery(derivedUnit
, false) != null);
2000 public SpecimenDescription
getDerivedUnitImageGallery(boolean createIfNotExists
) {
2001 if (!checkDerivedUnit()){
2006 textData
= inititializeTextDataWithSupportTest(Feature
.IMAGE(),
2007 derivedUnit
, createIfNotExists
, true);
2008 } catch (DerivedUnitFacadeNotSupportedException e
) {
2009 throw new IllegalStateException(notSupportMessage
, e
);
2011 if (textData
!= null) {
2012 return CdmBase
.deproxy(textData
.getInDescription(),
2013 SpecimenDescription
.class);
2019 public void setDerivedUnitImageGallery(SpecimenDescription imageGallery
)
2020 throws DerivedUnitFacadeNotSupportedException
{
2022 SpecimenDescription existingGallery
= getDerivedUnitImageGallery(false);
2024 // test attached specimens contain this.derivedUnit
2025 SpecimenOrObservationBase facadeDerivedUnit
= innerDerivedUnit();
2026 testSpecimenInImageGallery(imageGallery
, facadeDerivedUnit
);
2028 if (existingGallery
!= null) {
2029 if (existingGallery
!= imageGallery
) {
2030 throw new DerivedUnitFacadeNotSupportedException(
2031 "DerivedUnitFacade does not allow more than one image gallery");
2036 TextData textData
= testImageGallery(imageGallery
);
2037 this.derivedUnitMediaTextData
= textData
;
2042 * @param imageGallery
2043 * @throws DerivedUnitFacadeNotSupportedException
2045 private void testSpecimenInImageGallery(SpecimenDescription imageGallery
, SpecimenOrObservationBase specimen
)
2046 throws DerivedUnitFacadeNotSupportedException
{
2047 SpecimenOrObservationBase imageGallerySpecimen
= imageGallery
.getDescribedSpecimenOrObservation();
2048 if (imageGallerySpecimen
== null) {
2049 throw new DerivedUnitFacadeNotSupportedException(
2050 "Image Gallery has no Specimen attached. Please attache according specimen or field unit.");
2052 if (! imageGallerySpecimen
.equals(specimen
)) {
2053 throw new DerivedUnitFacadeNotSupportedException(
2054 "Image Gallery has not the facade's field object attached. Please add field object first " +
2055 "to image gallery specimenOrObservation list.");
2060 * Returns the media for the specimen.<BR>
2065 public List
<Media
> getDerivedUnitMedia() {
2066 if (! checkDerivedUnit()){
2067 return new ArrayList
<>();
2070 List
<Media
> result
= getMediaList(derivedUnit
, false);
2071 return result
== null ?
new ArrayList
<>() : result
;
2072 } catch (DerivedUnitFacadeNotSupportedException e
) {
2073 throw new IllegalStateException(notSupportMessage
, e
);
2077 public boolean removeDerivedUnitMedia(Media media
) {
2080 return removeMedia(media
, derivedUnit
);
2081 } catch (DerivedUnitFacadeNotSupportedException e
) {
2082 throw new IllegalStateException(notSupportMessage
, e
);
2088 public String
getAccessionNumber() {
2089 return ! checkDerivedUnit()?
null : derivedUnit
.getAccessionNumber();
2094 * The combination of collection code with accession number, barcode or collection number.
2098 public String
getSpecimenLabel() {
2099 if(checkDerivedUnit()){
2100 if(derivedUnit
.getCacheStrategy() instanceof DerivedUnitFacadeCacheStrategy
){
2101 return ((DerivedUnitFacadeCacheStrategy
)derivedUnit
.getCacheStrategy()).getSpecimenLabel(this);
2108 public void setAccessionNumber(String accessionNumber
) {
2110 derivedUnit
.setAccessionNumber(accessionNumber
);
2114 public String
getCatalogNumber() {
2115 return ! checkDerivedUnit()?
null : derivedUnit
.getCatalogNumber();
2118 public void setCatalogNumber(String catalogNumber
) {
2120 derivedUnit
.setCatalogNumber(catalogNumber
);
2124 public String
getBarcode() {
2125 return ! checkDerivedUnit()?
null : derivedUnit
.getBarcode();
2128 public void setBarcode(String barcode
) {
2130 derivedUnit
.setBarcode(barcode
);
2133 // Preservation Method
2136 * Only supported by specimen and fossils
2138 * @see #DerivedUnitType
2142 public PreservationMethod
getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException
{
2143 if (derivedUnit
!=null && derivedUnit
.getRecordBasis().isPreservedSpecimen()) {
2144 return CdmBase
.deproxy(derivedUnit
, DerivedUnit
.class).getPreservation();
2146 if (this.config
.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2147 throw new MethodNotSupportedByDerivedUnitTypeException(
2148 "A preservation method is only available in derived units of type 'Preserved Specimen' or one of its specializations like 'Fossil Specimen' ");
2156 * Only supported by specimen and fossils
2158 * @see #DerivedUnitType
2161 public void setPreservationMethod(PreservationMethod preservation
)
2162 throws MethodNotSupportedByDerivedUnitTypeException
{
2163 if (derivedUnit
!=null && derivedUnit
.getRecordBasis().isPreservedSpecimen()) {
2164 CdmBase
.deproxy(derivedUnit
, DerivedUnit
.class).setPreservation(preservation
);
2166 if (this.config
.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2167 throw new MethodNotSupportedByDerivedUnitTypeException(
2168 "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
2175 //preferred stable URI #5606
2176 public URI
getPreferredStableUri(){
2177 return baseUnit().getPreferredStableUri();
2179 public void setPreferredStableUri(URI stableUri
){
2180 baseUnit().setPreferredStableUri(stableUri
);
2184 // Stored under name
2186 public TaxonName
getStoredUnder() {
2187 return ! checkDerivedUnit()?
null : derivedUnit
.getStoredUnder();
2190 public void setStoredUnder(TaxonName storedUnder
) {
2192 derivedUnit
.setStoredUnder(storedUnder
);
2196 public String
getTitleCache() {
2197 SpecimenOrObservationBase
<?
> titledUnit
= baseUnit();
2199 if (!titledUnit
.isProtectedTitleCache()) {
2200 // always compute title cache anew as long as there are no property
2201 // change listeners on
2202 // field unit, gathering event etc
2203 titledUnit
.setTitleCache(null, false);
2205 return titledUnit
.getTitleCache();
2208 public boolean isProtectedTitleCache() {
2209 return baseUnit().isProtectedTitleCache();
2212 public void setTitleCache(String titleCache
, boolean isProtected
) {
2213 this.baseUnit().setTitleCache(titleCache
, isProtected
);
2217 * Returns the derived unit itself.
2219 * @return the derived unit
2221 public DerivedUnit
innerDerivedUnit() {
2222 return this.derivedUnit
;
2226 // * Returns the derived unit itself.
2228 // * @return the derived unit
2230 // public DerivedUnit innerDerivedUnit(boolean createIfNotExists) {
2231 // DerivedUnit result = this.derivedUnit;
2232 // if (result == null && createIfNotExists){
2233 // if (this.fieldUnit == null){
2234 // String message = "Field unit must exist to create derived unit.";
2235 // throw new IllegalStateException(message);
2238 // DerivationEvent derivationEvent = getDerivationEvent(true);
2239 // derivationEvent.addOriginal(fieldUnit);
2240 // return this.derivedUnit;
2245 private boolean hasDerivationEvent() {
2246 return getDerivationEvent() == null ?
false : true;
2249 private DerivationEvent
getDerivationEvent() {
2250 return getDerivationEvent(CREATE_NOT
);
2254 * Returns the derivation event. If no derivation event exists and <code>createIfNotExists</code>
2255 * is <code>true</code> a new derivation event is created and returned.
2256 * Otherwise <code>null</code> is returned.
2257 * @param createIfNotExists
2259 private DerivationEvent
getDerivationEvent(boolean createIfNotExists
) {
2260 DerivationEvent result
= null;
2261 if (derivedUnit
!= null){
2262 result
= derivedUnit
.getDerivedFrom();
2266 if (result
== null && createIfNotExists
) {
2267 DerivationEventType type
= null;
2268 if (isAccessioned(derivedUnit
)){
2269 type
= DerivationEventType
.ACCESSIONING();
2272 result
= DerivationEvent
.NewInstance(type
);
2273 derivedUnit
.setDerivedFrom(result
);
2279 * TODO still unclear which classes do definetly require accessioning.
2280 * Only return true for those classes which are clear.
2281 * @param derivedUnit
2284 private boolean isAccessioned(DerivedUnit derivedUnit
) {
2285 if (derivedUnit
.getRecordBasis().equals(SpecimenOrObservationType
.PreservedSpecimen
) ){
2286 return true; //maybe also subtypes should be true
2293 public String
getExsiccatum() throws MethodNotSupportedByDerivedUnitTypeException
{
2295 if (derivedUnit
.getRecordBasis().isPreservedSpecimen()) {
2296 return derivedUnit
.getExsiccatum();
2298 if (this.config
.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2299 throw new MethodNotSupportedByDerivedUnitTypeException(
2300 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2307 public void setExsiccatum(String exsiccatum
) throws Exception
{
2309 if (derivedUnit
.getRecordBasis().isPreservedSpecimen()) {
2310 derivedUnit
.setExsiccatum(exsiccatum
);
2312 if (this.config
.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2313 throw new MethodNotSupportedByDerivedUnitTypeException(
2314 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2322 * Returns the original label information of the derived unit.
2326 public String
getOriginalLabelInfo() {
2327 return ! checkDerivedUnit()?
null : derivedUnit
.getOriginalLabelInfo();
2329 public void setOriginalLabelInfo(String originalLabelInfo
) {
2331 derivedUnit
.setOriginalLabelInfo(originalLabelInfo
);
2335 public void addSource(IdentifiableSource source
) {
2336 this.baseUnit().addSource(source
);
2340 * Creates an {@link IOriginalSource orignal source} or type ,
2341 * adds it to the specimen and returns it.
2344 * @param microReference
2345 * @param originalNameString
2348 public IdentifiableSource
addSource(OriginalSourceType type
, Reference reference
, String microReference
, String originalNameString
) {
2349 IdentifiableSource source
= IdentifiableSource
.NewInstance(type
, null, null, reference
, microReference
);
2350 source
.setOriginalNameString(originalNameString
);
2356 public Set
<IdentifiableSource
> getSources() {
2357 return baseUnit().getSources();
2360 public void removeSource(IdentifiableSource source
) {
2361 this.baseUnit().removeSource(source
);
2364 //*** identifiers ***/
2367 public void addIdentifier(Identifier identifier
) {
2368 this.baseUnit().addIdentifier(identifier
);
2372 public List
<Identifier
> getIdentifiers() {
2373 return baseUnit().getIdentifiers();
2376 public void removeIdentifier(Identifier identifier
) {
2377 this.baseUnit().removeIdentifier(identifier
);
2381 public Set
<Rights
> getRights() {
2382 return baseUnit().getRights();
2386 * @return the collection
2389 public Collection
getCollection() {
2390 return ! checkDerivedUnit()?
null : derivedUnit
.getCollection();
2395 * the collection to set
2397 public void setCollection(Collection collection
) {
2399 derivedUnit
.setCollection(collection
);
2403 public void addAnnotation(Annotation annotation
) {
2404 this.baseUnit().addAnnotation(annotation
);
2408 public void getAnnotations() {
2409 this.baseUnit().getAnnotations();
2412 public void removeAnnotation(Annotation annotation
) {
2413 this.baseUnit().removeAnnotation(annotation
);
2416 // ******************************* Events ***************************
2418 //set of events that were currently fired by this facades field unit
2419 //to avoid recursive fireing of the same event
2420 private final Set
<PropertyChangeEvent
> fireingEvents
= new HashSet
<>();
2425 private void addNewEventPropagationListener(CdmBase listeningObject
) {
2426 //if there is already a listener, don't do anything
2427 for (PropertyChangeListener listener
: this.listeners
.keySet()){
2428 if (listeners
.get(listener
) == listeningObject
){
2432 //create new listener
2433 PropertyChangeListener listener
= new PropertyChangeListener() {
2435 public void propertyChange(PropertyChangeEvent event
) {
2436 if (derivedUnit
!= null){
2437 derivedUnit
.firePropertyChange(event
);
2439 if (! event
.getSource().equals(fieldUnit
) && ! fireingEvents
.contains(event
) ){
2440 fireingEvents
.add(event
);
2441 fieldUnit
.firePropertyChange(event
);
2442 fireingEvents
.remove(event
);
2447 //add listener to listening object and to list of listeners
2448 listeningObject
.addPropertyChangeListener(listener
);
2449 listeners
.put(listener
, listeningObject
);
2452 // **************** Other Collections ********************************
2455 * Creates a duplicate specimen which derives from the same derivation event
2456 * as the facade specimen and adds collection data to it (all data available
2457 * in DerivedUnit and Specimen. Data from SpecimenOrObservationBase and
2458 * above are not yet shared at the moment.
2461 * @param catalogNumber
2462 * @param accessionNumber
2463 * @param storedUnder
2464 * @param preservation
2467 public DerivedUnit
addDuplicate(Collection collection
, String catalogNumber
,
2468 String accessionNumber
, TaxonName storedUnder
, PreservationMethod preservation
){
2470 DerivedUnit duplicate
= DerivedUnit
.NewPreservedSpecimenInstance();
2471 duplicate
.setDerivedFrom(getDerivationEvent(CREATE
));
2472 duplicate
.setCollection(collection
);
2473 duplicate
.setCatalogNumber(catalogNumber
);
2474 duplicate
.setAccessionNumber(accessionNumber
);
2475 duplicate
.setStoredUnder(storedUnder
);
2476 duplicate
.setPreservation(preservation
);
2480 public void addDuplicate(DerivedUnit duplicateSpecimen
) {
2482 getDerivationEvent(CREATE
).addDerivative(duplicateSpecimen
);
2486 public Set
<DerivedUnit
> getDuplicates() {
2487 if (! checkDerivedUnit()){
2488 return new HashSet
<>();
2490 Set
<DerivedUnit
> result
= new HashSet
<>();
2491 if (hasDerivationEvent()) {
2492 for (DerivedUnit derivedUnit
: getDerivationEvent(CREATE
)
2493 .getDerivatives()) {
2494 if (derivedUnit
.isInstanceOf(DerivedUnit
.class)
2495 && !derivedUnit
.equals(this.derivedUnit
)) {
2496 result
.add(CdmBase
.deproxy(derivedUnit
, DerivedUnit
.class));
2503 public void removeDuplicate(DerivedUnit duplicateSpecimen
) {
2505 if (hasDerivationEvent()) {
2506 getDerivationEvent(CREATE
).removeDerivative(duplicateSpecimen
);
2510 public SpecimenOrObservationBase
<?
> baseUnit(){
2511 if(derivedUnit
!=null){
2514 else if(fieldUnit
!=null){
2518 throw new IllegalStateException("A DerivedUnitFacade must always have either a field unit or a derived unit");
2523 * @return true if <code>this.derivedUnit</code> exists
2525 private boolean checkDerivedUnit() {
2526 if (derivedUnit
== null){
2533 private void testDerivedUnit() /* throws MethodNotSupportedByDerivedUnitTypeException */ {
2534 if (derivedUnit
== null){
2535 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 unit");
2539 public void setType(SpecimenOrObservationType type
) {
2541 throw new IllegalArgumentException("The type of a specimen or observation may not be null");
2543 SpecimenOrObservationBase
<?
> baseUnit
= baseUnit();
2544 if(baseUnit
.isInstanceOf(FieldUnit
.class) && !type
.isFieldUnit()){
2545 throw new IllegalArgumentException("A FieldUnit may only be of type FieldUnit") ;
2547 else if(baseUnit
.isInstanceOf(DerivedUnit
.class) && type
.isFieldUnit()){
2548 throw new IllegalArgumentException("A derived unit may not be of type FieldUnit") ;
2550 baseUnit
.setRecordBasis(type
);
2553 public SpecimenOrObservationType
getType() {
2554 return baseUnit().getRecordBasis();
2558 * Closes this facade. As a minimum this method removes all listeners created by this facade from their
2559 * listening objects.
2561 public void close(){
2562 for (PropertyChangeListener listener
: this.listeners
.keySet()){
2563 CdmBase listeningObject
= listeners
.get(listener
);
2564 listeningObject
.removePropertyChangeListener(listener
);
2572 * First checks the inner field unit for the publish flag. If set to <code>true</code>
2573 * then <code>true</code> is returned. If the field unit is <code>null</code> the inner derived unit
2575 * @return <code>true</code> if this facade can be published
2577 public boolean isPublish(){
2578 if(fieldUnit
!=null){
2579 return fieldUnit
.isPublish();
2581 if(derivedUnit
!=null){
2582 return derivedUnit
.isPublish();