reintegrating mediawiki export
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / facade / DerivedUnitFacade.java
index 8e3bfba9ff0863360dcb8b722b0ed95823cb6ff7..1bc34f8c4acecec398304a930a1cf2d0a71bd645 100644 (file)
@@ -1,14 +1,16 @@
 /**\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
+ * 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
@@ -17,21 +19,28 @@ import java.util.List;
 import java.util.Map;\r
 import java.util.Set;\r
 \r
-import javax.mail.MethodNotSupportedException;\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.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.Sex;\r
 import eu.etaxonomy.cdm.model.description.SpecimenDescription;\r
-import eu.etaxonomy.cdm.model.description.Stage;\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
@@ -40,198 +49,429 @@ import eu.etaxonomy.cdm.model.media.Media;
 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.DerivedUnitBase;\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.FieldObservation;\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.Specimen;\r
 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;\r
-import eu.etaxonomy.cdm.model.reference.ReferenceBase;\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\r
- * one direct derivation event and there must be only one field observation (gathering event)\r
- * it derives from.<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
+       @SuppressWarnings("unused")\r
        private static final Logger logger = Logger.getLogger(DerivedUnitFacade.class);\r
-       \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
-       \r
-       /**\r
-        * Enum that defines the class the "Specimen" belongs to.\r
-        * Some methods of the facade are not available for certain classes\r
-        * and will throw an Exception when invoking them.\r
-        */\r
-       public enum DerivedUnitType{\r
-               Specimen ("Specimen"),\r
-               Observation("Observation"),\r
-               LivingBeing("Living Being"),\r
-               Fossil("Fossil"),\r
-               DerivedUnit("Derived Unit");\r
-               \r
-               String representation;\r
-               private DerivedUnitType(String representation){\r
-                       this.representation = representation;\r
-               }\r
-               \r
-               /**\r
-                * @return the representation\r
-                */\r
-               public String getRepresentation() {\r
-                       return representation;\r
-               }\r
-               \r
-               private DerivedUnitBase getNewDerivedUnitInstance(){\r
-                       if (this == DerivedUnitType.Specimen){\r
-                               return eu.etaxonomy.cdm.model.occurrence.Specimen.NewInstance();\r
-                       }else if (this == DerivedUnitType.Observation){\r
-                               return eu.etaxonomy.cdm.model.occurrence.Observation.NewInstance();\r
-                       }else if (this == DerivedUnitType.LivingBeing){\r
-                               return eu.etaxonomy.cdm.model.occurrence.LivingBeing.NewInstance();\r
-                       }else if (this == DerivedUnitType.Fossil){\r
-                               return eu.etaxonomy.cdm.model.occurrence.Fossil.NewInstance();\r
-                       }else if (this == DerivedUnitType.DerivedUnit){\r
-                               return eu.etaxonomy.cdm.model.occurrence.DerivedUnit.NewInstance();\r
-                       }else{\r
-                               throw new IllegalStateException("Unknown derived unit type " +  this.getRepresentation());\r
-                       }\r
-               }\r
-               \r
-       }\r
-       \r
-       \r
-       private DerivedUnitFacadeConfigurator config;\r
-       \r
-       //private GatheringEvent gatheringEvent;\r
-       private DerivedUnitType type;  //needed?\r
-       \r
-       private FieldObservation fieldObservation;\r
-       \r
-       private DerivedUnitBase derivedUnit;\r
+       private Map<PropertyChangeListener, CdmBase> listeners = new HashMap<PropertyChangeListener, CdmBase>();\r
+\r
+       // private GatheringEvent gatheringEvent;\r
+       private SpecimenOrObservationType type; // needed?\r
+\r
+       private FieldUnit fieldUnit;\r
 \r
-       //media - the text data holding the media\r
+       private final DerivedUnit derivedUnit;\r
+\r
+       // media - the text data holding the media\r
        private TextData derivedUnitMediaTextData;\r
        private TextData fieldObjectMediaTextData;\r
