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
.commons
.lang
.StringUtils
;
25 import org
.apache
.log4j
.Logger
;
27 import eu
.etaxonomy
.cdm
.api
.service
.IOccurrenceService
;
28 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
29 import eu
.etaxonomy
.cdm
.common
.UTF8
;
30 import eu
.etaxonomy
.cdm
.model
.agent
.AgentBase
;
31 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
32 import eu
.etaxonomy
.cdm
.model
.common
.Annotation
;
33 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
34 import eu
.etaxonomy
.cdm
.model
.common
.DefinedTerm
;
35 import eu
.etaxonomy
.cdm
.model
.common
.IOriginalSource
;
36 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
37 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
38 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
39 import eu
.etaxonomy
.cdm
.model
.common
.OriginalSourceType
;
40 import eu
.etaxonomy
.cdm
.model
.common
.TimePeriod
;
41 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
42 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
43 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
44 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
45 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
46 import eu
.etaxonomy
.cdm
.model
.location
.Point
;
47 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
48 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
49 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
50 import eu
.etaxonomy
.cdm
.model
.occurrence
.Collection
;
51 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEvent
;
52 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEventType
;
53 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
54 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
55 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldUnit
;
56 import eu
.etaxonomy
.cdm
.model
.occurrence
.GatheringEvent
;
57 import eu
.etaxonomy
.cdm
.model
.occurrence
.PreservationMethod
;
58 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
59 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationType
;
60 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
63 * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
64 * a specimen based view. It does not support all functionality available in the
65 * occurrence package.<BR>
66 * The most significant restriction is that a specimen may derive only from one
67 * direct derivation event and there must be only one field unit
68 * (gathering event) it derives from.<BR>
73 public class DerivedUnitFacade
{
74 private static final String METER
= "m";
76 @SuppressWarnings("unused")
77 private static final Logger logger
= Logger
.getLogger(DerivedUnitFacade
.class);
79 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 ";
81 private static final boolean CREATE
= true;
82 private static final boolean CREATE_NOT
= false;
84 private final DerivedUnitFacadeConfigurator config
;
86 private final Map
<PropertyChangeListener
, CdmBase
> listeners
= new HashMap
<PropertyChangeListener
, CdmBase
>();
88 private FieldUnit fieldUnit
;
90 private final DerivedUnit derivedUnit
;
92 // media - the text data holding the media
93 private TextData derivedUnitMediaTextData
;
94 private TextData fieldObjectMediaTextData
;
96 private TextData ecology
;
97 private TextData plantDescription
;
100 * Creates a derived unit facade for a new derived unit of type
106 public static DerivedUnitFacade
NewInstance(SpecimenOrObservationType type
) {
107 return new DerivedUnitFacade(type
, null, null);
111 * Creates a derived unit facade for a new derived unit of type
117 public static DerivedUnitFacade
NewInstance(SpecimenOrObservationType type
, FieldUnit fieldUnit
) {
118 return new DerivedUnitFacade(type
, fieldUnit
, null);
122 * Creates a derived unit facade for a new derived unit of type
126 * @param fieldUnit the field unit to use
127 * @param config the facade configurator to use
128 * //TODO are there any ambiguities to solve with defining a field unit or a configurator
131 public static DerivedUnitFacade
NewInstance(SpecimenOrObservationType type
, FieldUnit fieldUnit
, DerivedUnitFacadeConfigurator config
) {
132 return new DerivedUnitFacade(type
, fieldUnit
, config
);
137 * Creates a derived unit facade for a given derived unit using the default
142 * @throws DerivedUnitFacadeNotSupportedException
144 public static DerivedUnitFacade
NewInstance(DerivedUnit derivedUnit
)
145 throws DerivedUnitFacadeNotSupportedException
{
146 return new DerivedUnitFacade(derivedUnit
, null);
149 public static DerivedUnitFacade
NewInstance(DerivedUnit derivedUnit
,
150 DerivedUnitFacadeConfigurator config
)
151 throws DerivedUnitFacadeNotSupportedException
{
152 return new DerivedUnitFacade(derivedUnit
, config
);
155 // ****************** CONSTRUCTOR ******************************************
157 private DerivedUnitFacade(SpecimenOrObservationType type
, FieldUnit fieldUnit
, DerivedUnitFacadeConfigurator config
) {
159 config
= DerivedUnitFacadeConfigurator
.NewInstance();
161 this.config
= config
;
163 derivedUnit
= getNewDerivedUnitInstance(type
);
164 setFieldUnit(fieldUnit
);
165 if (derivedUnit
!= null){
168 setFieldUnitCacheStrategy();
172 private DerivedUnit
getNewDerivedUnitInstance( SpecimenOrObservationType type
) {
173 if (type
.isFieldUnit()){
175 }else if(type
.isAnyDerivedUnit()){
176 return DerivedUnit
.NewInstance(type
);
178 String message
= "Unknown specimen or observation type %s";
179 message
= String
.format(message
, type
.getMessage());
180 throw new IllegalStateException(message
);
184 private DerivedUnitFacade(DerivedUnit derivedUnit
, DerivedUnitFacadeConfigurator config
)
185 throws DerivedUnitFacadeNotSupportedException
{
187 if (config
== null) {
188 config
= DerivedUnitFacadeConfigurator
.NewInstance();
190 this.config
= config
;
193 this.derivedUnit
= derivedUnit
;
196 if (this.derivedUnit
.getDerivedFrom() != null) {
197 DerivationEvent derivationEvent
= getDerivationEvent(CREATE
);
199 Set
<FieldUnit
> fieldOriginals
= getFieldUnitOriginals(derivationEvent
, null);
200 if (fieldOriginals
.size() > 1) {
201 throw new DerivedUnitFacadeNotSupportedException(
202 "Specimen must not have more than 1 derivation event");
203 } else if (fieldOriginals
.size() == 0) {
204 // fieldUnit = FieldUnit.NewInstance();
205 } else if (fieldOriginals
.size() == 1) {
206 fieldUnit
= fieldOriginals
.iterator().next();
208 // getInitializedFieldUnit(fieldUnit);
209 if (config
.isFirePropertyChangeEvents()){
210 addNewEventPropagationListener(fieldUnit
);
213 throw new IllegalStateException("Illegal state");
217 this.derivedUnitMediaTextData
= inititializeTextDataWithSupportTest(Feature
.IMAGE(), this.derivedUnit
, false, true);
219 fieldObjectMediaTextData
= initializeFieldObjectTextDataWithSupportTest(Feature
.IMAGE(), false, true);
222 //direct media have been removed from specimenorobservationbase #3597
223 // // handle derivedUnit.getMedia()
224 // if (derivedUnit.getMedia().size() > 0) {
225 // // TODO better changed model here to allow only one place for images
226 // if (this.config.isMoveDerivedUnitMediaToGallery()) {
227 // Set<Media> mediaSet = derivedUnit.getMedia();
228 // for (Media media : mediaSet) {
229 // this.addDerivedUnitMedia(media);
231 // mediaSet.removeAll(getDerivedUnitMedia());
233 // throw new DerivedUnitFacadeNotSupportedException(
234 // "Specimen may not have direct media. Only (one) image gallery is allowed");
238 // // handle fieldUnit.getMedia()
239 // if (fieldUnit != null && fieldUnit.getMedia() != null
240 // && fieldUnit.getMedia().size() > 0) {
241 // // TODO better changed model here to allow only one place for images
242 // if (this.config.isMoveFieldObjectMediaToGallery()) {
243 // Set<Media> mediaSet = fieldUnit.getMedia();
244 // for (Media media : mediaSet) {
245 // this.addFieldObjectMedia(media);
247 // mediaSet.removeAll(getFieldObjectMedia());
249 // throw new DerivedUnitFacadeNotSupportedException(
250 // "Field object may not have direct media. Only (one) image gallery is allowed");
254 // test if descriptions are supported
255 ecology
= initializeFieldObjectTextDataWithSupportTest(
256 Feature
.ECOLOGY(), false, false);
257 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
258 Feature
.DESCRIPTION(), false, false);
264 private DerivedUnit
getInitializedDerivedUnit(
265 DerivedUnit derivedUnit
) {
266 IOccurrenceService occurrenceService
= this.config
267 .getOccurrenceService();
268 if (occurrenceService
== null) {
271 List
<String
> propertyPaths
= this.config
.getPropertyPaths();
272 if (propertyPaths
== null) {
275 propertyPaths
= getDerivedUnitPropertyPaths(propertyPaths
);
276 DerivedUnit result
= (DerivedUnit
) occurrenceService
.load(
277 derivedUnit
.getUuid(), propertyPaths
);
282 * Initializes the derived unit according to the configuartions property
283 * path. If the property path is <code>null</code> or no occurrence service
284 * is given the returned object is the same as the input parameter.
289 private FieldUnit
getInitializedFieldUnit(FieldUnit fieldUnit
) {
290 IOccurrenceService occurrenceService
= this.config
291 .getOccurrenceService();
292 if (occurrenceService
== null) {
295 List
<String
> propertyPaths
= this.config
.getPropertyPaths();
296 if (propertyPaths
== null) {
299 propertyPaths
= getFieldObjectPropertyPaths(propertyPaths
);
300 FieldUnit result
= (FieldUnit
) occurrenceService
.load(
301 fieldUnit
.getUuid(), propertyPaths
);
306 * Transforms the property paths in a way that the facade is handled just
307 * like an ordinary CdmBase object.<BR>
308 * E.g. a property path "collectinAreas" will be translated into
309 * gatheringEvent.collectingAreas
311 * @param propertyPaths
314 private List
<String
> getFieldObjectPropertyPaths(List
<String
> propertyPaths
) {
315 List
<String
> result
= new ArrayList
<String
>();
316 for (String facadePath
: propertyPaths
) {
317 // collecting areas (named area)
318 if (facadePath
.startsWith("collectingAreas")) {
319 facadePath
= "gatheringEvent." + facadePath
;
320 result
.add(facadePath
);
322 // collector (agentBase)
323 else if (facadePath
.startsWith("collector")) {
324 facadePath
= facadePath
.replace("collector",
325 "gatheringEvent.actor");
326 result
.add(facadePath
);
328 // exactLocation (agentBase)
329 else if (facadePath
.startsWith("exactLocation")) {
330 facadePath
= "gatheringEvent." + facadePath
;
331 result
.add(facadePath
);
333 // gatheringPeriod (TimePeriod)
334 else if (facadePath
.startsWith("gatheringPeriod")) {
335 facadePath
= facadePath
.replace("gatheringPeriod",
336 "gatheringEvent.timeperiod");
337 result
.add(facadePath
);
339 // (locality/ localityLanguage , LanguageString)
340 else if (facadePath
.startsWith("locality")) {
341 facadePath
= "gatheringEvent." + facadePath
;
342 result
.add(facadePath
);
345 // *********** FIELD OBJECT ************
346 // fieldObjectDefinitions (Map<language, languageString)
347 else if (facadePath
.startsWith("fieldObjectDefinitions")) {
348 // TODO or definition ???
349 facadePath
= facadePath
.replace("fieldObjectDefinitions",
351 result
.add(facadePath
);
353 // fieldObjectMedia (Media)
354 else if (facadePath
.startsWith("fieldObjectMedia")) {
356 facadePath
= facadePath
.replace("fieldObjectMedia",
357 "descriptions.elements.media");
358 result
.add(facadePath
);
361 // Gathering Event will always be added
362 result
.add("gatheringEvent");
367 * Gathering Event ==================== - gatheringEvent
370 * Field Object ================= - ecology/ ecologyAll (String) ??? -
371 * plant description (like ecology)
373 * - fieldObjectImageGallery (SpecimenDescription) - is automatically
374 * initialized via fieldObjectMedia
381 * Transforms the property paths in a way that the facade is handled just
382 * like an ordinary CdmBase object.<BR>
383 * E.g. a property path "collectinAreas" will be translated into
384 * gatheringEvent.collectingAreas
386 * Not needed (?) as the facade works with REST service property paths
387 * without using this method.
389 * @param propertyPaths
392 private List
<String
> getDerivedUnitPropertyPaths(List
<String
> propertyPaths
) {
393 List
<String
> result
= new ArrayList
<String
>();
394 for (String facadePath
: propertyPaths
) {
395 // determinations (DeterminationEvent)
396 if (facadePath
.startsWith("determinations")) {
397 facadePath
= "" + facadePath
; // no change
398 result
.add(facadePath
);
400 // storedUnder (TaxonNameBase)
401 else if (facadePath
.startsWith("storedUnder")) {
402 facadePath
= "" + facadePath
; // no change
403 result
.add(facadePath
);
405 // sources (IdentifiableSource)
406 else if (facadePath
.startsWith("sources")) {
407 facadePath
= "" + facadePath
; // no change
408 result
.add(facadePath
);
410 // collection (Collection)
411 else if (facadePath
.startsWith("collection")) {
412 facadePath
= "" + facadePath
; // no change
413 result
.add(facadePath
);
415 // (locality/ localityLanguage , LanguageString)
416 else if (facadePath
.startsWith("locality")) {
417 facadePath
= "gatheringEvent." + facadePath
;
418 result
.add(facadePath
);
421 // *********** FIELD OBJECT ************
422 // derivedUnitDefinitions (Map<language, languageString)
423 else if (facadePath
.startsWith("derivedUnitDefinitions")) {
424 // TODO or definition ???
425 facadePath
= facadePath
.replace("derivedUnitDefinitions",
427 result
.add(facadePath
);
430 // derivedUnitMedia (Media)
431 else if (facadePath
.startsWith("derivedUnitMedia")) {
433 facadePath
= facadePath
.replace("derivedUnitMedia",
434 "descriptions.elements.media");
435 result
.add(facadePath
);
441 * //TODO Derived Unit =====================
443 * - derivedUnitImageGallery (SpecimenDescription) - is automatically
444 * initialized via derivedUnitMedia
446 * - derivationEvent (DerivationEvent) - will always be initialized -
447 * duplicates (??? Specimen???) ???
456 private void setCacheStrategy() {
457 if (derivedUnit
== null) {
458 throw new NullPointerException(
459 "Facade's derviedUnit must not be null to set cache strategy");
461 derivedUnit
.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
462 setFieldUnitCacheStrategy();
466 private void setFieldUnitCacheStrategy() {
467 if (this.hasFieldObject()){
468 DerivedUnitFacadeFieldUnitCacheStrategy strategy
= new DerivedUnitFacadeFieldUnitCacheStrategy();
469 this.fieldUnit
.setCacheStrategy(strategy
);
475 * @param createIfNotExists
476 * @param isImageGallery
478 * @throws DerivedUnitFacadeNotSupportedException
480 private TextData
initializeFieldObjectTextDataWithSupportTest(
481 Feature feature
, boolean createIfNotExists
, boolean isImageGallery
)
482 throws DerivedUnitFacadeNotSupportedException
{
484 FieldUnit fieldObject
= getFieldUnit(createIfNotExists
);
485 if (fieldObject
== null) {
488 return inititializeTextDataWithSupportTest(feature
, fieldObject
,
489 createIfNotExists
, isImageGallery
);
495 * @param createIfNotExists
496 * @param isImageGallery
498 * @throws DerivedUnitFacadeNotSupportedException
500 private TextData
inititializeTextDataWithSupportTest(Feature feature
,
501 SpecimenOrObservationBase specimen
, boolean createIfNotExists
,
502 boolean isImageGallery
)
503 throws DerivedUnitFacadeNotSupportedException
{
504 if (feature
== null) {
507 TextData textData
= null;
508 if (createIfNotExists
) {
509 textData
= TextData
.NewInstance(feature
);
512 Set
<SpecimenDescription
> descriptions
;
513 if (isImageGallery
) {
514 descriptions
= specimen
.getSpecimenDescriptionImageGallery();
516 descriptions
= specimen
.getSpecimenDescriptions(false);
518 // no description exists yet for this specimen
519 if (descriptions
.size() == 0) {
520 if (createIfNotExists
) {
521 SpecimenDescription newSpecimenDescription
= SpecimenDescription
522 .NewInstance(specimen
);
523 newSpecimenDescription
.addElement(textData
);
524 newSpecimenDescription
.setImageGallery(isImageGallery
);
530 // description already exists
531 Set
<DescriptionElementBase
> existingTextData
= new HashSet
<DescriptionElementBase
>();
532 for (SpecimenDescription description
: descriptions
) {
533 // collect all existing text data
534 for (DescriptionElementBase element
: description
.getElements()) {
535 if (element
.isInstanceOf(TextData
.class)
536 && (feature
.equals(element
.getFeature()) || isImageGallery
)) {
537 existingTextData
.add(element
);
541 // use existing text data if exactly one exists
542 if (existingTextData
.size() > 1) {
543 throw new DerivedUnitFacadeNotSupportedException(
544 "Specimen facade does not support more than one description text data of type "
545 + feature
.getLabel());
547 } else if (existingTextData
.size() == 1) {
548 return CdmBase
.deproxy(existingTextData
.iterator().next(),
551 if (createIfNotExists
) {
552 SpecimenDescription description
= descriptions
.iterator()
554 description
.addElement(textData
);
561 * Tests if a given image gallery is supported by the derived unit facade.
562 * It returns the only text data attached to the given image gallery. If the
563 * given image gallery does not have text data attached, it is created and
566 * @param imageGallery
568 * @throws DerivedUnitFacadeNotSupportedException
570 private TextData
testImageGallery(SpecimenDescription imageGallery
)
571 throws DerivedUnitFacadeNotSupportedException
{
572 if (imageGallery
.isImageGallery() == false) {
573 throw new DerivedUnitFacadeNotSupportedException(
574 "Image gallery needs to have image gallery flag set");
576 if (imageGallery
.getElements().size() > 1) {
577 throw new DerivedUnitFacadeNotSupportedException(
578 "Image gallery must not have more then one description element");
581 if (imageGallery
.getElements().size() == 0) {
582 textData
= TextData
.NewInstance(Feature
.IMAGE());
583 imageGallery
.addElement(textData
);
585 if (!imageGallery
.getElements().iterator().next()
586 .isInstanceOf(TextData
.class)) {
587 throw new DerivedUnitFacadeNotSupportedException(
588 "Image gallery must only have TextData as element");
590 textData
= CdmBase
.deproxy(imageGallery
.getElements()
591 .iterator().next(), TextData
.class);
597 // ************************** METHODS
598 // *****************************************
600 private TextData
getDerivedUnitImageGalleryTextData(
601 boolean createIfNotExists
)
602 throws DerivedUnitFacadeNotSupportedException
{
603 if (this.derivedUnitMediaTextData
== null && createIfNotExists
) {
604 this.derivedUnitMediaTextData
= getImageGalleryTextData(
605 derivedUnit
, "Specimen");
607 return this.derivedUnitMediaTextData
;
610 private TextData
getObservationImageGalleryTextData(
611 boolean createIfNotExists
)
612 throws DerivedUnitFacadeNotSupportedException
{
613 if (this.fieldObjectMediaTextData
== null && createIfNotExists
) {
614 this.fieldObjectMediaTextData
= getImageGalleryTextData(fieldUnit
, "Field unit");
616 return this.fieldObjectMediaTextData
;
620 * @param derivationEvent
622 * @throws DerivedUnitFacadeNotSupportedException
624 private Set
<FieldUnit
> getFieldUnitOriginals(
625 DerivationEvent derivationEvent
,
626 Set
<SpecimenOrObservationBase
> recursionAvoidSet
)
627 throws DerivedUnitFacadeNotSupportedException
{
628 if (recursionAvoidSet
== null) {
629 recursionAvoidSet
= new HashSet
<SpecimenOrObservationBase
>();
631 Set
<FieldUnit
> result
= new HashSet
<FieldUnit
>();
632 Set
<SpecimenOrObservationBase
> originals
= derivationEvent
.getOriginals();
633 for (SpecimenOrObservationBase original
: originals
) {
634 if (original
.isInstanceOf(FieldUnit
.class)) {
635 result
.add(CdmBase
.deproxy(original
, FieldUnit
.class));
636 } else if (original
.isInstanceOf(DerivedUnit
.class)) {
637 // if specimen has already been tested exclude it from further
639 if (recursionAvoidSet
.contains(original
)) {
642 DerivedUnit derivedUnit
= CdmBase
.deproxy(original
,
644 DerivationEvent originalDerivation
= derivedUnit
.getDerivedFrom();
645 // Set<DerivationEvent> derivationEvents =
646 // original.getDerivationEvents();
647 // for (DerivationEvent originalDerivation : derivationEvents){
648 Set
<FieldUnit
> fieldUnits
= getFieldUnitOriginals(
649 originalDerivation
, recursionAvoidSet
);
650 result
.addAll(fieldUnits
);
653 throw new DerivedUnitFacadeNotSupportedException(
654 "Unhandled specimen or observation base type: "
655 + original
.getClass().getName());
662 // *********** MEDIA METHODS ******************************
665 // * Returns the media list for a specimen. Throws an exception if the
666 // existing specimen descriptions
667 // * are not supported by this facade.
668 // * @param specimen the specimen the media belongs to
669 // * @param specimenExceptionText text describing the specimen for exception
672 // * @throws DerivedUnitFacadeNotSupportedException
674 // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase
675 // specimen, String specimenExceptionText) throws
676 // DerivedUnitFacadeNotSupportedException{
677 // List<Media> result;
678 // SpecimenDescription imageGallery =
679 // getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
680 // TextData textData = getImageTextDataWithSupportTest(imageGallery,
681 // specimenExceptionText);
682 // result = textData.getMedia();
687 * Returns the media list for a specimen. Throws an exception if the
688 * existing specimen descriptions are not supported by this facade.
691 * the specimen the media belongs to
692 * @param specimenExceptionText
693 * text describing the specimen for exception messages
695 * @throws DerivedUnitFacadeNotSupportedException
697 private TextData
getImageGalleryTextData(SpecimenOrObservationBase specimen
, String specimenExceptionText
)
698 throws DerivedUnitFacadeNotSupportedException
{
700 SpecimenDescription imageGallery
= getImageGalleryWithSupportTest(
701 specimen
, specimenExceptionText
, true);
702 result
= getImageTextDataWithSupportTest(imageGallery
,
703 specimenExceptionText
);
708 * Returns the image gallery of the according specimen. Throws an exception
709 * if the attached image gallerie(s) are not supported by this facade. If no
710 * image gallery exists a new one is created if
711 * <code>createNewIfNotExists</code> is true and if specimen is not
715 * @param specimenText
716 * @param createNewIfNotExists
718 * @throws DerivedUnitFacadeNotSupportedException
720 private SpecimenDescription
getImageGalleryWithSupportTest(
721 SpecimenOrObservationBase
<?
> specimen
, String specimenText
,
722 boolean createNewIfNotExists
)
723 throws DerivedUnitFacadeNotSupportedException
{
724 if (specimen
== null) {
727 SpecimenDescription imageGallery
;
728 if (hasMultipleImageGalleries(specimen
)) {
729 throw new DerivedUnitFacadeNotSupportedException(specimenText
730 + " must not have more than 1 image gallery");
732 imageGallery
= getImageGallery(specimen
, createNewIfNotExists
);
733 getImageTextDataWithSupportTest(imageGallery
, specimenText
);
739 * Returns the media holding text data element of the image gallery. Throws
740 * an exception if multiple such text data already exist. Creates a new text
741 * data if none exists and adds it to the image gallery. If image gallery is
742 * <code>null</code> nothing happens.
744 * @param imageGallery
747 * @throws DerivedUnitFacadeNotSupportedException
749 private TextData
getImageTextDataWithSupportTest(
750 SpecimenDescription imageGallery
, String specimenText
)
751 throws DerivedUnitFacadeNotSupportedException
{
752 if (imageGallery
== null) {
755 TextData textData
= null;
756 for (DescriptionElementBase element
: imageGallery
.getElements()) {
757 if (element
.isInstanceOf(TextData
.class)
758 && element
.getFeature().equals(Feature
.IMAGE())) {
759 if (textData
!= null) {
760 throw new DerivedUnitFacadeNotSupportedException(
762 + " must not have more than 1 image text data element in image gallery");
764 textData
= CdmBase
.deproxy(element
, TextData
.class);
767 if (textData
== null) {
768 textData
= TextData
.NewInstance(Feature
.IMAGE());
769 imageGallery
.addElement(textData
);
775 * Checks, if a specimen belongs to more than one description that is an
781 private boolean hasMultipleImageGalleries(
782 SpecimenOrObservationBase
<?
> derivedUnit
) {
784 Set
<SpecimenDescription
> descriptions
= derivedUnit
785 .getSpecimenDescriptions();
786 for (SpecimenDescription description
: descriptions
) {
787 if (description
.isImageGallery()) {
795 * Returns the image gallery for a specimen. If there are multiple specimen
796 * descriptions marked as image galleries an arbitrary one is chosen. If no
797 * image gallery exists, a new one is created if
798 * <code>createNewIfNotExists</code> is <code>true</code>.<Br>
799 * If specimen is <code>null</code> a null pointer exception is thrown.
801 * @param createNewIfNotExists
804 private SpecimenDescription
getImageGallery(SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
) {
805 SpecimenDescription result
= null;
806 Set
<SpecimenDescription
> descriptions
= specimen
.getSpecimenDescriptions();
807 for (SpecimenDescription description
: descriptions
) {
808 if (description
.isImageGallery()) {
809 result
= description
;
813 if (result
== null && createIfNotExists
) {
814 result
= SpecimenDescription
.NewInstance(specimen
);
815 result
.setImageGallery(true);
821 * Adds a media to the specimens image gallery. If media is
822 * <code>null</code> nothing happens.
826 * @return true if media is not null (as specified by
827 * {@link java.util.Collection#add(Object) Collection.add(E e)}
828 * @throws DerivedUnitFacadeNotSupportedException
830 private boolean addMedia(Media media
, SpecimenOrObservationBase
<?
> specimen
) throws DerivedUnitFacadeNotSupportedException
{
832 List
<Media
> mediaList
= getMediaList(specimen
, true);
833 if (! mediaList
.contains(media
)){
834 return mediaList
.add(media
);
844 * Removes a media from the specimens image gallery.
848 * @return true if an element was removed as a result of this call (as
849 * specified by {@link java.util.Collection#remove(Object)
850 * Collection.remove(E e)}
851 * @throws DerivedUnitFacadeNotSupportedException
853 private boolean removeMedia(Media media
,
854 SpecimenOrObservationBase
<?
> specimen
)
855 throws DerivedUnitFacadeNotSupportedException
{
856 List
<Media
> mediaList
= getMediaList(specimen
, true);
857 return mediaList
== null ?
null : mediaList
.remove(media
);
860 private List
<Media
> getMediaList(SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
)
861 throws DerivedUnitFacadeNotSupportedException
{
862 TextData textData
= getMediaTextData(specimen
, createIfNotExists
);
863 return textData
== null ?
null : textData
.getMedia();
867 * Returns the one media list of a specimen which is part of the only image
868 * gallery that this specimen is part of.<BR>
869 * If these conditions are not hold an exception is thrwon.
873 * @throws DerivedUnitFacadeNotSupportedException
875 // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)
876 // throws DerivedUnitFacadeNotSupportedException {
877 // if (specimen == null){
880 // if (specimen == this.derivedUnit){
881 // return getDerivedUnitImageGalleryMedia();
882 // }else if (specimen == this.fieldUnit){
883 // return getObservationImageGalleryTextData();
885 // return getImageGalleryMedia(specimen, "Undefined specimen ");
890 * Returns the one media list of a specimen which is part of the only image
891 * gallery that this specimen is part of.<BR>
892 * If these conditions are not hold an exception is thrwon.
896 * @throws DerivedUnitFacadeNotSupportedException
898 private TextData
getMediaTextData(SpecimenOrObservationBase
<?
> specimen
,
899 boolean createIfNotExists
)
900 throws DerivedUnitFacadeNotSupportedException
{
901 if (specimen
== null) {
904 if (specimen
== this.derivedUnit
) {
905 return getDerivedUnitImageGalleryTextData(createIfNotExists
);
906 } else if (specimen
== this.fieldUnit
) {
907 return getObservationImageGalleryTextData(createIfNotExists
);
909 return getImageGalleryTextData(specimen
, "Undefined specimen ");
913 // ****************** GETTER / SETTER / ADDER / REMOVER
914 // ***********************/
916 // ****************** Gathering Event *********************************/
920 public NamedArea
getCountry() {
921 return (hasGatheringEvent() ?
getGatheringEvent(true).getCountry()
925 public void setCountry(NamedArea country
) {
926 getGatheringEvent(true).setCountry(country
);
930 public void addCollectingArea(NamedArea area
) {
931 getGatheringEvent(true).addCollectingArea(area
);
934 public void addCollectingAreas(java
.util
.Collection
<NamedArea
> areas
) {
935 for (NamedArea area
: areas
) {
936 getGatheringEvent(true).addCollectingArea(area
);
941 public Set
<NamedArea
> getCollectingAreas() {
942 return (hasGatheringEvent() ?
getGatheringEvent(true)
943 .getCollectingAreas() : null);
946 public void removeCollectingArea(NamedArea area
) {
947 if (hasGatheringEvent()) {
948 getGatheringEvent(true).removeCollectingArea(area
);
952 static final String ALTITUDE_POSTFIX
= " m";
955 * Returns the correctly formatted <code>absolute elevation</code> information.
956 * If absoluteElevationText is set, this will be returned,
957 * otherwise we absoluteElevation will be returned, followed by absoluteElevationMax
958 * if existing, separated by " - "
962 public String
absoluteElevationToString() {
963 if (! hasGatheringEvent()){
966 GatheringEvent ev
= getGatheringEvent(true);
967 if (StringUtils
.isNotBlank(ev
.getAbsoluteElevationText())){
968 return ev
.getAbsoluteElevationText();
970 String text
= ev
.getAbsoluteElevationText();
971 Integer min
= getAbsoluteElevation();
972 Integer max
= getAbsoluteElevationMaximum();
973 return distanceString(min
, max
, text
, METER
);
980 * meter above/below sea level of the surface
982 * @see #getAbsoluteElevationError()
983 * @see #getAbsoluteElevationRange()
986 public Integer
getAbsoluteElevation() {
987 return (hasGatheringEvent() ?
getGatheringEvent(true).getAbsoluteElevation() : null);
990 public void setAbsoluteElevation(Integer absoluteElevation
) {
991 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation
);
994 public void setAbsoluteElevationMax(Integer absoluteElevationMax
) {
995 getGatheringEvent(true).setAbsoluteElevationMax(absoluteElevationMax
);
998 public void setAbsoluteElevationText(String absoluteElevationText
) {
999 getGatheringEvent(true).setAbsoluteElevationText(absoluteElevationText
);
1003 * @see #getAbsoluteElevation()
1004 * @see #getAbsoluteElevationError()
1005 * @see #setAbsoluteElevationRange(Integer, Integer)
1006 * @see #getAbsoluteElevationMinimum()
1009 public Integer
getAbsoluteElevationMaximum() {
1010 if (!hasGatheringEvent()) {
1013 return getGatheringEvent(true).getAbsoluteElevationMax();
1018 * @see #getAbsoluteElevation()
1019 * @see #getAbsoluteElevationError()
1020 * @see #setAbsoluteElevationRange(Integer, Integer)
1021 * @see #getAbsoluteElevationMinimum()
1024 public String
getAbsoluteElevationText() {
1025 if (!hasGatheringEvent()) {
1028 return getGatheringEvent(true).getAbsoluteElevationText();
1033 * Convenience method to set absolute elevation minimum and maximum.
1035 * @see #setAbsoluteElevation(Integer)
1036 * @see #setAbsoluteElevationMax(Integer)
1037 * @param minimumElevation minimum of the range
1038 * @param maximumElevation maximum of the range
1040 public void setAbsoluteElevationRange(Integer minimumElevation
, Integer maximumElevation
) {
1041 getGatheringEvent(true).setAbsoluteElevation(minimumElevation
);
1042 getGatheringEvent(true).setAbsoluteElevationMax(maximumElevation
);
1047 public AgentBase
getCollector() {
1048 return (hasGatheringEvent() ?
getGatheringEvent(true).getCollector()
1052 public void setCollector(AgentBase collector
) {
1053 getGatheringEvent(true).setCollector(collector
);
1056 // collecting method
1058 public String
getCollectingMethod() {
1059 return (hasGatheringEvent() ?
getGatheringEvent(true).getCollectingMethod() : null);
1062 public void setCollectingMethod(String collectingMethod
) {
1063 getGatheringEvent(true).setCollectingMethod(collectingMethod
);
1066 // distance to ground
1069 * Returns the correctly formatted <code>distance to ground</code> information.
1070 * If distanceToGroundText is not blank, it will be returned,
1071 * otherwise distanceToGround will be returned, followed by distanceToGroundMax
1072 * if existing, separated by " - "
1076 public String
distanceToGroundToString() {
1077 if (! hasGatheringEvent()){
1080 GatheringEvent ev
= getGatheringEvent(true);
1081 String text
= ev
.getDistanceToGroundText();
1082 Double min
= getDistanceToGround();
1083 Double max
= getDistanceToGroundMax();
1084 return distanceString(min
, max
, text
, METER
);
1089 public Double
getDistanceToGround() {
1090 return (hasGatheringEvent() ?
getGatheringEvent(true).getDistanceToGround() : null);
1093 public void setDistanceToGround(Double distanceToGround
) {
1094 getGatheringEvent(true).setDistanceToGround(distanceToGround
);
1098 * @see #getDistanceToGround()
1099 * @see #getDistanceToGroundRange(Integer, Integer)
1102 public Double
getDistanceToGroundMax() {
1103 if (!hasGatheringEvent()) {
1106 return getGatheringEvent(true).getDistanceToGroundMax();
1110 public void setDistanceToGroundMax(Double distanceToGroundMax
) {
1111 getGatheringEvent(true).setDistanceToGroundMax(distanceToGroundMax
);
1115 * @see #getDistanceToGround()
1116 * @see #setDistanceToGroundRange(Integer, Integer)
1119 public String
getDistanceToGroundText() {
1120 if (!hasGatheringEvent()) {
1123 return getGatheringEvent(true).getDistanceToGroundText();
1126 public void setDistanceToGroundText(String distanceToGroundText
) {
1127 getGatheringEvent(true).setDistanceToGroundText(distanceToGroundText
);
1131 * Convenience method to set distance to ground minimum and maximum.
1133 * @see #getDistanceToGround()
1134 * @see #getDistanceToGroundMax()
1135 * @param minimumDistance minimum of the range
1136 * @param maximumDistance maximum of the range
1138 public void setDistanceToGroundRange(Double minimumDistance
, Double maximumDistance
) throws IllegalArgumentException
{
1139 getGatheringEvent(true).setDistanceToGround(minimumDistance
);
1140 getGatheringEvent(true).setDistanceToGroundMax(maximumDistance
);
1145 * Returns the correctly formatted <code>distance to water surface</code> information.
1146 * If distanceToWaterSurfaceText is not blank, it will be returned,
1147 * otherwise distanceToWaterSurface will be returned, followed by distanceToWatersurfaceMax
1148 * if existing, separated by " - "
1152 public String
distanceToWaterSurfaceToString() {
1153 if (! hasGatheringEvent()){
1156 GatheringEvent ev
= getGatheringEvent(true);
1157 String text
= ev
.getDistanceToWaterSurfaceText();
1158 Double min
= getDistanceToWaterSurface();
1159 Double max
= getDistanceToWaterSurfaceMax();
1160 return distanceString(min
, max
, text
, METER
);
1164 // distance to water surface
1166 public Double
getDistanceToWaterSurface() {
1167 return (hasGatheringEvent() ?
getGatheringEvent(true).getDistanceToWaterSurface() : null);
1170 public void setDistanceToWaterSurface(Double distanceToWaterSurface
) {
1171 getGatheringEvent(true).setDistanceToWaterSurface(distanceToWaterSurface
);
1175 * @see #getDistanceToWaterSurface()
1176 * @see #getDistanceToWaterSurfaceRange(Double, Double)
1179 public Double
getDistanceToWaterSurfaceMax() {
1180 if (!hasGatheringEvent()) {
1183 return getGatheringEvent(true).getDistanceToWaterSurfaceMax();
1187 public void setDistanceToWaterSurfaceMax(Double distanceToWaterSurfaceMax
) {
1188 getGatheringEvent(true).setDistanceToWaterSurfaceMax(distanceToWaterSurfaceMax
);
1192 * @see #getDistanceToWaterSurface()
1193 * @see #getDistanceToWaterSurfaceRange(Double, Double)
1196 public String
getDistanceToWaterSurfaceText() {
1197 if (!hasGatheringEvent()) {
1200 return getGatheringEvent(true).getDistanceToWaterSurfaceText();
1203 public void setDistanceToWaterSurfaceText(String distanceToWaterSurfaceText
) {
1204 getGatheringEvent(true).setDistanceToWaterSurfaceText(distanceToWaterSurfaceText
);
1208 * Convenience method to set distance to ground minimum and maximum.
1210 * @see #getDistanceToWaterSurface()
1211 * @see #getDistanceToWaterSurfaceMax()
1212 * @param minimumDistance minimum of the range, this is the distance which is closer to the water surface
1213 * @param maximumDistance maximum of the range, this is the distance which is farer to the water surface
1215 public void setDistanceToWaterSurfaceRange(Double minimumDistance
, Double maximumDistance
) throws IllegalArgumentException
{
1216 getGatheringEvent(true).setDistanceToWaterSurface(minimumDistance
);
1217 getGatheringEvent(true).setDistanceToWaterSurfaceMax(maximumDistance
);
1223 public Point
getExactLocation() {
1224 return (hasGatheringEvent() ?
getGatheringEvent(true).getExactLocation() : null);
1228 * Returns a sexagesimal representation of the exact location (e.g.
1229 * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty
1230 * string is returned.
1232 * @param includeEmptySeconds
1233 * @param includeReferenceSystem
1236 public String
getExactLocationText(boolean includeEmptySeconds
,
1237 boolean includeReferenceSystem
) {
1238 return (this.getExactLocation() == null ?
"" : this.getExactLocation()
1239 .toSexagesimalString(includeEmptySeconds
,
1240 includeReferenceSystem
));
1243 public void setExactLocation(Point exactLocation
) {
1244 getGatheringEvent(true).setExactLocation(exactLocation
);
1247 public void setExactLocationByParsing(String longitudeToParse
,
1248 String latitudeToParse
, ReferenceSystem referenceSystem
,
1249 Integer errorRadius
) throws ParseException
{
1250 Point point
= Point
.NewInstance(null, null, referenceSystem
,
1252 point
.setLongitudeByParsing(longitudeToParse
);
1253 point
.setLatitudeByParsing(latitudeToParse
);
1254 setExactLocation(point
);
1257 // gathering event description
1259 public String
getGatheringEventDescription() {
1260 return (hasGatheringEvent() ?
getGatheringEvent(true).getDescription()
1264 public void setGatheringEventDescription(String description
) {
1265 getGatheringEvent(true).setDescription(description
);
1270 public TimePeriod
getGatheringPeriod() {
1271 return (hasGatheringEvent() ?
getGatheringEvent(true).getTimeperiod()
1275 public void setGatheringPeriod(TimePeriod timeperiod
) {
1276 getGatheringEvent(true).setTimeperiod(timeperiod
);
1281 public LanguageString
getLocality() {
1282 return (hasGatheringEvent() ?
getGatheringEvent(true).getLocality()
1287 * convienience method for {@link #getLocality()}.
1288 * {@link LanguageString#getText() getText()}
1293 public String
getLocalityText() {
1294 LanguageString locality
= getLocality();
1295 if (locality
!= null) {
1296 return locality
.getText();
1302 * convienience method for {@link #getLocality()}.
1303 * {@link LanguageString#getLanguage() getLanguage()}
1308 public Language
getLocalityLanguage() {
1309 LanguageString locality
= getLocality();
1310 if (locality
!= null) {
1311 return locality
.getLanguage();
1317 * Sets the locality string in the default language
1321 public void setLocality(String locality
) {
1322 Language language
= Language
.DEFAULT();
1323 setLocality(locality
, language
);
1326 public void setLocality(String locality
, Language language
) {
1327 LanguageString langString
= LanguageString
.NewInstance(locality
, language
);
1328 setLocality(langString
);
1331 public void setLocality(LanguageString locality
) {
1332 getGatheringEvent(true).setLocality(locality
);
1336 * The gathering event will be used for the field object instead of the old
1337 * gathering event.<BR>
1338 * <B>This method will override all gathering values (see below).</B>
1340 * @see #getAbsoluteElevation()
1341 * @see #getAbsoluteElevationError()
1342 * @see #getDistanceToGround()
1343 * @see #getDistanceToWaterSurface()
1344 * @see #getExactLocation()
1345 * @see #getGatheringEventDescription()
1346 * @see #getGatheringPeriod()
1347 * @see #getCollectingAreas()
1348 * @see #getCollectingMethod()
1349 * @see #getLocality()
1350 * @see #getCollector()
1351 * @param gatheringEvent
1353 public void setGatheringEvent(GatheringEvent gatheringEvent
) {
1354 getFieldUnit(true).setGatheringEvent(gatheringEvent
);
1357 public boolean hasGatheringEvent() {
1358 return (getGatheringEvent(false) != null);
1361 public GatheringEvent
innerGatheringEvent() {
1362 return getGatheringEvent(false);
1365 public GatheringEvent
getGatheringEvent(boolean createIfNotExists
) {
1366 if (!hasFieldUnit() && !createIfNotExists
) {
1369 if (createIfNotExists
&& getFieldUnit(true).getGatheringEvent() == null) {
1370 GatheringEvent gatheringEvent
= GatheringEvent
.NewInstance();
1371 getFieldUnit(true).setGatheringEvent(gatheringEvent
);
1373 return getFieldUnit(true).getGatheringEvent();
1376 // ****************** Field Object ************************************/
1379 * Returns true if a field unit exists (even if all attributes are
1380 * empty or <code>null<code>.
1384 public boolean hasFieldObject() {
1385 return this.fieldUnit
!= null;
1390 public String
getEcology() {
1391 return getEcology(Language
.DEFAULT());
1394 public String
getEcology(Language language
) {
1395 LanguageString languageString
= getEcologyAll().get(language
);
1396 return (languageString
== null ?
null : languageString
.getText());
1399 // public String getEcologyPreferred(List<Language> languages){
1400 // LanguageString languageString =
1401 // getEcologyAll().getPreferredLanguageString(languages);
1402 // return languageString.getText();
1405 * Returns a copy of the multilanguage text holding the ecology data.
1407 * @see {@link TextData#getMultilanguageText()}
1411 public Map
<Language
, LanguageString
> getEcologyAll() {
1412 if (ecology
== null) {
1414 ecology
= initializeFieldObjectTextDataWithSupportTest(
1415 Feature
.ECOLOGY(), false, false);
1416 } catch (DerivedUnitFacadeNotSupportedException e
) {
1417 throw new IllegalStateException(notSupportMessage
, e
);
1419 if (ecology
== null) {
1420 return new HashMap
<Language
, LanguageString
>();
1423 return ecology
.getMultilanguageText();
1426 public void setEcology(String ecology
) {
1427 setEcology(ecology
, null);
1430 public void setEcology(String ecologyText
, Language language
) {
1431 if (language
== null) {
1432 language
= Language
.DEFAULT();
1434 if (ecology
== null) {
1436 ecology
= initializeFieldObjectTextDataWithSupportTest(
1437 Feature
.ECOLOGY(), true, false);
1438 } catch (DerivedUnitFacadeNotSupportedException e
) {
1439 throw new IllegalStateException(notSupportMessage
, e
);
1442 if (ecologyText
== null) {
1443 ecology
.removeText(language
);
1445 ecology
.putText(language
, ecologyText
);
1449 public void removeEcology(Language language
) {
1450 setEcology(null, language
);
1454 * Removes ecology for the default language
1456 public void removeEcology() {
1457 setEcology(null, null);
1460 public void removeEcologyAll() {
1464 // plant description
1466 public String
getPlantDescription() {
1467 return getPlantDescription(null);
1470 public String
getPlantDescription(Language language
) {
1471 if (language
== null) {
1472 language
= Language
.DEFAULT();
1474 LanguageString languageString
= getPlantDescriptionAll().get(language
);
1475 return (languageString
== null ?
null : languageString
.getText());
1478 // public String getPlantDescriptionPreferred(List<Language> languages){
1479 // LanguageString languageString =
1480 // getPlantDescriptionAll().getPreferredLanguageString(languages);
1481 // return languageString.getText();
1484 * Returns a copy of the multilanguage text holding the description data.
1486 * @see {@link TextData#getMultilanguageText()}
1490 public Map
<Language
, LanguageString
> getPlantDescriptionAll() {
1491 if (plantDescription
== null) {
1493 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
1494 Feature
.DESCRIPTION(), false, false);
1495 } catch (DerivedUnitFacadeNotSupportedException e
) {
1496 throw new IllegalStateException(notSupportMessage
, e
);
1498 if (plantDescription
== null) {
1499 return new HashMap
<Language
, LanguageString
>();
1502 return plantDescription
.getMultilanguageText();
1505 public void setPlantDescription(String plantDescription
) {
1506 setPlantDescription(plantDescription
, null);
1509 public void setPlantDescription(String plantDescriptionText
,
1510 Language language
) {
1511 if (language
== null) {
1512 language
= Language
.DEFAULT();
1514 if (plantDescription
== null) {
1516 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
1517 Feature
.DESCRIPTION(), true, false);
1518 } catch (DerivedUnitFacadeNotSupportedException e
) {
1519 throw new IllegalStateException(notSupportMessage
, e
);
1522 if (plantDescriptionText
== null) {
1523 plantDescription
.removeText(language
);
1525 plantDescription
.putText(language
, plantDescriptionText
);
1529 public void removePlantDescription(Language language
) {
1530 setPlantDescription(null, language
);
1533 // field object definition
1534 public void addFieldObjectDefinition(String text
, Language language
) {
1535 getFieldUnit(true).putDefinition(language
, text
);
1539 public Map
<Language
, LanguageString
> getFieldObjectDefinition() {
1540 if (!hasFieldUnit()) {
1541 return new HashMap
<Language
, LanguageString
>();
1543 return getFieldUnit(true).getDefinition();
1547 public String
getFieldObjectDefinition(Language language
) {
1548 Map
<Language
, LanguageString
> map
= getFieldObjectDefinition();
1549 LanguageString languageString
= (map
== null ?
null : map
.get(language
));
1550 if (languageString
!= null) {
1551 return languageString
.getText();
1557 public void removeFieldObjectDefinition(Language lang
) {
1558 if (hasFieldUnit()) {
1559 getFieldUnit(true).removeDefinition(lang
);
1564 public boolean addFieldObjectMedia(Media media
) {
1566 return addMedia(media
, getFieldUnit(true));
1567 } catch (DerivedUnitFacadeNotSupportedException e
) {
1568 throw new IllegalStateException(notSupportMessage
, e
);
1573 * Returns true, if an image gallery for the field object exists.<BR>
1574 * Returns also <code>true</code> if the image gallery is empty.
1578 public boolean hasFieldObjectImageGallery() {
1579 if (!hasFieldObject()) {
1582 return (getImageGallery(fieldUnit
, false) != null);
1586 public void setFieldObjectImageGallery(SpecimenDescription imageGallery
)
1587 throws DerivedUnitFacadeNotSupportedException
{
1588 SpecimenDescription existingGallery
= getFieldObjectImageGallery(false);
1590 // test attached specimens contain this.derivedUnit
1591 SpecimenOrObservationBase
<?
> facadeFieldUnit
= innerFieldUnit();
1592 testSpecimenInImageGallery(imageGallery
, facadeFieldUnit
);
1594 if (existingGallery
!= null) {
1595 if (existingGallery
!= imageGallery
) {
1596 throw new DerivedUnitFacadeNotSupportedException(
1597 "DerivedUnitFacade does not allow more than one image gallery");
1602 TextData textData
= testImageGallery(imageGallery
);
1603 this.fieldObjectMediaTextData
= textData
;
1608 * Returns the field object image gallery. If no such image gallery exists
1609 * and createIfNotExists is true an new one is created. Otherwise null is
1612 * @param createIfNotExists
1615 public SpecimenDescription
getFieldObjectImageGallery(
1616 boolean createIfNotExists
) {
1619 textData
= initializeFieldObjectTextDataWithSupportTest(
1620 Feature
.IMAGE(), createIfNotExists
, true);
1621 } catch (DerivedUnitFacadeNotSupportedException e
) {
1622 throw new IllegalStateException(notSupportMessage
, e
);
1624 if (textData
!= null) {
1625 return CdmBase
.deproxy(textData
.getInDescription(),
1626 SpecimenDescription
.class);
1633 * Returns the media for the field object.<BR>
1638 public List
<Media
> getFieldObjectMedia() {
1640 List
<Media
> result
= getMediaList(getFieldUnit(false), false);
1641 return result
== null ?
new ArrayList
<Media
>() : result
;
1642 } catch (DerivedUnitFacadeNotSupportedException e
) {
1643 throw new IllegalStateException(notSupportMessage
, e
);
1647 public boolean removeFieldObjectMedia(Media media
) {
1649 return removeMedia(media
, getFieldUnit(false));
1650 } catch (DerivedUnitFacadeNotSupportedException e
) {
1651 throw new IllegalStateException(notSupportMessage
, e
);
1657 public String
getFieldNumber() {
1658 if (!hasFieldUnit()) {
1661 return getFieldUnit(true).getFieldNumber();
1665 public void setFieldNumber(String fieldNumber
) {
1666 getFieldUnit(true).setFieldNumber(fieldNumber
);
1669 // primary collector
1671 public Person
getPrimaryCollector() {
1672 if (!hasFieldUnit()) {
1675 return getFieldUnit(true).getPrimaryCollector();
1679 public void setPrimaryCollector(Person primaryCollector
) {
1680 getFieldUnit(true).setPrimaryCollector(primaryCollector
);
1685 public String
getFieldNotes() {
1686 if (!hasFieldUnit()) {
1689 return getFieldUnit(true).getFieldNotes();
1693 public void setFieldNotes(String fieldNotes
) {
1694 getFieldUnit(true).setFieldNotes(fieldNotes
);
1697 // individual counts
1699 public Integer
getIndividualCount() {
1700 return (hasFieldUnit() ?
getFieldUnit(true).getIndividualCount() : null);
1703 public void setIndividualCount(Integer individualCount
) {
1704 getFieldUnit(true).setIndividualCount(individualCount
);
1709 public DefinedTerm
getLifeStage() {
1710 return (hasFieldUnit() ?
getFieldUnit(true).getLifeStage() : null);
1713 public void setLifeStage(DefinedTerm lifeStage
) {
1714 getFieldUnit(true).setLifeStage(lifeStage
);
1719 public DefinedTerm
getSex() {
1720 return (hasFieldUnit() ?
getFieldUnit(true).getSex(): null);
1723 public void setSex(DefinedTerm sex
) {
1724 getFieldUnit(true).setSex(sex
);
1729 public DefinedTerm
getKindOfUnit() {
1730 return (hasFieldUnit() ?
getFieldUnit(true).getKindOfUnit() : null);
1733 public void setKindOfUnit(DefinedTerm kindOfUnit
) {
1734 getFieldUnit(true).setKindOfUnit(kindOfUnit
);
1739 public boolean hasFieldUnit() {
1740 return (getFieldUnit(false) != null);
1744 * Returns the field unit as an object.
1748 public FieldUnit
innerFieldUnit() {
1749 return getFieldUnit(false);
1753 * Returns the field unit as an object.
1757 public FieldUnit
getFieldUnit(boolean createIfNotExists
) {
1758 if (fieldUnit
== null && createIfNotExists
) {
1759 setFieldUnit(FieldUnit
.NewInstance());
1761 return this.fieldUnit
;
1765 private void setFieldUnit(FieldUnit fieldUnit
) {
1766 this.fieldUnit
= fieldUnit
;
1767 if (fieldUnit
!= null){
1768 if (config
.isFirePropertyChangeEvents()){
1769 addNewEventPropagationListener(fieldUnit
);
1771 if (derivedUnit
!= null){
1772 DerivationEvent derivationEvent
= getDerivationEvent(CREATE
);
1773 derivationEvent
.addOriginal(fieldUnit
);
1775 setFieldUnitCacheStrategy();
1779 // ****************** Specimen *******************************************
1782 public void addDerivedUnitDefinition(String text
, Language language
) {
1783 innerDerivedUnit().putDefinition(language
, text
);
1787 public Map
<Language
, LanguageString
> getDerivedUnitDefinitions() {
1789 return this.derivedUnit
.getDefinition();
1793 public String
getDerivedUnitDefinition(Language language
) {
1795 Map
<Language
, LanguageString
> languageMap
= derivedUnit
.getDefinition();
1796 LanguageString languageString
= languageMap
.get(language
);
1797 if (languageString
!= null) {
1798 return languageString
.getText();
1804 public void removeDerivedUnitDefinition(Language lang
) {
1806 derivedUnit
.removeDefinition(lang
);
1810 public void addDetermination(DeterminationEvent determination
) {
1812 //TODO implement correct bidirectional mapping in model classes
1813 determination
.setIdentifiedUnit(derivedUnit
);
1814 derivedUnit
.addDetermination(determination
);
1818 public DeterminationEvent
getPreferredDetermination() {
1820 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1821 for (DeterminationEvent event
: events
){
1822 if (event
.getPreferredFlag() == true){
1830 * This method returns the preferred determination.
1831 * @see #getOtherDeterminations()
1832 * @see #getDeterminations()
1836 public void setPreferredDetermination(DeterminationEvent newEvent
) {
1838 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1839 for (DeterminationEvent event
: events
){
1840 if (event
.getPreferredFlag() == true){
1841 event
.setPreferredFlag(false);
1844 newEvent
.setPreferredFlag(true);
1845 events
.add(newEvent
);
1849 * This method returns all determinations except for the preferred one.
1850 * @see #getPreferredDetermination()
1851 * @see #getDeterminations()
1855 public Set
<DeterminationEvent
> getOtherDeterminations() {
1857 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1858 Set
<DeterminationEvent
> result
= new HashSet
<DeterminationEvent
>();
1859 for (DeterminationEvent event
: events
){
1860 if (event
.getPreferredFlag() != true){
1868 * This method returns all determination events. The preferred one {@link #getPreferredDetermination()}
1869 * and all others {@link #getOtherDeterminations()}.
1873 public Set
<DeterminationEvent
> getDeterminations() {
1875 return derivedUnit
.getDeterminations();
1878 public void removeDetermination(DeterminationEvent determination
) {
1880 derivedUnit
.removeDetermination(determination
);
1884 public boolean addDerivedUnitMedia(Media media
) {
1887 return addMedia(media
, derivedUnit
);
1888 } catch (DerivedUnitFacadeNotSupportedException e
) {
1889 throw new IllegalStateException(notSupportMessage
, e
);
1894 * Returns true, if an image gallery exists for the specimen.<BR>
1895 * Returns also <code>true</code> if the image gallery is empty.
1897 public boolean hasDerivedUnitImageGallery() {
1898 return (getImageGallery(derivedUnit
, false) != null);
1901 public SpecimenDescription
getDerivedUnitImageGallery(boolean createIfNotExists
) {
1905 textData
= inititializeTextDataWithSupportTest(Feature
.IMAGE(),
1906 derivedUnit
, createIfNotExists
, true);
1907 } catch (DerivedUnitFacadeNotSupportedException e
) {
1908 throw new IllegalStateException(notSupportMessage
, e
);
1910 if (textData
!= null) {
1911 return CdmBase
.deproxy(textData
.getInDescription(),
1912 SpecimenDescription
.class);
1918 public void setDerivedUnitImageGallery(SpecimenDescription imageGallery
)
1919 throws DerivedUnitFacadeNotSupportedException
{
1921 SpecimenDescription existingGallery
= getDerivedUnitImageGallery(false);
1923 // test attached specimens contain this.derivedUnit
1924 SpecimenOrObservationBase facadeDerivedUnit
= innerDerivedUnit();
1925 testSpecimenInImageGallery(imageGallery
, facadeDerivedUnit
);
1927 if (existingGallery
!= null) {
1928 if (existingGallery
!= imageGallery
) {
1929 throw new DerivedUnitFacadeNotSupportedException(
1930 "DerivedUnitFacade does not allow more than one image gallery");
1935 TextData textData
= testImageGallery(imageGallery
);
1936 this.derivedUnitMediaTextData
= textData
;
1941 * @param imageGallery
1942 * @throws DerivedUnitFacadeNotSupportedException
1944 private void testSpecimenInImageGallery(SpecimenDescription imageGallery
, SpecimenOrObservationBase specimen
)
1945 throws DerivedUnitFacadeNotSupportedException
{
1946 SpecimenOrObservationBase imageGallerySpecimen
= imageGallery
.getDescribedSpecimenOrObservation();
1947 if (imageGallerySpecimen
== null) {
1948 throw new DerivedUnitFacadeNotSupportedException(
1949 "Image Gallery has no Specimen attached. Please attache according specimen or field unit.");
1951 if (! imageGallerySpecimen
.equals(specimen
)) {
1952 throw new DerivedUnitFacadeNotSupportedException(
1953 "Image Gallery has not the facade's field object attached. Please add field object first " +
1954 "to image gallery specimenOrObservation list.");
1959 * Returns the media for the specimen.<BR>
1964 public List
<Media
> getDerivedUnitMedia() {
1967 List
<Media
> result
= getMediaList(derivedUnit
, false);
1968 return result
== null ?
new ArrayList
<Media
>() : result
;
1969 } catch (DerivedUnitFacadeNotSupportedException e
) {
1970 throw new IllegalStateException(notSupportMessage
, e
);
1974 public boolean removeDerivedUnitMedia(Media media
) {
1977 return removeMedia(media
, derivedUnit
);
1978 } catch (DerivedUnitFacadeNotSupportedException e
) {
1979 throw new IllegalStateException(notSupportMessage
, e
);
1985 public String
getAccessionNumber() {
1987 return derivedUnit
.getAccessionNumber();
1990 public void setAccessionNumber(String accessionNumber
) {
1992 derivedUnit
.setAccessionNumber(accessionNumber
);
1996 public String
getCatalogNumber() {
1998 return derivedUnit
.getCatalogNumber();
2001 public void setCatalogNumber(String catalogNumber
) {
2003 derivedUnit
.setCatalogNumber(catalogNumber
);
2007 public String
getBarcode() {
2009 return derivedUnit
.getBarcode();
2012 public void setBarcode(String barcode
) {
2014 derivedUnit
.setBarcode(barcode
);
2017 // Preservation Method
2020 * Only supported by specimen and fossils
2022 * @see #DerivedUnitType
2026 public PreservationMethod
getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException
{
2028 if (derivedUnit
.getRecordBasis().isPreservedSpecimen()) {
2029 return CdmBase
.deproxy(derivedUnit
, DerivedUnit
.class).getPreservation();
2031 if (this.config
.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2032 throw new MethodNotSupportedByDerivedUnitTypeException(
2033 "A preservation method is only available in derived units of type 'Preserved Specimen' or one of its specializations like 'Fossil Specimen' ");
2041 * Only supported by specimen and fossils
2043 * @see #DerivedUnitType
2046 public void setPreservationMethod(PreservationMethod preservation
)
2047 throws MethodNotSupportedByDerivedUnitTypeException
{
2049 if (derivedUnit
.getRecordBasis().isPreservedSpecimen()) {
2050 CdmBase
.deproxy(derivedUnit
, DerivedUnit
.class).setPreservation(preservation
);
2053 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2054 throw new MethodNotSupportedByDerivedUnitTypeException(
2055 "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
2062 // Stored under name
2064 public TaxonNameBase
getStoredUnder() {
2066 return derivedUnit
.getStoredUnder();
2069 public void setStoredUnder(TaxonNameBase storedUnder
) {
2071 derivedUnit
.setStoredUnder(storedUnder
);
2075 public String
getTitleCache() {
2076 SpecimenOrObservationBase
<?
> titledUnit
= getTitledUnit();
2078 if (!titledUnit
.isProtectedTitleCache()) {
2079 // always compute title cache anew as long as there are no property
2080 // change listeners on
2081 // field unit, gathering event etc
2082 titledUnit
.setTitleCache(null, false);
2084 return titledUnit
.getTitleCache();
2087 private SpecimenOrObservationBase
<?
> getTitledUnit(){
2088 return (derivedUnit
!= null )? derivedUnit
: fieldUnit
;
2091 public boolean isProtectedTitleCache() {
2092 return getTitledUnit().isProtectedTitleCache();
2095 public void setTitleCache(String titleCache
, boolean isProtected
) {
2096 this.getTitledUnit().setTitleCache(titleCache
, isProtected
);
2100 * Returns the derived unit itself.
2102 * @return the derived unit
2104 public DerivedUnit
innerDerivedUnit() {
2105 return this.derivedUnit
;
2109 // * Returns the derived unit itself.
2111 // * @return the derived unit
2113 // public DerivedUnit innerDerivedUnit(boolean createIfNotExists) {
2114 // DerivedUnit result = this.derivedUnit;
2115 // if (result == null && createIfNotExists){
2116 // if (this.fieldUnit == null){
2117 // String message = "Field unit must exist to create derived unit.";
2118 // throw new IllegalStateException(message);
2121 // DerivationEvent derivationEvent = getDerivationEvent(true);
2122 // derivationEvent.addOriginal(fieldUnit);
2123 // return this.derivedUnit;
2128 private boolean hasDerivationEvent() {
2129 return getDerivationEvent() == null ?
false : true;
2132 private DerivationEvent
getDerivationEvent() {
2133 return getDerivationEvent(CREATE_NOT
);
2137 * Returns the derivation event. If no derivation event exists and <code>createIfNotExists</code>
2138 * is <code>true</code> a new derivation event is created and returned.
2139 * Otherwise <code>null</code> is returned.
2140 * @param createIfNotExists
2142 private DerivationEvent
getDerivationEvent(boolean createIfNotExists
) {
2143 DerivationEvent result
= null;
2144 if (derivedUnit
!= null){
2145 result
= derivedUnit
.getDerivedFrom();
2149 if (result
== null && createIfNotExists
) {
2150 DerivationEventType type
= null;
2151 if (isAccessioned(derivedUnit
)){
2152 type
= DerivationEventType
.ACCESSIONING();
2155 result
= DerivationEvent
.NewInstance(type
);
2156 derivedUnit
.setDerivedFrom(result
);
2162 * TODO still unclear which classes do definetly require accessioning.
2163 * Only return true for those classes which are clear.
2164 * @param derivedUnit
2167 private boolean isAccessioned(DerivedUnit derivedUnit
) {
2168 if (derivedUnit
.getRecordBasis().equals(SpecimenOrObservationType
.PreservedSpecimen
) ){
2169 return true; //maybe also subtypes should be true
2176 public String
getExsiccatum() throws MethodNotSupportedByDerivedUnitTypeException
{
2178 if (derivedUnit
.getRecordBasis().isPreservedSpecimen()) {
2179 return derivedUnit
.getExsiccatum();
2181 if (this.config
.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2182 throw new MethodNotSupportedByDerivedUnitTypeException(
2183 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2190 public void setExsiccatum(String exsiccatum
) throws Exception
{
2192 if (derivedUnit
.getRecordBasis().isPreservedSpecimen()) {
2193 derivedUnit
.setExsiccatum(exsiccatum
);
2195 if (this.config
.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2196 throw new MethodNotSupportedByDerivedUnitTypeException(
2197 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2205 public void addSource(IdentifiableSource source
) {
2207 this.derivedUnit
.addSource(source
);
2211 * Creates an {@link IOriginalSource orignal source} or type ,
2212 * adds it to the specimen and returns it.
2215 * @param microReference
2216 * @param originalNameString
2219 public IdentifiableSource
addSource(OriginalSourceType type
, Reference reference
, String microReference
, String originalNameString
) {
2220 IdentifiableSource source
= IdentifiableSource
.NewInstance(type
, null, null, reference
, microReference
);
2221 source
.setOriginalNameString(originalNameString
);
2227 public Set
<IdentifiableSource
> getSources() {
2229 return derivedUnit
.getSources();
2232 public void removeSource(IdentifiableSource source
) {
2234 this.derivedUnit
.removeSource(source
);
2238 * @return the collection
2241 public Collection
getCollection() {
2243 return derivedUnit
.getCollection();
2248 * the collection to set
2250 public void setCollection(Collection collection
) {
2252 derivedUnit
.setCollection(collection
);
2256 public void addAnnotation(Annotation annotation
) {
2258 this.derivedUnit
.addAnnotation(annotation
);
2262 public void getAnnotations() {
2264 this.derivedUnit
.getAnnotations();
2267 public void removeAnnotation(Annotation annotation
) {
2269 this.derivedUnit
.removeAnnotation(annotation
);
2272 // ******************************* Events ***************************
2274 //set of events that were currently fired by this facades field unit
2275 //to avoid recursive fireing of the same event
2276 private final Set
<PropertyChangeEvent
> fireingEvents
= new HashSet
<PropertyChangeEvent
>();
2281 private void addNewEventPropagationListener(CdmBase listeningObject
) {
2282 //if there is already a listener, don't do anything
2283 for (PropertyChangeListener listener
: this.listeners
.keySet()){
2284 if (listeners
.get(listener
) == listeningObject
){
2288 //create new listener
2289 PropertyChangeListener listener
= new PropertyChangeListener() {
2291 public void propertyChange(PropertyChangeEvent event
) {
2292 if (derivedUnit
!= null){
2293 derivedUnit
.firePropertyChange(event
);
2295 if (! event
.getSource().equals(fieldUnit
) && ! fireingEvents
.contains(event
) ){
2296 fireingEvents
.add(event
);
2297 fieldUnit
.firePropertyChange(event
);
2298 fireingEvents
.remove(event
);
2303 //add listener to listening object and to list of listeners
2304 listeningObject
.addPropertyChangeListener(listener
);
2305 listeners
.put(listener
, listeningObject
);
2308 // **************** Other Collections ********************************
2311 * Creates a duplicate specimen which derives from the same derivation event
2312 * as the facade specimen and adds collection data to it (all data available
2313 * in DerivedUnit and Specimen. Data from SpecimenOrObservationBase and
2314 * above are not yet shared at the moment.
2317 * @param catalogNumber
2318 * @param accessionNumber
2319 * @param collectorsNumber
2320 * @param storedUnder
2321 * @param preservation
2324 public DerivedUnit
addDuplicate(Collection collection
, String catalogNumber
,
2325 String accessionNumber
, TaxonNameBase storedUnder
, PreservationMethod preservation
) {
2327 DerivedUnit duplicate
= DerivedUnit
.NewPreservedSpecimenInstance();
2328 duplicate
.setDerivedFrom(getDerivationEvent(CREATE
));
2329 duplicate
.setCollection(collection
);
2330 duplicate
.setCatalogNumber(catalogNumber
);
2331 duplicate
.setAccessionNumber(accessionNumber
);
2332 duplicate
.setStoredUnder(storedUnder
);
2333 duplicate
.setPreservation(preservation
);
2337 public void addDuplicate(DerivedUnit duplicateSpecimen
) {
2338 // TODO check derivedUnitType
2340 getDerivationEvent(CREATE
).addDerivative(duplicateSpecimen
);
2344 public Set
<DerivedUnit
> getDuplicates() {
2346 Set
<DerivedUnit
> result
= new HashSet
<DerivedUnit
>();
2347 if (hasDerivationEvent()) {
2348 for (DerivedUnit derivedUnit
: getDerivationEvent(CREATE
)
2349 .getDerivatives()) {
2350 if (derivedUnit
.isInstanceOf(DerivedUnit
.class)
2351 && !derivedUnit
.equals(this.derivedUnit
)) {
2352 result
.add(CdmBase
.deproxy(derivedUnit
, DerivedUnit
.class));
2359 public void removeDuplicate(DerivedUnit duplicateSpecimen
) {
2361 if (hasDerivationEvent()) {
2362 getDerivationEvent(CREATE
).removeDerivative(duplicateSpecimen
);
2368 private void testDerivedUnit() {
2369 if (derivedUnit
== null){
2370 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");
2374 public void setType(SpecimenOrObservationType type
) {
2375 if (type
== SpecimenOrObservationType
.FieldUnit
){
2376 throw new IllegalArgumentException("Type FieldUnit is not a legal record basis for a DerivedUnit");
2378 this.innerDerivedUnit().setRecordBasis(type
);
2381 public SpecimenOrObservationType
getType() {
2382 if(derivedUnit
==null && hasFieldUnit()){
2383 return getFieldUnit(CREATE_NOT
).getRecordBasis();
2385 else if(derivedUnit
!=null){
2386 return this.innerDerivedUnit().getRecordBasis();
2389 return SpecimenOrObservationType
.Unknown
;
2395 * Closes this facade. As a minimum this method removes all listeners created by this facade from their
2396 * listening objects.
2398 public void close(){
2399 for (PropertyChangeListener listener
: this.listeners
.keySet()){
2400 CdmBase listeningObject
= listeners
.get(listener
);
2401 listeningObject
.removePropertyChangeListener(listener
);
2407 * Computes the correct distance string for given values for min, max and text.
2408 * If text is not blank, text is returned, otherwise "min - max" or a single value is returned.
2409 * @param min min value as number
2410 * @param max max value as number
2411 * @param text text representation of distance
2412 * @return the formatted distance string
2414 private String
distanceString(Number min
, Number max
, String text
, String unit
) {
2415 if (StringUtils
.isNotBlank(text
)){
2418 String minStr
= min
== null?
null : String
.valueOf(min
);
2419 String maxStr
= max
== null?
null : String
.valueOf(max
);
2420 String result
= CdmUtils
.concat(UTF8
.EN_DASH_SPATIUM
.toString(), minStr
, maxStr
);
2421 if (StringUtils
.isNotBlank(result
) && StringUtils
.isNotBlank(unit
)){
2422 result
= result
+ " " + unit
;