-/**\r
- * Copyright (C) 2007 EDIT\r
- * European Distributed Institute of Taxonomy\r
- * http://www.e-taxonomy.eu\r
- *\r
- * The contents of this file are subject to the Mozilla Public License Version 1.1\r
- * See LICENSE.TXT at the top of this package for the full license terms.\r
- */\r
-\r
-package eu.etaxonomy.cdm.api.facade;\r
-\r
-import java.beans.PropertyChangeEvent;\r
-import java.beans.PropertyChangeListener;\r
-import java.text.ParseException;\r
-import java.util.ArrayList;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import javax.persistence.Transient;\r
-\r
-import org.apache.commons.lang.StringUtils;\r
-import org.apache.log4j.Logger;\r
-\r
-import eu.etaxonomy.cdm.api.service.IOccurrenceService;\r
-import eu.etaxonomy.cdm.common.CdmUtils;\r
-import eu.etaxonomy.cdm.common.UTF8;\r
-import eu.etaxonomy.cdm.model.agent.AgentBase;\r
-import eu.etaxonomy.cdm.model.agent.Person;\r
-import eu.etaxonomy.cdm.model.common.Annotation;\r
-import eu.etaxonomy.cdm.model.common.CdmBase;\r
-import eu.etaxonomy.cdm.model.common.DefinedTerm;\r
-import eu.etaxonomy.cdm.model.common.IOriginalSource;\r
-import eu.etaxonomy.cdm.model.common.IdentifiableSource;\r
-import eu.etaxonomy.cdm.model.common.Identifier;\r
-import eu.etaxonomy.cdm.model.common.Language;\r
-import eu.etaxonomy.cdm.model.common.LanguageString;\r
-import eu.etaxonomy.cdm.model.common.OriginalSourceType;\r
-import eu.etaxonomy.cdm.model.common.TimePeriod;\r
-import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
-import eu.etaxonomy.cdm.model.description.Feature;\r
-import eu.etaxonomy.cdm.model.description.SpecimenDescription;\r
-import eu.etaxonomy.cdm.model.description.TextData;\r
-import eu.etaxonomy.cdm.model.location.NamedArea;\r
-import eu.etaxonomy.cdm.model.location.Point;\r
-import eu.etaxonomy.cdm.model.location.ReferenceSystem;\r
-import eu.etaxonomy.cdm.model.media.Media;\r
-import eu.etaxonomy.cdm.model.media.Rights;\r
-import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
-import eu.etaxonomy.cdm.model.occurrence.Collection;\r
-import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;\r
-import eu.etaxonomy.cdm.model.occurrence.DerivationEventType;\r
-import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;\r
-import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;\r
-import eu.etaxonomy.cdm.model.occurrence.FieldUnit;\r
-import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;\r
-import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;\r
-import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;\r
-import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;\r
-import eu.etaxonomy.cdm.model.reference.Reference;\r
-\r
-/**\r
- * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from\r
- * a specimen based view. It does not support all functionality available in the\r
- * occurrence package.<BR>\r
- * The most significant restriction is that a specimen may derive only from one\r
- * direct derivation event and there must be only one field unit\r
- * (gathering event) it derives from.<BR>\r
- *\r
- * @author a.mueller\r
- * @date 14.05.2010\r
- */\r
-public class DerivedUnitFacade {\r
- private static final String METER = "m";\r
-\r
- @SuppressWarnings("unused")\r
- private static final Logger logger = Logger.getLogger(DerivedUnitFacade.class);\r
-\r
- 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 ";\r
-\r
- private static final boolean CREATE = true;\r
- private static final boolean CREATE_NOT = false;\r
-\r
- private final DerivedUnitFacadeConfigurator config;\r
-\r
- private final Map<PropertyChangeListener, CdmBase> listeners = new HashMap<PropertyChangeListener, CdmBase>();\r
-\r
- // Either fieldUnit or derivedUnit must not be null.\r
- private FieldUnit fieldUnit;\r
- private final DerivedUnit derivedUnit;\r
-\r
- // media - the text data holding the media\r
- private TextData derivedUnitMediaTextData;\r
- private TextData fieldObjectMediaTextData;\r
-\r
- private TextData ecology;\r
- private TextData plantDescription;\r
-\r
- /**\r
- * Creates a derived unit facade for a new derived unit of type\r
- * <code>type</code>.\r
- *\r
- * @param type\r
- * @return\r
- */\r
- public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type) {\r
- return new DerivedUnitFacade(type, null, null);\r
- }\r
-\r
- /**\r
- * Creates a derived unit facade for a new derived unit of type\r
- * <code>type</code>.\r
- *\r
- * @param type\r
- * @return\r
- */\r
- public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type, FieldUnit fieldUnit) {\r
- return new DerivedUnitFacade(type, fieldUnit, null);\r
- }\r
-\r
- /**\r
- * Creates a derived unit facade for a new derived unit of type\r
- * <code>type</code>.\r
- *\r
- * @param type\r
- * @param fieldUnit the field unit to use\r
- * @param config the facade configurator to use\r
- * //TODO are there any ambiguities to solve with defining a field unit or a configurator\r
- * @return\r
- */\r
- public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type, FieldUnit fieldUnit, DerivedUnitFacadeConfigurator config) {\r
- return new DerivedUnitFacade(type, fieldUnit, config);\r
- }\r
-\r
-\r
- /**\r
- * Creates a derived unit facade for a given derived unit using the default\r
- * configuration.\r
- *\r
- * @param derivedUnit\r
- * @return\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- return new DerivedUnitFacade(derivedUnit, null);\r
- }\r
-\r
- public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit,\r
- DerivedUnitFacadeConfigurator config)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- return new DerivedUnitFacade(derivedUnit, config);\r
- }\r
-\r
- // ****************** CONSTRUCTOR ******************************************\r
-\r
- private DerivedUnitFacade(SpecimenOrObservationType type, FieldUnit fieldUnit, DerivedUnitFacadeConfigurator config) {\r
- if (config == null){\r
- config = DerivedUnitFacadeConfigurator.NewInstance();\r
- }\r
- this.config = config;\r
- // derivedUnit\r
- derivedUnit = getNewDerivedUnitInstance(type);\r
- //TODO parameter checking should be solved in a more generic way if we start using other entity facades\r
- if(derivedUnit==null && fieldUnit==null && type.isFieldUnit()){\r
- this.fieldUnit = getFieldUnit(CREATE);\r
- }\r
- setFieldUnit(fieldUnit);\r
- if (derivedUnit != null){\r
- setCacheStrategy();\r
- }else{\r
- setFieldUnitCacheStrategy();\r
- }\r
- }\r
-\r
- private DerivedUnit getNewDerivedUnitInstance(SpecimenOrObservationType type) {\r
- if (type.isFieldUnit()){\r
- return null;\r
- }else if(type.isAnyDerivedUnit()){\r
- return DerivedUnit.NewInstance(type);\r
- } else {\r
- String message = "Unknown specimen or observation type %s";\r
- message = String.format(message, type.getMessage());\r
- throw new IllegalStateException(message);\r
- }\r
- }\r
-\r
- private DerivedUnitFacade(DerivedUnit derivedUnit, DerivedUnitFacadeConfigurator config)\r
- throws DerivedUnitFacadeNotSupportedException {\r
-\r
- if(derivedUnit==null){\r
- throw new IllegalArgumentException("DerivedUnit must not be null");\r
- }\r
-\r
- if (config == null) {\r
- config = DerivedUnitFacadeConfigurator.NewInstance();\r
- }\r
- this.config = config;\r
-\r
- // derived unit\r
- this.derivedUnit = derivedUnit;\r
-\r
- // derivation event\r
- if (this.derivedUnit.getDerivedFrom() != null) {\r
- DerivationEvent derivationEvent = getDerivationEvent(CREATE);\r
- // fieldUnit\r
- Set<FieldUnit> fieldOriginals = getFieldUnitOriginals(derivationEvent, null);\r
- if (fieldOriginals.size() > 1) {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- "Specimen must not have more than 1 derivation event");\r
- } else if (fieldOriginals.size() == 0) {\r
- // fieldUnit = FieldUnit.NewInstance();\r
- } else if (fieldOriginals.size() == 1) {\r
- fieldUnit = fieldOriginals.iterator().next();\r
- // ###fieldUnit =\r
- // getInitializedFieldUnit(fieldUnit);\r
- if (config.isFirePropertyChangeEvents()){\r
- addNewEventPropagationListener(fieldUnit);\r
- }\r
- } else {\r
- throw new IllegalStateException("Illegal state");\r
- }\r
- }\r
-\r
- this.derivedUnitMediaTextData = inititializeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);\r
-\r
- fieldObjectMediaTextData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), false, true);\r
-\r
-\r
-//direct media have been removed from specimenorobservationbase #3597\r
-// // handle derivedUnit.getMedia()\r
-// if (derivedUnit.getMedia().size() > 0) {\r
-// // TODO better changed model here to allow only one place for images\r
-// if (this.config.isMoveDerivedUnitMediaToGallery()) {\r
-// Set<Media> mediaSet = derivedUnit.getMedia();\r
-// for (Media media : mediaSet) {\r
-// this.addDerivedUnitMedia(media);\r
-// }\r
-// mediaSet.removeAll(getDerivedUnitMedia());\r
-// } else {\r
-// throw new DerivedUnitFacadeNotSupportedException(\r
-// "Specimen may not have direct media. Only (one) image gallery is allowed");\r
-// }\r
-// }\r
-//\r
-// // handle fieldUnit.getMedia()\r
-// if (fieldUnit != null && fieldUnit.getMedia() != null\r
-// && fieldUnit.getMedia().size() > 0) {\r
-// // TODO better changed model here to allow only one place for images\r
-// if (this.config.isMoveFieldObjectMediaToGallery()) {\r
-// Set<Media> mediaSet = fieldUnit.getMedia();\r
-// for (Media media : mediaSet) {\r
-// this.addFieldObjectMedia(media);\r
-// }\r
-// mediaSet.removeAll(getFieldObjectMedia());\r
-// } else {\r
-// throw new DerivedUnitFacadeNotSupportedException(\r
-// "Field object may not have direct media. Only (one) image gallery is allowed");\r
-// }\r
-// }\r
-\r
- // test if descriptions are supported\r
- ecology = initializeFieldObjectTextDataWithSupportTest(\r
- Feature.ECOLOGY(), false, false);\r
- plantDescription = initializeFieldObjectTextDataWithSupportTest(\r
- Feature.DESCRIPTION(), false, false);\r
-\r
- setCacheStrategy();\r
-\r
- }\r
-\r
- private DerivedUnit getInitializedDerivedUnit(\r
- DerivedUnit derivedUnit) {\r
- IOccurrenceService occurrenceService = this.config\r
- .getOccurrenceService();\r
- if (occurrenceService == null) {\r
- return derivedUnit;\r
- }\r
- List<String> propertyPaths = this.config.getPropertyPaths();\r
- if (propertyPaths == null) {\r
- return derivedUnit;\r
- }\r
- propertyPaths = getDerivedUnitPropertyPaths(propertyPaths);\r
- DerivedUnit result = (DerivedUnit) occurrenceService.load(\r
- derivedUnit.getUuid(), propertyPaths);\r
- return result;\r
- }\r
-\r
- /**\r
- * Initializes the derived unit according to the configuartions property\r
- * path. If the property path is <code>null</code> or no occurrence service\r
- * is given the returned object is the same as the input parameter.\r
- *\r
- * @param fieldUnit\r
- * @return\r
- */\r
- private FieldUnit getInitializedFieldUnit(FieldUnit fieldUnit) {\r
- IOccurrenceService occurrenceService = this.config\r
- .getOccurrenceService();\r
- if (occurrenceService == null) {\r
- return fieldUnit;\r
- }\r
- List<String> propertyPaths = this.config.getPropertyPaths();\r
- if (propertyPaths == null) {\r
- return fieldUnit;\r
- }\r
- propertyPaths = getFieldObjectPropertyPaths(propertyPaths);\r
- FieldUnit result = (FieldUnit) occurrenceService.load(\r
- fieldUnit.getUuid(), propertyPaths);\r
- return result;\r
- }\r
-\r
- /**\r
- * Transforms the property paths in a way that the facade is handled just\r
- * like an ordinary CdmBase object.<BR>\r
- * E.g. a property path "collectinAreas" will be translated into\r
- * gatheringEvent.collectingAreas\r
- *\r
- * @param propertyPaths\r
- * @return\r
- */\r
- private List<String> getFieldObjectPropertyPaths(List<String> propertyPaths) {\r
- List<String> result = new ArrayList<String>();\r
- for (String facadePath : propertyPaths) {\r
- // collecting areas (named area)\r
- if (facadePath.startsWith("collectingAreas")) {\r
- facadePath = "gatheringEvent." + facadePath;\r
- result.add(facadePath);\r
- }\r
- // collector (agentBase)\r
- else if (facadePath.startsWith("collector")) {\r
- facadePath = facadePath.replace("collector",\r
- "gatheringEvent.actor");\r
- result.add(facadePath);\r
- }\r
- // exactLocation (agentBase)\r
- else if (facadePath.startsWith("exactLocation")) {\r
- facadePath = "gatheringEvent." + facadePath;\r
- result.add(facadePath);\r
- }\r
- // gatheringPeriod (TimePeriod)\r
- else if (facadePath.startsWith("gatheringPeriod")) {\r
- facadePath = facadePath.replace("gatheringPeriod",\r
- "gatheringEvent.timeperiod");\r
- result.add(facadePath);\r
- }\r
- // (locality/ localityLanguage , LanguageString)\r
- else if (facadePath.startsWith("locality")) {\r
- facadePath = "gatheringEvent." + facadePath;\r
- result.add(facadePath);\r
- }\r
-\r
- // *********** FIELD OBJECT ************\r
- // fieldObjectDefinitions (Map<language, languageString)\r
- else if (facadePath.startsWith("fieldObjectDefinitions")) {\r
- // TODO or definition ???\r
- facadePath = facadePath.replace("fieldObjectDefinitions",\r
- "description");\r
- result.add(facadePath);\r
- }\r
- // fieldObjectMedia (Media)\r
- else if (facadePath.startsWith("fieldObjectMedia")) {\r
- // TODO ???\r
- facadePath = facadePath.replace("fieldObjectMedia",\r
- "descriptions.elements.media");\r
- result.add(facadePath);\r
- }\r
-\r
- // Gathering Event will always be added\r
- result.add("gatheringEvent");\r
-\r
- }\r
-\r
- /*\r
- * Gathering Event ==================== - gatheringEvent\r
- * (GatheringEvent)\r
- *\r
- * Field Object ================= - ecology/ ecologyAll (String) ??? -\r
- * plant description (like ecology)\r
- *\r
- * - fieldObjectImageGallery (SpecimenDescription) - is automatically\r
- * initialized via fieldObjectMedia\r
- */\r
-\r
- return result;\r
- }\r
-\r
- /**\r
- * Transforms the property paths in a way that the facade is handled just\r
- * like an ordinary CdmBase object.<BR>\r
- * E.g. a property path "collectinAreas" will be translated into\r
- * gatheringEvent.collectingAreas\r
- *\r
- * Not needed (?) as the facade works with REST service property paths\r
- * without using this method.\r
- *\r
- * @param propertyPaths\r
- * @return\r
- */\r
- private List<String> getDerivedUnitPropertyPaths(List<String> propertyPaths) {\r
- List<String> result = new ArrayList<String>();\r
- for (String facadePath : propertyPaths) {\r
- // determinations (DeterminationEvent)\r
- if (facadePath.startsWith("determinations")) {\r
- facadePath = "" + facadePath; // no change\r
- result.add(facadePath);\r
- }\r
- // storedUnder (TaxonNameBase)\r
- else if (facadePath.startsWith("storedUnder")) {\r
- facadePath = "" + facadePath; // no change\r
- result.add(facadePath);\r
- }\r
- // sources (IdentifiableSource)\r
- else if (facadePath.startsWith("sources")) {\r
- facadePath = "" + facadePath; // no change\r
- result.add(facadePath);\r
- }\r
- // collection (Collection)\r
- else if (facadePath.startsWith("collection")) {\r
- facadePath = "" + facadePath; // no change\r
- result.add(facadePath);\r
- }\r
- // (locality/ localityLanguage , LanguageString)\r
- else if (facadePath.startsWith("locality")) {\r
- facadePath = "gatheringEvent." + facadePath;\r
- result.add(facadePath);\r
- }\r
-\r
- // *********** FIELD OBJECT ************\r
- // derivedUnitDefinitions (Map<language, languageString)\r
- else if (facadePath.startsWith("derivedUnitDefinitions")) {\r
- // TODO or definition ???\r
- facadePath = facadePath.replace("derivedUnitDefinitions",\r
- "description");\r
- result.add(facadePath);\r
- }\r
-\r
- // derivedUnitMedia (Media)\r
- else if (facadePath.startsWith("derivedUnitMedia")) {\r
- // TODO ???\r
- facadePath = facadePath.replace("derivedUnitMedia",\r
- "descriptions.elements.media");\r
- result.add(facadePath);\r
- }\r
-\r
- }\r
-\r
- /*\r
- * //TODO Derived Unit =====================\r
- *\r
- * - derivedUnitImageGallery (SpecimenDescription) - is automatically\r
- * initialized via derivedUnitMedia\r
- *\r
- * - derivationEvent (DerivationEvent) - will always be initialized -\r
- * duplicates (??? Specimen???) ???\r
- */\r
-\r
- return result;\r
- }\r
-\r
- /**\r
- *\r
- */\r
- private void setCacheStrategy() {\r
- if (derivedUnit == null) {\r
- throw new NullPointerException(\r
- "Facade's derviedUnit must not be null to set cache strategy");\r
- }else{\r
- derivedUnit.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());\r
- setFieldUnitCacheStrategy();\r
- }\r
- }\r
-\r
- private void setFieldUnitCacheStrategy() {\r
- if (this.hasFieldObject()){\r
- DerivedUnitFacadeFieldUnitCacheStrategy strategy = new DerivedUnitFacadeFieldUnitCacheStrategy();\r
- this.fieldUnit.setCacheStrategy(strategy);\r
- }\r
- }\r
-\r
- /**\r
- * @param feature\r
- * @param createIfNotExists\r
- * @param isImageGallery\r
- * @return\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private TextData initializeFieldObjectTextDataWithSupportTest(\r
- Feature feature, boolean createIfNotExists, boolean isImageGallery)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- // field object\r
- FieldUnit fieldObject = getFieldUnit(createIfNotExists);\r
- if (fieldObject == null) {\r
- return null;\r
- }\r
- return inititializeTextDataWithSupportTest(feature, fieldObject,\r
- createIfNotExists, isImageGallery);\r
- }\r
-\r
- /**\r
- * @param feature\r
- * @param specimen\r
- * @param createIfNotExists\r
- * @param isImageGallery\r
- * @return\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private TextData inititializeTextDataWithSupportTest(Feature feature,\r
- SpecimenOrObservationBase specimen, boolean createIfNotExists,\r
- boolean isImageGallery)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- if (feature == null) {\r
- return null;\r
- }\r
- TextData textData = null;\r
- if (createIfNotExists) {\r
- textData = TextData.NewInstance(feature);\r
- }\r
-\r
- Set<SpecimenDescription> descriptions;\r
- if (isImageGallery) {\r
- descriptions = specimen.getSpecimenDescriptionImageGallery();\r
- } else {\r
- descriptions = specimen.getSpecimenDescriptions(false);\r
- }\r
- // no description exists yet for this specimen\r
- if (descriptions.size() == 0) {\r
- if (createIfNotExists) {\r
- SpecimenDescription newSpecimenDescription = SpecimenDescription\r
- .NewInstance(specimen);\r
- newSpecimenDescription.addElement(textData);\r
- newSpecimenDescription.setImageGallery(isImageGallery);\r
- return textData;\r
- } else {\r
- return null;\r
- }\r
- }\r
- // description already exists\r
- Set<DescriptionElementBase> existingTextData = new HashSet<DescriptionElementBase>();\r
- for (SpecimenDescription description : descriptions) {\r
- // collect all existing text data\r
- for (DescriptionElementBase element : description.getElements()) {\r
- if (element.isInstanceOf(TextData.class)\r
- && (feature.equals(element.getFeature()) || isImageGallery)) {\r
- existingTextData.add(element);\r
- }\r
- }\r
- }\r
- // use existing text data if exactly one exists\r
- if (existingTextData.size() > 1) {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- "Specimen facade does not support more than one description text data of type "\r
- + feature.getLabel());\r
-\r
- } else if (existingTextData.size() == 1) {\r
- return CdmBase.deproxy(existingTextData.iterator().next(),\r
- TextData.class);\r
- } else {\r
- if (createIfNotExists) {\r
- SpecimenDescription description = descriptions.iterator()\r
- .next();\r
- description.addElement(textData);\r
- }\r
- return textData;\r
- }\r
- }\r
-\r
- /**\r
- * Tests if a given image gallery is supported by the derived unit facade.\r
- * It returns the only text data attached to the given image gallery. If the\r
- * given image gallery does not have text data attached, it is created and\r
- * attached.\r
- *\r
- * @param imageGallery\r
- * @return\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private TextData testImageGallery(SpecimenDescription imageGallery)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- if (imageGallery.isImageGallery() == false) {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- "Image gallery needs to have image gallery flag set");\r
- }\r
- if (imageGallery.getElements().size() > 1) {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- "Image gallery must not have more then one description element");\r
- }\r
- TextData textData;\r
- if (imageGallery.getElements().size() == 0) {\r
- textData = TextData.NewInstance(Feature.IMAGE());\r
- imageGallery.addElement(textData);\r
- } else {\r
- if (!imageGallery.getElements().iterator().next()\r
- .isInstanceOf(TextData.class)) {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- "Image gallery must only have TextData as element");\r
- } else {\r
- textData = CdmBase.deproxy(imageGallery.getElements()\r
- .iterator().next(), TextData.class);\r
- }\r
- }\r
- return textData;\r
- }\r
-\r
- // ************************** METHODS\r
- // *****************************************\r
-\r
- private TextData getDerivedUnitImageGalleryTextData(\r
- boolean createIfNotExists)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- if (this.derivedUnitMediaTextData == null && createIfNotExists) {\r
- this.derivedUnitMediaTextData = getImageGalleryTextData(\r
- derivedUnit, "Specimen");\r
- }\r
- return this.derivedUnitMediaTextData;\r
- }\r
-\r
- private TextData getObservationImageGalleryTextData(\r
- boolean createIfNotExists)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- if (this.fieldObjectMediaTextData == null && createIfNotExists) {\r
- this.fieldObjectMediaTextData = getImageGalleryTextData(fieldUnit, "Field unit");\r
- }\r
- return this.fieldObjectMediaTextData;\r
- }\r
-\r
- /**\r
- * @param derivationEvent\r
- * @return\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private Set<FieldUnit> getFieldUnitOriginals(\r
- DerivationEvent derivationEvent,\r
- Set<SpecimenOrObservationBase> recursionAvoidSet)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- if (recursionAvoidSet == null) {\r
- recursionAvoidSet = new HashSet<SpecimenOrObservationBase>();\r
- }\r
- Set<FieldUnit> result = new HashSet<FieldUnit>();\r
- Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();\r
- for (SpecimenOrObservationBase original : originals) {\r
- if (original.isInstanceOf(FieldUnit.class)) {\r
- result.add(CdmBase.deproxy(original, FieldUnit.class));\r
- } else if (original.isInstanceOf(DerivedUnit.class)) {\r
- // if specimen has already been tested exclude it from further\r
- // recursion\r
- if (recursionAvoidSet.contains(original)) {\r
- continue;\r
- }\r
- DerivedUnit derivedUnit = CdmBase.deproxy(original, DerivedUnit.class);\r
- DerivationEvent originalDerivation = derivedUnit.getDerivedFrom();\r
- // Set<DerivationEvent> derivationEvents =\r
- // original.getDerivationEvents();\r
- // for (DerivationEvent originalDerivation : derivationEvents){\r
- if(originalDerivation!=null){\r
- Set<FieldUnit> fieldUnits = getFieldUnitOriginals(\r
- originalDerivation, recursionAvoidSet);\r
- result.addAll(fieldUnits);\r
- }\r
- // }\r
- } else {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- "Unhandled specimen or observation base type: "\r
- + original.getClass().getName());\r
- }\r
-\r
- }\r
- return result;\r
- }\r
-\r
- // *********** MEDIA METHODS ******************************\r
-\r
- // /**\r
- // * Returns the media list for a specimen. Throws an exception if the\r
- // existing specimen descriptions\r
- // * are not supported by this facade.\r
- // * @param specimen the specimen the media belongs to\r
- // * @param specimenExceptionText text describing the specimen for exception\r
- // messages\r
- // * @return\r
- // * @throws DerivedUnitFacadeNotSupportedException\r
- // */\r
- // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase\r
- // specimen, String specimenExceptionText) throws\r
- // DerivedUnitFacadeNotSupportedException{\r
- // List<Media> result;\r
- // SpecimenDescription imageGallery =\r
- // getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);\r
- // TextData textData = getImageTextDataWithSupportTest(imageGallery,\r
- // specimenExceptionText);\r
- // result = textData.getMedia();\r
- // return result;\r
- // }\r
-\r
- /**\r
- * Returns the media list for a specimen. Throws an exception if the\r
- * existing specimen descriptions are not supported by this facade.\r
- *\r
- * @param specimen\r
- * the specimen the media belongs to\r
- * @param specimenExceptionText\r
- * text describing the specimen for exception messages\r
- * @return\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private TextData getImageGalleryTextData(SpecimenOrObservationBase specimen, String specimenExceptionText)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- TextData result;\r
- SpecimenDescription imageGallery = getImageGalleryWithSupportTest(\r
- specimen, specimenExceptionText, true);\r
- result = getImageTextDataWithSupportTest(imageGallery,\r
- specimenExceptionText);\r
- return result;\r
- }\r
-\r
- /**\r
- * Returns the image gallery of the according specimen. Throws an exception\r
- * if the attached image gallerie(s) are not supported by this facade. If no\r
- * image gallery exists a new one is created if\r
- * <code>createNewIfNotExists</code> is true and if specimen is not\r
- * <code>null</code>.\r
- *\r
- * @param specimen\r
- * @param specimenText\r
- * @param createNewIfNotExists\r
- * @return\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private SpecimenDescription getImageGalleryWithSupportTest(\r
- SpecimenOrObservationBase<?> specimen, String specimenText,\r
- boolean createNewIfNotExists)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- if (specimen == null) {\r
- return null;\r
- }\r
- SpecimenDescription imageGallery;\r
- if (hasMultipleImageGalleries(specimen)) {\r
- throw new DerivedUnitFacadeNotSupportedException(specimenText\r
- + " must not have more than 1 image gallery");\r
- } else {\r
- imageGallery = getImageGallery(specimen, createNewIfNotExists);\r
- getImageTextDataWithSupportTest(imageGallery, specimenText);\r
- }\r
- return imageGallery;\r
- }\r
-\r
- /**\r
- * Returns the media holding text data element of the image gallery. Throws\r
- * an exception if multiple such text data already exist. Creates a new text\r
- * data if none exists and adds it to the image gallery. If image gallery is\r
- * <code>null</code> nothing happens.\r
- *\r
- * @param imageGallery\r
- * @param textData\r
- * @return\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private TextData getImageTextDataWithSupportTest(\r
- SpecimenDescription imageGallery, String specimenText)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- if (imageGallery == null) {\r
- return null;\r
- }\r
- TextData textData = null;\r
- for (DescriptionElementBase element : imageGallery.getElements()) {\r
- if (element.isInstanceOf(TextData.class)\r
- && element.getFeature().equals(Feature.IMAGE())) {\r
- if (textData != null) {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- specimenText\r
- + " must not have more than 1 image text data element in image gallery");\r
- }\r
- textData = CdmBase.deproxy(element, TextData.class);\r
- }\r
- }\r
- if (textData == null) {\r
- textData = TextData.NewInstance(Feature.IMAGE());\r
- imageGallery.addElement(textData);\r
- }\r
- return textData;\r
- }\r
-\r
- /**\r
- * Checks, if a specimen belongs to more than one description that is an\r
- * image gallery\r
- *\r
- * @param derivedUnit\r
- * @return\r
- */\r
- private boolean hasMultipleImageGalleries(\r
- SpecimenOrObservationBase<?> derivedUnit) {\r
- int count = 0;\r
- Set<SpecimenDescription> descriptions = derivedUnit\r
- .getSpecimenDescriptions();\r
- for (SpecimenDescription description : descriptions) {\r
- if (description.isImageGallery()) {\r
- count++;\r
- }\r
- }\r
- return (count > 1);\r
- }\r
-\r
- /**\r
- * Returns the image gallery for a specimen. If there are multiple specimen\r
- * descriptions marked as image galleries an arbitrary one is chosen. If no\r
- * image gallery exists, a new one is created if\r
- * <code>createNewIfNotExists</code> is <code>true</code>.<Br>\r
- * If specimen is <code>null</code> a null pointer exception is thrown.\r
- *\r
- * @param createNewIfNotExists\r
- * @return\r
- */\r
- private SpecimenDescription getImageGallery(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) {\r
- SpecimenDescription result = null;\r
- Set<SpecimenDescription> descriptions = specimen.getSpecimenDescriptions();\r
- for (SpecimenDescription description : descriptions) {\r
- if (description.isImageGallery()) {\r
- result = description;\r
- break;\r
- }\r
- }\r
- if (result == null && createIfNotExists) {\r
- result = SpecimenDescription.NewInstance(specimen);\r
- result.setImageGallery(true);\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * Adds a media to the specimens image gallery. If media is\r
- * <code>null</code> nothing happens.\r
- *\r
- * @param media\r
- * @param specimen\r
- * @return true if media is not null (as specified by\r
- * {@link java.util.Collection#add(Object) Collection.add(E e)}\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private boolean addMedia(Media media, SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {\r
- if (media != null) {\r
- List<Media> mediaList = getMediaList(specimen, true);\r
- if (! mediaList.contains(media)){\r
- return mediaList.add(media);\r
- }else{\r
- return true;\r
- }\r
- } else {\r
- return false;\r
- }\r
- }\r
-\r
- /**\r
- * Removes a media from the specimens image gallery.\r
- *\r
- * @param media\r
- * @param specimen\r
- * @return true if an element was removed as a result of this call (as\r
- * specified by {@link java.util.Collection#remove(Object)\r
- * Collection.remove(E e)}\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private boolean removeMedia(Media media,\r
- SpecimenOrObservationBase<?> specimen)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- List<Media> mediaList = getMediaList(specimen, true);\r
- return mediaList == null ? null : mediaList.remove(media);\r
- }\r
-\r
- private List<Media> getMediaList(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- TextData textData = getMediaTextData(specimen, createIfNotExists);\r
- return textData == null ? null : textData.getMedia();\r
- }\r
-\r
- /**\r
- * Returns the one media list of a specimen which is part of the only image\r
- * gallery that this specimen is part of.<BR>\r
- * If these conditions are not hold an exception is thrwon.\r
- *\r
- * @param specimen\r
- * @return\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)\r
- // throws DerivedUnitFacadeNotSupportedException {\r
- // if (specimen == null){\r
- // return null;\r
- // }\r
- // if (specimen == this.derivedUnit){\r
- // return getDerivedUnitImageGalleryMedia();\r
- // }else if (specimen == this.fieldUnit){\r
- // return getObservationImageGalleryTextData();\r
- // }else{\r
- // return getImageGalleryMedia(specimen, "Undefined specimen ");\r
- // }\r
- // }\r
-\r
- /**\r
- * Returns the one media list of a specimen which is part of the only image\r
- * gallery that this specimen is part of.<BR>\r
- * If these conditions are not hold an exception is thrown.\r
- *\r
- * @param specimen\r
- * @return\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private TextData getMediaTextData(SpecimenOrObservationBase<?> specimen,\r
- boolean createIfNotExists)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- if (specimen == null) {\r
- return null;\r
- }\r
- if (specimen == this.derivedUnit) {\r
- return getDerivedUnitImageGalleryTextData(createIfNotExists);\r
- } else if (specimen == this.fieldUnit) {\r
- return getObservationImageGalleryTextData(createIfNotExists);\r
- } else {\r
- return getImageGalleryTextData(specimen, "Undefined specimen ");\r
- }\r
- }\r
-\r
- // ****************** GETTER / SETTER / ADDER / REMOVER\r
- // ***********************/\r
-\r
- // ****************** Gathering Event *********************************/\r
-\r
- // country\r
- @Transient\r
- public NamedArea getCountry() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true).getCountry()\r
- : null);\r
- }\r
-\r
- public void setCountry(NamedArea country) {\r
- getGatheringEvent(true).setCountry(country);\r
- }\r
-\r
- // Collecting area\r
- public void addCollectingArea(NamedArea area) {\r
- getGatheringEvent(true).addCollectingArea(area);\r
- }\r
-\r
- public void addCollectingAreas(java.util.Collection<NamedArea> areas) {\r
- for (NamedArea area : areas) {\r
- getGatheringEvent(true).addCollectingArea(area);\r
- }\r
- }\r
-\r
- @Transient\r
- public Set<NamedArea> getCollectingAreas() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true)\r
- .getCollectingAreas() : null);\r
- }\r
-\r
- public void removeCollectingArea(NamedArea area) {\r
- if (hasGatheringEvent()) {\r
- getGatheringEvent(true).removeCollectingArea(area);\r
- }\r
- }\r
-\r
- static final String ALTITUDE_POSTFIX = " m";\r
-\r
- /**\r
- * Returns the correctly formatted <code>absolute elevation</code> information.\r
- * If absoluteElevationText is set, this will be returned,\r
- * otherwise we absoluteElevation will be returned, followed by absoluteElevationMax\r
- * if existing, separated by " - "\r
- * @return\r
- */\r
- @Transient\r
- public String absoluteElevationToString() {\r
- if (! hasGatheringEvent()){\r
- return null;\r
- }else{\r
- GatheringEvent ev = getGatheringEvent(true);\r
- if (StringUtils.isNotBlank(ev.getAbsoluteElevationText())){\r
- return ev.getAbsoluteElevationText();\r
- }else{\r
- String text = ev.getAbsoluteElevationText();\r
- Integer min = getAbsoluteElevation();\r
- Integer max = getAbsoluteElevationMaximum();\r
- return distanceString(min, max, text, METER);\r
- }\r
- }\r
- }\r
-\r
-\r
- /**\r
- * meter above/below sea level of the surface\r
- *\r
- * @see #getAbsoluteElevationError()\r
- * @see #getAbsoluteElevationRange()\r
- **/\r
- @Transient\r
- public Integer getAbsoluteElevation() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevation() : null);\r
- }\r
-\r
- public void setAbsoluteElevation(Integer absoluteElevation) {\r
- getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);\r
- }\r
-\r
- public void setAbsoluteElevationMax(Integer absoluteElevationMax) {\r
- getGatheringEvent(true).setAbsoluteElevationMax(absoluteElevationMax);\r
- }\r
-\r
- public void setAbsoluteElevationText(String absoluteElevationText) {\r
- getGatheringEvent(true).setAbsoluteElevationText(absoluteElevationText);\r
- }\r
-\r
- /**\r
- * @see #getAbsoluteElevation()\r
- * @see #getAbsoluteElevationError()\r
- * @see #setAbsoluteElevationRange(Integer, Integer)\r
- * @see #getAbsoluteElevationMinimum()\r
- */\r
- @Transient\r
- public Integer getAbsoluteElevationMaximum() {\r
- if (!hasGatheringEvent()) {\r
- return null;\r
- }else{\r
- return getGatheringEvent(true).getAbsoluteElevationMax();\r
- }\r
- }\r
-\r
- /**\r
- * @see #getAbsoluteElevation()\r
- * @see #getAbsoluteElevationError()\r
- * @see #setAbsoluteElevationRange(Integer, Integer)\r
- * @see #getAbsoluteElevationMinimum()\r
- */\r
- @Transient\r
- public String getAbsoluteElevationText() {\r
- if (!hasGatheringEvent()) {\r
- return null;\r
- }else{\r
- return getGatheringEvent(true).getAbsoluteElevationText();\r
- }\r
- }\r
-\r
- /**\r
- * Convenience method to set absolute elevation minimum and maximum.\r
- *\r
- * @see #setAbsoluteElevation(Integer)\r
- * @see #setAbsoluteElevationMax(Integer)\r
- * @param minimumElevation minimum of the range\r
- * @param maximumElevation maximum of the range\r
- */\r
- public void setAbsoluteElevationRange(Integer minimumElevation, Integer maximumElevation) {\r
- getGatheringEvent(true).setAbsoluteElevation(minimumElevation);\r
- getGatheringEvent(true).setAbsoluteElevationMax(maximumElevation);\r
- }\r
-\r
- // collector\r
- @Transient\r
- public AgentBase getCollector() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true).getCollector()\r
- : null);\r
- }\r
-\r
- public void setCollector(AgentBase collector) {\r
- getGatheringEvent(true).setCollector(collector);\r
- }\r
-\r
- // collecting method\r
- @Transient\r
- public String getCollectingMethod() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true).getCollectingMethod() : null);\r
- }\r
-\r
- public void setCollectingMethod(String collectingMethod) {\r
- getGatheringEvent(true).setCollectingMethod(collectingMethod);\r
- }\r
-\r
- // distance to ground\r
-\r
- /**\r
- * Returns the correctly formatted <code>distance to ground</code> information.\r
- * If distanceToGroundText is not blank, it will be returned,\r
- * otherwise distanceToGround will be returned, followed by distanceToGroundMax\r
- * if existing, separated by " - "\r
- * @return\r
- */\r
- @Transient\r
- public String distanceToGroundToString() {\r
- if (! hasGatheringEvent()){\r
- return null;\r
- }else{\r
- GatheringEvent ev = getGatheringEvent(true);\r
- String text = ev.getDistanceToGroundText();\r
- Double min = getDistanceToGround();\r
- Double max = getDistanceToGroundMax();\r
- return distanceString(min, max, text, METER);\r
- }\r
- }\r
-\r
- @Transient\r
- public Double getDistanceToGround() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToGround() : null);\r
- }\r
-\r
- public void setDistanceToGround(Double distanceToGround) {\r
- getGatheringEvent(true).setDistanceToGround(distanceToGround);\r
- }\r
-\r
- /**\r
- * @see #getDistanceToGround()\r
- * @see #getDistanceToGroundRange(Integer, Integer)\r
- */\r
- @Transient\r
- public Double getDistanceToGroundMax() {\r
- if (!hasGatheringEvent()) {\r
- return null;\r
- }else{\r
- return getGatheringEvent(true).getDistanceToGroundMax();\r
- }\r
- }\r
-\r
- public void setDistanceToGroundMax(Double distanceToGroundMax) {\r
- getGatheringEvent(true).setDistanceToGroundMax(distanceToGroundMax);\r
- }\r
-\r
- /**\r
- * @see #getDistanceToGround()\r
- * @see #setDistanceToGroundRange(Integer, Integer)\r
- */\r
- @Transient\r
- public String getDistanceToGroundText() {\r
- if (!hasGatheringEvent()) {\r
- return null;\r
- }else{\r
- return getGatheringEvent(true).getDistanceToGroundText();\r
- }\r
- }\r
- public void setDistanceToGroundText(String distanceToGroundText) {\r
- getGatheringEvent(true).setDistanceToGroundText(distanceToGroundText);\r
- }\r
-\r
- /**\r
- * Convenience method to set distance to ground minimum and maximum.\r
- *\r
- * @see #getDistanceToGround()\r
- * @see #getDistanceToGroundMax()\r
- * @param minimumDistance minimum of the range\r
- * @param maximumDistance maximum of the range\r
- */\r
- public void setDistanceToGroundRange(Double minimumDistance, Double maximumDistance) throws IllegalArgumentException{\r
- getGatheringEvent(true).setDistanceToGround(minimumDistance);\r
- getGatheringEvent(true).setDistanceToGroundMax(maximumDistance);\r
- }\r
-\r
-\r
- /**\r
- * Returns the correctly formatted <code>distance to water surface</code> information.\r
- * If distanceToWaterSurfaceText is not blank, it will be returned,\r
- * otherwise distanceToWaterSurface will be returned, followed by distanceToWatersurfaceMax\r
- * if existing, separated by " - "\r
- * @return\r
- */\r
- @Transient\r
- public String distanceToWaterSurfaceToString() {\r
- if (! hasGatheringEvent()){\r
- return null;\r
- }else{\r
- GatheringEvent ev = getGatheringEvent(true);\r
- String text = ev.getDistanceToWaterSurfaceText();\r
- Double min = getDistanceToWaterSurface();\r
- Double max = getDistanceToWaterSurfaceMax();\r
- return distanceString(min, max, text, METER);\r
- }\r
- }\r
-\r
- // distance to water surface\r
- @Transient\r
- public Double getDistanceToWaterSurface() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToWaterSurface() : null);\r
- }\r
-\r
- public void setDistanceToWaterSurface(Double distanceToWaterSurface) {\r
- getGatheringEvent(true).setDistanceToWaterSurface(distanceToWaterSurface);\r
- }\r
-\r
- /**\r
- * @see #getDistanceToWaterSurface()\r
- * @see #getDistanceToWaterSurfaceRange(Double, Double)\r
- */\r
- @Transient\r
- public Double getDistanceToWaterSurfaceMax() {\r
- if (!hasGatheringEvent()) {\r
- return null;\r
- }else{\r
- return getGatheringEvent(true).getDistanceToWaterSurfaceMax();\r
- }\r
- }\r
-\r
- public void setDistanceToWaterSurfaceMax(Double distanceToWaterSurfaceMax) {\r
- getGatheringEvent(true).setDistanceToWaterSurfaceMax(distanceToWaterSurfaceMax);\r
- }\r
-\r
- /**\r
- * @see #getDistanceToWaterSurface()\r
- * @see #getDistanceToWaterSurfaceRange(Double, Double)\r
- */\r
- @Transient\r
- public String getDistanceToWaterSurfaceText() {\r
- if (!hasGatheringEvent()) {\r
- return null;\r
- }else{\r
- return getGatheringEvent(true).getDistanceToWaterSurfaceText();\r
- }\r
- }\r
- public void setDistanceToWaterSurfaceText(String distanceToWaterSurfaceText) {\r
- getGatheringEvent(true).setDistanceToWaterSurfaceText(distanceToWaterSurfaceText);\r
- }\r
-\r
- /**\r
- * Convenience method to set distance to ground minimum and maximum.\r
- *\r
- * @see #getDistanceToWaterSurface()\r
- * @see #getDistanceToWaterSurfaceMax()\r
- * @param minimumDistance minimum of the range, this is the distance which is closer to the water surface\r
- * @param maximumDistance maximum of the range, this is the distance which is farer to the water surface\r
- */\r
- public void setDistanceToWaterSurfaceRange(Double minimumDistance, Double maximumDistance) throws IllegalArgumentException{\r
- getGatheringEvent(true).setDistanceToWaterSurface(minimumDistance);\r
- getGatheringEvent(true).setDistanceToWaterSurfaceMax(maximumDistance);\r
- }\r
-\r
-\r
- // exact location\r
- @Transient\r
- public Point getExactLocation() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true).getExactLocation() : null);\r
- }\r
-\r
- /**\r
- * Returns a sexagesimal representation of the exact location (e.g.\r
- * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty\r
- * string is returned.\r
- *\r
- * @param includeEmptySeconds\r
- * @param includeReferenceSystem\r
- * @return\r
- */\r
- public String getExactLocationText(boolean includeEmptySeconds,\r
- boolean includeReferenceSystem) {\r
- return (this.getExactLocation() == null ? "" : this.getExactLocation()\r
- .toSexagesimalString(includeEmptySeconds,\r
- includeReferenceSystem));\r
- }\r
-\r
- public void setExactLocation(Point exactLocation) {\r
- getGatheringEvent(true).setExactLocation(exactLocation);\r
- }\r
-\r
- public void setExactLocationByParsing(String longitudeToParse,\r
- String latitudeToParse, ReferenceSystem referenceSystem,\r
- Integer errorRadius) throws ParseException {\r
- Point point = Point.NewInstance(null, null, referenceSystem,\r
- errorRadius);\r
- point.setLongitudeByParsing(longitudeToParse);\r
- point.setLatitudeByParsing(latitudeToParse);\r
- setExactLocation(point);\r
- }\r
-\r
- // gathering event description\r
- @Transient\r
- public String getGatheringEventDescription() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true).getDescription()\r
- : null);\r
- }\r
-\r
- public void setGatheringEventDescription(String description) {\r
- getGatheringEvent(true).setDescription(description);\r
- }\r
-\r
- // gatering period\r
- @Transient\r
- public TimePeriod getGatheringPeriod() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true).getTimeperiod()\r
- : null);\r
- }\r
-\r
- public void setGatheringPeriod(TimePeriod timeperiod) {\r
- getGatheringEvent(true).setTimeperiod(timeperiod);\r
- }\r
-\r
- // locality\r
- @Transient\r
- public LanguageString getLocality() {\r
- return (hasGatheringEvent() ? getGatheringEvent(true).getLocality()\r
- : null);\r
- }\r
-\r
- /**\r
- * convienience method for {@link #getLocality()}.\r
- * {@link LanguageString#getText() getText()}\r
- *\r
- * @return\r
- */\r
- @Transient\r
- public String getLocalityText() {\r
- LanguageString locality = getLocality();\r
- if (locality != null) {\r
- return locality.getText();\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * convienience method for {@link #getLocality()}.\r
- * {@link LanguageString#getLanguage() getLanguage()}\r
- *\r
- * @return\r
- */\r
- @Transient\r
- public Language getLocalityLanguage() {\r
- LanguageString locality = getLocality();\r
- if (locality != null) {\r
- return locality.getLanguage();\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * Sets the locality string in the default language\r
- *\r
- * @param locality\r
- */\r
- public void setLocality(String locality) {\r
- Language language = Language.DEFAULT();\r
- setLocality(locality, language);\r
- }\r
-\r
- public void setLocality(String locality, Language language) {\r
- LanguageString langString = LanguageString.NewInstance(locality, language);\r
- setLocality(langString);\r
- }\r
-\r
- public void setLocality(LanguageString locality) {\r
- getGatheringEvent(true).setLocality(locality);\r
- }\r
-\r
- /**\r
- * The gathering event will be used for the field object instead of the old\r
- * gathering event.<BR>\r
- * <B>This method will override all gathering values (see below).</B>\r
- *\r
- * @see #getAbsoluteElevation()\r
- * @see #getAbsoluteElevationError()\r
- * @see #getDistanceToGround()\r
- * @see #getDistanceToWaterSurface()\r
- * @see #getExactLocation()\r
- * @see #getGatheringEventDescription()\r
- * @see #getGatheringPeriod()\r
- * @see #getCollectingAreas()\r
- * @see #getCollectingMethod()\r
- * @see #getLocality()\r
- * @see #getCollector()\r
- * @param gatheringEvent\r
- */\r
- public void setGatheringEvent(GatheringEvent gatheringEvent) {\r
- getFieldUnit(true).setGatheringEvent(gatheringEvent);\r
- }\r
-\r
- public boolean hasGatheringEvent() {\r
- return (getGatheringEvent(false) != null);\r
- }\r
-\r
- public GatheringEvent innerGatheringEvent() {\r
- return getGatheringEvent(false);\r
- }\r
-\r
- public GatheringEvent getGatheringEvent(boolean createIfNotExists) {\r
- if (!hasFieldUnit() && !createIfNotExists) {\r
- return null;\r
- }\r
- if (createIfNotExists && getFieldUnit(true).getGatheringEvent() == null) {\r
- GatheringEvent gatheringEvent = GatheringEvent.NewInstance();\r
- getFieldUnit(true).setGatheringEvent(gatheringEvent);\r
- }\r
- return getFieldUnit(true).getGatheringEvent();\r
- }\r
-\r
- // ****************** Field Object ************************************/\r
-\r
- /**\r
- * Returns true if a field unit exists (even if all attributes are\r
- * empty or <code>null<code>.\r
- *\r
- * @return\r
- */\r
- public boolean hasFieldObject() {\r
- return this.fieldUnit != null;\r
- }\r
-\r
- // ecology\r
- @Transient\r
- public String getEcology() {\r
- return getEcology(Language.DEFAULT());\r
- }\r
-\r
- public String getEcology(Language language) {\r
- LanguageString languageString = getEcologyAll().get(language);\r
- return (languageString == null ? null : languageString.getText());\r
- }\r
-\r
- // public String getEcologyPreferred(List<Language> languages){\r
- // LanguageString languageString =\r
- // getEcologyAll().getPreferredLanguageString(languages);\r
- // return languageString.getText();\r
- // }\r
- /**\r
- * Returns a copy of the multilanguage text holding the ecology data.\r
- *\r
- * @see {@link TextData#getMultilanguageText()}\r
- * @return\r
- */\r
- @Transient\r
- public Map<Language, LanguageString> getEcologyAll() {\r
- if (ecology == null) {\r
- try {\r
- ecology = initializeFieldObjectTextDataWithSupportTest(\r
- Feature.ECOLOGY(), false, false);\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- if (ecology == null) {\r
- return new HashMap<Language, LanguageString>();\r
- }\r
- }\r
- return ecology.getMultilanguageText();\r
- }\r
-\r
- public void setEcology(String ecology) {\r
- setEcology(ecology, null);\r
- }\r
-\r
- public void setEcology(String ecologyText, Language language) {\r
- if (language == null) {\r
- language = Language.DEFAULT();\r
- }\r
- boolean isEmpty = StringUtils.isBlank(ecologyText);\r
- if (ecology == null) {\r
- try {\r
- ecology = initializeFieldObjectTextDataWithSupportTest(\r
- Feature.ECOLOGY(), !isEmpty, false);\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- }\r
- if (ecology != null){\r
- if (ecologyText == null) {\r
- ecology.removeText(language);\r
- } else {\r
- ecology.putText(language, ecologyText);\r
- }\r
- }\r
- }\r
-\r
- public void removeEcology(Language language) {\r
- setEcology(null, language);\r
- }\r
-\r
- /**\r
- * Removes ecology for the default language\r
- */\r
- public void removeEcology() {\r
- setEcology(null, null);\r
- }\r
-\r
- public void removeEcologyAll() {\r
-\r
- }\r
-\r
- // plant description\r
- @Transient\r
- public String getPlantDescription() {\r
- return getPlantDescription(null);\r
- }\r
-\r
- public String getPlantDescription(Language language) {\r
- if (language == null) {\r
- language = Language.DEFAULT();\r
- }\r
- LanguageString languageString = getPlantDescriptionAll().get(language);\r
- return (languageString == null ? null : languageString.getText());\r
- }\r
-\r
- // public String getPlantDescriptionPreferred(List<Language> languages){\r
- // LanguageString languageString =\r
- // getPlantDescriptionAll().getPreferredLanguageString(languages);\r
- // return languageString.getText();\r
- // }\r
- /**\r
- * Returns a copy of the multilanguage text holding the description data.\r
- *\r
- * @see {@link TextData#getMultilanguageText()}\r
- * @return\r
- */\r
- @Transient\r
- public Map<Language, LanguageString> getPlantDescriptionAll() {\r
- if (plantDescription == null) {\r
- try {\r
- plantDescription = initializeFieldObjectTextDataWithSupportTest(\r
- Feature.DESCRIPTION(), false, false);\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- if (plantDescription == null) {\r
- return new HashMap<Language, LanguageString>();\r
- }\r
- }\r
- return plantDescription.getMultilanguageText();\r
- }\r
-\r
- public void setPlantDescription(String plantDescription) {\r
- setPlantDescription(plantDescription, null);\r
- }\r
-\r
- public void setPlantDescription(String plantDescriptionText, Language language) {\r
- if (language == null) {\r
- language = Language.DEFAULT();\r
- }\r
- boolean isEmpty = StringUtils.isBlank(plantDescriptionText);\r
- if (plantDescription == null) {\r
- try {\r
- plantDescription = initializeFieldObjectTextDataWithSupportTest(\r
- Feature.DESCRIPTION(), !isEmpty, false);\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- }\r
- if (plantDescription != null){\r
- if (plantDescriptionText == null) {\r
- plantDescription.removeText(language);\r
- } else {\r
- plantDescription.putText(language, plantDescriptionText);\r
- }\r
- }\r
- }\r
-\r
- public void removePlantDescription(Language language) {\r
- setPlantDescription(null, language);\r
- }\r
-\r
- // field object definition\r
- public void addFieldObjectDefinition(String text, Language language) {\r
- getFieldUnit(true).putDefinition(language, text);\r
- }\r
-\r
- @Transient\r
- public Map<Language, LanguageString> getFieldObjectDefinition() {\r
- if (!hasFieldUnit()) {\r
- return new HashMap<Language, LanguageString>();\r
- } else {\r
- return getFieldUnit(true).getDefinition();\r
- }\r
- }\r
-\r
- public String getFieldObjectDefinition(Language language) {\r
- Map<Language, LanguageString> map = getFieldObjectDefinition();\r
- LanguageString languageString = (map == null ? null : map.get(language));\r
- if (languageString != null) {\r
- return languageString.getText();\r
- } else {\r
- return null;\r
- }\r
- }\r
-\r
- public void removeFieldObjectDefinition(Language lang) {\r
- if (hasFieldUnit()) {\r
- getFieldUnit(true).removeDefinition(lang);\r
- }\r
- }\r
-\r
- // media\r
- public boolean addFieldObjectMedia(Media media) {\r
- try {\r
- return addMedia(media, getFieldUnit(true));\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- }\r
-\r
- /**\r
- * Returns true, if an image gallery for the field object exists.<BR>\r
- * Returns also <code>true</code> if the image gallery is empty.\r
- *\r
- * @return\r
- */\r
- public boolean hasFieldObjectImageGallery() {\r
- if (!hasFieldObject()) {\r
- return false;\r
- } else {\r
- return (getImageGallery(fieldUnit, false) != null);\r
- }\r
- }\r
-\r
- public void setFieldObjectImageGallery(SpecimenDescription imageGallery)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- SpecimenDescription existingGallery = getFieldObjectImageGallery(false);\r
-\r
- // test attached specimens contain this.derivedUnit\r
- SpecimenOrObservationBase<?> facadeFieldUnit = innerFieldUnit();\r
- testSpecimenInImageGallery(imageGallery, facadeFieldUnit);\r
-\r
- if (existingGallery != null) {\r
- if (existingGallery != imageGallery) {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- "DerivedUnitFacade does not allow more than one image gallery");\r
- } else {\r
- // do nothing\r
- }\r
- } else {\r
- TextData textData = testImageGallery(imageGallery);\r
- this.fieldObjectMediaTextData = textData;\r
- }\r
- }\r
-\r
- /**\r
- * Returns the field object image gallery. If no such image gallery exists\r
- * and createIfNotExists is true an new one is created. Otherwise null is\r
- * returned.\r
- *\r
- * @param createIfNotExists\r
- * @return\r
- */\r
- public SpecimenDescription getFieldObjectImageGallery(\r
- boolean createIfNotExists) {\r
- TextData textData;\r
- try {\r
- textData = initializeFieldObjectTextDataWithSupportTest(\r
- Feature.IMAGE(), createIfNotExists, true);\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- if (textData != null) {\r
- return CdmBase.deproxy(textData.getInDescription(),\r
- SpecimenDescription.class);\r
- } else {\r
- return null;\r
- }\r
- }\r
-\r
- /**\r
- * Returns the media for the field object.<BR>\r
- *\r
- * @return\r
- */\r
- @Transient\r
- public List<Media> getFieldObjectMedia() {\r
- try {\r
- List<Media> result = getMediaList(getFieldUnit(false), false);\r
- return result == null ? new ArrayList<Media>() : result;\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- }\r
-\r
- public boolean removeFieldObjectMedia(Media media) {\r
- try {\r
- return removeMedia(media, getFieldUnit(false));\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- }\r
-\r
- // field number\r
- @Transient\r
- public String getFieldNumber() {\r
- if (!hasFieldUnit()) {\r
- return null;\r
- } else {\r
- return getFieldUnit(true).getFieldNumber();\r
- }\r
- }\r
-\r
- public void setFieldNumber(String fieldNumber) {\r
- getFieldUnit(true).setFieldNumber(fieldNumber);\r
- }\r
-\r
- // primary collector\r
- @Transient\r
- public Person getPrimaryCollector() {\r
- if (!hasFieldUnit()) {\r
- return null;\r
- } else {\r
- return getFieldUnit(true).getPrimaryCollector();\r
- }\r
- }\r
-\r
- public void setPrimaryCollector(Person primaryCollector) {\r
- getFieldUnit(true).setPrimaryCollector(primaryCollector);\r
- }\r
-\r
- // field notes\r
- @Transient\r
- public String getFieldNotes() {\r
- if (!hasFieldUnit()) {\r
- return null;\r
- } else {\r
- return getFieldUnit(true).getFieldNotes();\r
- }\r
- }\r
-\r
- public void setFieldNotes(String fieldNotes) {\r
- getFieldUnit(true).setFieldNotes(fieldNotes);\r
- }\r
-\r
- // individual counts\r
- @Transient\r
- public Integer getIndividualCount() {\r
- return (hasFieldUnit() ? getFieldUnit(true).getIndividualCount() : null);\r
- }\r
-\r
- public void setIndividualCount(Integer individualCount) {\r
- getFieldUnit(true).setIndividualCount(individualCount);\r
- }\r
-\r
- // life stage\r
- @Transient\r
- public DefinedTerm getLifeStage() {\r
- return (hasFieldUnit() ? getFieldUnit(true).getLifeStage() : null);\r
- }\r
-\r
- public void setLifeStage(DefinedTerm lifeStage) {\r
- getFieldUnit(true).setLifeStage(lifeStage);\r
- }\r
-\r
- // sex\r
- @Transient\r
- public DefinedTerm getSex() {\r
- return (hasFieldUnit() ? getFieldUnit(true).getSex(): null);\r
- }\r
-\r
- public void setSex(DefinedTerm sex) {\r
- getFieldUnit(true).setSex(sex);\r
- }\r
-\r
- // kind of Unit\r
- @Transient\r
- public DefinedTerm getKindOfUnit() {\r
- return (hasFieldUnit() ? getFieldUnit(true).getKindOfUnit() : null);\r
- }\r
-\r
- public void setKindOfUnit(DefinedTerm kindOfUnit) {\r
- getFieldUnit(true).setKindOfUnit(kindOfUnit);\r
- }\r
-\r
-\r
- // field unit\r
- public boolean hasFieldUnit() {\r
- return (getFieldUnit(CREATE_NOT) != null);\r
- }\r
-\r
- /**\r
- * Returns the field unit as an object.\r
- *\r
- * @return\r
- */\r
- public FieldUnit innerFieldUnit() {\r
- return getFieldUnit(CREATE_NOT);\r
- }\r
-\r
- /**\r
- * Returns the field unit as an object.\r
- *\r
- * @return\r
- */\r
- public FieldUnit getFieldUnit(boolean createIfNotExists) {\r
- if (fieldUnit == null && createIfNotExists) {\r
- setFieldUnit(FieldUnit.NewInstance());\r
- }\r
- return this.fieldUnit;\r
- }\r
-\r
-\r
- private void setFieldUnit(FieldUnit fieldUnit) {\r
- this.fieldUnit = fieldUnit;\r
- if (fieldUnit != null){\r
- if (config.isFirePropertyChangeEvents()){\r
- addNewEventPropagationListener(fieldUnit);\r
- }\r
- if (derivedUnit != null){\r
- DerivationEvent derivationEvent = getDerivationEvent(CREATE);\r
- derivationEvent.addOriginal(fieldUnit);\r
- }\r
- setFieldUnitCacheStrategy();\r
- }\r
- }\r
-\r
- // ****************** Specimen *******************************************\r
-\r
- // Definition\r
- public void addDerivedUnitDefinition(String text, Language language) {\r
- innerDerivedUnit().putDefinition(language, text);\r
- }\r
-\r
- @Transient\r
- public Map<Language, LanguageString> getDerivedUnitDefinitions() {\r
- return ! checkDerivedUnit()? null : this.derivedUnit.getDefinition();\r
- }\r
-\r
-\r
- public String getDerivedUnitDefinition(Language language) {\r
- if (! checkDerivedUnit()){\r
- return null;\r
- }\r
- Map<Language, LanguageString> languageMap = derivedUnit.getDefinition();\r
- LanguageString languageString = languageMap.get(language);\r
- if (languageString != null) {\r
- return languageString.getText();\r
- } else {\r
- return null;\r
- }\r
- }\r
-\r
- public void removeDerivedUnitDefinition(Language lang) {\r
- testDerivedUnit();\r
- derivedUnit.removeDefinition(lang);\r
- }\r
-\r
- // Determination\r
- public void addDetermination(DeterminationEvent determination) {\r
- //TODO implement correct bidirectional mapping in model classes\r
- determination.setIdentifiedUnit(baseUnit());\r
- baseUnit().addDetermination(determination);\r
- }\r
-\r
- @Transient\r
- public DeterminationEvent getPreferredDetermination() {\r
- Set<DeterminationEvent> events = baseUnit().getDeterminations();\r
- for (DeterminationEvent event : events){\r
- if (event.getPreferredFlag() == true){\r
- return event;\r
- }\r
- }\r
- return null;\r
- }\r
-\r
- /**\r
- * This method returns the preferred determination.\r
- * @see #getOtherDeterminations()\r
- * @see #getDeterminations()\r
- * @return\r
- */\r
- @Transient\r
- public void setPreferredDetermination(DeterminationEvent newEvent) {\r
- Set<DeterminationEvent> events = baseUnit().getDeterminations();\r
- for (DeterminationEvent event : events){\r
- if (event.getPreferredFlag() == true){\r
- event.setPreferredFlag(false);\r
- }\r
- }\r
- newEvent.setPreferredFlag(true);\r
- events.add(newEvent);\r
- }\r
-\r
- /**\r
- * This method returns all determinations except for the preferred one.\r
- * @see #getPreferredDetermination()\r
- * @see #getDeterminations()\r
- * @return\r
- */\r
- @Transient\r
- public Set<DeterminationEvent> getOtherDeterminations() {\r
- Set<DeterminationEvent> events = baseUnit().getDeterminations();\r
- Set<DeterminationEvent> result = new HashSet<DeterminationEvent>();\r
- for (DeterminationEvent event : events){\r
- if (event.getPreferredFlag() != true){\r
- result.add(event);\r
- }\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * This method returns all determination events. The preferred one {@link #getPreferredDetermination()}\r
- * and all others {@link #getOtherDeterminations()}.\r
- * @return\r
- */\r
- @Transient\r
- public Set<DeterminationEvent> getDeterminations() {\r
- return baseUnit().getDeterminations();\r
- }\r
-\r
- public void removeDetermination(DeterminationEvent determination) {\r
- baseUnit().removeDetermination(determination);\r
- }\r
-\r
- // Media\r
- public boolean addDerivedUnitMedia(Media media) {\r
- testDerivedUnit();\r
- try {\r
- return addMedia(media, derivedUnit);\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- }\r
-\r
- /**\r
- * Returns true, if an image gallery exists for the specimen.<BR>\r
- * Returns also <code>true</code> if the image gallery is empty.\r
- */\r
- public boolean hasDerivedUnitImageGallery() {\r
- return (getImageGallery(derivedUnit, false) != null);\r
- }\r
-\r
- public SpecimenDescription getDerivedUnitImageGallery(boolean createIfNotExists) {\r
- if (!checkDerivedUnit()){\r
- return null;\r
- }\r
- TextData textData;\r
- try {\r
- textData = inititializeTextDataWithSupportTest(Feature.IMAGE(),\r
- derivedUnit, createIfNotExists, true);\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- if (textData != null) {\r
- return CdmBase.deproxy(textData.getInDescription(),\r
- SpecimenDescription.class);\r
- } else {\r
- return null;\r
- }\r
- }\r
-\r
- public void setDerivedUnitImageGallery(SpecimenDescription imageGallery)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- testDerivedUnit();\r
- SpecimenDescription existingGallery = getDerivedUnitImageGallery(false);\r
-\r
- // test attached specimens contain this.derivedUnit\r
- SpecimenOrObservationBase facadeDerivedUnit = innerDerivedUnit();\r
- testSpecimenInImageGallery(imageGallery, facadeDerivedUnit);\r
-\r
- if (existingGallery != null) {\r
- if (existingGallery != imageGallery) {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- "DerivedUnitFacade does not allow more than one image gallery");\r
- } else {\r
- // do nothing\r
- }\r
- } else {\r
- TextData textData = testImageGallery(imageGallery);\r
- this.derivedUnitMediaTextData = textData;\r
- }\r
- }\r
-\r
- /**\r
- * @param imageGallery\r
- * @throws DerivedUnitFacadeNotSupportedException\r
- */\r
- private void testSpecimenInImageGallery(SpecimenDescription imageGallery, SpecimenOrObservationBase specimen)\r
- throws DerivedUnitFacadeNotSupportedException {\r
- SpecimenOrObservationBase imageGallerySpecimen = imageGallery.getDescribedSpecimenOrObservation();\r
- if (imageGallerySpecimen == null) {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- "Image Gallery has no Specimen attached. Please attache according specimen or field unit.");\r
- }\r
- if (! imageGallerySpecimen.equals(specimen)) {\r
- throw new DerivedUnitFacadeNotSupportedException(\r
- "Image Gallery has not the facade's field object attached. Please add field object first " +\r
- "to image gallery specimenOrObservation list.");\r
- }\r
- }\r
-\r
- /**\r
- * Returns the media for the specimen.<BR>\r
- *\r
- * @return\r
- */\r
- @Transient\r
- public List<Media> getDerivedUnitMedia() {\r
- if (! checkDerivedUnit()){\r
- return new ArrayList<Media>();\r
- }\r
- try {\r
- List<Media> result = getMediaList(derivedUnit, false);\r
- return result == null ? new ArrayList<Media>() : result;\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- }\r
-\r
- public boolean removeDerivedUnitMedia(Media media) {\r
- testDerivedUnit();\r
- try {\r
- return removeMedia(media, derivedUnit);\r
- } catch (DerivedUnitFacadeNotSupportedException e) {\r
- throw new IllegalStateException(notSupportMessage, e);\r
- }\r
- }\r
-\r
- // Accession Number\r
- @Transient\r
- public String getAccessionNumber() {\r
- return ! checkDerivedUnit()? null : derivedUnit.getAccessionNumber();\r
- }\r
-\r
- public void setAccessionNumber(String accessionNumber) {\r
- testDerivedUnit();\r
- derivedUnit.setAccessionNumber(accessionNumber);\r
- }\r
-\r
- @Transient\r
- public String getCatalogNumber() {\r
- return ! checkDerivedUnit()? null : derivedUnit.getCatalogNumber();\r
- }\r
-\r
- public void setCatalogNumber(String catalogNumber) {\r
- testDerivedUnit();\r
- derivedUnit.setCatalogNumber(catalogNumber);\r
- }\r
-\r
- @Transient\r
- public String getBarcode() {\r
- return ! checkDerivedUnit()? null : derivedUnit.getBarcode();\r
- }\r
-\r
- public void setBarcode(String barcode) {\r
- testDerivedUnit();\r
- derivedUnit.setBarcode(barcode);\r
- }\r
-\r
- // Preservation Method\r
-\r
- /**\r
- * Only supported by specimen and fossils\r
- *\r
- * @see #DerivedUnitType\r
- * @return\r
- */\r
- @Transient\r
- public PreservationMethod getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException {\r
- if (derivedUnit!=null && derivedUnit.getRecordBasis().isPreservedSpecimen()) {\r
- return CdmBase.deproxy(derivedUnit, DerivedUnit.class).getPreservation();\r
- } else {\r
- if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {\r
- throw new MethodNotSupportedByDerivedUnitTypeException(\r
- "A preservation method is only available in derived units of type 'Preserved Specimen' or one of its specializations like 'Fossil Specimen' ");\r
- } else {\r
- return null;\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Only supported by specimen and fossils\r
- *\r
- * @see #DerivedUnitType\r
- * @return\r
- */\r
- public void setPreservationMethod(PreservationMethod preservation)\r
- throws MethodNotSupportedByDerivedUnitTypeException {\r
- if (derivedUnit!=null && derivedUnit.getRecordBasis().isPreservedSpecimen()) {\r
- CdmBase.deproxy(derivedUnit, DerivedUnit.class).setPreservation(preservation);\r
- } else {\r
- if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {\r
- throw new MethodNotSupportedByDerivedUnitTypeException(\r
- "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");\r
- } else {\r
- return;\r
- }\r
- }\r
- }\r
-\r
- // Stored under name\r
- @Transient\r
- public TaxonNameBase getStoredUnder() {\r
- return ! checkDerivedUnit()? null : derivedUnit.getStoredUnder();\r
- }\r
-\r
- public void setStoredUnder(TaxonNameBase storedUnder) {\r
- testDerivedUnit();\r
- derivedUnit.setStoredUnder(storedUnder);\r
- }\r
-\r
- // title cache\r
- public String getTitleCache() {\r
- SpecimenOrObservationBase<?> titledUnit = baseUnit();\r
-\r
- if (!titledUnit.isProtectedTitleCache()) {\r
- // always compute title cache anew as long as there are no property\r
- // change listeners on\r
- // field unit, gathering event etc\r
- titledUnit.setTitleCache(null, false);\r
- }\r
- return titledUnit.getTitleCache();\r
- }\r
-\r
- public boolean isProtectedTitleCache() {\r
- return baseUnit().isProtectedTitleCache();\r
- }\r
-\r
- public void setTitleCache(String titleCache, boolean isProtected) {\r
- this.baseUnit().setTitleCache(titleCache, isProtected);\r
- }\r
-\r
- /**\r
- * Returns the derived unit itself.\r
- *\r
- * @return the derived unit\r
- */\r
- public DerivedUnit innerDerivedUnit() {\r
- return this.derivedUnit;\r
- }\r
-\r
-// /**\r
-// * Returns the derived unit itself.\r
-// *\r
-// * @return the derived unit\r
-// */\r
-// public DerivedUnit innerDerivedUnit(boolean createIfNotExists) {\r
-// DerivedUnit result = this.derivedUnit;\r
-// if (result == null && createIfNotExists){\r
-// if (this.fieldUnit == null){\r
-// String message = "Field unit must exist to create derived unit.";\r
-// throw new IllegalStateException(message);\r
-// }else{\r
-// DerivedUnit =\r
-// DerivationEvent derivationEvent = getDerivationEvent(true);\r
-// derivationEvent.addOriginal(fieldUnit);\r
-// return this.derivedUnit;\r
-// }\r
-// }\r
-// }\r
-\r
- private boolean hasDerivationEvent() {\r
- return getDerivationEvent() == null ? false : true;\r
- }\r
-\r
- private DerivationEvent getDerivationEvent() {\r
- return getDerivationEvent(CREATE_NOT);\r
- }\r
-\r
- /**\r
- * Returns the derivation event. If no derivation event exists and <code>createIfNotExists</code>\r
- * is <code>true</code> a new derivation event is created and returned.\r
- * Otherwise <code>null</code> is returned.\r
- * @param createIfNotExists\r
- */\r
- private DerivationEvent getDerivationEvent(boolean createIfNotExists) {\r
- DerivationEvent result = null;\r
- if (derivedUnit != null){\r
- result = derivedUnit.getDerivedFrom();\r
- }else{\r
- return null;\r
- }\r
- if (result == null && createIfNotExists) {\r
- DerivationEventType type = null;\r
- if (isAccessioned(derivedUnit)){\r
- type = DerivationEventType.ACCESSIONING();\r
- }\r
-\r
- result = DerivationEvent.NewInstance(type);\r
- derivedUnit.setDerivedFrom(result);\r
- }\r
- return result;\r
- }\r
-\r
- /**\r
- * TODO still unclear which classes do definetly require accessioning.\r
- * Only return true for those classes which are clear.\r
- * @param derivedUnit\r
- * @return\r
- */\r
- private boolean isAccessioned(DerivedUnit derivedUnit) {\r
- if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen) ){\r
- return true; //maybe also subtypes should be true\r
- }else{\r
- return false;\r
- }\r
- }\r
-\r
- @Transient\r
- public String getExsiccatum() throws MethodNotSupportedByDerivedUnitTypeException {\r
- testDerivedUnit();\r
- if (derivedUnit.getRecordBasis().isPreservedSpecimen()) {\r
- return derivedUnit.getExsiccatum();\r
- } else {\r
- if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {\r
- throw new MethodNotSupportedByDerivedUnitTypeException(\r
- "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");\r
- } else {\r
- return null;\r
- }\r
- }\r
- }\r
-\r
- public void setExsiccatum(String exsiccatum) throws Exception {\r
- testDerivedUnit();\r
- if (derivedUnit.getRecordBasis().isPreservedSpecimen()) {\r
- derivedUnit.setExsiccatum(exsiccatum);\r
- } else {\r
- if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {\r
- throw new MethodNotSupportedByDerivedUnitTypeException(\r
- "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");\r
- } else {\r
- return;\r
- }\r
- }\r
- }\r
-\r
- /**\r
- * Returns the original label information of the derived unit.\r
- * @return\r
- */\r
- @Transient\r
- public String getOriginalLabelInfo() {\r
- return ! checkDerivedUnit()? null : derivedUnit.getOriginalLabelInfo();\r
- }\r
- public void setOriginalLabelInfo(String originalLabelInfo) {\r
- testDerivedUnit();\r
- derivedUnit.setOriginalLabelInfo(originalLabelInfo);\r
- }\r
-\r
- // **** sources **/\r
- public void addSource(IdentifiableSource source) {\r
- this.baseUnit().addSource(source);\r
- }\r
-\r
- /**\r
- * Creates an {@link IOriginalSource orignal source} or type ,\r
- * adds it to the specimen and returns it.\r
- *\r
- * @param reference\r
- * @param microReference\r
- * @param originalNameString\r
- * @return\r
- */\r
- public IdentifiableSource addSource(OriginalSourceType type, Reference reference, String microReference, String originalNameString) {\r
- IdentifiableSource source = IdentifiableSource.NewInstance(type, null, null, reference, microReference);\r
- source.setOriginalNameString(originalNameString);\r
- addSource(source);\r
- return source;\r
- }\r
-\r
- @Transient\r
- public Set<IdentifiableSource> getSources() {\r
- return baseUnit().getSources();\r
- }\r
-\r
- public void removeSource(IdentifiableSource source) {\r
- this.baseUnit().removeSource(source);\r
- }\r
-\r
- //*** identifiers ***/\r
-\r
-\r
- public void addIdentifier(Identifier identifier) {\r
- this.baseUnit().addIdentifier(identifier);\r
- }\r
-\r
- @Transient\r
- public List<Identifier> getIdentifiers() {\r
- return baseUnit().getIdentifiers();\r
- }\r
-\r
- public void removeIdentifier(Identifier identifier) {\r
- this.baseUnit().removeIdentifier(identifier);\r
- }\r
-\r
- @Transient\r
- public Set<Rights> getRights() {\r
- return baseUnit().getRights();\r
- }\r
-\r
- /**\r
- * @return the collection\r
- */\r
- @Transient\r
- public Collection getCollection() {\r
- return ! checkDerivedUnit()? null : derivedUnit.getCollection();\r
- }\r
-\r
- /**\r
- * @param collection\r
- * the collection to set\r
- */\r
- public void setCollection(Collection collection) {\r
- testDerivedUnit();\r
- derivedUnit.setCollection(collection);\r
- }\r
-\r
- // annotation\r
- public void addAnnotation(Annotation annotation) {\r
- this.baseUnit().addAnnotation(annotation);\r
- }\r
-\r
- @Transient\r
- public void getAnnotations() {\r
- this.baseUnit().getAnnotations();\r
- }\r
-\r
- public void removeAnnotation(Annotation annotation) {\r
- this.baseUnit().removeAnnotation(annotation);\r
- }\r
-\r
- // ******************************* Events ***************************\r
-\r
- //set of events that were currently fired by this facades field unit\r
- //to avoid recursive fireing of the same event\r
- private final Set<PropertyChangeEvent> fireingEvents = new HashSet<PropertyChangeEvent>();\r
-\r
- /**\r
- * @return\r
- */\r
- private void addNewEventPropagationListener(CdmBase listeningObject) {\r
- //if there is already a listener, don't do anything\r
- for (PropertyChangeListener listener : this.listeners.keySet()){\r
- if (listeners.get(listener) == listeningObject){\r
- return;\r
- }\r
- }\r
- //create new listener\r
- PropertyChangeListener listener = new PropertyChangeListener() {\r
- @Override\r
- public void propertyChange(PropertyChangeEvent event) {\r
- if (derivedUnit != null){\r
- derivedUnit.firePropertyChange(event);\r
- }else{\r
- if (! event.getSource().equals(fieldUnit) && ! fireingEvents.contains(event) ){\r
- fireingEvents.add(event);\r
- fieldUnit.firePropertyChange(event);\r
- fireingEvents.remove(event);\r
- }\r
- }\r
- }\r
- };\r
- //add listener to listening object and to list of listeners\r
- listeningObject.addPropertyChangeListener(listener);\r
- listeners.put(listener, listeningObject);\r
- }\r
-\r
- // **************** Other Collections ********************************\r
-\r
- /**\r
- * Creates a duplicate specimen which derives from the same derivation event\r
- * as the facade specimen and adds collection data to it (all data available\r
- * in DerivedUnit and Specimen. Data from SpecimenOrObservationBase and\r
- * above are not yet shared at the moment.\r
- *\r
- * @param collection\r
- * @param catalogNumber\r
- * @param accessionNumber\r
- * @param storedUnder\r
- * @param preservation\r
- * @return\r
- */\r
- public DerivedUnit addDuplicate(Collection collection, String catalogNumber,\r
- String accessionNumber, TaxonNameBase storedUnder, PreservationMethod preservation){\r
- testDerivedUnit();\r
- DerivedUnit duplicate = DerivedUnit.NewPreservedSpecimenInstance();\r
- duplicate.setDerivedFrom(getDerivationEvent(CREATE));\r
- duplicate.setCollection(collection);\r
- duplicate.setCatalogNumber(catalogNumber);\r
- duplicate.setAccessionNumber(accessionNumber);\r
- duplicate.setStoredUnder(storedUnder);\r
- duplicate.setPreservation(preservation);\r
- return duplicate;\r
- }\r
-\r
- public void addDuplicate(DerivedUnit duplicateSpecimen) {\r
- testDerivedUnit();\r
- getDerivationEvent(CREATE).addDerivative(duplicateSpecimen);\r
- }\r
-\r
- @Transient\r
- public Set<DerivedUnit> getDuplicates() {\r
- if (! checkDerivedUnit()){\r
- return new HashSet<DerivedUnit>();\r
- }\r
- Set<DerivedUnit> result = new HashSet<DerivedUnit>();\r
- if (hasDerivationEvent()) {\r
- for (DerivedUnit derivedUnit : getDerivationEvent(CREATE)\r
- .getDerivatives()) {\r
- if (derivedUnit.isInstanceOf(DerivedUnit.class)\r
- && !derivedUnit.equals(this.derivedUnit)) {\r
- result.add(CdmBase.deproxy(derivedUnit, DerivedUnit.class));\r
- }\r
- }\r
- }\r
- return result;\r
- }\r
-\r
- public void removeDuplicate(DerivedUnit duplicateSpecimen) {\r
- testDerivedUnit();\r
- if (hasDerivationEvent()) {\r
- getDerivationEvent(CREATE).removeDerivative(duplicateSpecimen);\r
- }\r
- }\r
-\r
- public SpecimenOrObservationBase<?> baseUnit(){\r
- if(derivedUnit!=null){\r
- return derivedUnit;\r
- }\r
- else if(fieldUnit!=null){\r
- return fieldUnit;\r
- }\r
- else{\r
- throw new IllegalStateException("A DerivedUnitFacade must always have either a field unit or a derived unit");\r
- }\r
- }\r
-\r
-\r
- private boolean checkDerivedUnit() {\r
- if (derivedUnit == null){\r
- return false;\r
- }else{\r
- return true;\r
- }\r
- }\r
- \r
- private void testDerivedUnit() /* throws MethodNotSupportedByDerivedUnitTypeException */ {\r
- if (derivedUnit == null){\r
- 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");\r
- }\r
- }\r
-\r
- public void setType(SpecimenOrObservationType type) {\r
- if (type == null){\r
- throw new IllegalArgumentException("The type of a specimen or observation may not be null");\r
- }\r
- SpecimenOrObservationBase<?> baseUnit = baseUnit();\r
- if(baseUnit.isInstanceOf(FieldUnit.class) && !type.isFieldUnit()){\r
- throw new IllegalArgumentException("A FieldUnit may only be of type FieldUnit") ;\r
- }\r
- else if(baseUnit.isInstanceOf(DerivedUnit.class) && type.isFieldUnit()){\r
- throw new IllegalArgumentException("A derived unit may not be of type FieldUnit") ;\r
- }\r
- baseUnit.setRecordBasis(type);\r
- }\r
-\r
- public SpecimenOrObservationType getType() {\r
- return baseUnit().getRecordBasis();\r
- }\r
-\r
- /**\r
- * Closes this facade. As a minimum this method removes all listeners created by this facade from their\r
- * listening objects.\r
- */\r
- public void close(){\r
- for (PropertyChangeListener listener : this.listeners.keySet()){\r
- CdmBase listeningObject = listeners.get(listener);\r
- listeningObject.removePropertyChangeListener(listener);\r
- }\r
- }\r
-\r
-\r
- /**\r
- * Computes the correct distance string for given values for min, max and text.\r
- * If text is not blank, text is returned, otherwise "min - max" or a single value is returned.\r
- * @param min min value as number\r
- * @param max max value as number\r
- * @param text text representation of distance\r
- * @return the formatted distance string\r
- */\r
- private String distanceString(Number min, Number max, String text, String unit) {\r
- if (StringUtils.isNotBlank(text)){\r
- return text;\r
- }else{\r
- String minStr = min == null? null : String.valueOf(min);\r
- String maxStr = max == null? null : String.valueOf(max);\r
- String result = CdmUtils.concat(UTF8.EN_DASH_SPATIUM.toString(), minStr, maxStr);\r
- if (StringUtils.isNotBlank(result) && StringUtils.isNotBlank(unit)){\r
- result = result + " " + unit;\r
- }\r
- return result;\r
- }\r
- }\r
-\r
- /**\r
- * First checks the inner field unit for the publish flag. If set to <code>true</code>\r
- * then <code>true</code> is returned. If the field unit is <code>null</code> the inner derived unit\r
- * is checked.\r
- * @return <code>true</code> if this facade can be published\r
- */\r
- public boolean isPublish(){\r
- if(fieldUnit!=null){\r
- return fieldUnit.isPublish();\r
- }\r
- if(derivedUnit!=null){\r
- return derivedUnit.isPublish();\r
- }\r
- return false;\r
- }\r
-}\r
+/**
+ * Copyright (C) 2007 EDIT
+ * European Distributed Institute of Taxonomy
+ * http://www.e-taxonomy.eu
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * See LICENSE.TXT at the top of this package for the full license terms.
+ */
+
+package eu.etaxonomy.cdm.api.facade;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.persistence.Transient;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
+import eu.etaxonomy.cdm.api.service.IOccurrenceService;
+import eu.etaxonomy.cdm.common.CdmUtils;
+import eu.etaxonomy.cdm.common.UTF8;
+import eu.etaxonomy.cdm.model.agent.AgentBase;
+import eu.etaxonomy.cdm.model.agent.Person;
+import eu.etaxonomy.cdm.model.common.Annotation;
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.common.DefinedTerm;
+import eu.etaxonomy.cdm.model.common.IOriginalSource;
+import eu.etaxonomy.cdm.model.common.IdentifiableSource;
+import eu.etaxonomy.cdm.model.common.Identifier;
+import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.model.common.LanguageString;
+import eu.etaxonomy.cdm.model.common.OriginalSourceType;
+import eu.etaxonomy.cdm.model.common.TimePeriod;
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+import eu.etaxonomy.cdm.model.description.Feature;
+import eu.etaxonomy.cdm.model.description.SpecimenDescription;
+import eu.etaxonomy.cdm.model.description.TextData;
+import eu.etaxonomy.cdm.model.location.NamedArea;
+import eu.etaxonomy.cdm.model.location.Point;
+import eu.etaxonomy.cdm.model.location.ReferenceSystem;
+import eu.etaxonomy.cdm.model.media.Media;
+import eu.etaxonomy.cdm.model.media.Rights;
+import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+import eu.etaxonomy.cdm.model.occurrence.Collection;
+import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
+import eu.etaxonomy.cdm.model.occurrence.DerivationEventType;
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
+import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
+import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
+import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
+import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
+import eu.etaxonomy.cdm.model.reference.Reference;
+
+/**
+ * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
+ * a specimen based view. It does not support all functionality available in the
+ * occurrence package.<BR>
+ * The most significant restriction is that a specimen may derive only from one
+ * direct derivation event and there must be only one field unit
+ * (gathering event) it derives from.<BR>
+ *
+ * @author a.mueller
+ * @date 14.05.2010
+ */
+public class DerivedUnitFacade {
+ private static final String METER = "m";
+
+ @SuppressWarnings("unused")
+ private static final Logger logger = Logger.getLogger(DerivedUnitFacade.class);
+
+ 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 ";
+
+ private static final boolean CREATE = true;
+ private static final boolean CREATE_NOT = false;
+
+ private final DerivedUnitFacadeConfigurator config;
+
+ private final Map<PropertyChangeListener, CdmBase> listeners = new HashMap<PropertyChangeListener, CdmBase>();
+
+ // Either fieldUnit or derivedUnit must not be null.
+ private FieldUnit fieldUnit;
+ private final DerivedUnit derivedUnit;
+
+ // media - the text data holding the media
+ private TextData derivedUnitMediaTextData;
+ private TextData fieldObjectMediaTextData;
+
+ private TextData ecology;
+ private TextData plantDescription;
+
+ /**
+ * Creates a derived unit facade for a new derived unit of type
+ * <code>type</code>.
+ *
+ * @param type
+ * @return
+ */
+ public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type) {
+ return new DerivedUnitFacade(type, null, null);
+ }
+
+ /**
+ * Creates a derived unit facade for a new derived unit of type
+ * <code>type</code>.
+ *
+ * @param type
+ * @return
+ */
+ public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type, FieldUnit fieldUnit) {
+ return new DerivedUnitFacade(type, fieldUnit, null);
+ }
+
+ /**
+ * Creates a derived unit facade for a new derived unit of type
+ * <code>type</code>.
+ *
+ * @param type
+ * @param fieldUnit the field unit to use
+ * @param config the facade configurator to use
+ * //TODO are there any ambiguities to solve with defining a field unit or a configurator
+ * @return
+ */
+ public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type, FieldUnit fieldUnit, DerivedUnitFacadeConfigurator config) {
+ return new DerivedUnitFacade(type, fieldUnit, config);
+ }
+
+
+ /**
+ * Creates a derived unit facade for a given derived unit using the default
+ * configuration.
+ *
+ * @param derivedUnit
+ * @return
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit)
+ throws DerivedUnitFacadeNotSupportedException {
+ return new DerivedUnitFacade(derivedUnit, null);
+ }
+
+ public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit,
+ DerivedUnitFacadeConfigurator config)
+ throws DerivedUnitFacadeNotSupportedException {
+ return new DerivedUnitFacade(derivedUnit, config);
+ }
+
+ // ****************** CONSTRUCTOR ******************************************
+
+ private DerivedUnitFacade(SpecimenOrObservationType type, FieldUnit fieldUnit, DerivedUnitFacadeConfigurator config) {
+ if (config == null){
+ config = DerivedUnitFacadeConfigurator.NewInstance();
+ }
+ this.config = config;
+ // derivedUnit
+ derivedUnit = getNewDerivedUnitInstance(type);
+ //TODO parameter checking should be solved in a more generic way if we start using other entity facades
+ if(derivedUnit==null && fieldUnit==null && type.isFieldUnit()){
+ this.fieldUnit = getFieldUnit(CREATE);
+ }
+ setFieldUnit(fieldUnit);
+ if (derivedUnit != null){
+ setCacheStrategy();
+ }else{
+ setFieldUnitCacheStrategy();
+ }
+ }
+
+ private DerivedUnit getNewDerivedUnitInstance(SpecimenOrObservationType type) {
+ if (type.isFieldUnit()){
+ return null;
+ }else if(type.isAnyDerivedUnit()){
+ return DerivedUnit.NewInstance(type);
+ } else {
+ String message = "Unknown specimen or observation type %s";
+ message = String.format(message, type.getMessage());
+ throw new IllegalStateException(message);
+ }
+ }
+
+ private DerivedUnitFacade(DerivedUnit derivedUnit, DerivedUnitFacadeConfigurator config)
+ throws DerivedUnitFacadeNotSupportedException {
+
+ if(derivedUnit==null){
+ throw new IllegalArgumentException("DerivedUnit must not be null");
+ }
+
+ if (config == null) {
+ config = DerivedUnitFacadeConfigurator.NewInstance();
+ }
+ this.config = config;
+
+ // derived unit
+ this.derivedUnit = derivedUnit;
+
+ // derivation event
+ if (this.derivedUnit.getDerivedFrom() != null) {
+ DerivationEvent derivationEvent = getDerivationEvent(CREATE);
+ // fieldUnit
+ Set<FieldUnit> fieldOriginals = getFieldUnitOriginals(derivationEvent, null);
+ if (fieldOriginals.size() > 1) {
+ throw new DerivedUnitFacadeNotSupportedException(
+ "Specimen must not have more than 1 derivation event");
+ } else if (fieldOriginals.size() == 0) {
+ // fieldUnit = FieldUnit.NewInstance();
+ } else if (fieldOriginals.size() == 1) {
+ fieldUnit = fieldOriginals.iterator().next();
+ // ###fieldUnit =
+ // getInitializedFieldUnit(fieldUnit);
+ if (config.isFirePropertyChangeEvents()){
+ addNewEventPropagationListener(fieldUnit);
+ }
+ } else {
+ throw new IllegalStateException("Illegal state");
+ }
+ }
+
+ this.derivedUnitMediaTextData = inititializeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);
+
+ fieldObjectMediaTextData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), false, true);
+
+
+//direct media have been removed from specimenorobservationbase #3597
+// // handle derivedUnit.getMedia()
+// if (derivedUnit.getMedia().size() > 0) {
+// // TODO better changed model here to allow only one place for images
+// if (this.config.isMoveDerivedUnitMediaToGallery()) {
+// Set<Media> mediaSet = derivedUnit.getMedia();
+// for (Media media : mediaSet) {
+// this.addDerivedUnitMedia(media);
+// }
+// mediaSet.removeAll(getDerivedUnitMedia());
+// } else {
+// throw new DerivedUnitFacadeNotSupportedException(
+// "Specimen may not have direct media. Only (one) image gallery is allowed");
+// }
+// }
+//
+// // handle fieldUnit.getMedia()
+// if (fieldUnit != null && fieldUnit.getMedia() != null
+// && fieldUnit.getMedia().size() > 0) {
+// // TODO better changed model here to allow only one place for images
+// if (this.config.isMoveFieldObjectMediaToGallery()) {
+// Set<Media> mediaSet = fieldUnit.getMedia();
+// for (Media media : mediaSet) {
+// this.addFieldObjectMedia(media);
+// }
+// mediaSet.removeAll(getFieldObjectMedia());
+// } else {
+// throw new DerivedUnitFacadeNotSupportedException(
+// "Field object may not have direct media. Only (one) image gallery is allowed");
+// }
+// }
+
+ // test if descriptions are supported
+ ecology = initializeFieldObjectTextDataWithSupportTest(
+ Feature.ECOLOGY(), false, false);
+ plantDescription = initializeFieldObjectTextDataWithSupportTest(
+ Feature.DESCRIPTION(), false, false);
+
+ setCacheStrategy();
+
+ }
+
+ private DerivedUnit getInitializedDerivedUnit(
+ DerivedUnit derivedUnit) {
+ IOccurrenceService occurrenceService = this.config
+ .getOccurrenceService();
+ if (occurrenceService == null) {
+ return derivedUnit;
+ }
+ List<String> propertyPaths = this.config.getPropertyPaths();
+ if (propertyPaths == null) {
+ return derivedUnit;
+ }
+ propertyPaths = getDerivedUnitPropertyPaths(propertyPaths);
+ DerivedUnit result = (DerivedUnit) occurrenceService.load(
+ derivedUnit.getUuid(), propertyPaths);
+ return result;
+ }
+
+ /**
+ * Initializes the derived unit according to the configuartions property
+ * path. If the property path is <code>null</code> or no occurrence service
+ * is given the returned object is the same as the input parameter.
+ *
+ * @param fieldUnit
+ * @return
+ */
+ private FieldUnit getInitializedFieldUnit(FieldUnit fieldUnit) {
+ IOccurrenceService occurrenceService = this.config
+ .getOccurrenceService();
+ if (occurrenceService == null) {
+ return fieldUnit;
+ }
+ List<String> propertyPaths = this.config.getPropertyPaths();
+ if (propertyPaths == null) {
+ return fieldUnit;
+ }
+ propertyPaths = getFieldObjectPropertyPaths(propertyPaths);
+ FieldUnit result = (FieldUnit) occurrenceService.load(
+ fieldUnit.getUuid(), propertyPaths);
+ return result;
+ }
+
+ /**
+ * Transforms the property paths in a way that the facade is handled just
+ * like an ordinary CdmBase object.<BR>
+ * E.g. a property path "collectinAreas" will be translated into
+ * gatheringEvent.collectingAreas
+ *
+ * @param propertyPaths
+ * @return
+ */
+ private List<String> getFieldObjectPropertyPaths(List<String> propertyPaths) {
+ List<String> result = new ArrayList<String>();
+ for (String facadePath : propertyPaths) {
+ // collecting areas (named area)
+ if (facadePath.startsWith("collectingAreas")) {
+ facadePath = "gatheringEvent." + facadePath;
+ result.add(facadePath);
+ }
+ // collector (agentBase)
+ else if (facadePath.startsWith("collector")) {
+ facadePath = facadePath.replace("collector",
+ "gatheringEvent.actor");
+ result.add(facadePath);
+ }
+ // exactLocation (agentBase)
+ else if (facadePath.startsWith("exactLocation")) {
+ facadePath = "gatheringEvent." + facadePath;
+ result.add(facadePath);
+ }
+ // gatheringPeriod (TimePeriod)
+ else if (facadePath.startsWith("gatheringPeriod")) {
+ facadePath = facadePath.replace("gatheringPeriod",
+ "gatheringEvent.timeperiod");
+ result.add(facadePath);
+ }
+ // (locality/ localityLanguage , LanguageString)
+ else if (facadePath.startsWith("locality")) {
+ facadePath = "gatheringEvent." + facadePath;
+ result.add(facadePath);
+ }
+
+ // *********** FIELD OBJECT ************
+ // fieldObjectDefinitions (Map<language, languageString)
+ else if (facadePath.startsWith("fieldObjectDefinitions")) {
+ // TODO or definition ???
+ facadePath = facadePath.replace("fieldObjectDefinitions",
+ "description");
+ result.add(facadePath);
+ }
+ // fieldObjectMedia (Media)
+ else if (facadePath.startsWith("fieldObjectMedia")) {
+ // TODO ???
+ facadePath = facadePath.replace("fieldObjectMedia",
+ "descriptions.elements.media");
+ result.add(facadePath);
+ }
+
+ // Gathering Event will always be added
+ result.add("gatheringEvent");
+
+ }
+
+ /*
+ * Gathering Event ==================== - gatheringEvent
+ * (GatheringEvent)
+ *
+ * Field Object ================= - ecology/ ecologyAll (String) ??? -
+ * plant description (like ecology)
+ *
+ * - fieldObjectImageGallery (SpecimenDescription) - is automatically
+ * initialized via fieldObjectMedia
+ */
+
+ return result;
+ }
+
+ /**
+ * Transforms the property paths in a way that the facade is handled just
+ * like an ordinary CdmBase object.<BR>
+ * E.g. a property path "collectinAreas" will be translated into
+ * gatheringEvent.collectingAreas
+ *
+ * Not needed (?) as the facade works with REST service property paths
+ * without using this method.
+ *
+ * @param propertyPaths
+ * @return
+ */
+ private List<String> getDerivedUnitPropertyPaths(List<String> propertyPaths) {
+ List<String> result = new ArrayList<String>();
+ for (String facadePath : propertyPaths) {
+ // determinations (DeterminationEvent)
+ if (facadePath.startsWith("determinations")) {
+ facadePath = "" + facadePath; // no change
+ result.add(facadePath);
+ }
+ // storedUnder (TaxonNameBase)
+ else if (facadePath.startsWith("storedUnder")) {
+ facadePath = "" + facadePath; // no change
+ result.add(facadePath);
+ }
+ // sources (IdentifiableSource)
+ else if (facadePath.startsWith("sources")) {
+ facadePath = "" + facadePath; // no change
+ result.add(facadePath);
+ }
+ // collection (Collection)
+ else if (facadePath.startsWith("collection")) {
+ facadePath = "" + facadePath; // no change
+ result.add(facadePath);
+ }
+ // (locality/ localityLanguage , LanguageString)
+ else if (facadePath.startsWith("locality")) {
+ facadePath = "gatheringEvent." + facadePath;
+ result.add(facadePath);
+ }
+
+ // *********** FIELD OBJECT ************
+ // derivedUnitDefinitions (Map<language, languageString)
+ else if (facadePath.startsWith("derivedUnitDefinitions")) {
+ // TODO or definition ???
+ facadePath = facadePath.replace("derivedUnitDefinitions",
+ "description");
+ result.add(facadePath);
+ }
+
+ // derivedUnitMedia (Media)
+ else if (facadePath.startsWith("derivedUnitMedia")) {
+ // TODO ???
+ facadePath = facadePath.replace("derivedUnitMedia",
+ "descriptions.elements.media");
+ result.add(facadePath);
+ }
+
+ }
+
+ /*
+ * //TODO Derived Unit =====================
+ *
+ * - derivedUnitImageGallery (SpecimenDescription) - is automatically
+ * initialized via derivedUnitMedia
+ *
+ * - derivationEvent (DerivationEvent) - will always be initialized -
+ * duplicates (??? Specimen???) ???
+ */
+
+ return result;
+ }
+
+ /**
+ *
+ */
+ private void setCacheStrategy() {
+ if (derivedUnit == null) {
+ throw new NullPointerException(
+ "Facade's derviedUnit must not be null to set cache strategy");
+ }else{
+ derivedUnit.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
+ setFieldUnitCacheStrategy();
+ }
+ }
+
+ private void setFieldUnitCacheStrategy() {
+ if (this.hasFieldObject()){
+ DerivedUnitFacadeFieldUnitCacheStrategy strategy = new DerivedUnitFacadeFieldUnitCacheStrategy();
+ this.fieldUnit.setCacheStrategy(strategy);
+ }
+ }
+
+ /**
+ * @param feature
+ * @param createIfNotExists
+ * @param isImageGallery
+ * @return
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private TextData initializeFieldObjectTextDataWithSupportTest(
+ Feature feature, boolean createIfNotExists, boolean isImageGallery)
+ throws DerivedUnitFacadeNotSupportedException {
+ // field object
+ FieldUnit fieldObject = getFieldUnit(createIfNotExists);
+ if (fieldObject == null) {
+ return null;
+ }
+ return inititializeTextDataWithSupportTest(feature, fieldObject,
+ createIfNotExists, isImageGallery);
+ }
+
+ /**
+ * @param feature
+ * @param specimen
+ * @param createIfNotExists
+ * @param isImageGallery
+ * @return
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private TextData inititializeTextDataWithSupportTest(Feature feature,
+ SpecimenOrObservationBase<?> specimen, boolean createIfNotExists,
+ boolean isImageGallery)
+ throws DerivedUnitFacadeNotSupportedException {
+ if (feature == null) {
+ return null;
+ }
+ TextData textData = null;
+ if (createIfNotExists) {
+ textData = TextData.NewInstance(feature);
+ }
+
+ Set<SpecimenDescription> descriptions;
+ if (isImageGallery) {
+ descriptions = specimen.getSpecimenDescriptionImageGallery();
+ } else {
+ descriptions = specimen.getSpecimenDescriptions(false);
+ }
+ // no description exists yet for this specimen
+ if (descriptions.size() == 0) {
+ if (createIfNotExists) {
+ SpecimenDescription newSpecimenDescription = SpecimenDescription
+ .NewInstance(specimen);
+ newSpecimenDescription.addElement(textData);
+ newSpecimenDescription.setImageGallery(isImageGallery);
+ return textData;
+ } else {
+ return null;
+ }
+ }
+ // description already exists
+ Set<DescriptionElementBase> existingTextData = new HashSet<DescriptionElementBase>();
+ for (SpecimenDescription description : descriptions) {
+ // collect all existing text data
+ for (DescriptionElementBase element : description.getElements()) {
+ if (element.isInstanceOf(TextData.class)
+ && (feature.equals(element.getFeature()) || isImageGallery)) {
+ existingTextData.add(element);
+ }
+ }
+ }
+ // use existing text data if exactly one exists
+ if (existingTextData.size() > 1) {
+ throw new DerivedUnitFacadeNotSupportedException(
+ "Specimen facade does not support more than one description text data of type "
+ + feature.getLabel());
+
+ } else if (existingTextData.size() == 1) {
+ return CdmBase.deproxy(existingTextData.iterator().next(),
+ TextData.class);
+ } else {
+ if (createIfNotExists) {
+ SpecimenDescription description = descriptions.iterator()
+ .next();
+ description.addElement(textData);
+ }
+ return textData;
+ }
+ }
+
+ /**
+ * Tests if a given image gallery is supported by the derived unit facade.
+ * It returns the only text data attached to the given image gallery. If the
+ * given image gallery does not have text data attached, it is created and
+ * attached.
+ *
+ * @param imageGallery
+ * @return
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private TextData testImageGallery(SpecimenDescription imageGallery)
+ throws DerivedUnitFacadeNotSupportedException {
+ if (imageGallery.isImageGallery() == false) {
+ throw new DerivedUnitFacadeNotSupportedException(
+ "Image gallery needs to have image gallery flag set");
+ }
+ if (imageGallery.getElements().size() > 1) {
+ throw new DerivedUnitFacadeNotSupportedException(
+ "Image gallery must not have more then one description element");
+ }
+ TextData textData;
+ if (imageGallery.getElements().size() == 0) {
+ textData = TextData.NewInstance(Feature.IMAGE());
+ imageGallery.addElement(textData);
+ } else {
+ if (!imageGallery.getElements().iterator().next()
+ .isInstanceOf(TextData.class)) {
+ throw new DerivedUnitFacadeNotSupportedException(
+ "Image gallery must only have TextData as element");
+ } else {
+ textData = CdmBase.deproxy(imageGallery.getElements()
+ .iterator().next(), TextData.class);
+ }
+ }
+ return textData;
+ }
+
+ // ************************** METHODS
+ // *****************************************
+
+ private TextData getDerivedUnitImageGalleryTextData(
+ boolean createIfNotExists)
+ throws DerivedUnitFacadeNotSupportedException {
+ if (this.derivedUnitMediaTextData == null && createIfNotExists) {
+ this.derivedUnitMediaTextData = getImageGalleryTextData(
+ derivedUnit, "Specimen");
+ }
+ return this.derivedUnitMediaTextData;
+ }
+
+ private TextData getObservationImageGalleryTextData(
+ boolean createIfNotExists)
+ throws DerivedUnitFacadeNotSupportedException {
+ if (this.fieldObjectMediaTextData == null && createIfNotExists) {
+ this.fieldObjectMediaTextData = getImageGalleryTextData(fieldUnit, "Field unit");
+ }
+ return this.fieldObjectMediaTextData;
+ }
+
+ /**
+ * @param derivationEvent
+ * @return
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private Set<FieldUnit> getFieldUnitOriginals(
+ DerivationEvent derivationEvent,
+ Set<SpecimenOrObservationBase> recursionAvoidSet)
+ throws DerivedUnitFacadeNotSupportedException {
+ if (recursionAvoidSet == null) {
+ recursionAvoidSet = new HashSet<SpecimenOrObservationBase>();
+ }
+ Set<FieldUnit> result = new HashSet<FieldUnit>();
+ Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
+ for (SpecimenOrObservationBase original : originals) {
+ if (original.isInstanceOf(FieldUnit.class)) {
+ result.add(CdmBase.deproxy(original, FieldUnit.class));
+ } else if (original.isInstanceOf(DerivedUnit.class)) {
+ // if specimen has already been tested exclude it from further
+ // recursion
+ if (recursionAvoidSet.contains(original)) {
+ continue;
+ }
+ DerivedUnit derivedUnit = CdmBase.deproxy(original, DerivedUnit.class);
+ DerivationEvent originalDerivation = derivedUnit.getDerivedFrom();
+ // Set<DerivationEvent> derivationEvents =
+ // original.getDerivationEvents();
+ // for (DerivationEvent originalDerivation : derivationEvents){
+ if(originalDerivation!=null){
+ Set<FieldUnit> fieldUnits = getFieldUnitOriginals(
+ originalDerivation, recursionAvoidSet);
+ result.addAll(fieldUnits);
+ }
+ // }
+ } else {
+ throw new DerivedUnitFacadeNotSupportedException(
+ "Unhandled specimen or observation base type: "
+ + original.getClass().getName());
+ }
+
+ }
+ return result;
+ }
+
+ // *********** MEDIA METHODS ******************************
+
+ // /**
+ // * Returns the media list for a specimen. Throws an exception if the
+ // existing specimen descriptions
+ // * are not supported by this facade.
+ // * @param specimen the specimen the media belongs to
+ // * @param specimenExceptionText text describing the specimen for exception
+ // messages
+ // * @return
+ // * @throws DerivedUnitFacadeNotSupportedException
+ // */
+ // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase
+ // specimen, String specimenExceptionText) throws
+ // DerivedUnitFacadeNotSupportedException{
+ // List<Media> result;
+ // SpecimenDescription imageGallery =
+ // getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
+ // TextData textData = getImageTextDataWithSupportTest(imageGallery,
+ // specimenExceptionText);
+ // result = textData.getMedia();
+ // return result;
+ // }
+
+ /**
+ * Returns the media list for a specimen. Throws an exception if the
+ * existing specimen descriptions are not supported by this facade.
+ *
+ * @param specimen
+ * the specimen the media belongs to
+ * @param specimenExceptionText
+ * text describing the specimen for exception messages
+ * @return
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private TextData getImageGalleryTextData(SpecimenOrObservationBase specimen, String specimenExceptionText)
+ throws DerivedUnitFacadeNotSupportedException {
+ TextData result;
+ SpecimenDescription imageGallery = getImageGalleryWithSupportTest(
+ specimen, specimenExceptionText, true);
+ result = getImageTextDataWithSupportTest(imageGallery,
+ specimenExceptionText);
+ return result;
+ }
+
+ /**
+ * Returns the image gallery of the according specimen. Throws an exception
+ * if the attached image gallerie(s) are not supported by this facade. If no
+ * image gallery exists a new one is created if
+ * <code>createNewIfNotExists</code> is true and if specimen is not
+ * <code>null</code>.
+ *
+ * @param specimen
+ * @param specimenText
+ * @param createNewIfNotExists
+ * @return
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private SpecimenDescription getImageGalleryWithSupportTest(
+ SpecimenOrObservationBase<?> specimen, String specimenText,
+ boolean createNewIfNotExists)
+ throws DerivedUnitFacadeNotSupportedException {
+ if (specimen == null) {
+ return null;
+ }
+ SpecimenDescription imageGallery;
+ if (hasMultipleImageGalleries(specimen)) {
+ throw new DerivedUnitFacadeNotSupportedException(specimenText
+ + " must not have more than 1 image gallery");
+ } else {
+ imageGallery = getImageGallery(specimen, createNewIfNotExists);
+ getImageTextDataWithSupportTest(imageGallery, specimenText);
+ }
+ return imageGallery;
+ }
+
+ /**
+ * Returns the media holding text data element of the image gallery. Throws
+ * an exception if multiple such text data already exist. Creates a new text
+ * data if none exists and adds it to the image gallery. If image gallery is
+ * <code>null</code> nothing happens.
+ *
+ * @param imageGallery
+ * @param textData
+ * @return
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private TextData getImageTextDataWithSupportTest(
+ SpecimenDescription imageGallery, String specimenText)
+ throws DerivedUnitFacadeNotSupportedException {
+ if (imageGallery == null) {
+ return null;
+ }
+ TextData textData = null;
+ for (DescriptionElementBase element : imageGallery.getElements()) {
+ if (element.isInstanceOf(TextData.class)
+ && element.getFeature().equals(Feature.IMAGE())) {
+ if (textData != null) {
+ throw new DerivedUnitFacadeNotSupportedException(
+ specimenText
+ + " must not have more than 1 image text data element in image gallery");
+ }
+ textData = CdmBase.deproxy(element, TextData.class);
+ }
+ }
+ if (textData == null) {
+ textData = TextData.NewInstance(Feature.IMAGE());
+ imageGallery.addElement(textData);
+ }
+ return textData;
+ }
+
+ /**
+ * Checks, if a specimen belongs to more than one description that is an
+ * image gallery
+ *
+ * @param derivedUnit
+ * @return
+ */
+ private boolean hasMultipleImageGalleries(
+ SpecimenOrObservationBase<?> derivedUnit) {
+ int count = 0;
+ Set<SpecimenDescription> descriptions = derivedUnit
+ .getSpecimenDescriptions();
+ for (SpecimenDescription description : descriptions) {
+ if (description.isImageGallery()) {
+ count++;
+ }
+ }
+ return (count > 1);
+ }
+
+ /**
+ * Returns the image gallery for a specimen. If there are multiple specimen
+ * descriptions marked as image galleries an arbitrary one is chosen. If no
+ * image gallery exists, a new one is created if
+ * <code>createNewIfNotExists</code> is <code>true</code>.<Br>
+ * If specimen is <code>null</code> a null pointer exception is thrown.
+ *
+ * @param createNewIfNotExists
+ * @return
+ */
+ private SpecimenDescription getImageGallery(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) {
+ SpecimenDescription result = null;
+ Set<SpecimenDescription> descriptions = specimen.getSpecimenDescriptions();
+ for (SpecimenDescription description : descriptions) {
+ if (description.isImageGallery()) {
+ result = description;
+ break;
+ }
+ }
+ if (result == null && createIfNotExists) {
+ result = SpecimenDescription.NewInstance(specimen);
+ result.setImageGallery(true);
+ }
+ return result;
+ }
+
+ /**
+ * Adds a media to the specimens image gallery. If media is
+ * <code>null</code> nothing happens.
+ *
+ * @param media
+ * @param specimen
+ * @return true if media is not null (as specified by
+ * {@link java.util.Collection#add(Object) Collection.add(E e)}
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private boolean addMedia(Media media, SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {
+ if (media != null) {
+ List<Media> mediaList = getMediaList(specimen, true);
+ if (! mediaList.contains(media)){
+ return mediaList.add(media);
+ }else{
+ return true;
+ }
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Removes a media from the specimens image gallery.
+ *
+ * @param media
+ * @param specimen
+ * @return true if an element was removed as a result of this call (as
+ * specified by {@link java.util.Collection#remove(Object)
+ * Collection.remove(E e)}
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private boolean removeMedia(Media media,
+ SpecimenOrObservationBase<?> specimen)
+ throws DerivedUnitFacadeNotSupportedException {
+ List<Media> mediaList = getMediaList(specimen, true);
+ return mediaList == null ? null : mediaList.remove(media);
+ }
+
+ private List<Media> getMediaList(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists)
+ throws DerivedUnitFacadeNotSupportedException {
+ TextData textData = getMediaTextData(specimen, createIfNotExists);
+ return textData == null ? null : textData.getMedia();
+ }
+
+ /**
+ * Returns the one media list of a specimen which is part of the only image
+ * gallery that this specimen is part of.<BR>
+ * If these conditions are not hold an exception is thrwon.
+ *
+ * @param specimen
+ * @return
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)
+ // throws DerivedUnitFacadeNotSupportedException {
+ // if (specimen == null){
+ // return null;
+ // }
+ // if (specimen == this.derivedUnit){
+ // return getDerivedUnitImageGalleryMedia();
+ // }else if (specimen == this.fieldUnit){
+ // return getObservationImageGalleryTextData();
+ // }else{
+ // return getImageGalleryMedia(specimen, "Undefined specimen ");
+ // }
+ // }
+
+ /**
+ * Returns the one media list of a specimen which is part of the only image
+ * gallery that this specimen is part of.<BR>
+ * If these conditions are not hold an exception is thrown.
+ *
+ * @param specimen
+ * @return
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private TextData getMediaTextData(SpecimenOrObservationBase<?> specimen,
+ boolean createIfNotExists)
+ throws DerivedUnitFacadeNotSupportedException {
+ if (specimen == null) {
+ return null;
+ }
+ if (specimen == this.derivedUnit) {
+ return getDerivedUnitImageGalleryTextData(createIfNotExists);
+ } else if (specimen == this.fieldUnit) {
+ return getObservationImageGalleryTextData(createIfNotExists);
+ } else {
+ return getImageGalleryTextData(specimen, "Undefined specimen ");
+ }
+ }
+
+ // ****************** GETTER / SETTER / ADDER / REMOVER
+ // ***********************/
+
+ // ****************** Gathering Event *********************************/
+
+ // country
+ @Transient
+ public NamedArea getCountry() {
+ return (hasGatheringEvent() ? getGatheringEvent(true).getCountry()
+ : null);
+ }
+
+ public void setCountry(NamedArea country) {
+ getGatheringEvent(true).setCountry(country);
+ }
+
+ // Collecting area
+ public void addCollectingArea(NamedArea area) {
+ getGatheringEvent(true).addCollectingArea(area);
+ }
+
+ public void addCollectingAreas(java.util.Collection<NamedArea> areas) {
+ for (NamedArea area : areas) {
+ getGatheringEvent(true).addCollectingArea(area);
+ }
+ }
+
+ @Transient
+ public Set<NamedArea> getCollectingAreas() {
+ return (hasGatheringEvent() ? getGatheringEvent(true)
+ .getCollectingAreas() : null);
+ }
+
+ public void removeCollectingArea(NamedArea area) {
+ if (hasGatheringEvent()) {
+ getGatheringEvent(true).removeCollectingArea(area);
+ }
+ }
+
+ static final String ALTITUDE_POSTFIX = " m";
+
+ /**
+ * Returns the correctly formatted <code>absolute elevation</code> information.
+ * If absoluteElevationText is set, this will be returned,
+ * otherwise we absoluteElevation will be returned, followed by absoluteElevationMax
+ * if existing, separated by " - "
+ * @return
+ */
+ @Transient
+ public String absoluteElevationToString() {
+ if (! hasGatheringEvent()){
+ return null;
+ }else{
+ GatheringEvent ev = getGatheringEvent(true);
+ if (StringUtils.isNotBlank(ev.getAbsoluteElevationText())){
+ return ev.getAbsoluteElevationText();
+ }else{
+ String text = ev.getAbsoluteElevationText();
+ Integer min = getAbsoluteElevation();
+ Integer max = getAbsoluteElevationMaximum();
+ return distanceString(min, max, text, METER);
+ }
+ }
+ }
+
+
+ /**
+ * meter above/below sea level of the surface
+ *
+ * @see #getAbsoluteElevationError()
+ * @see #getAbsoluteElevationRange()
+ **/
+ @Transient
+ public Integer getAbsoluteElevation() {
+ return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevation() : null);
+ }
+
+ public void setAbsoluteElevation(Integer absoluteElevation) {
+ getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);
+ }
+
+ public void setAbsoluteElevationMax(Integer absoluteElevationMax) {
+ getGatheringEvent(true).setAbsoluteElevationMax(absoluteElevationMax);
+ }
+
+ public void setAbsoluteElevationText(String absoluteElevationText) {
+ getGatheringEvent(true).setAbsoluteElevationText(absoluteElevationText);
+ }
+
+ /**
+ * @see #getAbsoluteElevation()
+ * @see #getAbsoluteElevationError()
+ * @see #setAbsoluteElevationRange(Integer, Integer)
+ * @see #getAbsoluteElevationMinimum()
+ */
+ @Transient
+ public Integer getAbsoluteElevationMaximum() {
+ if (!hasGatheringEvent()) {
+ return null;
+ }else{
+ return getGatheringEvent(true).getAbsoluteElevationMax();
+ }
+ }
+
+ /**
+ * @see #getAbsoluteElevation()
+ * @see #getAbsoluteElevationError()
+ * @see #setAbsoluteElevationRange(Integer, Integer)
+ * @see #getAbsoluteElevationMinimum()
+ */
+ @Transient
+ public String getAbsoluteElevationText() {
+ if (!hasGatheringEvent()) {
+ return null;
+ }else{
+ return getGatheringEvent(true).getAbsoluteElevationText();
+ }
+ }
+
+ /**
+ * Convenience method to set absolute elevation minimum and maximum.
+ *
+ * @see #setAbsoluteElevation(Integer)
+ * @see #setAbsoluteElevationMax(Integer)
+ * @param minimumElevation minimum of the range
+ * @param maximumElevation maximum of the range
+ */
+ public void setAbsoluteElevationRange(Integer minimumElevation, Integer maximumElevation) {
+ getGatheringEvent(true).setAbsoluteElevation(minimumElevation);
+ getGatheringEvent(true).setAbsoluteElevationMax(maximumElevation);
+ }
+
+ // collector
+ @Transient
+ public AgentBase getCollector() {
+ return (hasGatheringEvent() ? getGatheringEvent(true).getCollector()
+ : null);
+ }
+
+ public void setCollector(AgentBase collector) {
+ getGatheringEvent(true).setCollector(collector);
+ }
+
+ // collecting method
+ @Transient
+ public String getCollectingMethod() {
+ return (hasGatheringEvent() ? getGatheringEvent(true).getCollectingMethod() : null);
+ }
+
+ public void setCollectingMethod(String collectingMethod) {
+ getGatheringEvent(true).setCollectingMethod(collectingMethod);
+ }
+
+ // distance to ground
+
+ /**
+ * Returns the correctly formatted <code>distance to ground</code> information.
+ * If distanceToGroundText is not blank, it will be returned,
+ * otherwise distanceToGround will be returned, followed by distanceToGroundMax
+ * if existing, separated by " - "
+ * @return
+ */
+ @Transient
+ public String distanceToGroundToString() {
+ if (! hasGatheringEvent()){
+ return null;
+ }else{
+ GatheringEvent ev = getGatheringEvent(true);
+ String text = ev.getDistanceToGroundText();
+ Double min = getDistanceToGround();
+ Double max = getDistanceToGroundMax();
+ return distanceString(min, max, text, METER);
+ }
+ }
+
+ @Transient
+ public Double getDistanceToGround() {
+ return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToGround() : null);
+ }
+
+ public void setDistanceToGround(Double distanceToGround) {
+ getGatheringEvent(true).setDistanceToGround(distanceToGround);
+ }
+
+ /**
+ * @see #getDistanceToGround()
+ * @see #getDistanceToGroundRange(Integer, Integer)
+ */
+ @Transient
+ public Double getDistanceToGroundMax() {
+ if (!hasGatheringEvent()) {
+ return null;
+ }else{
+ return getGatheringEvent(true).getDistanceToGroundMax();
+ }
+ }
+
+ public void setDistanceToGroundMax(Double distanceToGroundMax) {
+ getGatheringEvent(true).setDistanceToGroundMax(distanceToGroundMax);
+ }
+
+ /**
+ * @see #getDistanceToGround()
+ * @see #setDistanceToGroundRange(Integer, Integer)
+ */
+ @Transient
+ public String getDistanceToGroundText() {
+ if (!hasGatheringEvent()) {
+ return null;
+ }else{
+ return getGatheringEvent(true).getDistanceToGroundText();
+ }
+ }
+ public void setDistanceToGroundText(String distanceToGroundText) {
+ getGatheringEvent(true).setDistanceToGroundText(distanceToGroundText);
+ }
+
+ /**
+ * Convenience method to set distance to ground minimum and maximum.
+ *
+ * @see #getDistanceToGround()
+ * @see #getDistanceToGroundMax()
+ * @param minimumDistance minimum of the range
+ * @param maximumDistance maximum of the range
+ */
+ public void setDistanceToGroundRange(Double minimumDistance, Double maximumDistance) throws IllegalArgumentException{
+ getGatheringEvent(true).setDistanceToGround(minimumDistance);
+ getGatheringEvent(true).setDistanceToGroundMax(maximumDistance);
+ }
+
+
+ /**
+ * Returns the correctly formatted <code>distance to water surface</code> information.
+ * If distanceToWaterSurfaceText is not blank, it will be returned,
+ * otherwise distanceToWaterSurface will be returned, followed by distanceToWatersurfaceMax
+ * if existing, separated by " - "
+ * @return
+ */
+ @Transient
+ public String distanceToWaterSurfaceToString() {
+ if (! hasGatheringEvent()){
+ return null;
+ }else{
+ GatheringEvent ev = getGatheringEvent(true);
+ String text = ev.getDistanceToWaterSurfaceText();
+ Double min = getDistanceToWaterSurface();
+ Double max = getDistanceToWaterSurfaceMax();
+ return distanceString(min, max, text, METER);
+ }
+ }
+
+ // distance to water surface
+ @Transient
+ public Double getDistanceToWaterSurface() {
+ return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToWaterSurface() : null);
+ }
+
+ public void setDistanceToWaterSurface(Double distanceToWaterSurface) {
+ getGatheringEvent(true).setDistanceToWaterSurface(distanceToWaterSurface);
+ }
+
+ /**
+ * @see #getDistanceToWaterSurface()
+ * @see #getDistanceToWaterSurfaceRange(Double, Double)
+ */
+ @Transient
+ public Double getDistanceToWaterSurfaceMax() {
+ if (!hasGatheringEvent()) {
+ return null;
+ }else{
+ return getGatheringEvent(true).getDistanceToWaterSurfaceMax();
+ }
+ }
+
+ public void setDistanceToWaterSurfaceMax(Double distanceToWaterSurfaceMax) {
+ getGatheringEvent(true).setDistanceToWaterSurfaceMax(distanceToWaterSurfaceMax);
+ }
+
+ /**
+ * @see #getDistanceToWaterSurface()
+ * @see #getDistanceToWaterSurfaceRange(Double, Double)
+ */
+ @Transient
+ public String getDistanceToWaterSurfaceText() {
+ if (!hasGatheringEvent()) {
+ return null;
+ }else{
+ return getGatheringEvent(true).getDistanceToWaterSurfaceText();
+ }
+ }
+ public void setDistanceToWaterSurfaceText(String distanceToWaterSurfaceText) {
+ getGatheringEvent(true).setDistanceToWaterSurfaceText(distanceToWaterSurfaceText);
+ }
+
+ /**
+ * Convenience method to set distance to ground minimum and maximum.
+ *
+ * @see #getDistanceToWaterSurface()
+ * @see #getDistanceToWaterSurfaceMax()
+ * @param minimumDistance minimum of the range, this is the distance which is closer to the water surface
+ * @param maximumDistance maximum of the range, this is the distance which is farer to the water surface
+ */
+ public void setDistanceToWaterSurfaceRange(Double minimumDistance, Double maximumDistance) throws IllegalArgumentException{
+ getGatheringEvent(true).setDistanceToWaterSurface(minimumDistance);
+ getGatheringEvent(true).setDistanceToWaterSurfaceMax(maximumDistance);
+ }
+
+
+ // exact location
+ @Transient
+ public Point getExactLocation() {
+ return (hasGatheringEvent() ? getGatheringEvent(true).getExactLocation() : null);
+ }
+
+ /**
+ * Returns a sexagesimal representation of the exact location (e.g.
+ * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty
+ * string is returned.
+ *
+ * @param includeEmptySeconds
+ * @param includeReferenceSystem
+ * @return
+ */
+ public String getExactLocationText(boolean includeEmptySeconds,
+ boolean includeReferenceSystem) {
+ return (this.getExactLocation() == null ? "" : this.getExactLocation()
+ .toSexagesimalString(includeEmptySeconds,
+ includeReferenceSystem));
+ }
+
+ public void setExactLocation(Point exactLocation) {
+ getGatheringEvent(true).setExactLocation(exactLocation);
+ }
+
+ public void setExactLocationByParsing(String longitudeToParse,
+ String latitudeToParse, ReferenceSystem referenceSystem,
+ Integer errorRadius) throws ParseException {
+ Point point = Point.NewInstance(null, null, referenceSystem,
+ errorRadius);
+ point.setLongitudeByParsing(longitudeToParse);
+ point.setLatitudeByParsing(latitudeToParse);
+ setExactLocation(point);
+ }
+
+ // gathering event description
+ @Transient
+ public String getGatheringEventDescription() {
+ return (hasGatheringEvent() ? getGatheringEvent(true).getDescription()
+ : null);
+ }
+
+ public void setGatheringEventDescription(String description) {
+ getGatheringEvent(true).setDescription(description);
+ }
+
+ // gatering period
+ @Transient
+ public TimePeriod getGatheringPeriod() {
+ return (hasGatheringEvent() ? getGatheringEvent(true).getTimeperiod()
+ : null);
+ }
+
+ public void setGatheringPeriod(TimePeriod timeperiod) {
+ getGatheringEvent(true).setTimeperiod(timeperiod);
+ }
+
+ // locality
+ @Transient
+ public LanguageString getLocality() {
+ return (hasGatheringEvent() ? getGatheringEvent(true).getLocality()
+ : null);
+ }
+
+ /**
+ * convienience method for {@link #getLocality()}.
+ * {@link LanguageString#getText() getText()}
+ *
+ * @return
+ */
+ @Transient
+ public String getLocalityText() {
+ LanguageString locality = getLocality();
+ if (locality != null) {
+ return locality.getText();
+ }
+ return null;
+ }
+
+ /**
+ * convienience method for {@link #getLocality()}.
+ * {@link LanguageString#getLanguage() getLanguage()}
+ *
+ * @return
+ */
+ @Transient
+ public Language getLocalityLanguage() {
+ LanguageString locality = getLocality();
+ if (locality != null) {
+ return locality.getLanguage();
+ }
+ return null;
+ }
+
+ /**
+ * Sets the locality string in the default language
+ *
+ * @param locality
+ */
+ public void setLocality(String locality) {
+ Language language = Language.DEFAULT();
+ setLocality(locality, language);
+ }
+
+ public void setLocality(String locality, Language language) {
+ LanguageString langString = LanguageString.NewInstance(locality, language);
+ setLocality(langString);
+ }
+
+ public void setLocality(LanguageString locality) {
+ getGatheringEvent(true).setLocality(locality);
+ }
+
+ /**
+ * The gathering event will be used for the field object instead of the old
+ * gathering event.<BR>
+ * <B>This method will override all gathering values (see below).</B>
+ *
+ * @see #getAbsoluteElevation()
+ * @see #getAbsoluteElevationError()
+ * @see #getDistanceToGround()
+ * @see #getDistanceToWaterSurface()
+ * @see #getExactLocation()
+ * @see #getGatheringEventDescription()
+ * @see #getGatheringPeriod()
+ * @see #getCollectingAreas()
+ * @see #getCollectingMethod()
+ * @see #getLocality()
+ * @see #getCollector()
+ * @param gatheringEvent
+ */
+ public void setGatheringEvent(GatheringEvent gatheringEvent) {
+ getFieldUnit(true).setGatheringEvent(gatheringEvent);
+ }
+
+ public boolean hasGatheringEvent() {
+ return (getGatheringEvent(false) != null);
+ }
+
+ public GatheringEvent innerGatheringEvent() {
+ return getGatheringEvent(false);
+ }
+
+ public GatheringEvent getGatheringEvent(boolean createIfNotExists) {
+ if (!hasFieldUnit() && !createIfNotExists) {
+ return null;
+ }
+ if (createIfNotExists && getFieldUnit(true).getGatheringEvent() == null) {
+ GatheringEvent gatheringEvent = GatheringEvent.NewInstance();
+ getFieldUnit(true).setGatheringEvent(gatheringEvent);
+ }
+ return getFieldUnit(true).getGatheringEvent();
+ }
+
+ // ****************** Field Object ************************************/
+
+ /**
+ * Returns true if a field unit exists (even if all attributes are
+ * empty or <code>null<code>.
+ *
+ * @return
+ */
+ public boolean hasFieldObject() {
+ return this.fieldUnit != null;
+ }
+
+ // ecology
+ @Transient
+ public String getEcology() {
+ return getEcology(Language.DEFAULT());
+ }
+
+ public String getEcology(Language language) {
+ LanguageString languageString = getEcologyAll().get(language);
+ return (languageString == null ? null : languageString.getText());
+ }
+
+ // public String getEcologyPreferred(List<Language> languages){
+ // LanguageString languageString =
+ // getEcologyAll().getPreferredLanguageString(languages);
+ // return languageString.getText();
+ // }
+ /**
+ * Returns a copy of the multilanguage text holding the ecology data.
+ *
+ * @see {@link TextData#getMultilanguageText()}
+ * @return
+ */
+ @Transient
+ public Map<Language, LanguageString> getEcologyAll() {
+ if (ecology == null) {
+ try {
+ ecology = initializeFieldObjectTextDataWithSupportTest(
+ Feature.ECOLOGY(), false, false);
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ if (ecology == null) {
+ return new HashMap<Language, LanguageString>();
+ }
+ }
+ return ecology.getMultilanguageText();
+ }
+
+ public void setEcology(String ecology) {
+ setEcology(ecology, null);
+ }
+
+ public void setEcology(String ecologyText, Language language) {
+ if (language == null) {
+ language = Language.DEFAULT();
+ }
+ boolean isEmpty = StringUtils.isBlank(ecologyText);
+ if (ecology == null) {
+ try {
+ ecology = initializeFieldObjectTextDataWithSupportTest(
+ Feature.ECOLOGY(), !isEmpty, false);
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ }
+ if (ecology != null){
+ if (ecologyText == null) {
+ ecology.removeText(language);
+ } else {
+ ecology.putText(language, ecologyText);
+ }
+ }
+ }
+
+ public void removeEcology(Language language) {
+ setEcology(null, language);
+ }
+
+ /**
+ * Removes ecology for the default language
+ */
+ public void removeEcology() {
+ setEcology(null, null);
+ }
+
+ public void removeEcologyAll() {
+
+ }
+
+ // plant description
+ @Transient
+ public String getPlantDescription() {
+ return getPlantDescription(null);
+ }
+
+ public String getPlantDescription(Language language) {
+ if (language == null) {
+ language = Language.DEFAULT();
+ }
+ LanguageString languageString = getPlantDescriptionAll().get(language);
+ return (languageString == null ? null : languageString.getText());
+ }
+
+ // public String getPlantDescriptionPreferred(List<Language> languages){
+ // LanguageString languageString =
+ // getPlantDescriptionAll().getPreferredLanguageString(languages);
+ // return languageString.getText();
+ // }
+ /**
+ * Returns a copy of the multilanguage text holding the description data.
+ *
+ * @see {@link TextData#getMultilanguageText()}
+ * @return
+ */
+ @Transient
+ public Map<Language, LanguageString> getPlantDescriptionAll() {
+ if (plantDescription == null) {
+ try {
+ plantDescription = initializeFieldObjectTextDataWithSupportTest(
+ Feature.DESCRIPTION(), false, false);
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ if (plantDescription == null) {
+ return new HashMap<Language, LanguageString>();
+ }
+ }
+ return plantDescription.getMultilanguageText();
+ }
+
+ public void setPlantDescription(String plantDescription) {
+ setPlantDescription(plantDescription, null);
+ }
+
+ public void setPlantDescription(String plantDescriptionText, Language language) {
+ if (language == null) {
+ language = Language.DEFAULT();
+ }
+ boolean isEmpty = StringUtils.isBlank(plantDescriptionText);
+ if (plantDescription == null) {
+ try {
+ plantDescription = initializeFieldObjectTextDataWithSupportTest(
+ Feature.DESCRIPTION(), !isEmpty, false);
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ }
+ if (plantDescription != null){
+ if (plantDescriptionText == null) {
+ plantDescription.removeText(language);
+ } else {
+ plantDescription.putText(language, plantDescriptionText);
+ }
+ }
+ }
+
+ public void removePlantDescription(Language language) {
+ setPlantDescription(null, language);
+ }
+
+ // field object definition
+ public void addFieldObjectDefinition(String text, Language language) {
+ getFieldUnit(true).putDefinition(language, text);
+ }
+
+ @Transient
+ public Map<Language, LanguageString> getFieldObjectDefinition() {
+ if (!hasFieldUnit()) {
+ return new HashMap<Language, LanguageString>();
+ } else {
+ return getFieldUnit(true).getDefinition();
+ }
+ }
+
+ public String getFieldObjectDefinition(Language language) {
+ Map<Language, LanguageString> map = getFieldObjectDefinition();
+ LanguageString languageString = (map == null ? null : map.get(language));
+ if (languageString != null) {
+ return languageString.getText();
+ } else {
+ return null;
+ }
+ }
+
+ public void removeFieldObjectDefinition(Language lang) {
+ if (hasFieldUnit()) {
+ getFieldUnit(true).removeDefinition(lang);
+ }
+ }
+
+ // media
+ public boolean addFieldObjectMedia(Media media) {
+ try {
+ return addMedia(media, getFieldUnit(true));
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ }
+
+ /**
+ * Returns true, if an image gallery for the field object exists.<BR>
+ * Returns also <code>true</code> if the image gallery is empty.
+ *
+ * @return
+ */
+ public boolean hasFieldObjectImageGallery() {
+ if (!hasFieldObject()) {
+ return false;
+ } else {
+ return (getImageGallery(fieldUnit, false) != null);
+ }
+ }
+
+ public void setFieldObjectImageGallery(SpecimenDescription imageGallery)
+ throws DerivedUnitFacadeNotSupportedException {
+ SpecimenDescription existingGallery = getFieldObjectImageGallery(false);
+
+ // test attached specimens contain this.derivedUnit
+ SpecimenOrObservationBase<?> facadeFieldUnit = innerFieldUnit();
+ testSpecimenInImageGallery(imageGallery, facadeFieldUnit);
+
+ if (existingGallery != null) {
+ if (existingGallery != imageGallery) {
+ throw new DerivedUnitFacadeNotSupportedException(
+ "DerivedUnitFacade does not allow more than one image gallery");
+ } else {
+ // do nothing
+ }
+ } else {
+ TextData textData = testImageGallery(imageGallery);
+ this.fieldObjectMediaTextData = textData;
+ }
+ }
+
+ /**
+ * Returns the field object image gallery. If no such image gallery exists
+ * and createIfNotExists is true an new one is created. Otherwise null is
+ * returned.
+ *
+ * @param createIfNotExists
+ * @return
+ */
+ public SpecimenDescription getFieldObjectImageGallery(
+ boolean createIfNotExists) {
+ TextData textData;
+ try {
+ textData = initializeFieldObjectTextDataWithSupportTest(
+ Feature.IMAGE(), createIfNotExists, true);
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ if (textData != null) {
+ return CdmBase.deproxy(textData.getInDescription(),
+ SpecimenDescription.class);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Returns the media for the field object.<BR>
+ *
+ * @return
+ */
+ @Transient
+ public List<Media> getFieldObjectMedia() {
+ try {
+ List<Media> result = getMediaList(getFieldUnit(false), false);
+ return result == null ? new ArrayList<Media>() : result;
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ }
+
+ public boolean removeFieldObjectMedia(Media media) {
+ try {
+ return removeMedia(media, getFieldUnit(false));
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ }
+
+ // field number
+ @Transient
+ public String getFieldNumber() {
+ if (!hasFieldUnit()) {
+ return null;
+ } else {
+ return getFieldUnit(true).getFieldNumber();
+ }
+ }
+
+ public void setFieldNumber(String fieldNumber) {
+ getFieldUnit(true).setFieldNumber(fieldNumber);
+ }
+
+ // primary collector
+ @Transient
+ public Person getPrimaryCollector() {
+ if (!hasFieldUnit()) {
+ return null;
+ } else {
+ return getFieldUnit(true).getPrimaryCollector();
+ }
+ }
+
+ public void setPrimaryCollector(Person primaryCollector) {
+ getFieldUnit(true).setPrimaryCollector(primaryCollector);
+ }
+
+ // field notes
+ @Transient
+ public String getFieldNotes() {
+ if (!hasFieldUnit()) {
+ return null;
+ } else {
+ return getFieldUnit(true).getFieldNotes();
+ }
+ }
+
+ public void setFieldNotes(String fieldNotes) {
+ getFieldUnit(true).setFieldNotes(fieldNotes);
+ }
+
+ // individual counts
+ @Transient
+ public Integer getIndividualCount() {
+ return (hasFieldUnit() ? getFieldUnit(true).getIndividualCount() : null);
+ }
+
+ public void setIndividualCount(Integer individualCount) {
+ getFieldUnit(true).setIndividualCount(individualCount);
+ }
+
+ // life stage
+ @Transient
+ public DefinedTerm getLifeStage() {
+ return (hasFieldUnit() ? getFieldUnit(true).getLifeStage() : null);
+ }
+
+ public void setLifeStage(DefinedTerm lifeStage) {
+ getFieldUnit(true).setLifeStage(lifeStage);
+ }
+
+ // sex
+ @Transient
+ public DefinedTerm getSex() {
+ return (hasFieldUnit() ? getFieldUnit(true).getSex(): null);
+ }
+
+ public void setSex(DefinedTerm sex) {
+ getFieldUnit(true).setSex(sex);
+ }
+
+ // kind of Unit
+ @Transient
+ public DefinedTerm getKindOfUnit() {
+ return (hasFieldUnit() ? getFieldUnit(true).getKindOfUnit() : null);
+ }
+
+ public void setKindOfUnit(DefinedTerm kindOfUnit) {
+ getFieldUnit(true).setKindOfUnit(kindOfUnit);
+ }
+
+
+ // field unit
+ public boolean hasFieldUnit() {
+ return (getFieldUnit(CREATE_NOT) != null);
+ }
+
+ /**
+ * Returns the field unit as an object.
+ *
+ * @return
+ */
+ public FieldUnit innerFieldUnit() {
+ return getFieldUnit(CREATE_NOT);
+ }
+
+ /**
+ * Returns the field unit as an object.
+ *
+ * @return
+ */
+ public FieldUnit getFieldUnit(boolean createIfNotExists) {
+ if (fieldUnit == null && createIfNotExists) {
+ setFieldUnit(FieldUnit.NewInstance());
+ }
+ return this.fieldUnit;
+ }
+
+
+ private void setFieldUnit(FieldUnit fieldUnit) {
+ this.fieldUnit = fieldUnit;
+ if (fieldUnit != null){
+ if (config.isFirePropertyChangeEvents()){
+ addNewEventPropagationListener(fieldUnit);
+ }
+ if (derivedUnit != null){
+ DerivationEvent derivationEvent = getDerivationEvent(CREATE);
+ derivationEvent.addOriginal(fieldUnit);
+ }
+ setFieldUnitCacheStrategy();
+ }
+ }
+
+ // ****************** Specimen *******************************************
+
+ // Definition
+ public void addDerivedUnitDefinition(String text, Language language) {
+ innerDerivedUnit().putDefinition(language, text);
+ }
+
+ @Transient
+ public Map<Language, LanguageString> getDerivedUnitDefinitions() {
+ return ! checkDerivedUnit()? null : this.derivedUnit.getDefinition();
+ }
+
+
+ public String getDerivedUnitDefinition(Language language) {
+ if (! checkDerivedUnit()){
+ return null;
+ }
+ Map<Language, LanguageString> languageMap = derivedUnit.getDefinition();
+ LanguageString languageString = languageMap.get(language);
+ if (languageString != null) {
+ return languageString.getText();
+ } else {
+ return null;
+ }
+ }
+
+ public void removeDerivedUnitDefinition(Language lang) {
+ testDerivedUnit();
+ derivedUnit.removeDefinition(lang);
+ }
+
+ // Determination
+ public void addDetermination(DeterminationEvent determination) {
+ //TODO implement correct bidirectional mapping in model classes
+ determination.setIdentifiedUnit(baseUnit());
+ baseUnit().addDetermination(determination);
+ }
+
+ @Transient
+ public DeterminationEvent getPreferredDetermination() {
+ Set<DeterminationEvent> events = baseUnit().getDeterminations();
+ for (DeterminationEvent event : events){
+ if (event.getPreferredFlag() == true){
+ return event;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * This method returns the preferred determination.
+ * @see #getOtherDeterminations()
+ * @see #getDeterminations()
+ * @return
+ */
+ @Transient
+ public void setPreferredDetermination(DeterminationEvent newEvent) {
+ Set<DeterminationEvent> events = baseUnit().getDeterminations();
+ for (DeterminationEvent event : events){
+ if (event.getPreferredFlag() == true){
+ event.setPreferredFlag(false);
+ }
+ }
+ newEvent.setPreferredFlag(true);
+ events.add(newEvent);
+ }
+
+ /**
+ * This method returns all determinations except for the preferred one.
+ * @see #getPreferredDetermination()
+ * @see #getDeterminations()
+ * @return
+ */
+ @Transient
+ public Set<DeterminationEvent> getOtherDeterminations() {
+ Set<DeterminationEvent> events = baseUnit().getDeterminations();
+ Set<DeterminationEvent> result = new HashSet<DeterminationEvent>();
+ for (DeterminationEvent event : events){
+ if (event.getPreferredFlag() != true){
+ result.add(event);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * This method returns all determination events. The preferred one {@link #getPreferredDetermination()}
+ * and all others {@link #getOtherDeterminations()}.
+ * @return
+ */
+ @Transient
+ public Set<DeterminationEvent> getDeterminations() {
+ return baseUnit().getDeterminations();
+ }
+
+ public void removeDetermination(DeterminationEvent determination) {
+ baseUnit().removeDetermination(determination);
+ }
+
+ // Media
+ public boolean addDerivedUnitMedia(Media media) {
+ testDerivedUnit();
+ try {
+ return addMedia(media, derivedUnit);
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ }
+
+ /**
+ * Returns true, if an image gallery exists for the specimen.<BR>
+ * Returns also <code>true</code> if the image gallery is empty.
+ */
+ public boolean hasDerivedUnitImageGallery() {
+ return (getImageGallery(derivedUnit, false) != null);
+ }
+
+ public SpecimenDescription getDerivedUnitImageGallery(boolean createIfNotExists) {
+ if (!checkDerivedUnit()){
+ return null;
+ }
+ TextData textData;
+ try {
+ textData = inititializeTextDataWithSupportTest(Feature.IMAGE(),
+ derivedUnit, createIfNotExists, true);
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ if (textData != null) {
+ return CdmBase.deproxy(textData.getInDescription(),
+ SpecimenDescription.class);
+ } else {
+ return null;
+ }
+ }
+
+ public void setDerivedUnitImageGallery(SpecimenDescription imageGallery)
+ throws DerivedUnitFacadeNotSupportedException {
+ testDerivedUnit();
+ SpecimenDescription existingGallery = getDerivedUnitImageGallery(false);
+
+ // test attached specimens contain this.derivedUnit
+ SpecimenOrObservationBase facadeDerivedUnit = innerDerivedUnit();
+ testSpecimenInImageGallery(imageGallery, facadeDerivedUnit);
+
+ if (existingGallery != null) {
+ if (existingGallery != imageGallery) {
+ throw new DerivedUnitFacadeNotSupportedException(
+ "DerivedUnitFacade does not allow more than one image gallery");
+ } else {
+ // do nothing
+ }
+ } else {
+ TextData textData = testImageGallery(imageGallery);
+ this.derivedUnitMediaTextData = textData;
+ }
+ }
+
+ /**
+ * @param imageGallery
+ * @throws DerivedUnitFacadeNotSupportedException
+ */
+ private void testSpecimenInImageGallery(SpecimenDescription imageGallery, SpecimenOrObservationBase specimen)
+ throws DerivedUnitFacadeNotSupportedException {
+ SpecimenOrObservationBase imageGallerySpecimen = imageGallery.getDescribedSpecimenOrObservation();
+ if (imageGallerySpecimen == null) {
+ throw new DerivedUnitFacadeNotSupportedException(
+ "Image Gallery has no Specimen attached. Please attache according specimen or field unit.");
+ }
+ if (! imageGallerySpecimen.equals(specimen)) {
+ throw new DerivedUnitFacadeNotSupportedException(
+ "Image Gallery has not the facade's field object attached. Please add field object first " +
+ "to image gallery specimenOrObservation list.");
+ }
+ }
+
+ /**
+ * Returns the media for the specimen.<BR>
+ *
+ * @return
+ */
+ @Transient
+ public List<Media> getDerivedUnitMedia() {
+ if (! checkDerivedUnit()){
+ return new ArrayList<Media>();
+ }
+ try {
+ List<Media> result = getMediaList(derivedUnit, false);
+ return result == null ? new ArrayList<Media>() : result;
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ }
+
+ public boolean removeDerivedUnitMedia(Media media) {
+ testDerivedUnit();
+ try {
+ return removeMedia(media, derivedUnit);
+ } catch (DerivedUnitFacadeNotSupportedException e) {
+ throw new IllegalStateException(notSupportMessage, e);
+ }
+ }
+
+ // Accession Number
+ @Transient
+ public String getAccessionNumber() {
+ return ! checkDerivedUnit()? null : derivedUnit.getAccessionNumber();
+ }
+
+ public void setAccessionNumber(String accessionNumber) {
+ testDerivedUnit();
+ derivedUnit.setAccessionNumber(accessionNumber);
+ }
+
+ @Transient
+ public String getCatalogNumber() {
+ return ! checkDerivedUnit()? null : derivedUnit.getCatalogNumber();
+ }
+
+ public void setCatalogNumber(String catalogNumber) {
+ testDerivedUnit();
+ derivedUnit.setCatalogNumber(catalogNumber);
+ }
+
+ @Transient
+ public String getBarcode() {
+ return ! checkDerivedUnit()? null : derivedUnit.getBarcode();
+ }
+
+ public void setBarcode(String barcode) {
+ testDerivedUnit();
+ derivedUnit.setBarcode(barcode);
+ }
+
+ // Preservation Method
+
+ /**
+ * Only supported by specimen and fossils
+ *
+ * @see #DerivedUnitType
+ * @return
+ */
+ @Transient
+ public PreservationMethod getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException {
+ if (derivedUnit!=null && derivedUnit.getRecordBasis().isPreservedSpecimen()) {
+ return CdmBase.deproxy(derivedUnit, DerivedUnit.class).getPreservation();
+ } else {
+ if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
+ throw new MethodNotSupportedByDerivedUnitTypeException(
+ "A preservation method is only available in derived units of type 'Preserved Specimen' or one of its specializations like 'Fossil Specimen' ");
+ } else {
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Only supported by specimen and fossils
+ *
+ * @see #DerivedUnitType
+ * @return
+ */
+ public void setPreservationMethod(PreservationMethod preservation)
+ throws MethodNotSupportedByDerivedUnitTypeException {
+ if (derivedUnit!=null && derivedUnit.getRecordBasis().isPreservedSpecimen()) {
+ CdmBase.deproxy(derivedUnit, DerivedUnit.class).setPreservation(preservation);
+ } else {
+ if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
+ throw new MethodNotSupportedByDerivedUnitTypeException(
+ "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
+ } else {
+ return;
+ }
+ }
+ }
+
+ // Stored under name
+ @Transient
+ public TaxonNameBase getStoredUnder() {
+ return ! checkDerivedUnit()? null : derivedUnit.getStoredUnder();
+ }
+
+ public void setStoredUnder(TaxonNameBase storedUnder) {
+ testDerivedUnit();
+ derivedUnit.setStoredUnder(storedUnder);
+ }
+
+ // title cache
+ public String getTitleCache() {
+ SpecimenOrObservationBase<?> titledUnit = baseUnit();
+
+ if (!titledUnit.isProtectedTitleCache()) {
+ // always compute title cache anew as long as there are no property
+ // change listeners on
+ // field unit, gathering event etc
+ titledUnit.setTitleCache(null, false);
+ }
+ return titledUnit.getTitleCache();
+ }
+
+ public boolean isProtectedTitleCache() {
+ return baseUnit().isProtectedTitleCache();
+ }
+
+ public void setTitleCache(String titleCache, boolean isProtected) {
+ this.baseUnit().setTitleCache(titleCache, isProtected);
+ }
+
+ /**
+ * Returns the derived unit itself.
+ *
+ * @return the derived unit
+ */
+ public DerivedUnit innerDerivedUnit() {
+ return this.derivedUnit;
+ }
+
+// /**
+// * Returns the derived unit itself.
+// *
+// * @return the derived unit
+// */
+// public DerivedUnit innerDerivedUnit(boolean createIfNotExists) {
+// DerivedUnit result = this.derivedUnit;
+// if (result == null && createIfNotExists){
+// if (this.fieldUnit == null){
+// String message = "Field unit must exist to create derived unit.";
+// throw new IllegalStateException(message);
+// }else{
+// DerivedUnit =
+// DerivationEvent derivationEvent = getDerivationEvent(true);
+// derivationEvent.addOriginal(fieldUnit);
+// return this.derivedUnit;
+// }
+// }
+// }
+
+ private boolean hasDerivationEvent() {
+ return getDerivationEvent() == null ? false : true;
+ }
+
+ private DerivationEvent getDerivationEvent() {
+ return getDerivationEvent(CREATE_NOT);
+ }
+
+ /**
+ * Returns the derivation event. If no derivation event exists and <code>createIfNotExists</code>
+ * is <code>true</code> a new derivation event is created and returned.
+ * Otherwise <code>null</code> is returned.
+ * @param createIfNotExists
+ */
+ private DerivationEvent getDerivationEvent(boolean createIfNotExists) {
+ DerivationEvent result = null;
+ if (derivedUnit != null){
+ result = derivedUnit.getDerivedFrom();
+ }else{
+ return null;
+ }
+ if (result == null && createIfNotExists) {
+ DerivationEventType type = null;
+ if (isAccessioned(derivedUnit)){
+ type = DerivationEventType.ACCESSIONING();
+ }
+
+ result = DerivationEvent.NewInstance(type);
+ derivedUnit.setDerivedFrom(result);
+ }
+ return result;
+ }
+
+ /**
+ * TODO still unclear which classes do definetly require accessioning.
+ * Only return true for those classes which are clear.
+ * @param derivedUnit
+ * @return
+ */
+ private boolean isAccessioned(DerivedUnit derivedUnit) {
+ if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen) ){
+ return true; //maybe also subtypes should be true
+ }else{
+ return false;
+ }
+ }
+
+ @Transient
+ public String getExsiccatum() throws MethodNotSupportedByDerivedUnitTypeException {
+ testDerivedUnit();
+ if (derivedUnit.getRecordBasis().isPreservedSpecimen()) {
+ return derivedUnit.getExsiccatum();
+ } else {
+ if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
+ throw new MethodNotSupportedByDerivedUnitTypeException(
+ "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
+ } else {
+ return null;
+ }
+ }
+ }
+
+ public void setExsiccatum(String exsiccatum) throws Exception {
+ testDerivedUnit();
+ if (derivedUnit.getRecordBasis().isPreservedSpecimen()) {
+ derivedUnit.setExsiccatum(exsiccatum);
+ } else {
+ if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
+ throw new MethodNotSupportedByDerivedUnitTypeException(
+ "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
+ } else {
+ return;
+ }
+ }
+ }
+
+ /**
+ * Returns the original label information of the derived unit.
+ * @return
+ */
+ @Transient
+ public String getOriginalLabelInfo() {
+ return ! checkDerivedUnit()? null : derivedUnit.getOriginalLabelInfo();
+ }
+ public void setOriginalLabelInfo(String originalLabelInfo) {
+ testDerivedUnit();
+ derivedUnit.setOriginalLabelInfo(originalLabelInfo);
+ }
+
+ // **** sources **/
+ public void addSource(IdentifiableSource source) {
+ this.baseUnit().addSource(source);
+ }
+
+ /**
+ * Creates an {@link IOriginalSource orignal source} or type ,
+ * adds it to the specimen and returns it.
+ *
+ * @param reference
+ * @param microReference
+ * @param originalNameString
+ * @return
+ */
+ public IdentifiableSource addSource(OriginalSourceType type, Reference reference, String microReference, String originalNameString) {
+ IdentifiableSource source = IdentifiableSource.NewInstance(type, null, null, reference, microReference);
+ source.setOriginalNameString(originalNameString);
+ addSource(source);
+ return source;
+ }
+
+ @Transient
+ public Set<IdentifiableSource> getSources() {
+ return baseUnit().getSources();
+ }
+
+ public void removeSource(IdentifiableSource source) {
+ this.baseUnit().removeSource(source);
+ }
+
+ //*** identifiers ***/
+
+
+ public void addIdentifier(Identifier identifier) {
+ this.baseUnit().addIdentifier(identifier);
+ }
+
+ @Transient
+ public List<Identifier> getIdentifiers() {
+ return baseUnit().getIdentifiers();
+ }
+
+ public void removeIdentifier(Identifier identifier) {
+ this.baseUnit().removeIdentifier(identifier);
+ }
+
+ @Transient
+ public Set<Rights> getRights() {
+ return baseUnit().getRights();
+ }
+
+ /**
+ * @return the collection
+ */
+ @Transient
+ public Collection getCollection() {
+ return ! checkDerivedUnit()? null : derivedUnit.getCollection();
+ }
+
+ /**
+ * @param collection
+ * the collection to set
+ */
+ public void setCollection(Collection collection) {
+ testDerivedUnit();
+ derivedUnit.setCollection(collection);
+ }
+
+ // annotation
+ public void addAnnotation(Annotation annotation) {
+ this.baseUnit().addAnnotation(annotation);
+ }
+
+ @Transient
+ public void getAnnotations() {
+ this.baseUnit().getAnnotations();
+ }
+
+ public void removeAnnotation(Annotation annotation) {
+ this.baseUnit().removeAnnotation(annotation);
+ }
+
+ // ******************************* Events ***************************
+
+ //set of events that were currently fired by this facades field unit
+ //to avoid recursive fireing of the same event
+ private final Set<PropertyChangeEvent> fireingEvents = new HashSet<PropertyChangeEvent>();
+
+ /**
+ * @return
+ */
+ private void addNewEventPropagationListener(CdmBase listeningObject) {
+ //if there is already a listener, don't do anything
+ for (PropertyChangeListener listener : this.listeners.keySet()){
+ if (listeners.get(listener) == listeningObject){
+ return;
+ }
+ }
+ //create new listener
+ PropertyChangeListener listener = new PropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent event) {
+ if (derivedUnit != null){
+ derivedUnit.firePropertyChange(event);
+ }else{
+ if (! event.getSource().equals(fieldUnit) && ! fireingEvents.contains(event) ){
+ fireingEvents.add(event);
+ fieldUnit.firePropertyChange(event);
+ fireingEvents.remove(event);
+ }
+ }
+ }
+ };
+ //add listener to listening object and to list of listeners
+ listeningObject.addPropertyChangeListener(listener);
+ listeners.put(listener, listeningObject);
+ }
+
+ // **************** Other Collections ********************************
+
+ /**
+ * Creates a duplicate specimen which derives from the same derivation event
+ * as the facade specimen and adds collection data to it (all data available
+ * in DerivedUnit and Specimen. Data from SpecimenOrObservationBase and
+ * above are not yet shared at the moment.
+ *
+ * @param collection
+ * @param catalogNumber
+ * @param accessionNumber
+ * @param storedUnder
+ * @param preservation
+ * @return
+ */
+ public DerivedUnit addDuplicate(Collection collection, String catalogNumber,
+ String accessionNumber, TaxonNameBase storedUnder, PreservationMethod preservation){
+ testDerivedUnit();
+ DerivedUnit duplicate = DerivedUnit.NewPreservedSpecimenInstance();
+ duplicate.setDerivedFrom(getDerivationEvent(CREATE));
+ duplicate.setCollection(collection);
+ duplicate.setCatalogNumber(catalogNumber);
+ duplicate.setAccessionNumber(accessionNumber);
+ duplicate.setStoredUnder(storedUnder);
+ duplicate.setPreservation(preservation);
+ return duplicate;
+ }
+
+ public void addDuplicate(DerivedUnit duplicateSpecimen) {
+ testDerivedUnit();
+ getDerivationEvent(CREATE).addDerivative(duplicateSpecimen);
+ }
+
+ @Transient
+ public Set<DerivedUnit> getDuplicates() {
+ if (! checkDerivedUnit()){
+ return new HashSet<DerivedUnit>();
+ }
+ Set<DerivedUnit> result = new HashSet<DerivedUnit>();
+ if (hasDerivationEvent()) {
+ for (DerivedUnit derivedUnit : getDerivationEvent(CREATE)
+ .getDerivatives()) {
+ if (derivedUnit.isInstanceOf(DerivedUnit.class)
+ && !derivedUnit.equals(this.derivedUnit)) {
+ result.add(CdmBase.deproxy(derivedUnit, DerivedUnit.class));
+ }
+ }
+ }
+ return result;
+ }
+
+ public void removeDuplicate(DerivedUnit duplicateSpecimen) {
+ testDerivedUnit();
+ if (hasDerivationEvent()) {
+ getDerivationEvent(CREATE).removeDerivative(duplicateSpecimen);
+ }
+ }
+
+ public SpecimenOrObservationBase<?> baseUnit(){
+ if(derivedUnit!=null){
+ return derivedUnit;
+ }
+ else if(fieldUnit!=null){
+ return fieldUnit;
+ }
+ else{
+ throw new IllegalStateException("A DerivedUnitFacade must always have either a field unit or a derived unit");
+ }
+ }
+
+
+ private boolean checkDerivedUnit() {
+ if (derivedUnit == null){
+ return false;
+ }else{
+ return true;
+ }
+ }
+
+ private void testDerivedUnit() /* throws MethodNotSupportedByDerivedUnitTypeException */ {
+ if (derivedUnit == null){
+ 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");
+ }
+ }
+
+ public void setType(SpecimenOrObservationType type) {
+ if (type == null){
+ throw new IllegalArgumentException("The type of a specimen or observation may not be null");
+ }
+ SpecimenOrObservationBase<?> baseUnit = baseUnit();
+ if(baseUnit.isInstanceOf(FieldUnit.class) && !type.isFieldUnit()){
+ throw new IllegalArgumentException("A FieldUnit may only be of type FieldUnit") ;
+ }
+ else if(baseUnit.isInstanceOf(DerivedUnit.class) && type.isFieldUnit()){
+ throw new IllegalArgumentException("A derived unit may not be of type FieldUnit") ;
+ }
+ baseUnit.setRecordBasis(type);
+ }
+
+ public SpecimenOrObservationType getType() {
+ return baseUnit().getRecordBasis();
+ }
+
+ /**
+ * Closes this facade. As a minimum this method removes all listeners created by this facade from their
+ * listening objects.
+ */
+ public void close(){
+ for (PropertyChangeListener listener : this.listeners.keySet()){
+ CdmBase listeningObject = listeners.get(listener);
+ listeningObject.removePropertyChangeListener(listener);
+ }
+ }
+
+
+ /**
+ * Computes the correct distance string for given values for min, max and text.
+ * If text is not blank, text is returned, otherwise "min - max" or a single value is returned.
+ * @param min min value as number
+ * @param max max value as number
+ * @param text text representation of distance
+ * @return the formatted distance string
+ */
+ private String distanceString(Number min, Number max, String text, String unit) {
+ if (StringUtils.isNotBlank(text)){
+ return text;
+ }else{
+ String minStr = min == null? null : String.valueOf(min);
+ String maxStr = max == null? null : String.valueOf(max);
+ String result = CdmUtils.concat(UTF8.EN_DASH_SPATIUM.toString(), minStr, maxStr);
+ if (StringUtils.isNotBlank(result) && StringUtils.isNotBlank(unit)){
+ result = result + " " + unit;
+ }
+ return result;
+ }
+ }
+
+ /**
+ * First checks the inner field unit for the publish flag. If set to <code>true</code>
+ * then <code>true</code> is returned. If the field unit is <code>null</code> the inner derived unit
+ * is checked.
+ * @return <code>true</code> if this facade can be published
+ */
+ public boolean isPublish(){
+ if(fieldUnit!=null){
+ return fieldUnit.isPublish();
+ }
+ if(derivedUnit!=null){
+ return derivedUnit.isPublish();
+ }
+ return false;
+ }
+}