-       \r
-       \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
-       public static DerivedUnitFacade NewInstance(DerivedUnitType type){\r
-               return new DerivedUnitFacade(type);\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
-       public static DerivedUnitFacade NewInstance(DerivedUnitBase derivedUnit) throws DerivedUnitFacadeNotSupportedException{\r
-               return new DerivedUnitFacade(derivedUnit, null, DerivedUnitType.Specimen);\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
-       public static DerivedUnitFacade NewInstance(DerivedUnitBase derivedUnit, DerivedUnitFacadeConfigurator config) throws DerivedUnitFacadeNotSupportedException{\r
-               return new DerivedUnitFacade(derivedUnit, config, DerivedUnitType.Specimen);\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
-// ****************** CONSTRUCTOR ****************************************************\r
-       \r
-       private DerivedUnitFacade(DerivedUnitType type){\r
-               this.config = DerivedUnitFacadeConfigurator.NewInstance();\r
-               \r
-               //derivedUnit\r
-               derivedUnit = type.getNewDerivedUnitInstance();\r
-               setCacheStrategy();\r
+       public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit,\r
+                       DerivedUnitFacadeConfigurator config)\r
+                       throws DerivedUnitFacadeNotSupportedException {\r
+               return new DerivedUnitFacade(derivedUnit, config);\r
        }\r
-       \r
-       private DerivedUnitFacade(DerivedUnitBase derivedUnit, DerivedUnitFacadeConfigurator config, DerivedUnitType type) throws DerivedUnitFacadeNotSupportedException{\r
-               //this.type = type;  ??\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
-               \r
-               //derived unit\r
+               this.type = type;\r
+               // derivedUnit\r
+               derivedUnit = getNewDerivedUnitInstance(type);\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 (config == null) {\r
+                       config = DerivedUnitFacadeConfigurator.NewInstance();\r
+               }\r
+               this.config = config;\r
+\r
+               // derived unit\r
                this.derivedUnit = derivedUnit;\r
-               setCacheStrategy();\r
+               this.type = derivedUnit.getRecordBasis();\r
                \r
-               //derivation event\r
-               if (this.derivedUnit.getDerivedFrom() != null){\r
-                       DerivationEvent derivationEvent = getDerivationEvent(true);\r
-                       //fieldObservation\r
-                       Set<FieldObservation> fieldOriginals = getFieldObservationsOriginals(derivationEvent, null);\r
-                       if (fieldOriginals.size() > 1){\r
-                               throw new DerivedUnitFacadeNotSupportedException("Specimen must not have more than 1 derivation event");\r
-                       }else if (fieldOriginals.size() == 0){\r
-                               //fieldObservation = FieldObservation.NewInstance();\r
-                       }else if (fieldOriginals.size() == 1){\r
-                               fieldObservation = fieldOriginals.iterator().next();\r
-                       }else{\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
-\r
-               //test if unsupported\r
                \r
-               //media\r
-               //specimen\r
-//             String objectTypeExceptionText = "Specimen";\r
-//             SpecimenDescription imageGallery = getImageGalleryWithSupportTest(derivedUnit, objectTypeExceptionText, false);\r
-//             getImageTextDataWithSupportTest(imageGallery, objectTypeExceptionText);\r
-               this.derivedUnitMediaTextData = inititialzeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);\r
-               \r
-               //field observation\r
-//             objectTypeExceptionText = "Field observation";\r
-//             imageGallery = getImageGalleryWithSupportTest(fieldObservation, objectTypeExceptionText, false);\r
-//             getImageTextDataWithSupportTest(imageGallery, objectTypeExceptionText);\r
+               this.derivedUnitMediaTextData = inititializeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);\r
+\r
                fieldObjectMediaTextData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), false, true);\r
+\r
                \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("Specimen may not have direct media. Only (one) image gallery is allowed");\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
-               //handle fieldObservation.getMedia()\r
-               if (fieldObservation != null && fieldObservation.getMedia() != null && fieldObservation.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 = fieldObservation.getMedia();\r
-                               for (Media media : mediaSet){\r
-                                       this.addFieldObjectMedia(media);\r
-                               }\r
-                               mediaSet.removeAll(getFieldObjectMedia());\r
-                       }else{\r
-                               throw new DerivedUnitFacadeNotSupportedException("Field object may not have direct media. Only (one) image gallery is allowed");\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
-               //test if descriptions are supported\r
-               ecology = initializeFieldObjectTextDataWithSupportTest(Feature.ECOLOGY(), false, false);\r
-               plantDescription = initializeFieldObjectTextDataWithSupportTest(Feature.DESCRIPTION(), false, false);\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
         */\r
        private void setCacheStrategy() {\r
-               derivedUnit.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());\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
@@ -240,16 +480,18 @@ public class DerivedUnitFacade {
         * @return\r
         * @throws DerivedUnitFacadeNotSupportedException\r
         */\r
-       private TextData initializeFieldObjectTextDataWithSupportTest(Feature feature, boolean createIfNotExists, boolean isImageGallery) throws DerivedUnitFacadeNotSupportedException {\r
-               //field object\r
-               FieldObservation fieldObject = getFieldObservation(createIfNotExists) ;\r
-               if (fieldObject == null){\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 inititialzeTextDataWithSupportTest(feature, fieldObject, createIfNotExists, isImageGallery);\r
+               return inititializeTextDataWithSupportTest(feature, fieldObject,\r
+                               createIfNotExists, isImageGallery);\r
        }\r
 \r
-\r
        /**\r
         * @param feature\r
         * @param specimen\r
@@ -258,186 +500,274 @@ public class DerivedUnitFacade {
         * @return\r
         * @throws DerivedUnitFacadeNotSupportedException\r
         */\r
-       private TextData inititialzeTextDataWithSupportTest(Feature feature, SpecimenOrObservationBase specimen, boolean createIfNotExists, \r
-                               boolean isImageGallery) throws DerivedUnitFacadeNotSupportedException {\r
-               if (feature == null ){\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
+               if (createIfNotExists) {\r
                        textData = TextData.NewInstance(feature);\r
                }\r
-               \r
+\r
                Set<SpecimenDescription> descriptions;\r
-               if (isImageGallery){\r
+               if (isImageGallery) {\r
                        descriptions = specimen.getSpecimenDescriptionImageGallery();\r
-               }else{\r
+               } else {\r
                        descriptions = specimen.getSpecimenDescriptions(false);\r
                }\r
-               if (descriptions.size() == 0){\r
-                       if (createIfNotExists){\r
-                               SpecimenDescription newSpecimenDescription = SpecimenDescription.NewInstance(specimen);\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
+                       } else {\r
                                return null;\r
                        }\r
                }\r
+               // description already exists\r
                Set<DescriptionElementBase> existingTextData = new HashSet<DescriptionElementBase>();\r
-               for (SpecimenDescription description : descriptions){\r
-                       for (DescriptionElementBase element: description.getElements()){\r
-                               if (element.isInstanceOf(TextData.class) && ( feature.equals(element.getFeature() )|| isImageGallery ) ){\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
-               if (existingTextData.size() > 1){\r
-                       throw new DerivedUnitFacadeNotSupportedException("Specimen facade does not support more than one description text data of type " + feature.getLabel());\r
-                       \r
-               }else if (existingTextData.size() == 1){\r
-                       return CdmBase.deproxy(existingTextData.iterator().next(), TextData.class);\r
-               }else{\r
-                       SpecimenDescription description = descriptions.iterator().next();\r
-                       description.addElement(textData);\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
-//************************** METHODS ***************************************** \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
-       private TextData getDerivedUnitImageGalleryTextData(boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException{\r
-               if (this.derivedUnitMediaTextData == null && createIfNotExists){\r
-                       this.derivedUnitMediaTextData = getImageGalleryTextData(derivedUnit, "Specimen");\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(boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException{\r
-               if (this.fieldObjectMediaTextData == null && createIfNotExists){\r
-                       this.fieldObjectMediaTextData = getImageGalleryTextData(fieldObservation, "Field observation");\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
-       \r
        /**\r
-        * @param derivationEvent2\r
+        * @param derivationEvent\r
         * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException \r
+        * @throws DerivedUnitFacadeNotSupportedException\r
         */\r
-       private Set<FieldObservation> getFieldObservationsOriginals(DerivationEvent derivationEvent, Set<SpecimenOrObservationBase> recursionAvoidSet) throws DerivedUnitFacadeNotSupportedException {\r
-               if (recursionAvoidSet == null){\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<FieldObservation> result = new HashSet<FieldObservation>();\r
+               Set<FieldUnit> result = new HashSet<FieldUnit>();\r
                Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();\r
-               for (SpecimenOrObservationBase original : originals){\r
-                       if (original.isInstanceOf(FieldObservation.class)){\r
-                               result.add(CdmBase.deproxy(original, FieldObservation.class));\r
-                       }else if (original.isInstanceOf(DerivedUnitBase.class)){\r
-                               //if specimen has already been tested exclude it from further recursion\r
-                               if (recursionAvoidSet.contains(original)){\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
-                               DerivedUnitBase derivedUnit = CdmBase.deproxy(original, DerivedUnitBase.class);\r
+                               DerivedUnit derivedUnit = CdmBase.deproxy(original,\r
+                                               DerivedUnit.class);\r
                                DerivationEvent originalDerivation = derivedUnit.getDerivedFrom();\r
-//                             Set<DerivationEvent> derivationEvents = original.getDerivationEvents(); \r
-//                             for (DerivationEvent originalDerivation : derivationEvents){\r
-                                       Set<FieldObservation> fieldObservations = getFieldObservationsOriginals(originalDerivation, recursionAvoidSet);\r
-                                       result.addAll(fieldObservations);\r
-//                             }\r
-                       }else{\r
-                               throw new DerivedUnitFacadeNotSupportedException("Unhandled specimen or observation base type: " + original.getClass().getName() );\r
+                               // Set<DerivationEvent> derivationEvents =\r
+                               // original.getDerivationEvents();\r
+                               // for (DerivationEvent originalDerivation : derivationEvents){\r
+                               Set<FieldUnit> fieldUnits = getFieldUnitOriginals(\r
+                                               originalDerivation, recursionAvoidSet);\r
+                               result.addAll(fieldUnits);\r
+                               // }\r
+                       } else {\r
+                               throw new DerivedUnitFacadeNotSupportedException(\r
+                                               "Unhandled specimen or observation base type: "\r
+                                                               + original.getClass().getName());\r
                        }\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 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 messages\r
-//      * @return\r
-//      * @throws DerivedUnitFacadeNotSupportedException\r
-//      */\r
-//     private List<Media> getImageGalleryMedia(SpecimenOrObservationBase specimen, String specimenExceptionText) throws DerivedUnitFacadeNotSupportedException{\r
-//             List<Media> result;\r
-//             SpecimenDescription imageGallery = getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);\r
-//             TextData textData = getImageTextDataWithSupportTest(imageGallery, specimenExceptionText);\r
-//             result = textData.getMedia();\r
-//             return result;\r
-//     }\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 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 messages\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) throws DerivedUnitFacadeNotSupportedException{\r
+       private TextData getImageGalleryTextData(SpecimenOrObservationBase specimen, String specimenExceptionText)\r
+                       throws DerivedUnitFacadeNotSupportedException {\r
                TextData result;\r
-               SpecimenDescription imageGallery = getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);\r
-               result = getImageTextDataWithSupportTest(imageGallery, specimenExceptionText);\r
+               SpecimenDescription imageGallery = getImageGalleryWithSupportTest(\r
+                               specimen, specimenExceptionText, true);\r
+               result = getImageTextDataWithSupportTest(imageGallery,\r
+                               specimenExceptionText);\r
                return result;\r
        }\r
-       \r
-       \r
+\r
        /**\r
-        * Returns the image gallery of the according specimen. Throws an exception if the attached\r
-        * image gallerie(s) are not supported by this facade.\r
-        * If no image gallery exists a new one is created if <code>createNewIfNotExists</code> is true and\r
-        * if specimen is not <code>null</code>.\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(SpecimenOrObservationBase<?> specimen, String specimenText, boolean createNewIfNotExists) throws DerivedUnitFacadeNotSupportedException{\r
-               if (specimen == null){\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 + " must not have more than 1 image gallery");\r
-               }else{\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
        /**\r
-        * Returns the media holding text data element of the image gallery. Throws an exception if multiple\r
-        * such text data already exist.\r
-        * Creates a new text data if none exists and adds it to the image gallery.\r
-        * If image gallery is <code>null</code> nothing happens.\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
+        * @throws DerivedUnitFacadeNotSupportedException\r
         */\r
-       private TextData getImageTextDataWithSupportTest(SpecimenDescription imageGallery, String specimenText) throws DerivedUnitFacadeNotSupportedException {\r
-               if (imageGallery == null){\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) && element.getFeature().equals(Feature.IMAGE())){\r
-                               if (textData != null){\r
-                                       throw new DerivedUnitFacadeNotSupportedException( specimenText + " must not have more than 1 image text data element in image gallery");\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
+               if (textData == null) {\r
                        textData = TextData.NewInstance(Feature.IMAGE());\r
                        imageGallery.addElement(textData);\r
                }\r
@@ -445,357 +775,569 @@ public class DerivedUnitFacade {
        }\r
 \r
        /**\r
-        * Checks, if a specimen belongs to more than one description that is an image gallery\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(SpecimenOrObservationBase<?> derivedUnit){\r
+       private boolean hasMultipleImageGalleries(\r
+                       SpecimenOrObservationBase<?> derivedUnit) {\r
                int count = 0;\r
-               Set<SpecimenDescription> descriptions= derivedUnit.getSpecimenDescriptions();\r
-               for (SpecimenDescription description : descriptions){\r
-                       if (description.isImageGallery()){\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
        /**\r
-        * Returns the image gallery for a specimen. If there are multiple specimen descriptions\r
-        * marked as image galleries an arbitrary one is chosen.\r
-        * If no image gallery exists, a new one is created if <code>createNewIfNotExists</code>\r
-        * is <code>true</code>.<Br>\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
+               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
+               if (result == null && createIfNotExists) {\r
                        result = SpecimenDescription.NewInstance(specimen);\r
                        result.setImageGallery(true);\r
                }\r
                return result;\r
        }\r
-       \r
+\r
        /**\r
-        * Adds a media to the specimens image gallery. If media is <code>null</code> nothing happens.\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 {@link java.util.Collection#add(Object) Collection.add(E e)} \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 = getMedia(specimen, true);\r
-                       return mediaList.add(media);\r
-               }else{\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 specified by {@link java.util.Collection#remove(Object) Collection.remove(E e)} \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, SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {\r
-               List<Media> mediaList = getMedia(specimen, true);\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> getMedia(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException {\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
        /**\r
-        * Returns the one media list of a specimen which is part of the only image gallery that \r
-        * this specimen is part of.<BR>\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) throws DerivedUnitFacadeNotSupportedException {\r
-//             if (specimen == null){\r
-//                     return null;\r
-//             }\r
-//             if (specimen == this.derivedUnit){\r
-//                     return getDerivedUnitImageGalleryMedia();\r
-//             }else if (specimen == this.fieldObservation){\r
-//                     return getObservationImageGalleryTextData();\r
-//             }else{\r
-//                     return getImageGalleryMedia(specimen, "Undefined specimen ");\r
-//             }\r
-//     }\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 gallery that \r
-        * this specimen is part of.<BR>\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 TextData getMediaTextData(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) throws DerivedUnitFacadeNotSupportedException {\r
-               if (specimen == null){\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
+               if (specimen == this.derivedUnit) {\r
                        return getDerivedUnitImageGalleryTextData(createIfNotExists);\r
-               }else if (specimen == this.fieldObservation){\r
+               } else if (specimen == this.fieldUnit) {\r
                        return getObservationImageGalleryTextData(createIfNotExists);\r
-               }else{\r
+               } else {\r
                        return getImageGalleryTextData(specimen, "Undefined specimen ");\r
                }\r
        }\r
-       \r
-       \r
-//****************** GETTER / SETTER / ADDER / REMOVER ***********************/        \r
-       \r
-// ****************** Gathering Event *********************************/\r
-       \r
-       //Collecting area\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
+               for (NamedArea area : areas) {\r
                        getGatheringEvent(true).addCollectingArea(area);\r
                }\r
        }\r
+\r
+       @Transient\r
        public Set<NamedArea> getCollectingAreas() {\r
-               return  (hasGatheringEvent() ? getGatheringEvent(true).getCollectingAreas() : null);\r
+               return (hasGatheringEvent() ? getGatheringEvent(true)\r
+                               .getCollectingAreas() : null);\r
        }\r
+\r
        public void removeCollectingArea(NamedArea area) {\r
-               if (hasGatheringEvent()){\r
+               if (hasGatheringEvent()) {\r
                        getGatheringEvent(true).removeCollectingArea(area);\r
                }\r
        }\r
 \r
-       //absolute elevation  \r
-       /** meter above/below sea level of the surface \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);\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
-       //absolute elevation error\r
-       public Integer getAbsoluteElevationError() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevationError() : null);\r
+       public void setAbsoluteElevationMax(Integer absoluteElevationMax) {\r
+               getGatheringEvent(true).setAbsoluteElevationMax(absoluteElevationMax);\r
        }\r
-       public void setAbsoluteElevationError(Integer absoluteElevationError) {\r
-               getGatheringEvent(true).setAbsoluteElevationError(absoluteElevationError);\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 #getAbsoluteElevationMaximum()\r
+        * @see #getAbsoluteElevationMinimum()\r
         */\r
-       public Integer getAbsoluteElevationMinimum(){\r
-               if ( ! hasGatheringEvent() ){\r
+       @Transient\r
+       public Integer getAbsoluteElevationMaximum() {\r
+               if (!hasGatheringEvent()) {\r
                        return null;\r
+               }else{\r
+                       return getGatheringEvent(true).getAbsoluteElevationMax();\r
                }\r
-               Integer minimum = getGatheringEvent(true).getAbsoluteElevation();\r
-               if (getGatheringEvent(true).getAbsoluteElevationError() != null){\r
-                       minimum = minimum -  getGatheringEvent(true).getAbsoluteElevationError();\r
-               }\r
-               return minimum;\r
        }\r
+       \r
        /**\r
         * @see #getAbsoluteElevation()\r
         * @see #getAbsoluteElevationError()\r
         * @see #setAbsoluteElevationRange(Integer, Integer)\r
         * @see #getAbsoluteElevationMinimum()\r
         */\r
-       public Integer getAbsoluteElevationMaximum(){\r
-               if ( ! hasGatheringEvent() ){\r
+       @Transient\r
+       public String getAbsoluteElevationText() {\r
+               if (!hasGatheringEvent()) {\r
                        return null;\r
+               }else{\r
+                       return getGatheringEvent(true).getAbsoluteElevationText();\r
                }\r
-               Integer maximum = getGatheringEvent(true).getAbsoluteElevation();\r
-               if (getGatheringEvent(true).getAbsoluteElevationError() != null){\r
-                       maximum = maximum +  getGatheringEvent(true).getAbsoluteElevationError();\r
-               }\r
-               return maximum;\r
        }\r
 \r
-       \r
        /**\r
-        * This method replaces absoluteElevation and absoulteElevationError by\r
-        * internally translating minimum and maximum values into\r
-        * average and error values. As all these values are integer based\r
-        * it is necessary that the distance is between minimum and maximum is <b>even</b>,\r
-        * otherwise we will get a rounding error resulting in a maximum that is increased\r
-        * by 1.\r
+        * Convenience method to set absolute elevation minimum and maximum.\r
+        * \r
         * @see #setAbsoluteElevation(Integer)\r
-        * @see #setAbsoluteElevationError(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
-               if (minimumElevation == null || maximumElevation == null){\r
-                       Integer elevation = minimumElevation;\r
-                       Integer error = 0;\r
-                       if (minimumElevation == null){\r
-                               elevation = maximumElevation;\r
-                               if (elevation == null){\r
-                                       error = null;\r
-                               }\r
-                       }\r
-                       getGatheringEvent(true).setAbsoluteElevation(elevation);\r
-                       getGatheringEvent(true).setAbsoluteElevationError(error);\r
-               }else{\r
-                       if (! isEvenDistance(minimumElevation, maximumElevation) ){\r
-                               throw new IllegalArgumentException("Distance between minimum and maximum elevation must be even but was " + Math.abs(minimumElevation - maximumElevation));\r
-                       }\r
-                       Integer absoluteElevationError = Math.abs(maximumElevation - minimumElevation);\r
-                       absoluteElevationError = absoluteElevationError / 2;\r
-                       Integer absoluteElevation = minimumElevation + absoluteElevationError;\r
-                       getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);\r
-                       getGatheringEvent(true).setAbsoluteElevationError(absoluteElevationError);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * @param minimumElevation\r
-        * @param maximumElevation\r
-        * @return\r
-        */\r
-       private boolean isEvenDistance(Integer minimumElevation, Integer maximumElevation) {\r
-               Integer diff = ( maximumElevation - minimumElevation);\r
-               Integer testDiff = (diff /2) *2 ;\r
-               return (testDiff == diff);\r
+       public void setAbsoluteElevationRange(Integer minimumElevation, Integer maximumElevation) {\r
+               getGatheringEvent(true).setAbsoluteElevation(minimumElevation);\r
+               getGatheringEvent(true).setAbsoluteElevationMax(maximumElevation);\r
        }\r
 \r
-       //collector\r
+       // collector\r
+       @Transient\r
        public AgentBase getCollector() {\r
-               return  (hasGatheringEvent() ? getGatheringEvent(true).getCollector() : null);\r
+               return (hasGatheringEvent() ? getGatheringEvent(true).getCollector()\r
+                               : null);\r
        }\r
-       public void setCollector(AgentBase collector){\r
+\r
+       public void setCollector(AgentBase collector) {\r
                getGatheringEvent(true).setCollector(collector);\r
        }\r
 \r
-       //collecting method\r
+       // collecting method\r
+       @Transient\r
        public String getCollectingMethod() {\r
-               return  (hasGatheringEvent() ? getGatheringEvent(true).getCollectingMethod() : null);\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
-       public Integer getDistanceToGround() {\r
-               return  (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToGround() : null);\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);\r
+               }\r
        }\r
-       public void setDistanceToGround(Integer distanceToGround) {\r
-               getGatheringEvent(true).setDistanceToGround(distanceToGround);\r
+\r
+       @Transient\r
+       public Double getDistanceToGround() {\r
+               return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToGround() : null);\r
        }\r
 \r
-       //distance to water surface\r
-       public Integer getDistanceToWaterSurface() {\r
-               return  (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToWaterSurface() : null);\r
+       public void setDistanceToGround(Double distanceToGround) {\r
+               getGatheringEvent(true).setDistanceToGround(distanceToGround);\r
        }\r
-       public void setDistanceToWaterSurface(Integer distanceToWaterSurface) {\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);\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
-       //exact location\r
-       public Point getExactLocation() {\r
-               return  (hasGatheringEvent() ? getGatheringEvent(true).getExactLocation() : null );\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. 12°59'N, 35°23E).\r
-        * If the exact location is <code>null</code> the empty string is returned.\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, boolean includeReferenceSystem){\r
-               return (this.getExactLocation() == null ? "" : this.getExactLocation().toSexagesimalString(includeEmptySeconds, includeReferenceSystem));\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
-       public void setExactLocationByParsing(String longitudeToParse, String latitudeToParse, ReferenceSystem referenceSystem, Integer errorRadius) throws ParseException{\r
-               Point point = Point.NewInstance(null, null, referenceSystem, errorRadius);\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
+\r
+       // gathering event description\r
+       @Transient\r
        public String getGatheringEventDescription() {\r
-               return  (hasGatheringEvent() ? getGatheringEvent(true).getDescription() : null);\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
+       // gatering period\r
+       @Transient\r
        public TimePeriod getGatheringPeriod() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getTimeperiod() : null);\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
-       public LanguageString getLocality(){\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getLocality() : null);\r
+       // locality\r
+       @Transient\r
+       public LanguageString getLocality() {\r
+               return (hasGatheringEvent() ? getGatheringEvent(true).getLocality()\r
+                               : null);\r
        }\r
-       public String getLocalityText(){\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
+               if (locality != null) {\r
                        return locality.getText();\r
                }\r
                return null;\r
        }\r
-       public Language getLocalityLanguage(){\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
+               if (locality != null) {\r
                        return locality.getLanguage();\r
                }\r
                return null;\r
        }\r
-       \r
+\r
        /**\r
         * Sets the locality string in the default language\r
+        * \r
         * @param locality\r
         */\r
-       public void setLocality(String locality){\r
+       public void setLocality(String locality) {\r
                Language language = Language.DEFAULT();\r
                setLocality(locality, language);\r
        }\r
-       public void setLocality(String locality, Language language){\r
+\r
+       public void setLocality(String locality, Language language) {\r
                LanguageString langString = LanguageString.NewInstance(locality, language);\r
                setLocality(langString);\r
        }\r
-       public void setLocality(LanguageString locality){\r
+\r
+       public void setLocality(LanguageString locality) {\r
                getGatheringEvent(true).setLocality(locality);\r
        }\r
-       \r
+\r
        /**\r
-        * The gathering event will be used for the field object instead of the old gathering event.<BR>\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
@@ -806,388 +1348,632 @@ public class DerivedUnitFacade {
         * @see #getCollectingAreas()\r
         * @see #getCollectingMethod()\r
         * @see #getLocality()\r
-        * @see #getCollector() \r
+        * @see #getCollector()\r
         * @param gatheringEvent\r
         */\r
        public void setGatheringEvent(GatheringEvent gatheringEvent) {\r
-               getFieldObservation(true).setGatheringEvent(gatheringEvent);\r
+               getFieldUnit(true).setGatheringEvent(gatheringEvent);\r
        }\r
-       public boolean hasGatheringEvent(){\r
+\r
+       public boolean hasGatheringEvent() {\r
                return (getGatheringEvent(false) != null);\r
        }\r
-       public GatheringEvent getGatheringEvent() {\r
+\r
+       public GatheringEvent innerGatheringEvent() {\r
                return getGatheringEvent(false);\r
        }\r
-       \r
+\r
        public GatheringEvent getGatheringEvent(boolean createIfNotExists) {\r
-               if (! hasFieldObservation() && ! createIfNotExists){\r
+               if (!hasFieldUnit() && !createIfNotExists) {\r
                        return null;\r
                }\r
-               if (createIfNotExists && getFieldObservation(true).getGatheringEvent() == null ){\r
+               if (createIfNotExists && getFieldUnit(true).getGatheringEvent() == null) {\r
                        GatheringEvent gatheringEvent = GatheringEvent.NewInstance();\r
-                       getFieldObservation(true).setGatheringEvent(gatheringEvent);\r
+                       getFieldUnit(true).setGatheringEvent(gatheringEvent);\r
                }\r
-               return getFieldObservation(true).getGatheringEvent();\r
+               return getFieldUnit(true).getGatheringEvent();\r
        }\r
-       \r
-// ****************** Field Object ************************************/\r
-       \r
+\r
+       // ****************** Field Object ************************************/\r
+\r
        /**\r
-        * Returns true if a field observation exists (even if all attributes are empty or <code>null<code>.\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.fieldObservation != null;\r
+       public boolean hasFieldObject() {\r
+               return this.fieldUnit != null;\r
        }\r
-       \r
-       //ecology\r
-       public String getEcology(){\r
+\r
+       // ecology\r
+       @Transient\r
+       public String getEcology() {\r
                return getEcology(Language.DEFAULT());\r
        }\r
-       public String getEcology(Language language){\r
+\r
+       public String getEcology(Language language) {\r
                LanguageString languageString = getEcologyAll().get(language);\r
                return (languageString == null ? null : languageString.getText());\r
        }\r
-//     public String getEcologyPreferred(List<Language> languages){\r
-//             LanguageString languageString = getEcologyAll().getPreferredLanguageString(languages);\r
-//             return languageString.getText();\r
-//     }\r
-       public Map<Language, LanguageString> getEcologyAll(){\r
-               if (ecology == null){\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(Feature.ECOLOGY(), true, false);\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
+\r
+       public void setEcology(String ecology) {\r
                setEcology(ecology, null);\r
        }\r
-       public void setEcology(String ecologyText, Language language){\r
-               if (language == null){\r
+\r
+       public void setEcology(String ecologyText, Language language) {\r
+               if (language == null) {\r
                        language = Language.DEFAULT();\r
                }\r
-               if (ecology == null){\r
+               if (ecology == null) {\r
                        try {\r
-                               ecology = initializeFieldObjectTextDataWithSupportTest(Feature.ECOLOGY(), true, false);\r
+                               ecology = initializeFieldObjectTextDataWithSupportTest(\r
+                                               Feature.ECOLOGY(), true, false);\r
                        } catch (DerivedUnitFacadeNotSupportedException e) {\r
                                throw new IllegalStateException(notSupportMessage, e);\r
                        }\r
                }\r
-               if (ecologyText == null){\r
+               if (ecologyText == null) {\r
                        ecology.removeText(language);\r
-               }else{\r
-                       ecology.putText(ecologyText, language);\r
+               } else {\r
+                       ecology.putText(language, ecologyText);\r
                }\r
        }\r
-       public void removeEcology(Language language){\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
+       public void removeEcology() {\r
                setEcology(null, null);\r
        }\r
-       public void removeEcologyAll(){\r
-               \r
+\r
+       public void removeEcologyAll() {\r
+\r
        }\r
 \r
-       \r
-       //plant description\r
-       public String getPlantDescription(){\r
+       // plant description\r
+       @Transient\r
+       public String getPlantDescription() {\r
                return getPlantDescription(null);\r
        }\r
-       public String getPlantDescription(Language language){\r
-               if (language == null){\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
-//     public String getPlantDescriptionPreferred(List<Language> languages){\r
-//             LanguageString languageString = getPlantDescriptionAll().getPreferredLanguageString(languages);\r
-//             return languageString.getText();\r
-//     }\r
-       public Map<Language, LanguageString> getPlantDescriptionAll(){\r
-               if (plantDescription == null){\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(Feature.DESCRIPTION(), true, false);\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
-       public void setPlantDescription(String plantDescription){\r
+\r
+       public void setPlantDescription(String plantDescription) {\r
                setPlantDescription(plantDescription, null);\r
        }\r
-       public void setPlantDescription(String plantDescriptionText, Language language){\r
-               if (language == null){\r
+\r
+       public void setPlantDescription(String plantDescriptionText,\r
+                       Language language) {\r
+               if (language == null) {\r
                        language = Language.DEFAULT();\r
                }\r
-               if (plantDescription == null){\r
+               if (plantDescription == null) {\r
                        try {\r
-                               plantDescription = initializeFieldObjectTextDataWithSupportTest(Feature.DESCRIPTION(), true, false);\r
+                               plantDescription = initializeFieldObjectTextDataWithSupportTest(\r
+                                               Feature.DESCRIPTION(), true, false);\r
                        } catch (DerivedUnitFacadeNotSupportedException e) {\r
                                throw new IllegalStateException(notSupportMessage, e);\r
                        }\r
                }\r
-               if (plantDescriptionText == null){\r
+               if (plantDescriptionText == null) {\r
                        plantDescription.removeText(language);\r
-               }else{\r
-                       plantDescription.putText(plantDescriptionText, language);\r
+               } else {\r
+                       plantDescription.putText(language, plantDescriptionText);\r
                }\r
        }\r
-       public void removePlantDescription(Language language){\r
+\r
+       public void removePlantDescription(Language language) {\r
                setPlantDescription(null, language);\r
        }\r
-       \r
 \r
-       \r
-       //field object definition\r
+       // field object definition\r
        public void addFieldObjectDefinition(String text, Language language) {\r
-               getFieldObservation(true).addDefinition(text, language);\r
+               getFieldUnit(true).putDefinition(language, text);\r
        }\r
+\r
+       @Transient\r
        public Map<Language, LanguageString> getFieldObjectDefinition() {\r
-               if (! hasFieldObservation()){\r
+               if (!hasFieldUnit()) {\r
                        return new HashMap<Language, LanguageString>();\r
-               }else{\r
-                       return getFieldObservation(true).getDefinition();\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
+               LanguageString languageString = (map == null ? null : map.get(language));\r
+               if (languageString != null) {\r
                        return languageString.getText();\r
-               }else {\r
+               } else {\r
                        return null;\r
                }\r
        }\r
+\r
        public void removeFieldObjectDefinition(Language lang) {\r
-               if (hasFieldObservation()){\r
-                       getFieldObservation(true).removeDefinition(lang);\r
+               if (hasFieldUnit()) {\r
+                       getFieldUnit(true).removeDefinition(lang);\r
                }\r
        }\r
-       \r
 \r
-       //media\r
-       public boolean addFieldObjectMedia(Media media)  {\r
+       // media\r
+       public boolean addFieldObjectMedia(Media media) {\r
                try {\r
-                       return addMedia(media, getFieldObservation(true));\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
+        * Returns also <code>true</code> if the image gallery is empty.\r
+        * \r
         * @return\r
         */\r
-       public boolean hasFieldObjectImageGallery(){\r
-               if (! hasFieldObject()){\r
+       public boolean hasFieldObjectImageGallery() {\r
+               if (!hasFieldObject()) {\r
                        return false;\r
-               }else{\r
-                       return (getImageGallery(fieldObservation, false) != null);\r
+               } else {\r
+                       return (getImageGallery(fieldUnit, false) != null);\r
                }\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(boolean createIfNotExists){\r
+       public SpecimenDescription getFieldObjectImageGallery(\r
+                       boolean createIfNotExists) {\r
                TextData textData;\r
                try {\r
-                       textData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), createIfNotExists, true);\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(), SpecimenDescription.class);\r
-               }else{\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 = getMedia(getFieldObservation(false), false);\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, getFieldObservation(false));\r
+                       return removeMedia(media, getFieldUnit(false));\r
                } catch (DerivedUnitFacadeNotSupportedException e) {\r
                        throw new IllegalStateException(notSupportMessage, e);\r
                }\r
        }\r
 \r
-       //field number\r
+       // field number\r
+       @Transient\r
        public String getFieldNumber() {\r
-               if (! hasFieldObservation()){\r
+               if (!hasFieldUnit()) {\r
                        return null;\r
-               }else{\r
-                       return getFieldObservation(true).getFieldNumber();\r
+               } else {\r
+                       return getFieldUnit(true).getFieldNumber();\r
                }\r
        }\r
+\r
        public void setFieldNumber(String fieldNumber) {\r
-               getFieldObservation(true).setFieldNumber(fieldNumber);\r
+               getFieldUnit(true).setFieldNumber(fieldNumber);\r
        }\r
 \r
-       \r
-       //field notes\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 (! hasFieldObservation()){\r
+               if (!hasFieldUnit()) {\r
                        return null;\r
-               }else{\r
-                       return getFieldObservation(true).getFieldNotes();\r
+               } else {\r
+                       return getFieldUnit(true).getFieldNotes();\r
                }\r
        }\r
+\r
        public void setFieldNotes(String fieldNotes) {\r
-               getFieldObservation(true).setFieldNotes(fieldNotes);\r
+               getFieldUnit(true).setFieldNotes(fieldNotes);\r
        }\r
 \r
-\r
-       //individual counts\r
+       // individual counts\r
+       @Transient\r
        public Integer getIndividualCount() {\r
-               return (hasFieldObservation()? getFieldObservation(true).getIndividualCount() : null );\r
+               return (hasFieldUnit() ? getFieldUnit(true).getIndividualCount() : null);\r
        }\r
+\r
        public void setIndividualCount(Integer individualCount) {\r
-               getFieldObservation(true).setIndividualCount(individualCount);\r
+               getFieldUnit(true).setIndividualCount(individualCount);\r
        }\r
 \r
-       //life stage\r
-       public Stage getLifeStage() {\r
-               return (hasFieldObservation()? getFieldObservation(true).getLifeStage() : null );\r
+       // life stage\r
+       @Transient\r
+       public DefinedTerm getLifeStage() {\r
+               return (hasFieldUnit() ? getFieldUnit(true).getLifeStage() : null);\r
        }\r
-       public void setLifeStage(Stage lifeStage) {\r
-               getFieldObservation(true).setLifeStage(lifeStage);\r
+\r
+       public void setLifeStage(DefinedTerm lifeStage) {\r
+               getFieldUnit(true).setLifeStage(lifeStage);\r
        }\r
 \r
-       //sex\r
-       public Sex getSex() {\r
-               return (hasFieldObservation()? getFieldObservation(true).getSex() : null );\r
+       // sex\r
+       @Transient\r
+       public DefinedTerm getSex() {\r
+               return (hasFieldUnit() ? getFieldUnit(true).getSex(): null);\r
        }\r
-       public void setSex(Sex sex) {\r
-               getFieldObservation(true).setSex(sex);\r
+\r
+       public void setSex(DefinedTerm sex) {\r
+               getFieldUnit(true).setSex(sex);\r
        }\r
        \r
-       \r
-       //field observation\r
-       public boolean hasFieldObservation(){\r
-               return (getFieldObservation(false) != null);\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(false) != null);\r
+       }\r
+\r
        /**\r
-        * Returns the field observation as an object.\r
+        * Returns the field unit as an object.\r
+        * \r
         * @return\r
         */\r
-       public FieldObservation getFieldObservation(){\r
-               return getFieldObservation(false);\r
+       public FieldUnit innerFieldUnit() {\r
+               return getFieldUnit(false);\r
        }\r
 \r
        /**\r
-        * Returns the field observation as an object.\r
+        * Returns the field unit as an object.\r
+        * \r
         * @return\r
         */\r
-       public FieldObservation getFieldObservation(boolean createIfNotExists){\r
-               if (fieldObservation == null && createIfNotExists){\r
-                       fieldObservation = FieldObservation.NewInstance();\r
-                       DerivationEvent derivationEvent = getDerivationEvent(true);\r
-                       derivationEvent.addOriginal(fieldObservation);\r
+       public FieldUnit getFieldUnit(boolean createIfNotExists) {\r
+               if (fieldUnit == null && createIfNotExists) {\r
+                       setFieldUnit(FieldUnit.NewInstance());\r
                }\r
-               return this.fieldObservation;\r
+               return this.fieldUnit;\r
        }\r
-\r
        \r
 \r
-//****************** Specimen **************************************************       \r
-       \r
-       //Definition\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
-               derivedUnit.addDefinition(text, language);\r
+               innerDerivedUnit().putDefinition(language, text);\r
        }\r
-       public Map<Language, LanguageString> getDerivedUnitDefinitions(){\r
+\r
+       @Transient\r
+       public Map<Language, LanguageString> getDerivedUnitDefinitions() {\r
+               testDerivedUnit();\r
                return this.derivedUnit.getDefinition();\r
        }\r
+\r
+\r
        public String getDerivedUnitDefinition(Language language) {\r
-               Map<Language,LanguageString> languageMap = derivedUnit.getDefinition();\r
+               testDerivedUnit();\r
+               Map<Language, LanguageString> languageMap = derivedUnit.getDefinition();\r
                LanguageString languageString = languageMap.get(language);\r
-               if (languageString != null){\r
+               if (languageString != null) {\r
                        return languageString.getText();\r
-               }else {\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
+       // Determination\r
        public void addDetermination(DeterminationEvent determination) {\r
+               testDerivedUnit();\r
+               //TODO implement correct bidirectional mapping in model classes\r
+               determination.setIdentifiedUnit(derivedUnit);\r
                derivedUnit.addDetermination(determination);\r
        }\r
+\r
+       @Transient\r
+       public DeterminationEvent getPreferredDetermination() {\r
+               testDerivedUnit();\r
+               Set<DeterminationEvent> events = derivedUnit.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
+               testDerivedUnit();\r
+               Set<DeterminationEvent> events = derivedUnit.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
+               testDerivedUnit();\r
+               Set<DeterminationEvent> events = derivedUnit.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
+               testDerivedUnit();\r
                return derivedUnit.getDeterminations();\r
        }\r
+\r
        public void removeDetermination(DeterminationEvent determination) {\r
+               testDerivedUnit();\r
                derivedUnit.removeDetermination(determination);\r
        }\r
-       \r
-       //Media\r
-       public boolean addDerivedUnitMedia(Media media)  {\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
+        * Returns also <code>true</code> if the image gallery is empty.\r
         */\r
-       public boolean hasDerivedUnitImageGallery(){\r
+       public boolean hasDerivedUnitImageGallery() {\r
                return (getImageGallery(derivedUnit, false) != null);\r
        }\r
-       \r
-       public SpecimenDescription getDerivedUnitImageGallery(boolean createIfNotExists){\r
+\r
+       public SpecimenDescription getDerivedUnitImageGallery(boolean createIfNotExists) {\r
+               testDerivedUnit();\r
                TextData textData;\r
                try {\r
-                       textData = inititialzeTextDataWithSupportTest(Feature.IMAGE(), derivedUnit, createIfNotExists, true);\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(), SpecimenDescription.class);\r
-               }else{\r
+               if (textData != null) {\r
+                       return CdmBase.deproxy(textData.getInDescription(),\r
+                                       SpecimenDescription.class);\r
+               } else {\r
                        return null;\r
                }\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
+               testDerivedUnit();\r
                try {\r
-                       List<Media> result = getMedia(derivedUnit, false);\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
@@ -1195,167 +1981,339 @@ public class DerivedUnitFacade {
                }\r
        }\r
 \r
-       \r
-       //Accession Number\r
+       // Accession Number\r
+       @Transient\r
        public String getAccessionNumber() {\r
+               testDerivedUnit();\r
                return derivedUnit.getAccessionNumber();\r
        }\r
+\r
        public void setAccessionNumber(String accessionNumber) {\r
+               testDerivedUnit();\r
                derivedUnit.setAccessionNumber(accessionNumber);\r
        }\r
 \r
-       //Catalog Number\r
+       @Transient\r
        public String getCatalogNumber() {\r
+               testDerivedUnit();\r
                return derivedUnit.getCatalogNumber();\r
        }\r
+\r
        public void setCatalogNumber(String catalogNumber) {\r
+               testDerivedUnit();\r
                derivedUnit.setCatalogNumber(catalogNumber);\r
        }\r
 \r
-       //Preservation Method\r
-       \r
+       @Transient\r
+       public String getBarcode() {\r
+               testDerivedUnit();\r
+               return 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.isInstanceOf(Specimen.class)){\r
-                       return CdmBase.deproxy(derivedUnit, Specimen.class).getPreservation();\r
-               }else{\r
-                       throw new MethodNotSupportedByDerivedUnitTypeException("A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");\r
+               testDerivedUnit();\r
+               if (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)throws MethodNotSupportedByDerivedUnitTypeException  {\r
-               if (derivedUnit.isInstanceOf(Specimen.class)){\r
-                       CdmBase.deproxy(derivedUnit, Specimen.class).setPreservation(preservation);\r
-               }else{\r
-                       throw new MethodNotSupportedByDerivedUnitTypeException("A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");\r
-                       \r
+       public void setPreservationMethod(PreservationMethod preservation)\r
+                       throws MethodNotSupportedByDerivedUnitTypeException {\r
+               testDerivedUnit();\r
+               if (derivedUnit.getRecordBasis().isPreservedSpecimen()) {\r
+                       CdmBase.deproxy(derivedUnit, DerivedUnit.class).setPreservation(preservation);\r
+               } else {\r
+                       if (this.config\r
+                                       .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
+       // Stored under name\r
+       @Transient\r
        public TaxonNameBase getStoredUnder() {\r
+               testDerivedUnit();\r
                return derivedUnit.getStoredUnder();\r
        }\r
+\r
        public void setStoredUnder(TaxonNameBase storedUnder) {\r
+               testDerivedUnit();\r
                derivedUnit.setStoredUnder(storedUnder);\r
        }\r
 \r
-       //colletors number\r
-       public String getCollectorsNumber() {\r
-               return derivedUnit.getCollectorsNumber();\r
+       // title cache\r
+       public String getTitleCache() {\r
+               SpecimenOrObservationBase<?> titledUnit = getTitledUnit();\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
-       public void setCollectorsNumber(String collectorsNumber) {\r
-               this.derivedUnit.setCollectorsNumber(collectorsNumber);\r
+       \r
+       private SpecimenOrObservationBase<?> getTitledUnit(){\r
+               return (derivedUnit != null )? derivedUnit : fieldUnit;\r
        }\r
 \r
-       //title cache\r
-       public String getTitleCache() {\r
-               if (! derivedUnit.isProtectedTitleCache()){\r
-                       //always compute title cache anew as long as there are no property change listeners on \r
-                       //field observation, gathering event etc \r
-                       derivedUnit.setTitleCache(null, false);\r
-               }\r
-               return this.derivedUnit.getTitleCache();\r
+       public boolean isProtectedTitleCache() {\r
+               return getTitledUnit().isProtectedTitleCache();\r
        }\r
+\r
        public void setTitleCache(String titleCache, boolean isProtected) {\r
-               this.derivedUnit.setTitleCache(titleCache, isProtected);\r
+               this.getTitledUnit().setTitleCache(titleCache, isProtected);\r
        }\r
 \r
-\r
        /**\r
         * Returns the derived unit itself.\r
+        * \r
         * @return the derived unit\r
         */\r
-       public DerivedUnitBase getDerivedUnit() {\r
+       public DerivedUnit innerDerivedUnit() {\r
                return this.derivedUnit;\r
        }\r
        \r
-       private boolean hasDerivationEvent(){\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
-       private DerivationEvent getDerivationEvent(){\r
+\r
+       private DerivationEvent getDerivationEvent() {\r
                return getDerivationEvent(false);\r
        }\r
-       private DerivationEvent getDerivationEvent(boolean createIfNotExists){\r
-               DerivationEvent result = derivedUnit.getDerivedFrom();\r
-               if (result == null){\r
-                       result = DerivationEvent.NewInstance();\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
-       public String getExsiccatum() {\r
-               logger.warn("Exsiccatum method not yet supported. Needs model change");\r
-               return null;\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
-       public String setExsiccatum() throws MethodNotSupportedException{\r
-               throw new MethodNotSupportedException("Exsiccatum method not yet supported. Needs model change");\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
-       \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
        // **** sources **/\r
-       public void addSource(IdentifiableSource source){\r
+       public void addSource(IdentifiableSource source) {\r
+               testDerivedUnit();\r
                this.derivedUnit.addSource(source);\r
        }\r
+\r
        /**\r
-        * Creates an orignal source, adds it to the specimen and returns it.\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(ReferenceBase reference, String microReference, String originalNameString){\r
-               IdentifiableSource source = IdentifiableSource.NewInstance(reference, microReference);\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
-               derivedUnit.addSource(source);\r
+               addSource(source);\r
                return source;\r
        }\r
-       \r
-       public Set<IdentifiableSource> getSources(){\r
+\r
+       @Transient\r
+       public Set<IdentifiableSource> getSources() {\r
+               testDerivedUnit();\r
                return derivedUnit.getSources();\r
        }\r
 \r
-       public void removeSource(IdentifiableSource source){\r
+       public void removeSource(IdentifiableSource source) {\r
+               testDerivedUnit();\r
                this.derivedUnit.removeSource(source);\r
        }\r
-       \r
-       \r
+\r
        /**\r
         * @return the collection\r
         */\r
+       @Transient\r
        public Collection getCollection() {\r
+               testDerivedUnit();\r
                return derivedUnit.getCollection();\r
        }\r
 \r
-\r
        /**\r
-        * @param collection the collection to set\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
+               testDerivedUnit();\r
+               this.derivedUnit.addAnnotation(annotation);\r
+       }\r
+\r
+       @Transient\r
+       public void getAnnotations() {\r
+               testDerivedUnit();\r
+               this.derivedUnit.getAnnotations();\r
+       }\r
+\r
+       public void removeAnnotation(Annotation annotation) {\r
+               testDerivedUnit();\r
+               this.derivedUnit.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 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
-       \r
+       // **************** Other Collections ********************************\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 in\r
-        * DerivedUnitBase and Specimen. Data from SpecimenOrObservationBase and above\r
-        * are not yet shared at the moment. \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
@@ -1364,41 +2322,93 @@ public class DerivedUnitFacade {
         * @param preservation\r
         * @return\r
         */\r
-       public Specimen addDuplicate(Collection collection, String catalogNumber, String accessionNumber, \r
-                               String collectorsNumber, TaxonNameBase storedUnder, PreservationMethod preservation){\r
-               Specimen duplicate = Specimen.NewInstance();\r
-               duplicate.setDerivedFrom(getDerivationEvent(true));\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.setCollectorsNumber(collectorsNumber);\r
                duplicate.setStoredUnder(storedUnder);\r
                duplicate.setPreservation(preservation);\r
                return duplicate;\r
        }\r
-       \r
-       public void addDuplicate(DerivedUnitBase duplicateSpecimen){\r
-               //TODO check derivedUnitType\r
-               getDerivationEvent(true).addDerivative(duplicateSpecimen);  \r
-       }\r
-       public Set<Specimen> getDuplicates(){\r
-               Set<Specimen> result = new HashSet<Specimen>();\r
-               if (hasDerivationEvent()){\r
-                       for (DerivedUnitBase derivedUnit: getDerivationEvent(true).getDerivatives()){\r
-                               if (derivedUnit.isInstanceOf(Specimen.class) && ! derivedUnit.equals(this.derivedUnit)){\r
-                                       result.add(CdmBase.deproxy(derivedUnit, Specimen.class));\r
+\r
+       public void addDuplicate(DerivedUnit duplicateSpecimen) {\r
+               // TODO check derivedUnitType\r
+               testDerivedUnit();\r
+               getDerivationEvent(CREATE).addDerivative(duplicateSpecimen);\r
+       }\r
+\r
+       @Transient\r
+       public Set<DerivedUnit> getDuplicates() {\r
+               testDerivedUnit();\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
-       public void removeDuplicate(Specimen duplicateSpecimen){\r
-               if (hasDerivationEvent()){\r
-                       getDerivationEvent(true).removeDerivative(duplicateSpecimen);\r
+\r
+       public void removeDuplicate(DerivedUnit duplicateSpecimen) {\r
+               testDerivedUnit();\r
+               if (hasDerivationEvent()) {\r
+                       getDerivationEvent(CREATE).removeDerivative(duplicateSpecimen);\r
                }\r
        }\r
        \r
        \r
+\r
+       private void testDerivedUnit() {\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
+               this.type = type;\r
+       }\r
+\r
+       public SpecimenOrObservationType getType() {\r
+               return type;\r
+       }\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) {\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
+                       return result;\r
+               }\r
+       }\r
 }\r