2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.cdm
.api
.facade
;
12 import java
.beans
.PropertyChangeEvent
;
13 import java
.beans
.PropertyChangeListener
;
14 import java
.text
.ParseException
;
15 import java
.util
.ArrayList
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.List
;
22 import javax
.persistence
.Transient
;
24 import org
.apache
.log4j
.Logger
;
26 import eu
.etaxonomy
.cdm
.api
.service
.IOccurrenceService
;
27 import eu
.etaxonomy
.cdm
.model
.agent
.AgentBase
;
28 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
29 import eu
.etaxonomy
.cdm
.model
.common
.Annotation
;
30 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
31 import eu
.etaxonomy
.cdm
.model
.common
.IdentifiableSource
;
32 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
33 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
34 import eu
.etaxonomy
.cdm
.model
.common
.TimePeriod
;
35 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
36 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
37 import eu
.etaxonomy
.cdm
.model
.description
.Sex
;
38 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
39 import eu
.etaxonomy
.cdm
.model
.description
.Stage
;
40 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
41 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
42 import eu
.etaxonomy
.cdm
.model
.location
.Point
;
43 import eu
.etaxonomy
.cdm
.model
.location
.ReferenceSystem
;
44 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
45 import eu
.etaxonomy
.cdm
.model
.name
.TaxonNameBase
;
46 import eu
.etaxonomy
.cdm
.model
.occurrence
.Collection
;
47 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEvent
;
48 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivationEventType
;
49 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
50 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnitBase
;
51 import eu
.etaxonomy
.cdm
.model
.occurrence
.DeterminationEvent
;
52 import eu
.etaxonomy
.cdm
.model
.occurrence
.FieldObservation
;
53 import eu
.etaxonomy
.cdm
.model
.occurrence
.GatheringEvent
;
54 import eu
.etaxonomy
.cdm
.model
.occurrence
.PreservationMethod
;
55 import eu
.etaxonomy
.cdm
.model
.occurrence
.Specimen
;
56 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
57 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
60 * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
61 * a specimen based view. It does not support all functionality available in the
62 * occurrence package.<BR>
63 * The most significant restriction is that a specimen may derive only from one
64 * direct derivation event and there must be only one field observation
65 * (gathering event) it derives from.<BR>
70 public class DerivedUnitFacade
{
71 @SuppressWarnings("unused")
72 private static final Logger logger
= Logger
.getLogger(DerivedUnitFacade
.class);
74 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 ";
76 private static final boolean CREATE
= true;
77 private static final boolean CREATE_NOT
= false;
80 * Enum that defines the class the "Specimen" belongs to. Some methods of
81 * the facade are not available for certain classes and will throw an
82 * Exception when invoking them.
84 public enum DerivedUnitType
{
86 Observation("Observation"),
87 LivingBeing("Living Being"),
89 DerivedUnit("Derived Unit"),
90 //Field Observation is experimental, please handle with care (it is the only type which does not
91 //have a derivedUnit and therefore throws exceptions for all method on derivedUnit attributes
92 FieldObservation("FieldObservation");
94 String representation
;
96 private DerivedUnitType(String representation
) {
97 this.representation
= representation
;
101 * @return the representation
103 public String
getRepresentation() {
104 return representation
;
107 public DerivedUnitBase
<?
> getNewDerivedUnitInstance() {
108 if (this == DerivedUnitType
.Specimen
) {
109 return eu
.etaxonomy
.cdm
.model
.occurrence
.Specimen
.NewInstance();
110 } else if (this == DerivedUnitType
.Observation
) {
111 return eu
.etaxonomy
.cdm
.model
.occurrence
.Observation
.NewInstance();
112 } else if (this == DerivedUnitType
.LivingBeing
) {
113 return eu
.etaxonomy
.cdm
.model
.occurrence
.LivingBeing
.NewInstance();
114 } else if (this == DerivedUnitType
.Fossil
) {
115 return eu
.etaxonomy
.cdm
.model
.occurrence
.Fossil
.NewInstance();
116 } else if (this == DerivedUnitType
.DerivedUnit
) {
117 return eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
.NewInstance();
118 } else if (this == DerivedUnitType
.FieldObservation
) {
121 String message
= "Unknown derived unit type %s";
122 message
= String
.format(message
, this.getRepresentation());
123 throw new IllegalStateException(message
);
127 public static DerivedUnitType
valueOf2(String type
) {
131 type
= type
.replace(" ", "").toLowerCase();
132 if (type
.equals("specimen")) {
134 } else if (type
.equals("livingbeing")) {
136 } else if (type
.equals("observation")) {
138 } else if (type
.equals("fossil")) {
140 } else if (type
.equals("fieldobservation")) {
141 return DerivedUnitType
.FieldObservation
;
142 } else if (type
.equals("unknown")) {
143 return DerivedUnitType
.DerivedUnit
;
144 } else if (type
.equals("derivedunit")) {
145 return DerivedUnitType
.DerivedUnit
;
150 public static DerivedUnitType
valueOf2(Class
<?
extends SpecimenOrObservationBase
> clazz
) {
154 if (clazz
.equals(Specimen
.class)) {
156 } else if (clazz
.equals(eu
.etaxonomy
.cdm
.model
.occurrence
.LivingBeing
.class)) {
158 } else if (clazz
.equals(eu
.etaxonomy
.cdm
.model
.occurrence
.Observation
.class)) {
160 } else if (clazz
.equals(eu
.etaxonomy
.cdm
.model
.occurrence
.Fossil
.class)) {
162 } else if (clazz
.equals(FieldObservation
.class)) {
163 return DerivedUnitType
.FieldObservation
;
164 } else if (clazz
.equals(DerivedUnit
.class)) {
165 return DerivedUnitType
.DerivedUnit
;
172 private final DerivedUnitFacadeConfigurator config
;
174 private Map
<PropertyChangeListener
, CdmBase
> listeners
= new HashMap
<PropertyChangeListener
, CdmBase
>();
176 // private GatheringEvent gatheringEvent;
177 private DerivedUnitType type
; // needed?
179 private FieldObservation fieldObservation
;
181 private final DerivedUnitBase derivedUnit
;
183 // media - the text data holding the media
184 private TextData derivedUnitMediaTextData
;
185 private TextData fieldObjectMediaTextData
;
187 private TextData ecology
;
188 private TextData plantDescription
;
191 * Creates a derived unit facade for a new derived unit of type
197 public static DerivedUnitFacade
NewInstance(DerivedUnitType type
) {
198 return new DerivedUnitFacade(type
, null, null);
202 * Creates a derived unit facade for a new derived unit of type
208 public static DerivedUnitFacade
NewInstance(DerivedUnitType type
, FieldObservation fieldObservation
) {
209 return new DerivedUnitFacade(type
, fieldObservation
, null);
213 * Creates a derived unit facade for a new derived unit of type
217 * @param fieldObservation the field observation to use
218 * @param config the facade configurator to use
219 * //TODO are there any ambiguities to solve with defining a field observation or a configurator
222 public static DerivedUnitFacade
NewInstance(DerivedUnitType type
, FieldObservation fieldObservation
, DerivedUnitFacadeConfigurator config
) {
223 return new DerivedUnitFacade(type
, fieldObservation
, config
);
228 * Creates a derived unit facade for a given derived unit using the default
233 * @throws DerivedUnitFacadeNotSupportedException
235 public static DerivedUnitFacade
NewInstance(DerivedUnitBase derivedUnit
)
236 throws DerivedUnitFacadeNotSupportedException
{
237 return new DerivedUnitFacade(derivedUnit
, null);
240 public static DerivedUnitFacade
NewInstance(DerivedUnitBase derivedUnit
,
241 DerivedUnitFacadeConfigurator config
)
242 throws DerivedUnitFacadeNotSupportedException
{
243 return new DerivedUnitFacade(derivedUnit
, config
);
246 // ****************** CONSTRUCTOR ******************************************
248 private DerivedUnitFacade(DerivedUnitType type
, FieldObservation fieldObservation
, DerivedUnitFacadeConfigurator config
) {
250 config
= DerivedUnitFacadeConfigurator
.NewInstance();
252 this.config
= config
;
255 derivedUnit
= type
.getNewDerivedUnitInstance();
256 setFieldObservation(fieldObservation
);
257 if (derivedUnit
!= null){
260 setFieldObservationCacheStrategy();
264 private DerivedUnitFacade(DerivedUnitBase derivedUnit
,
265 DerivedUnitFacadeConfigurator config
)
266 throws DerivedUnitFacadeNotSupportedException
{
268 if (config
== null) {
269 config
= DerivedUnitFacadeConfigurator
.NewInstance();
271 this.config
= config
;
274 this.derivedUnit
= derivedUnit
;
275 this.type
= DerivedUnitType
.valueOf2(this.derivedUnit
.getClass());
278 if (this.derivedUnit
.getDerivedFrom() != null) {
279 DerivationEvent derivationEvent
= getDerivationEvent(CREATE
);
281 Set
<FieldObservation
> fieldOriginals
= getFieldObservationsOriginals(
282 derivationEvent
, null);
283 if (fieldOriginals
.size() > 1) {
284 throw new DerivedUnitFacadeNotSupportedException(
285 "Specimen must not have more than 1 derivation event");
286 } else if (fieldOriginals
.size() == 0) {
287 // fieldObservation = FieldObservation.NewInstance();
288 } else if (fieldOriginals
.size() == 1) {
289 fieldObservation
= fieldOriginals
.iterator().next();
290 // ###fieldObservation =
291 // getInitializedFieldObservation(fieldObservation);
292 if (config
.isFirePropertyChangeEvents()){
293 addNewEventPropagationListener(fieldObservation
);
296 throw new IllegalStateException("Illegal state");
300 this.derivedUnitMediaTextData
= inititializeTextDataWithSupportTest(
301 Feature
.IMAGE(), this.derivedUnit
, false, true);
303 fieldObjectMediaTextData
= initializeFieldObjectTextDataWithSupportTest(
304 Feature
.IMAGE(), false, true);
306 // handle derivedUnit.getMedia()
307 if (derivedUnit
.getMedia().size() > 0) {
308 // TODO better changed model here to allow only one place for images
309 if (this.config
.isMoveDerivedUnitMediaToGallery()) {
310 Set
<Media
> mediaSet
= derivedUnit
.getMedia();
311 for (Media media
: mediaSet
) {
312 this.addDerivedUnitMedia(media
);
314 mediaSet
.removeAll(getDerivedUnitMedia());
316 throw new DerivedUnitFacadeNotSupportedException(
317 "Specimen may not have direct media. Only (one) image gallery is allowed");
321 // handle fieldObservation.getMedia()
322 if (fieldObservation
!= null && fieldObservation
.getMedia() != null
323 && fieldObservation
.getMedia().size() > 0) {
324 // TODO better changed model here to allow only one place for images
325 if (this.config
.isMoveFieldObjectMediaToGallery()) {
326 Set
<Media
> mediaSet
= fieldObservation
.getMedia();
327 for (Media media
: mediaSet
) {
328 this.addFieldObjectMedia(media
);
330 mediaSet
.removeAll(getFieldObjectMedia());
332 throw new DerivedUnitFacadeNotSupportedException(
333 "Field object may not have direct media. Only (one) image gallery is allowed");
337 // test if descriptions are supported
338 ecology
= initializeFieldObjectTextDataWithSupportTest(
339 Feature
.ECOLOGY(), false, false);
340 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
341 Feature
.DESCRIPTION(), false, false);
347 private DerivedUnitBase
getInitializedDerivedUnit(
348 DerivedUnitBase derivedUnit
) {
349 IOccurrenceService occurrenceService
= this.config
350 .getOccurrenceService();
351 if (occurrenceService
== null) {
354 List
<String
> propertyPaths
= this.config
.getPropertyPaths();
355 if (propertyPaths
== null) {
358 propertyPaths
= getDerivedUnitPropertyPaths(propertyPaths
);
359 DerivedUnitBase result
= (DerivedUnitBase
) occurrenceService
.load(
360 derivedUnit
.getUuid(), propertyPaths
);
365 * Initializes the derived unit according to the configuartions property
366 * path. If the property path is <code>null</code> or no occurrence service
367 * is given the returned object is the same as the input parameter.
369 * @param fieldObservation2
372 private FieldObservation
getInitializedFieldObservation(
373 FieldObservation fieldObservation
) {
374 IOccurrenceService occurrenceService
= this.config
375 .getOccurrenceService();
376 if (occurrenceService
== null) {
377 return fieldObservation
;
379 List
<String
> propertyPaths
= this.config
.getPropertyPaths();
380 if (propertyPaths
== null) {
381 return fieldObservation
;
383 propertyPaths
= getFieldObjectPropertyPaths(propertyPaths
);
384 FieldObservation result
= (FieldObservation
) occurrenceService
.load(
385 fieldObservation
.getUuid(), propertyPaths
);
390 * Transforms the property paths in a way that the facade is handled just
391 * like an ordinary CdmBase object.<BR>
392 * E.g. a property path "collectinAreas" will be translated into
393 * gatheringEvent.collectingAreas
395 * @param propertyPaths
398 private List
<String
> getFieldObjectPropertyPaths(List
<String
> propertyPaths
) {
399 List
<String
> result
= new ArrayList
<String
>();
400 for (String facadePath
: propertyPaths
) {
401 // collecting areas (named area)
402 if (facadePath
.startsWith("collectingAreas")) {
403 facadePath
= "gatheringEvent." + facadePath
;
404 result
.add(facadePath
);
406 // collector (agentBase)
407 else if (facadePath
.startsWith("collector")) {
408 facadePath
= facadePath
.replace("collector",
409 "gatheringEvent.actor");
410 result
.add(facadePath
);
412 // exactLocation (agentBase)
413 else if (facadePath
.startsWith("exactLocation")) {
414 facadePath
= "gatheringEvent." + facadePath
;
415 result
.add(facadePath
);
417 // gatheringPeriod (TimePeriod)
418 else if (facadePath
.startsWith("gatheringPeriod")) {
419 facadePath
= facadePath
.replace("gatheringPeriod",
420 "gatheringEvent.timeperiod");
421 result
.add(facadePath
);
423 // (locality/ localityLanguage , LanguageString)
424 else if (facadePath
.startsWith("locality")) {
425 facadePath
= "gatheringEvent." + facadePath
;
426 result
.add(facadePath
);
429 // *********** FIELD OBJECT ************
430 // fieldObjectDefinitions (Map<language, languageString)
431 else if (facadePath
.startsWith("fieldObjectDefinitions")) {
432 // TODO or definition ???
433 facadePath
= facadePath
.replace("fieldObjectDefinitions",
435 result
.add(facadePath
);
437 // fieldObjectMedia (Media)
438 else if (facadePath
.startsWith("fieldObjectMedia")) {
440 facadePath
= facadePath
.replace("fieldObjectMedia",
441 "descriptions.elements.media");
442 result
.add(facadePath
);
445 // Gathering Event will always be added
446 result
.add("gatheringEvent");
451 * Gathering Event ==================== - gatheringEvent
454 * Field Object ================= - ecology/ ecologyAll (String) ??? -
455 * plant description (like ecology)
457 * - fieldObjectImageGallery (SpecimenDescription) - is automatically
458 * initialized via fieldObjectMedia
465 * Transforms the property paths in a way that the facade is handled just
466 * like an ordinary CdmBase object.<BR>
467 * E.g. a property path "collectinAreas" will be translated into
468 * gatheringEvent.collectingAreas
470 * Not needed (?) as the facade works with REST service property paths
471 * without using this method.
473 * @param propertyPaths
476 private List
<String
> getDerivedUnitPropertyPaths(List
<String
> propertyPaths
) {
477 List
<String
> result
= new ArrayList
<String
>();
478 for (String facadePath
: propertyPaths
) {
479 // determinations (DeterminationEvent)
480 if (facadePath
.startsWith("determinations")) {
481 facadePath
= "" + facadePath
; // no change
482 result
.add(facadePath
);
484 // storedUnder (TaxonNameBase)
485 else if (facadePath
.startsWith("storedUnder")) {
486 facadePath
= "" + facadePath
; // no change
487 result
.add(facadePath
);
489 // sources (IdentifiableSource)
490 else if (facadePath
.startsWith("sources")) {
491 facadePath
= "" + facadePath
; // no change
492 result
.add(facadePath
);
494 // collection (Collection)
495 else if (facadePath
.startsWith("collection")) {
496 facadePath
= "" + facadePath
; // no change
497 result
.add(facadePath
);
499 // (locality/ localityLanguage , LanguageString)
500 else if (facadePath
.startsWith("locality")) {
501 facadePath
= "gatheringEvent." + facadePath
;
502 result
.add(facadePath
);
505 // *********** FIELD OBJECT ************
506 // derivedUnitDefinitions (Map<language, languageString)
507 else if (facadePath
.startsWith("derivedUnitDefinitions")) {
508 // TODO or definition ???
509 facadePath
= facadePath
.replace("derivedUnitDefinitions",
511 result
.add(facadePath
);
514 // derivedUnitMedia (Media)
515 else if (facadePath
.startsWith("derivedUnitMedia")) {
517 facadePath
= facadePath
.replace("derivedUnitMedia",
518 "descriptions.elements.media");
519 result
.add(facadePath
);
525 * //TODO Derived Unit =====================
527 * - derivedUnitImageGallery (SpecimenDescription) - is automatically
528 * initialized via derivedUnitMedia
530 * - derivationEvent (DerivationEvent) - will always be initialized -
531 * duplicates (??? Specimen???) ???
540 private void setCacheStrategy() {
541 if (derivedUnit
== null) {
542 throw new NullPointerException(
543 "Facade's derviedUnit must not be null to set cache strategy");
545 derivedUnit
.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
546 setFieldObservationCacheStrategy();
550 private void setFieldObservationCacheStrategy() {
551 if (this.hasFieldObject()){
552 DerivedUnitFacadeFieldObservationCacheStrategy strategy
= new DerivedUnitFacadeFieldObservationCacheStrategy();
553 this.fieldObservation
.setCacheStrategy(strategy
);
559 * @param createIfNotExists
560 * @param isImageGallery
562 * @throws DerivedUnitFacadeNotSupportedException
564 private TextData
initializeFieldObjectTextDataWithSupportTest(
565 Feature feature
, boolean createIfNotExists
, boolean isImageGallery
)
566 throws DerivedUnitFacadeNotSupportedException
{
568 FieldObservation fieldObject
= getFieldObservation(createIfNotExists
);
569 if (fieldObject
== null) {
572 return inititializeTextDataWithSupportTest(feature
, fieldObject
,
573 createIfNotExists
, isImageGallery
);
579 * @param createIfNotExists
580 * @param isImageGallery
582 * @throws DerivedUnitFacadeNotSupportedException
584 private TextData
inititializeTextDataWithSupportTest(Feature feature
,
585 SpecimenOrObservationBase specimen
, boolean createIfNotExists
,
586 boolean isImageGallery
)
587 throws DerivedUnitFacadeNotSupportedException
{
588 if (feature
== null) {
591 TextData textData
= null;
592 if (createIfNotExists
) {
593 textData
= TextData
.NewInstance(feature
);
596 Set
<SpecimenDescription
> descriptions
;
597 if (isImageGallery
) {
598 descriptions
= specimen
.getSpecimenDescriptionImageGallery();
600 descriptions
= specimen
.getSpecimenDescriptions(false);
602 // no description exists yet for this specimen
603 if (descriptions
.size() == 0) {
604 if (createIfNotExists
) {
605 SpecimenDescription newSpecimenDescription
= SpecimenDescription
606 .NewInstance(specimen
);
607 newSpecimenDescription
.addElement(textData
);
608 newSpecimenDescription
.setImageGallery(isImageGallery
);
614 // description already exists
615 Set
<DescriptionElementBase
> existingTextData
= new HashSet
<DescriptionElementBase
>();
616 for (SpecimenDescription description
: descriptions
) {
617 // collect all existing text data
618 for (DescriptionElementBase element
: description
.getElements()) {
619 if (element
.isInstanceOf(TextData
.class)
620 && (feature
.equals(element
.getFeature()) || isImageGallery
)) {
621 existingTextData
.add(element
);
625 // use existing text data if exactly one exists
626 if (existingTextData
.size() > 1) {
627 throw new DerivedUnitFacadeNotSupportedException(
628 "Specimen facade does not support more than one description text data of type "
629 + feature
.getLabel());
631 } else if (existingTextData
.size() == 1) {
632 return CdmBase
.deproxy(existingTextData
.iterator().next(),
635 if (createIfNotExists
) {
636 SpecimenDescription description
= descriptions
.iterator()
638 description
.addElement(textData
);
645 * Tests if a given image gallery is supported by the derived unit facade.
646 * It returns the only text data attached to the given image gallery. If the
647 * given image gallery does not have text data attached, it is created and
650 * @param imageGallery
652 * @throws DerivedUnitFacadeNotSupportedException
654 private TextData
testImageGallery(SpecimenDescription imageGallery
)
655 throws DerivedUnitFacadeNotSupportedException
{
656 if (imageGallery
.isImageGallery() == false) {
657 throw new DerivedUnitFacadeNotSupportedException(
658 "Image gallery needs to have image gallery flag set");
660 if (imageGallery
.getElements().size() > 1) {
661 throw new DerivedUnitFacadeNotSupportedException(
662 "Image gallery must not have more then one description element");
665 if (imageGallery
.getElements().size() == 0) {
666 textData
= TextData
.NewInstance(Feature
.IMAGE());
667 imageGallery
.addElement(textData
);
669 if (!imageGallery
.getElements().iterator().next()
670 .isInstanceOf(TextData
.class)) {
671 throw new DerivedUnitFacadeNotSupportedException(
672 "Image gallery must only have TextData as element");
674 textData
= CdmBase
.deproxy(imageGallery
.getElements()
675 .iterator().next(), TextData
.class);
681 // ************************** METHODS
682 // *****************************************
684 private TextData
getDerivedUnitImageGalleryTextData(
685 boolean createIfNotExists
)
686 throws DerivedUnitFacadeNotSupportedException
{
687 if (this.derivedUnitMediaTextData
== null && createIfNotExists
) {
688 this.derivedUnitMediaTextData
= getImageGalleryTextData(
689 derivedUnit
, "Specimen");
691 return this.derivedUnitMediaTextData
;
694 private TextData
getObservationImageGalleryTextData(
695 boolean createIfNotExists
)
696 throws DerivedUnitFacadeNotSupportedException
{
697 if (this.fieldObjectMediaTextData
== null && createIfNotExists
) {
698 this.fieldObjectMediaTextData
= getImageGalleryTextData(
699 fieldObservation
, "Field observation");
701 return this.fieldObjectMediaTextData
;
705 * @param derivationEvent
707 * @throws DerivedUnitFacadeNotSupportedException
709 private Set
<FieldObservation
> getFieldObservationsOriginals(
710 DerivationEvent derivationEvent
,
711 Set
<SpecimenOrObservationBase
> recursionAvoidSet
)
712 throws DerivedUnitFacadeNotSupportedException
{
713 if (recursionAvoidSet
== null) {
714 recursionAvoidSet
= new HashSet
<SpecimenOrObservationBase
>();
716 Set
<FieldObservation
> result
= new HashSet
<FieldObservation
>();
717 Set
<SpecimenOrObservationBase
> originals
= derivationEvent
.getOriginals();
718 for (SpecimenOrObservationBase original
: originals
) {
719 if (original
.isInstanceOf(FieldObservation
.class)) {
720 result
.add(CdmBase
.deproxy(original
, FieldObservation
.class));
721 } else if (original
.isInstanceOf(DerivedUnitBase
.class)) {
722 // if specimen has already been tested exclude it from further
724 if (recursionAvoidSet
.contains(original
)) {
727 DerivedUnitBase derivedUnit
= CdmBase
.deproxy(original
,
728 DerivedUnitBase
.class);
729 DerivationEvent originalDerivation
= derivedUnit
.getDerivedFrom();
730 // Set<DerivationEvent> derivationEvents =
731 // original.getDerivationEvents();
732 // for (DerivationEvent originalDerivation : derivationEvents){
733 Set
<FieldObservation
> fieldObservations
= getFieldObservationsOriginals(
734 originalDerivation
, recursionAvoidSet
);
735 result
.addAll(fieldObservations
);
738 throw new DerivedUnitFacadeNotSupportedException(
739 "Unhandled specimen or observation base type: "
740 + original
.getClass().getName());
747 // *********** MEDIA METHODS ******************************
750 // * Returns the media list for a specimen. Throws an exception if the
751 // existing specimen descriptions
752 // * are not supported by this facade.
753 // * @param specimen the specimen the media belongs to
754 // * @param specimenExceptionText text describing the specimen for exception
757 // * @throws DerivedUnitFacadeNotSupportedException
759 // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase
760 // specimen, String specimenExceptionText) throws
761 // DerivedUnitFacadeNotSupportedException{
762 // List<Media> result;
763 // SpecimenDescription imageGallery =
764 // getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
765 // TextData textData = getImageTextDataWithSupportTest(imageGallery,
766 // specimenExceptionText);
767 // result = textData.getMedia();
772 * Returns the media list for a specimen. Throws an exception if the
773 * existing specimen descriptions are not supported by this facade.
776 * the specimen the media belongs to
777 * @param specimenExceptionText
778 * text describing the specimen for exception messages
780 * @throws DerivedUnitFacadeNotSupportedException
782 private TextData
getImageGalleryTextData(SpecimenOrObservationBase specimen
, String specimenExceptionText
)
783 throws DerivedUnitFacadeNotSupportedException
{
785 SpecimenDescription imageGallery
= getImageGalleryWithSupportTest(
786 specimen
, specimenExceptionText
, true);
787 result
= getImageTextDataWithSupportTest(imageGallery
,
788 specimenExceptionText
);
793 * Returns the image gallery of the according specimen. Throws an exception
794 * if the attached image gallerie(s) are not supported by this facade. If no
795 * image gallery exists a new one is created if
796 * <code>createNewIfNotExists</code> is true and if specimen is not
800 * @param specimenText
801 * @param createNewIfNotExists
803 * @throws DerivedUnitFacadeNotSupportedException
805 private SpecimenDescription
getImageGalleryWithSupportTest(
806 SpecimenOrObservationBase
<?
> specimen
, String specimenText
,
807 boolean createNewIfNotExists
)
808 throws DerivedUnitFacadeNotSupportedException
{
809 if (specimen
== null) {
812 SpecimenDescription imageGallery
;
813 if (hasMultipleImageGalleries(specimen
)) {
814 throw new DerivedUnitFacadeNotSupportedException(specimenText
815 + " must not have more than 1 image gallery");
817 imageGallery
= getImageGallery(specimen
, createNewIfNotExists
);
818 getImageTextDataWithSupportTest(imageGallery
, specimenText
);
824 * Returns the media holding text data element of the image gallery. Throws
825 * an exception if multiple such text data already exist. Creates a new text
826 * data if none exists and adds it to the image gallery. If image gallery is
827 * <code>null</code> nothing happens.
829 * @param imageGallery
832 * @throws DerivedUnitFacadeNotSupportedException
834 private TextData
getImageTextDataWithSupportTest(
835 SpecimenDescription imageGallery
, String specimenText
)
836 throws DerivedUnitFacadeNotSupportedException
{
837 if (imageGallery
== null) {
840 TextData textData
= null;
841 for (DescriptionElementBase element
: imageGallery
.getElements()) {
842 if (element
.isInstanceOf(TextData
.class)
843 && element
.getFeature().equals(Feature
.IMAGE())) {
844 if (textData
!= null) {
845 throw new DerivedUnitFacadeNotSupportedException(
847 + " must not have more than 1 image text data element in image gallery");
849 textData
= CdmBase
.deproxy(element
, TextData
.class);
852 if (textData
== null) {
853 textData
= TextData
.NewInstance(Feature
.IMAGE());
854 imageGallery
.addElement(textData
);
860 * Checks, if a specimen belongs to more than one description that is an
866 private boolean hasMultipleImageGalleries(
867 SpecimenOrObservationBase
<?
> derivedUnit
) {
869 Set
<SpecimenDescription
> descriptions
= derivedUnit
870 .getSpecimenDescriptions();
871 for (SpecimenDescription description
: descriptions
) {
872 if (description
.isImageGallery()) {
880 * Returns the image gallery for a specimen. If there are multiple specimen
881 * descriptions marked as image galleries an arbitrary one is chosen. If no
882 * image gallery exists, a new one is created if
883 * <code>createNewIfNotExists</code> is <code>true</code>.<Br>
884 * If specimen is <code>null</code> a null pointer exception is thrown.
886 * @param createNewIfNotExists
889 private SpecimenDescription
getImageGallery(SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
) {
890 SpecimenDescription result
= null;
891 Set
<SpecimenDescription
> descriptions
= specimen
.getSpecimenDescriptions();
892 for (SpecimenDescription description
: descriptions
) {
893 if (description
.isImageGallery()) {
894 result
= description
;
898 if (result
== null && createIfNotExists
) {
899 result
= SpecimenDescription
.NewInstance(specimen
);
900 result
.setImageGallery(true);
906 * Adds a media to the specimens image gallery. If media is
907 * <code>null</code> nothing happens.
911 * @return true if media is not null (as specified by
912 * {@link java.util.Collection#add(Object) Collection.add(E e)}
913 * @throws DerivedUnitFacadeNotSupportedException
915 private boolean addMedia(Media media
, SpecimenOrObservationBase
<?
> specimen
) throws DerivedUnitFacadeNotSupportedException
{
917 List
<Media
> mediaList
= getMediaList(specimen
, true);
918 if (! mediaList
.contains(media
)){
919 return mediaList
.add(media
);
929 * Removes a media from the specimens image gallery.
933 * @return true if an element was removed as a result of this call (as
934 * specified by {@link java.util.Collection#remove(Object)
935 * Collection.remove(E e)}
936 * @throws DerivedUnitFacadeNotSupportedException
938 private boolean removeMedia(Media media
,
939 SpecimenOrObservationBase
<?
> specimen
)
940 throws DerivedUnitFacadeNotSupportedException
{
941 List
<Media
> mediaList
= getMediaList(specimen
, true);
942 return mediaList
== null ?
null : mediaList
.remove(media
);
945 private List
<Media
> getMediaList(SpecimenOrObservationBase
<?
> specimen
, boolean createIfNotExists
)
946 throws DerivedUnitFacadeNotSupportedException
{
947 TextData textData
= getMediaTextData(specimen
, createIfNotExists
);
948 return textData
== null ?
null : textData
.getMedia();
952 * Returns the one media list of a specimen which is part of the only image
953 * gallery that this specimen is part of.<BR>
954 * If these conditions are not hold an exception is thrwon.
958 * @throws DerivedUnitFacadeNotSupportedException
960 // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)
961 // throws DerivedUnitFacadeNotSupportedException {
962 // if (specimen == null){
965 // if (specimen == this.derivedUnit){
966 // return getDerivedUnitImageGalleryMedia();
967 // }else if (specimen == this.fieldObservation){
968 // return getObservationImageGalleryTextData();
970 // return getImageGalleryMedia(specimen, "Undefined specimen ");
975 * Returns the one media list of a specimen which is part of the only image
976 * gallery that this specimen is part of.<BR>
977 * If these conditions are not hold an exception is thrwon.
981 * @throws DerivedUnitFacadeNotSupportedException
983 private TextData
getMediaTextData(SpecimenOrObservationBase
<?
> specimen
,
984 boolean createIfNotExists
)
985 throws DerivedUnitFacadeNotSupportedException
{
986 if (specimen
== null) {
989 if (specimen
== this.derivedUnit
) {
990 return getDerivedUnitImageGalleryTextData(createIfNotExists
);
991 } else if (specimen
== this.fieldObservation
) {
992 return getObservationImageGalleryTextData(createIfNotExists
);
994 return getImageGalleryTextData(specimen
, "Undefined specimen ");
998 // ****************** GETTER / SETTER / ADDER / REMOVER
999 // ***********************/
1001 // ****************** Gathering Event *********************************/
1005 public NamedArea
getCountry() {
1006 return (hasGatheringEvent() ?
getGatheringEvent(true).getCountry()
1010 public void setCountry(NamedArea country
) {
1011 getGatheringEvent(true).setCountry(country
);
1015 public void addCollectingArea(NamedArea area
) {
1016 getGatheringEvent(true).addCollectingArea(area
);
1019 public void addCollectingAreas(java
.util
.Collection
<NamedArea
> areas
) {
1020 for (NamedArea area
: areas
) {
1021 getGatheringEvent(true).addCollectingArea(area
);
1026 public Set
<NamedArea
> getCollectingAreas() {
1027 return (hasGatheringEvent() ?
getGatheringEvent(true)
1028 .getCollectingAreas() : null);
1031 public void removeCollectingArea(NamedArea area
) {
1032 if (hasGatheringEvent()) {
1033 getGatheringEvent(true).removeCollectingArea(area
);
1037 // absolute elevation
1039 * meter above/below sea level of the surface
1041 * @see #getAbsoluteElevationError()
1042 * @see #getAbsoluteElevationRange()
1045 public Integer
getAbsoluteElevation() {
1046 return (hasGatheringEvent() ?
getGatheringEvent(true)
1047 .getAbsoluteElevation() : null);
1050 public void setAbsoluteElevation(Integer absoluteElevation
) {
1051 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation
);
1054 // absolute elevation error
1056 public Integer
getAbsoluteElevationError() {
1057 return (hasGatheringEvent() ?
getGatheringEvent(true)
1058 .getAbsoluteElevationError() : null);
1061 public void setAbsoluteElevationError(Integer absoluteElevationError
) {
1062 getGatheringEvent(true).setAbsoluteElevationError(
1063 absoluteElevationError
);
1067 * @see #getAbsoluteElevation()
1068 * @see #getAbsoluteElevationError()
1069 * @see #setAbsoluteElevationRange(Integer, Integer)
1070 * @see #getAbsoluteElevationMaximum()
1073 public Integer
getAbsoluteElevationMinimum() {
1074 if (!hasGatheringEvent()) {
1077 Integer minimum
= getGatheringEvent(true).getAbsoluteElevation();
1078 if (getGatheringEvent(true).getAbsoluteElevationError() != null) {
1080 - getGatheringEvent(true).getAbsoluteElevationError();
1086 * @see #getAbsoluteElevation()
1087 * @see #getAbsoluteElevationError()
1088 * @see #setAbsoluteElevationRange(Integer, Integer)
1089 * @see #getAbsoluteElevationMinimum()
1092 public Integer
getAbsoluteElevationMaximum() {
1093 if (!hasGatheringEvent()) {
1096 Integer maximum
= getGatheringEvent(true).getAbsoluteElevation();
1097 if (getGatheringEvent(true).getAbsoluteElevationError() != null) {
1099 + getGatheringEvent(true).getAbsoluteElevationError();
1105 * This method replaces absoluteElevation and absoulteElevationError by
1106 * internally translating minimum and maximum values into average and error
1107 * values. As all these values are integer based it is necessary that the
1108 * distance is between minimum and maximum is <b>even</b>, otherwise we will
1109 * get a rounding error resulting in a maximum that is increased by 1.
1111 * @see #setAbsoluteElevation(Integer)
1112 * @see #setAbsoluteElevationError(Integer)
1113 * @param minimumElevation
1114 * minimum of the range
1115 * @param maximumElevation
1116 * maximum of the range
1117 * @throws IllegalArgumentException
1119 public void setAbsoluteElevationRange(Integer minimumElevation
, Integer maximumElevation
) throws IllegalArgumentException
{
1120 if (minimumElevation
== null || maximumElevation
== null) {
1121 Integer elevation
= minimumElevation
;
1123 if (minimumElevation
== null) {
1124 elevation
= maximumElevation
;
1125 if (elevation
== null) {
1129 getGatheringEvent(true).setAbsoluteElevation(elevation
);
1130 getGatheringEvent(true).setAbsoluteElevationError(error
);
1132 if (!isEvenDistance(minimumElevation
, maximumElevation
)) {
1133 throw new IllegalArgumentException(
1134 "Distance between minimum and maximum elevation must be even but was "
1135 + Math
.abs(minimumElevation
- maximumElevation
));
1137 Integer absoluteElevationError
= Math
.abs(maximumElevation
1138 - minimumElevation
);
1139 absoluteElevationError
= absoluteElevationError
/ 2;
1140 Integer absoluteElevation
= minimumElevation
1141 + absoluteElevationError
;
1142 getGatheringEvent(true).setAbsoluteElevation(absoluteElevation
);
1143 getGatheringEvent(true).setAbsoluteElevationError(
1144 absoluteElevationError
);
1149 * @param minimumElevation
1150 * @param maximumElevation
1153 public boolean isEvenDistance(Integer minimumElevation
,
1154 Integer maximumElevation
) {
1155 Integer diff
= (maximumElevation
- minimumElevation
);
1156 return diff
% 2 == 0;
1161 public AgentBase
getCollector() {
1162 return (hasGatheringEvent() ?
getGatheringEvent(true).getCollector()
1166 public void setCollector(AgentBase collector
) {
1167 getGatheringEvent(true).setCollector(collector
);
1170 // collecting method
1172 public String
getCollectingMethod() {
1173 return (hasGatheringEvent() ?
getGatheringEvent(true)
1174 .getCollectingMethod() : null);
1177 public void setCollectingMethod(String collectingMethod
) {
1178 getGatheringEvent(true).setCollectingMethod(collectingMethod
);
1181 // distance to ground
1183 public Integer
getDistanceToGround() {
1184 return (hasGatheringEvent() ?
getGatheringEvent(true)
1185 .getDistanceToGround() : null);
1188 public void setDistanceToGround(Integer distanceToGround
) {
1189 getGatheringEvent(true).setDistanceToGround(distanceToGround
);
1192 // distance to water surface
1194 public Integer
getDistanceToWaterSurface() {
1195 return (hasGatheringEvent() ?
getGatheringEvent(true)
1196 .getDistanceToWaterSurface() : null);
1199 public void setDistanceToWaterSurface(Integer distanceToWaterSurface
) {
1200 getGatheringEvent(true).setDistanceToWaterSurface(
1201 distanceToWaterSurface
);
1206 public Point
getExactLocation() {
1207 return (hasGatheringEvent() ?
getGatheringEvent(true).getExactLocation() : null);
1211 * Returns a sexagesimal representation of the exact location (e.g.
1212 * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty
1213 * string is returned.
1215 * @param includeEmptySeconds
1216 * @param includeReferenceSystem
1219 public String
getExactLocationText(boolean includeEmptySeconds
,
1220 boolean includeReferenceSystem
) {
1221 return (this.getExactLocation() == null ?
"" : this.getExactLocation()
1222 .toSexagesimalString(includeEmptySeconds
,
1223 includeReferenceSystem
));
1226 public void setExactLocation(Point exactLocation
) {
1227 getGatheringEvent(true).setExactLocation(exactLocation
);
1230 public void setExactLocationByParsing(String longitudeToParse
,
1231 String latitudeToParse
, ReferenceSystem referenceSystem
,
1232 Integer errorRadius
) throws ParseException
{
1233 Point point
= Point
.NewInstance(null, null, referenceSystem
,
1235 point
.setLongitudeByParsing(longitudeToParse
);
1236 point
.setLatitudeByParsing(latitudeToParse
);
1237 setExactLocation(point
);
1240 // gathering event description
1242 public String
getGatheringEventDescription() {
1243 return (hasGatheringEvent() ?
getGatheringEvent(true).getDescription()
1247 public void setGatheringEventDescription(String description
) {
1248 getGatheringEvent(true).setDescription(description
);
1253 public TimePeriod
getGatheringPeriod() {
1254 return (hasGatheringEvent() ?
getGatheringEvent(true).getTimeperiod()
1258 public void setGatheringPeriod(TimePeriod timeperiod
) {
1259 getGatheringEvent(true).setTimeperiod(timeperiod
);
1264 public LanguageString
getLocality() {
1265 return (hasGatheringEvent() ?
getGatheringEvent(true).getLocality()
1270 * convienience method for {@link #getLocality()}.
1271 * {@link LanguageString#getText() getText()}
1276 public String
getLocalityText() {
1277 LanguageString locality
= getLocality();
1278 if (locality
!= null) {
1279 return locality
.getText();
1285 * convienience method for {@link #getLocality()}.
1286 * {@link LanguageString#getLanguage() getLanguage()}
1291 public Language
getLocalityLanguage() {
1292 LanguageString locality
= getLocality();
1293 if (locality
!= null) {
1294 return locality
.getLanguage();
1300 * Sets the locality string in the default language
1304 public void setLocality(String locality
) {
1305 Language language
= Language
.DEFAULT();
1306 setLocality(locality
, language
);
1309 public void setLocality(String locality
, Language language
) {
1310 LanguageString langString
= LanguageString
.NewInstance(locality
,
1312 setLocality(langString
);
1315 public void setLocality(LanguageString locality
) {
1316 getGatheringEvent(true).setLocality(locality
);
1320 * The gathering event will be used for the field object instead of the old
1321 * gathering event.<BR>
1322 * <B>This method will override all gathering values (see below).</B>
1324 * @see #getAbsoluteElevation()
1325 * @see #getAbsoluteElevationError()
1326 * @see #getDistanceToGround()
1327 * @see #getDistanceToWaterSurface()
1328 * @see #getExactLocation()
1329 * @see #getGatheringEventDescription()
1330 * @see #getGatheringPeriod()
1331 * @see #getCollectingAreas()
1332 * @see #getCollectingMethod()
1333 * @see #getLocality()
1334 * @see #getCollector()
1335 * @param gatheringEvent
1337 public void setGatheringEvent(GatheringEvent gatheringEvent
) {
1338 getFieldObservation(true).setGatheringEvent(gatheringEvent
);
1341 public boolean hasGatheringEvent() {
1342 return (getGatheringEvent(false) != null);
1345 public GatheringEvent
innerGatheringEvent() {
1346 return getGatheringEvent(false);
1349 public GatheringEvent
getGatheringEvent(boolean createIfNotExists
) {
1350 if (!hasFieldObservation() && !createIfNotExists
) {
1353 if (createIfNotExists
&& getFieldObservation(true).getGatheringEvent() == null) {
1354 GatheringEvent gatheringEvent
= GatheringEvent
.NewInstance();
1355 getFieldObservation(true).setGatheringEvent(gatheringEvent
);
1357 return getFieldObservation(true).getGatheringEvent();
1360 // ****************** Field Object ************************************/
1363 * Returns true if a field observation exists (even if all attributes are
1364 * empty or <code>null<code>.
1368 public boolean hasFieldObject() {
1369 return this.fieldObservation
!= null;
1374 public String
getEcology() {
1375 return getEcology(Language
.DEFAULT());
1378 public String
getEcology(Language language
) {
1379 LanguageString languageString
= getEcologyAll().get(language
);
1380 return (languageString
== null ?
null : languageString
.getText());
1383 // public String getEcologyPreferred(List<Language> languages){
1384 // LanguageString languageString =
1385 // getEcologyAll().getPreferredLanguageString(languages);
1386 // return languageString.getText();
1389 * Returns a copy of the multilanguage text holding the ecology data.
1391 * @see {@link TextData#getMultilanguageText()}
1395 public Map
<Language
, LanguageString
> getEcologyAll() {
1396 if (ecology
== null) {
1398 ecology
= initializeFieldObjectTextDataWithSupportTest(
1399 Feature
.ECOLOGY(), false, false);
1400 } catch (DerivedUnitFacadeNotSupportedException e
) {
1401 throw new IllegalStateException(notSupportMessage
, e
);
1403 if (ecology
== null) {
1404 return new HashMap
<Language
, LanguageString
>();
1407 return ecology
.getMultilanguageText();
1410 public void setEcology(String ecology
) {
1411 setEcology(ecology
, null);
1414 public void setEcology(String ecologyText
, Language language
) {
1415 if (language
== null) {
1416 language
= Language
.DEFAULT();
1418 if (ecology
== null) {
1420 ecology
= initializeFieldObjectTextDataWithSupportTest(
1421 Feature
.ECOLOGY(), true, false);
1422 } catch (DerivedUnitFacadeNotSupportedException e
) {
1423 throw new IllegalStateException(notSupportMessage
, e
);
1426 if (ecologyText
== null) {
1427 ecology
.removeText(language
);
1429 ecology
.putText(language
, ecologyText
);
1433 public void removeEcology(Language language
) {
1434 setEcology(null, language
);
1438 * Removes ecology for the default language
1440 public void removeEcology() {
1441 setEcology(null, null);
1444 public void removeEcologyAll() {
1448 // plant description
1450 public String
getPlantDescription() {
1451 return getPlantDescription(null);
1454 public String
getPlantDescription(Language language
) {
1455 if (language
== null) {
1456 language
= Language
.DEFAULT();
1458 LanguageString languageString
= getPlantDescriptionAll().get(language
);
1459 return (languageString
== null ?
null : languageString
.getText());
1462 // public String getPlantDescriptionPreferred(List<Language> languages){
1463 // LanguageString languageString =
1464 // getPlantDescriptionAll().getPreferredLanguageString(languages);
1465 // return languageString.getText();
1468 * Returns a copy of the multilanguage text holding the description data.
1470 * @see {@link TextData#getMultilanguageText()}
1474 public Map
<Language
, LanguageString
> getPlantDescriptionAll() {
1475 if (plantDescription
== null) {
1477 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
1478 Feature
.DESCRIPTION(), false, false);
1479 } catch (DerivedUnitFacadeNotSupportedException e
) {
1480 throw new IllegalStateException(notSupportMessage
, e
);
1482 if (plantDescription
== null) {
1483 return new HashMap
<Language
, LanguageString
>();
1486 return plantDescription
.getMultilanguageText();
1489 public void setPlantDescription(String plantDescription
) {
1490 setPlantDescription(plantDescription
, null);
1493 public void setPlantDescription(String plantDescriptionText
,
1494 Language language
) {
1495 if (language
== null) {
1496 language
= Language
.DEFAULT();
1498 if (plantDescription
== null) {
1500 plantDescription
= initializeFieldObjectTextDataWithSupportTest(
1501 Feature
.DESCRIPTION(), true, false);
1502 } catch (DerivedUnitFacadeNotSupportedException e
) {
1503 throw new IllegalStateException(notSupportMessage
, e
);
1506 if (plantDescriptionText
== null) {
1507 plantDescription
.removeText(language
);
1509 plantDescription
.putText(language
, plantDescriptionText
);
1513 public void removePlantDescription(Language language
) {
1514 setPlantDescription(null, language
);
1517 // field object definition
1518 public void addFieldObjectDefinition(String text
, Language language
) {
1519 getFieldObservation(true).putDefinition(language
, text
);
1523 public Map
<Language
, LanguageString
> getFieldObjectDefinition() {
1524 if (!hasFieldObservation()) {
1525 return new HashMap
<Language
, LanguageString
>();
1527 return getFieldObservation(true).getDefinition();
1531 public String
getFieldObjectDefinition(Language language
) {
1532 Map
<Language
, LanguageString
> map
= getFieldObjectDefinition();
1533 LanguageString languageString
= (map
== null ?
null : map
.get(language
));
1534 if (languageString
!= null) {
1535 return languageString
.getText();
1541 public void removeFieldObjectDefinition(Language lang
) {
1542 if (hasFieldObservation()) {
1543 getFieldObservation(true).removeDefinition(lang
);
1548 public boolean addFieldObjectMedia(Media media
) {
1550 return addMedia(media
, getFieldObservation(true));
1551 } catch (DerivedUnitFacadeNotSupportedException e
) {
1552 throw new IllegalStateException(notSupportMessage
, e
);
1557 * Returns true, if an image gallery for the field object exists.<BR>
1558 * Returns also <code>true</code> if the image gallery is empty.
1562 public boolean hasFieldObjectImageGallery() {
1563 if (!hasFieldObject()) {
1566 return (getImageGallery(fieldObservation
, false) != null);
1570 public void setFieldObjectImageGallery(SpecimenDescription imageGallery
)
1571 throws DerivedUnitFacadeNotSupportedException
{
1572 SpecimenDescription existingGallery
= getFieldObjectImageGallery(false);
1574 // test attached specimens contain this.derivedUnit
1575 SpecimenOrObservationBase
<?
> facadeFieldObservation
= innerFieldObservation();
1576 testSpecimenInImageGallery(imageGallery
, facadeFieldObservation
);
1578 if (existingGallery
!= null) {
1579 if (existingGallery
!= imageGallery
) {
1580 throw new DerivedUnitFacadeNotSupportedException(
1581 "DerivedUnitFacade does not allow more than one image gallery");
1586 TextData textData
= testImageGallery(imageGallery
);
1587 this.fieldObjectMediaTextData
= textData
;
1592 * Returns the field object image gallery. If no such image gallery exists
1593 * and createIfNotExists is true an new one is created. Otherwise null is
1596 * @param createIfNotExists
1599 public SpecimenDescription
getFieldObjectImageGallery(
1600 boolean createIfNotExists
) {
1603 textData
= initializeFieldObjectTextDataWithSupportTest(
1604 Feature
.IMAGE(), createIfNotExists
, true);
1605 } catch (DerivedUnitFacadeNotSupportedException e
) {
1606 throw new IllegalStateException(notSupportMessage
, e
);
1608 if (textData
!= null) {
1609 return CdmBase
.deproxy(textData
.getInDescription(),
1610 SpecimenDescription
.class);
1617 * Returns the media for the field object.<BR>
1622 public List
<Media
> getFieldObjectMedia() {
1624 List
<Media
> result
= getMediaList(getFieldObservation(false), false);
1625 return result
== null ?
new ArrayList
<Media
>() : result
;
1626 } catch (DerivedUnitFacadeNotSupportedException e
) {
1627 throw new IllegalStateException(notSupportMessage
, e
);
1631 public boolean removeFieldObjectMedia(Media media
) {
1633 return removeMedia(media
, getFieldObservation(false));
1634 } catch (DerivedUnitFacadeNotSupportedException e
) {
1635 throw new IllegalStateException(notSupportMessage
, e
);
1641 public String
getFieldNumber() {
1642 if (!hasFieldObservation()) {
1645 return getFieldObservation(true).getFieldNumber();
1649 public void setFieldNumber(String fieldNumber
) {
1650 getFieldObservation(true).setFieldNumber(fieldNumber
);
1653 // primary collector
1655 public Person
getPrimaryCollector() {
1656 if (!hasFieldObservation()) {
1659 return getFieldObservation(true).getPrimaryCollector();
1663 public void setPrimaryCollector(Person primaryCollector
) {
1664 getFieldObservation(true).setPrimaryCollector(primaryCollector
);
1669 public String
getFieldNotes() {
1670 if (!hasFieldObservation()) {
1673 return getFieldObservation(true).getFieldNotes();
1677 public void setFieldNotes(String fieldNotes
) {
1678 getFieldObservation(true).setFieldNotes(fieldNotes
);
1681 // individual counts
1683 public Integer
getIndividualCount() {
1684 return (hasFieldObservation() ?
getFieldObservation(true)
1685 .getIndividualCount() : null);
1688 public void setIndividualCount(Integer individualCount
) {
1689 getFieldObservation(true).setIndividualCount(individualCount
);
1694 public Stage
getLifeStage() {
1695 return (hasFieldObservation() ?
getFieldObservation(true)
1696 .getLifeStage() : null);
1699 public void setLifeStage(Stage lifeStage
) {
1700 getFieldObservation(true).setLifeStage(lifeStage
);
1705 public Sex
getSex() {
1706 return (hasFieldObservation() ?
getFieldObservation(true).getSex()
1710 public void setSex(Sex sex
) {
1711 getFieldObservation(true).setSex(sex
);
1714 // field observation
1715 public boolean hasFieldObservation() {
1716 return (getFieldObservation(false) != null);
1720 * Returns the field observation as an object.
1724 public FieldObservation
innerFieldObservation() {
1725 return getFieldObservation(false);
1729 * Returns the field observation as an object.
1733 public FieldObservation
getFieldObservation(boolean createIfNotExists
) {
1734 if (fieldObservation
== null && createIfNotExists
) {
1735 setFieldObservation(FieldObservation
.NewInstance());
1737 return this.fieldObservation
;
1741 private void setFieldObservation(FieldObservation fieldObservation
) {
1742 this.fieldObservation
= fieldObservation
;
1743 if (fieldObservation
!= null){
1744 if (config
.isFirePropertyChangeEvents()){
1745 addNewEventPropagationListener(fieldObservation
);
1747 if (derivedUnit
!= null){
1748 DerivationEvent derivationEvent
= getDerivationEvent(CREATE
);
1749 derivationEvent
.addOriginal(fieldObservation
);
1751 setFieldObservationCacheStrategy();
1755 // ****************** Specimen *******************************************
1758 public void addDerivedUnitDefinition(String text
, Language language
) {
1759 innerDerivedUnit().putDefinition(language
, text
);
1763 public Map
<Language
, LanguageString
> getDerivedUnitDefinitions() {
1765 return this.derivedUnit
.getDefinition();
1769 public String
getDerivedUnitDefinition(Language language
) {
1771 Map
<Language
, LanguageString
> languageMap
= derivedUnit
.getDefinition();
1772 LanguageString languageString
= languageMap
.get(language
);
1773 if (languageString
!= null) {
1774 return languageString
.getText();
1780 public void removeDerivedUnitDefinition(Language lang
) {
1782 derivedUnit
.removeDefinition(lang
);
1786 public void addDetermination(DeterminationEvent determination
) {
1788 //TODO implement correct bidirectional mapping in model classes
1789 determination
.setIdentifiedUnit(derivedUnit
);
1790 derivedUnit
.addDetermination(determination
);
1794 public DeterminationEvent
getPreferredDetermination() {
1796 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1797 for (DeterminationEvent event
: events
){
1798 if (event
.getPreferredFlag() == true){
1806 * This method returns the preferred determination.
1807 * @see #getOtherDeterminations()
1808 * @see #getDeterminations()
1812 public void setPreferredDetermination(DeterminationEvent newEvent
) {
1814 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1815 for (DeterminationEvent event
: events
){
1816 if (event
.getPreferredFlag() == true){
1817 event
.setPreferredFlag(false);
1820 newEvent
.setPreferredFlag(true);
1821 events
.add(newEvent
);
1825 * This method returns all determinations except for the preferred one.
1826 * @see #getPreferredDetermination()
1827 * @see #getDeterminations()
1831 public Set
<DeterminationEvent
> getOtherDeterminations() {
1833 Set
<DeterminationEvent
> events
= derivedUnit
.getDeterminations();
1834 Set
<DeterminationEvent
> result
= new HashSet
<DeterminationEvent
>();
1835 for (DeterminationEvent event
: events
){
1836 if (event
.getPreferredFlag() != true){
1844 * This method returns all determination events. The preferred one {@link #getPreferredDetermination()}
1845 * and all others {@link #getOtherDeterminations()}.
1849 public Set
<DeterminationEvent
> getDeterminations() {
1851 return derivedUnit
.getDeterminations();
1854 public void removeDetermination(DeterminationEvent determination
) {
1856 derivedUnit
.removeDetermination(determination
);
1860 public boolean addDerivedUnitMedia(Media media
) {
1863 return addMedia(media
, derivedUnit
);
1864 } catch (DerivedUnitFacadeNotSupportedException e
) {
1865 throw new IllegalStateException(notSupportMessage
, e
);
1870 * Returns true, if an image gallery exists for the specimen.<BR>
1871 * Returns also <code>true</code> if the image gallery is empty.
1873 public boolean hasDerivedUnitImageGallery() {
1874 return (getImageGallery(derivedUnit
, false) != null);
1877 public SpecimenDescription
getDerivedUnitImageGallery(boolean createIfNotExists
) {
1881 textData
= inititializeTextDataWithSupportTest(Feature
.IMAGE(),
1882 derivedUnit
, createIfNotExists
, true);
1883 } catch (DerivedUnitFacadeNotSupportedException e
) {
1884 throw new IllegalStateException(notSupportMessage
, e
);
1886 if (textData
!= null) {
1887 return CdmBase
.deproxy(textData
.getInDescription(),
1888 SpecimenDescription
.class);
1894 public void setDerivedUnitImageGallery(SpecimenDescription imageGallery
)
1895 throws DerivedUnitFacadeNotSupportedException
{
1897 SpecimenDescription existingGallery
= getDerivedUnitImageGallery(false);
1899 // test attached specimens contain this.derivedUnit
1900 SpecimenOrObservationBase facadeDerivedUnit
= innerDerivedUnit();
1901 testSpecimenInImageGallery(imageGallery
, facadeDerivedUnit
);
1903 if (existingGallery
!= null) {
1904 if (existingGallery
!= imageGallery
) {
1905 throw new DerivedUnitFacadeNotSupportedException(
1906 "DerivedUnitFacade does not allow more than one image gallery");
1911 TextData textData
= testImageGallery(imageGallery
);
1912 this.derivedUnitMediaTextData
= textData
;
1917 * @param imageGallery
1918 * @throws DerivedUnitFacadeNotSupportedException
1920 private void testSpecimenInImageGallery(SpecimenDescription imageGallery
, SpecimenOrObservationBase specimen
)
1921 throws DerivedUnitFacadeNotSupportedException
{
1922 Set
<SpecimenOrObservationBase
> imageGallerySpecimens
= imageGallery
.getDescribedSpecimenOrObservations();
1923 if (imageGallerySpecimens
.size() < 1) {
1924 throw new DerivedUnitFacadeNotSupportedException(
1925 "Image Gallery has no Specimen attached. Please attache according specimen or field observation.");
1927 if (!imageGallerySpecimens
.contains(specimen
)) {
1928 throw new DerivedUnitFacadeNotSupportedException(
1929 "Image Gallery has not the facade's field object attached. Please add field object first to image gallery specimenOrObservation list.");
1934 * Returns the media for the specimen.<BR>
1939 public List
<Media
> getDerivedUnitMedia() {
1942 List
<Media
> result
= getMediaList(derivedUnit
, false);
1943 return result
== null ?
new ArrayList
<Media
>() : result
;
1944 } catch (DerivedUnitFacadeNotSupportedException e
) {
1945 throw new IllegalStateException(notSupportMessage
, e
);
1949 public boolean removeDerivedUnitMedia(Media media
) {
1952 return removeMedia(media
, derivedUnit
);
1953 } catch (DerivedUnitFacadeNotSupportedException e
) {
1954 throw new IllegalStateException(notSupportMessage
, e
);
1960 public String
getAccessionNumber() {
1962 return derivedUnit
.getAccessionNumber();
1965 public void setAccessionNumber(String accessionNumber
) {
1967 derivedUnit
.setAccessionNumber(accessionNumber
);
1971 public String
getCatalogNumber() {
1973 return derivedUnit
.getCatalogNumber();
1976 public void setCatalogNumber(String catalogNumber
) {
1978 derivedUnit
.setCatalogNumber(catalogNumber
);
1982 public String
getBarcode() {
1984 return derivedUnit
.getBarcode();
1987 public void setBarcode(String barcode
) {
1989 derivedUnit
.setBarcode(barcode
);
1992 // Preservation Method
1995 * Only supported by specimen and fossils
1997 * @see #DerivedUnitType
2001 public PreservationMethod
getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException
{
2003 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2004 return CdmBase
.deproxy(derivedUnit
, Specimen
.class)
2008 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2009 throw new MethodNotSupportedByDerivedUnitTypeException(
2010 "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
2018 * Only supported by specimen and fossils
2020 * @see #DerivedUnitType
2023 public void setPreservationMethod(PreservationMethod preservation
)
2024 throws MethodNotSupportedByDerivedUnitTypeException
{
2026 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2027 CdmBase
.deproxy(derivedUnit
, Specimen
.class).setPreservation(
2031 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2032 throw new MethodNotSupportedByDerivedUnitTypeException(
2033 "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
2040 // Stored under name
2042 public TaxonNameBase
getStoredUnder() {
2044 return derivedUnit
.getStoredUnder();
2047 public void setStoredUnder(TaxonNameBase storedUnder
) {
2049 derivedUnit
.setStoredUnder(storedUnder
);
2053 public String
getTitleCache() {
2054 SpecimenOrObservationBase
<?
> titledUnit
= getTitledUnit();
2056 if (!titledUnit
.isProtectedTitleCache()) {
2057 // always compute title cache anew as long as there are no property
2058 // change listeners on
2059 // field observation, gathering event etc
2060 titledUnit
.setTitleCache(null, false);
2062 return titledUnit
.getTitleCache();
2065 private SpecimenOrObservationBase
<?
> getTitledUnit(){
2066 return (derivedUnit
!= null )? derivedUnit
: fieldObservation
;
2069 public boolean isProtectedTitleCache() {
2070 return getTitledUnit().isProtectedTitleCache();
2073 public void setTitleCache(String titleCache
, boolean isProtected
) {
2074 this.getTitledUnit().setTitleCache(titleCache
, isProtected
);
2078 * Returns the derived unit itself.
2080 * @return the derived unit
2082 public DerivedUnitBase
innerDerivedUnit() {
2083 return this.derivedUnit
;
2087 // * Returns the derived unit itself.
2089 // * @return the derived unit
2091 // public DerivedUnitBase innerDerivedUnit(boolean createIfNotExists) {
2092 // DerivedUnit result = this.derivedUnit;
2093 // if (result == null && createIfNotExists){
2094 // if (this.fieldObservation == null){
2095 // String message = "Field observation must exist to create derived unit.";
2096 // throw new IllegalStateException(message);
2099 // DerivationEvent derivationEvent = getDerivationEvent(true);
2100 // derivationEvent.addOriginal(fieldObservation);
2101 // return this.derivedUnit;
2106 private boolean hasDerivationEvent() {
2107 return getDerivationEvent() == null ?
false : true;
2110 private DerivationEvent
getDerivationEvent() {
2111 return getDerivationEvent(false);
2115 * Returns the derivation event. If no derivation event exists and <code>createIfNotExists</code>
2116 * is <code>true</code> a new derivation event is created and returned.
2117 * Otherwise <code>null</code> is returned.
2118 * @param createIfNotExists
2120 private DerivationEvent
getDerivationEvent(boolean createIfNotExists
) {
2121 DerivationEvent result
= null;
2122 if (derivedUnit
!= null){
2123 result
= derivedUnit
.getDerivedFrom();
2127 if (result
== null && createIfNotExists
) {
2128 DerivationEventType type
= null;
2129 if (isAccessioned(derivedUnit
)){
2130 type
= DerivationEventType
.ACCESSIONING();
2133 result
= DerivationEvent
.NewInstance(type
);
2134 derivedUnit
.setDerivedFrom(result
);
2140 * TODO still unclear which classes do definetly require accessioning.
2141 * Only return true for those classes which are clear.
2142 * @param derivedUnit
2145 private boolean isAccessioned(DerivedUnitBase
<?
> derivedUnit
) {
2146 if (derivedUnit
.isInstanceOf(Specimen
.class) ){
2147 return CdmBase
.deproxy(derivedUnit
, Specimen
.class).getClass().equals(Specimen
.class);
2154 public String
getExsiccatum()
2155 throws MethodNotSupportedByDerivedUnitTypeException
{
2157 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2158 return CdmBase
.deproxy(derivedUnit
, Specimen
.class).getExsiccatum();
2161 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2162 throw new MethodNotSupportedByDerivedUnitTypeException(
2163 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2170 public void setExsiccatum(String exsiccatum
) throws Exception
{
2172 if (derivedUnit
.isInstanceOf(Specimen
.class)) {
2173 CdmBase
.deproxy(derivedUnit
, Specimen
.class).setExsiccatum(
2177 .isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
2178 throw new MethodNotSupportedByDerivedUnitTypeException(
2179 "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
2187 public void addSource(IdentifiableSource source
) {
2189 this.derivedUnit
.addSource(source
);
2193 * Creates an orignal source, adds it to the specimen and returns it.
2196 * @param microReference
2197 * @param originalNameString
2200 public IdentifiableSource
addSource(Reference reference
, String microReference
, String originalNameString
) {
2201 IdentifiableSource source
= IdentifiableSource
.NewInstance(reference
, microReference
);
2202 source
.setOriginalNameString(originalNameString
);
2208 public Set
<IdentifiableSource
> getSources() {
2210 return derivedUnit
.getSources();
2213 public void removeSource(IdentifiableSource source
) {
2215 this.derivedUnit
.removeSource(source
);
2219 * @return the collection
2222 public Collection
getCollection() {
2224 return derivedUnit
.getCollection();
2229 * the collection to set
2231 public void setCollection(Collection collection
) {
2233 derivedUnit
.setCollection(collection
);
2237 public void addAnnotation(Annotation annotation
) {
2239 this.derivedUnit
.addAnnotation(annotation
);
2243 public void getAnnotations() {
2245 this.derivedUnit
.getAnnotations();
2248 public void removeAnnotation(Annotation annotation
) {
2250 this.derivedUnit
.removeAnnotation(annotation
);
2253 // ******************************* Events ***************************
2255 //set of events that were currently fired by this facades field observation
2256 //to avoid recursive fireing of the same event
2257 private Set
<PropertyChangeEvent
> fireingEvents
= new HashSet
<PropertyChangeEvent
>();
2262 private void addNewEventPropagationListener(CdmBase listeningObject
) {
2263 //if there is already a listener, don't do anything
2264 for (PropertyChangeListener listener
: this.listeners
.keySet()){
2265 if (listeners
.get(listener
) == listeningObject
){
2269 //create new listener
2270 PropertyChangeListener listener
= new PropertyChangeListener() {
2272 public void propertyChange(PropertyChangeEvent event
) {
2273 if (derivedUnit
!= null){
2274 derivedUnit
.firePropertyChange(event
);
2276 if (! event
.getSource().equals(fieldObservation
) && ! fireingEvents
.contains(event
) ){
2277 fireingEvents
.add(event
);
2278 fieldObservation
.firePropertyChange(event
);
2279 fireingEvents
.remove(event
);
2284 //add listener to listening object and to list of listeners
2285 listeningObject
.addPropertyChangeListener(listener
);
2286 listeners
.put(listener
, listeningObject
);
2289 // **************** Other Collections ********************************
2292 * Creates a duplicate specimen which derives from the same derivation event
2293 * as the facade specimen and adds collection data to it (all data available
2294 * in DerivedUnitBase and Specimen. Data from SpecimenOrObservationBase and
2295 * above are not yet shared at the moment.
2298 * @param catalogNumber
2299 * @param accessionNumber
2300 * @param collectorsNumber
2301 * @param storedUnder
2302 * @param preservation
2305 public Specimen
addDuplicate(Collection collection
, String catalogNumber
,
2306 String accessionNumber
,
2307 TaxonNameBase storedUnder
, PreservationMethod preservation
) {
2309 Specimen duplicate
= Specimen
.NewInstance();
2310 duplicate
.setDerivedFrom(getDerivationEvent(CREATE
));
2311 duplicate
.setCollection(collection
);
2312 duplicate
.setCatalogNumber(catalogNumber
);
2313 duplicate
.setAccessionNumber(accessionNumber
);
2314 duplicate
.setStoredUnder(storedUnder
);
2315 duplicate
.setPreservation(preservation
);
2319 public void addDuplicate(DerivedUnitBase duplicateSpecimen
) {
2320 // TODO check derivedUnitType
2322 getDerivationEvent(CREATE
).addDerivative(duplicateSpecimen
);
2326 public Set
<Specimen
> getDuplicates() {
2328 Set
<Specimen
> result
= new HashSet
<Specimen
>();
2329 if (hasDerivationEvent()) {
2330 for (DerivedUnitBase derivedUnit
: getDerivationEvent(CREATE
)
2331 .getDerivatives()) {
2332 if (derivedUnit
.isInstanceOf(Specimen
.class)
2333 && !derivedUnit
.equals(this.derivedUnit
)) {
2334 result
.add(CdmBase
.deproxy(derivedUnit
, Specimen
.class));
2341 public void removeDuplicate(Specimen duplicateSpecimen
) {
2343 if (hasDerivationEvent()) {
2344 getDerivationEvent(CREATE
).removeDerivative(duplicateSpecimen
);
2350 private void testDerivedUnit() {
2351 if (derivedUnit
== null){
2352 throw new IllegalStateException("This method is not allowed for this specimen or observation type. Probably you have tried to add specimen(derived unit) information to a field observation");
2356 public void setType(DerivedUnitType type
) {
2360 public DerivedUnitType
getType() {
2366 * Closes this facade. As a minimum this method removes all listeners created by this facade from their
2367 * listening objects.
2369 public void close(){
2370 for (PropertyChangeListener listener
: this.listeners
.keySet()){
2371 CdmBase listeningObject
= listeners
.get(listener
);
2372 listeningObject
.removePropertyChangeListener(listener
);