Fix javassist error for specimen cache strategy loading #5545
authorAndreas Müller <a.mueller@bgbm.org>
Fri, 12 Feb 2016 12:54:38 +0000 (13:54 +0100)
committerAndreas Müller <a.mueller@bgbm.org>
Fri, 12 Feb 2016 12:58:00 +0000 (13:58 +0100)
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/occurrence/DerivedUnit.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/occurrence/FieldUnit.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/model/occurrence/SpecimenOrObservationBase.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/facade/DerivedUnitFacade.java
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/facade/DerivedUnitFacadeCacheStrategyInjectionTest.java
cdmlib-services/src/test/java/eu/etaxonomy/cdm/api/facade/DerivedUnitFacadeCacheStrategyTest.java
cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/facade/DerivedUnitFacadeCacheStrategyInjectionTest.xml [new file with mode: 0644]
cdmlib-test/src/main/resources/dbscripts/001-cdm.h2.sql

index cabd5c60338e3c9131ac29e2e86a92f6ad27fb9d..80c18db1c8c0ebdc09f407cf1b7625b8eb15fcdd 100644 (file)
@@ -42,6 +42,7 @@ import eu.etaxonomy.cdm.model.molecular.DnaSample;
 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
 import eu.etaxonomy.cdm.model.name.TaxonNameBase;
 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
+import eu.etaxonomy.cdm.strategy.cache.common.IdentifiableEntityDefaultCacheStrategy;
 
 /**
  * A derived unit is regarded as derived from a field unit,
@@ -189,6 +190,7 @@ public class DerivedUnit extends SpecimenOrObservationBase<IIdentifiableEntityCa
        //Constructor: For hibernate use only
        protected DerivedUnit() {
            super();
+        initDefaultCacheStrategy();
        }
 
 
@@ -198,6 +200,7 @@ public class DerivedUnit extends SpecimenOrObservationBase<IIdentifiableEntityCa
         */
        protected DerivedUnit(SpecimenOrObservationType recordBasis) {
                super(recordBasis);
+        initDefaultCacheStrategy();
        }
 
 
@@ -206,7 +209,7 @@ public class DerivedUnit extends SpecimenOrObservationBase<IIdentifiableEntityCa
         * @param fieldUnit existing field unit from where this unit is derived
         */
        protected DerivedUnit(SpecimenOrObservationType recordBasis, FieldUnit fieldUnit) {
-               super(recordBasis);
+               this(recordBasis);
                DerivationEvent derivedFrom = new DerivationEvent();
                // TODO: should be done in a more controlled way. Probably by making derivation event implement a general relationship interface (for bidirectional add/remove etc)
                fieldUnit.addDerivationEvent(derivedFrom);
@@ -226,24 +229,42 @@ public class DerivedUnit extends SpecimenOrObservationBase<IIdentifiableEntityCa
                field.setGatheringEvent(gatheringEvent);
        }
 
-    private static Class<?> facadeCacheStrategyClass;
-
-
-    @Override
-    protected void setFacadeCacheStrategyClass(Class<?> facadeCacheStrategyClass){
-        this.facadeCacheStrategyClass = facadeCacheStrategyClass;
+    private static final String facadeStrategyClassName = "eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeCacheStrategy";
+    /**
+     * Sets the default cache strategy
+     */
+       @Override
+    protected void initDefaultCacheStrategy() {
+        try {
+            String facadeClassName = facadeStrategyClassName;
+            Class<?> facadeClass = Class.forName(facadeClassName);
+            try {
+                this.cacheStrategy = (IIdentifiableEntityCacheStrategy)facadeClass.newInstance();
+            } catch (InstantiationException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        } catch (ClassNotFoundException e) {
+            this.cacheStrategy = new IdentifiableEntityDefaultCacheStrategy<SpecimenOrObservationBase>();
+        }
     }
+//
+//    private static Class<?> facadeCacheStrategyClass;
+//
+//
+//    @Override
+//    protected void setFacadeCacheStrategyClass(Class<?> facadeCacheStrategyClass){
+//        this.facadeCacheStrategyClass = facadeCacheStrategyClass;
+//    }
+//
+//
+//    @Override
+//    protected Class<?> getFacadeCacheStrategyClass(){
+//         return facadeCacheStrategyClass;
+//     }
 
 
-    @Override
-    protected Class<?> getFacadeCacheStrategyClass(){
-           return facadeCacheStrategyClass;
-       }
-
-    @Override
-    protected String facadeStrategyClassName() {
-        return "eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeCacheStrategy";
-    }
 
 // ******************** GETTER / SETTER *************************************/
 
index 5134a13c702765854d2ca7dbb6957336d59e6ed1..29b21abf2e10bd510f1dcea693a5fd2eab54de0a 100644 (file)
@@ -38,6 +38,7 @@ import org.springframework.beans.factory.annotation.Configurable;
 
 import eu.etaxonomy.cdm.model.agent.Person;
 import eu.etaxonomy.cdm.strategy.cache.common.IIdentifiableEntityCacheStrategy;
+import eu.etaxonomy.cdm.strategy.cache.common.IdentifiableEntityDefaultCacheStrategy;
 
 /**
  *
@@ -113,33 +114,34 @@ public class FieldUnit extends SpecimenOrObservationBase<IIdentifiableEntityCach
         */
        protected FieldUnit(){
                super(SpecimenOrObservationType.FieldUnit);
-//             this.cacheStrategy = new IdentifiableEntityDefaultCacheStrategy<FieldUnit>();
+        initDefaultCacheStrategy();
        }
 
 //****************************** CACHE STRATEGY **************************************/
 
-    private static Class<?> facadeCacheStrategyClass;
-
-
-    @Override
-    protected void setFacadeCacheStrategyClass(Class<?> facadeCacheStrategyClass){
-        this.facadeCacheStrategyClass = facadeCacheStrategyClass;
-    }
-
-
-    @Override
-    protected Class<?> getFacadeCacheStrategyClass(){
-        return facadeCacheStrategyClass;
-    }
-
+    private static final String facadeStrategyClassName =
+            "eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeFieldUnitCacheStrategy";
     /**
-     * @return
+     * Sets the default cache strategy
      */
     @Override
-    protected String facadeStrategyClassName() {
-        return "eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeFieldUnitCacheStrategy";
+    protected void initDefaultCacheStrategy() {
+        try {
+            String facadeClassName = facadeStrategyClassName;
+            Class<?> facadeClass = Class.forName(facadeClassName);
+            try {
+                this.cacheStrategy = (IIdentifiableEntityCacheStrategy)facadeClass.newInstance();
+            } catch (InstantiationException e) {
+                e.printStackTrace();
+            } catch (IllegalAccessException e) {
+                e.printStackTrace();
+            }
+        } catch (ClassNotFoundException e) {
+            this.cacheStrategy = new IdentifiableEntityDefaultCacheStrategy<FieldUnit>();
+        }
     }
 
+
 // ************************ GETTER / SETTER *******************************************
 
        public GatheringEvent getGatheringEvent() {
index 3548383426bfbe79894b88ca08ec2df45e64bd5a..b8bd0bbf15ad13e5a349f882fefff2d4803e125a 100644 (file)
@@ -194,52 +194,29 @@ public abstract class SpecimenOrObservationBase<S extends IIdentifiableEntityCac
 
 //********************************** CONSTRUCTOR *********************************/
 
-      //for hibernate use only
-      @Deprecated
-      protected SpecimenOrObservationBase(){
-          super();
-          setDefaultCacheStrategy();
-      }
+    //for hibernate use only
+    @Deprecated
+    protected SpecimenOrObservationBase(){
+        super();
+    }
 
     protected SpecimenOrObservationBase(SpecimenOrObservationType recordBasis) {
         super();
         if (recordBasis == null){ throw new IllegalArgumentException("RecordBasis must not be null");}
         this.recordBasis = recordBasis;
-        setDefaultCacheStrategy();
     }
 
-    //start very dirty workaround to have a cache strategy for specimen/observations
 
     /**
-     * Sets the default cache strategy
+     * Subclasses should implement setting the default cache strate
      */
-    protected void setDefaultCacheStrategy() {
-        if (getFacadeCacheStrategyClass() == null){
-            try {
-                setFacadeCacheStrategyClass(Class.forName(facadeStrategyClassName()));
+    protected abstract void initDefaultCacheStrategy();
 
-            } catch (ClassNotFoundException e) {
-                //TODO look for other cache strategy
-            }
-        }
-        if (getFacadeCacheStrategyClass() != null){
-            try {
-                this.cacheStrategy = (S)getFacadeCacheStrategyClass().newInstance();
-            } catch (InstantiationException e) {
-                e.printStackTrace();
-            } catch (IllegalAccessException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    protected abstract void setFacadeCacheStrategyClass(Class<?> facadeCacheStrategyClass);
-    protected abstract Class<?> getFacadeCacheStrategyClass();
-    protected abstract String facadeStrategyClassName();
 
 //************************* GETTER / SETTER ***********************/
 
 
+
     /**
      * @see #recordBasis
      * @return
index 923e8137e024c4685cc985cf0d3b10ce0928fa7f..31febeeefe82ba36c3567840e392e86ecc3cbf1a 100644 (file)
-/**\r
- * Copyright (C) 2007 EDIT\r
- * European Distributed Institute of Taxonomy\r
- * http://www.e-taxonomy.eu\r
- *\r
- * The contents of this file are subject to the Mozilla Public License Version 1.1\r
- * See LICENSE.TXT at the top of this package for the full license terms.\r
- */\r
-\r
-package eu.etaxonomy.cdm.api.facade;\r
-\r
-import java.beans.PropertyChangeEvent;\r
-import java.beans.PropertyChangeListener;\r
-import java.text.ParseException;\r
-import java.util.ArrayList;\r
-import java.util.HashMap;\r
-import java.util.HashSet;\r
-import java.util.List;\r
-import java.util.Map;\r
-import java.util.Set;\r
-\r
-import javax.persistence.Transient;\r
-\r
-import org.apache.commons.lang.StringUtils;\r
-import org.apache.log4j.Logger;\r
-\r
-import eu.etaxonomy.cdm.api.service.IOccurrenceService;\r
-import eu.etaxonomy.cdm.common.CdmUtils;\r
-import eu.etaxonomy.cdm.common.UTF8;\r
-import eu.etaxonomy.cdm.model.agent.AgentBase;\r
-import eu.etaxonomy.cdm.model.agent.Person;\r
-import eu.etaxonomy.cdm.model.common.Annotation;\r
-import eu.etaxonomy.cdm.model.common.CdmBase;\r
-import eu.etaxonomy.cdm.model.common.DefinedTerm;\r
-import eu.etaxonomy.cdm.model.common.IOriginalSource;\r
-import eu.etaxonomy.cdm.model.common.IdentifiableSource;\r
-import eu.etaxonomy.cdm.model.common.Identifier;\r
-import eu.etaxonomy.cdm.model.common.Language;\r
-import eu.etaxonomy.cdm.model.common.LanguageString;\r
-import eu.etaxonomy.cdm.model.common.OriginalSourceType;\r
-import eu.etaxonomy.cdm.model.common.TimePeriod;\r
-import eu.etaxonomy.cdm.model.description.DescriptionElementBase;\r
-import eu.etaxonomy.cdm.model.description.Feature;\r
-import eu.etaxonomy.cdm.model.description.SpecimenDescription;\r
-import eu.etaxonomy.cdm.model.description.TextData;\r
-import eu.etaxonomy.cdm.model.location.NamedArea;\r
-import eu.etaxonomy.cdm.model.location.Point;\r
-import eu.etaxonomy.cdm.model.location.ReferenceSystem;\r
-import eu.etaxonomy.cdm.model.media.Media;\r
-import eu.etaxonomy.cdm.model.media.Rights;\r
-import eu.etaxonomy.cdm.model.name.TaxonNameBase;\r
-import eu.etaxonomy.cdm.model.occurrence.Collection;\r
-import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;\r
-import eu.etaxonomy.cdm.model.occurrence.DerivationEventType;\r
-import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;\r
-import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;\r
-import eu.etaxonomy.cdm.model.occurrence.FieldUnit;\r
-import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;\r
-import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;\r
-import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;\r
-import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;\r
-import eu.etaxonomy.cdm.model.reference.Reference;\r
-\r
-/**\r
- * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from\r
- * a specimen based view. It does not support all functionality available in the\r
- * occurrence package.<BR>\r
- * The most significant restriction is that a specimen may derive only from one\r
- * direct derivation event and there must be only one field unit\r
- * (gathering event) it derives from.<BR>\r
- *\r
- * @author a.mueller\r
- * @date 14.05.2010\r
- */\r
-public class DerivedUnitFacade {\r
-       private static final String METER = "m";\r
-\r
-       @SuppressWarnings("unused")\r
-       private static final Logger logger = Logger.getLogger(DerivedUnitFacade.class);\r
-\r
-       private static final String notSupportMessage = "A specimen facade not supported exception has occurred at a place where this should not have happened. The developer should implement not support check properly during class initialization ";\r
-\r
-       private static final boolean CREATE = true;\r
-       private static final boolean CREATE_NOT = false;\r
-\r
-       private final DerivedUnitFacadeConfigurator config;\r
-\r
-       private final Map<PropertyChangeListener, CdmBase> listeners = new HashMap<PropertyChangeListener, CdmBase>();\r
-\r
-       // Either fieldUnit or derivedUnit must not be null.\r
-       private FieldUnit fieldUnit;\r
-       private final DerivedUnit derivedUnit;\r
-\r
-       // media - the text data holding the media\r
-       private TextData derivedUnitMediaTextData;\r
-       private TextData fieldObjectMediaTextData;\r
-\r
-       private TextData ecology;\r
-       private TextData plantDescription;\r
-\r
-       /**\r
-        * Creates a derived unit facade for a new derived unit of type\r
-        * <code>type</code>.\r
-        *\r
-        * @param type\r
-        * @return\r
-        */\r
-       public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type) {\r
-               return new DerivedUnitFacade(type, null, null);\r
-       }\r
-\r
-       /**\r
-        * Creates a derived unit facade for a new derived unit of type\r
-        * <code>type</code>.\r
-        *\r
-        * @param type\r
-        * @return\r
-        */\r
-       public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type, FieldUnit fieldUnit) {\r
-               return new DerivedUnitFacade(type, fieldUnit, null);\r
-       }\r
-\r
-       /**\r
-        * Creates a derived unit facade for a new derived unit of type\r
-        * <code>type</code>.\r
-        *\r
-        * @param type\r
-        * @param fieldUnit the field unit to use\r
-        * @param config the facade configurator to use\r
-        * //TODO are there any ambiguities to solve with defining a field unit or a configurator\r
-        * @return\r
-        */\r
-       public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type, FieldUnit fieldUnit, DerivedUnitFacadeConfigurator config) {\r
-               return new DerivedUnitFacade(type, fieldUnit, config);\r
-       }\r
-\r
-\r
-       /**\r
-        * Creates a derived unit facade for a given derived unit using the default\r
-        * configuration.\r
-        *\r
-        * @param derivedUnit\r
-        * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               return new DerivedUnitFacade(derivedUnit, null);\r
-       }\r
-\r
-       public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit,\r
-                       DerivedUnitFacadeConfigurator config)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               return new DerivedUnitFacade(derivedUnit, config);\r
-       }\r
-\r
-       // ****************** CONSTRUCTOR ******************************************\r
-\r
-       private DerivedUnitFacade(SpecimenOrObservationType type, FieldUnit fieldUnit, DerivedUnitFacadeConfigurator config) {\r
-               if (config == null){\r
-                       config = DerivedUnitFacadeConfigurator.NewInstance();\r
-               }\r
-               this.config = config;\r
-               // derivedUnit\r
-               derivedUnit = getNewDerivedUnitInstance(type);\r
-               //TODO parameter checking should be solved in a more generic way if we start using other entity facades\r
-               if(derivedUnit==null && fieldUnit==null && type.isFieldUnit()){\r
-                   this.fieldUnit = getFieldUnit(CREATE);\r
-               }\r
-               setFieldUnit(fieldUnit);\r
-               if (derivedUnit != null){\r
-                       setCacheStrategy();\r
-               }else{\r
-                       setFieldUnitCacheStrategy();\r
-               }\r
-       }\r
-\r
-       private DerivedUnit getNewDerivedUnitInstance(SpecimenOrObservationType type) {\r
-               if (type.isFieldUnit()){\r
-                       return null;\r
-               }else if(type.isAnyDerivedUnit()){\r
-                       return DerivedUnit.NewInstance(type);\r
-               } else {\r
-                       String message = "Unknown specimen or observation type %s";\r
-                       message = String.format(message, type.getMessage());\r
-                       throw new IllegalStateException(message);\r
-               }\r
-       }\r
-\r
-       private DerivedUnitFacade(DerivedUnit derivedUnit, DerivedUnitFacadeConfigurator config)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-\r
-           if(derivedUnit==null){\r
-               throw new IllegalArgumentException("DerivedUnit must not be null");\r
-           }\r
-\r
-               if (config == null) {\r
-                       config = DerivedUnitFacadeConfigurator.NewInstance();\r
-               }\r
-               this.config = config;\r
-\r
-               // derived unit\r
-               this.derivedUnit = derivedUnit;\r
-\r
-               // derivation event\r
-               if (this.derivedUnit.getDerivedFrom() != null) {\r
-                       DerivationEvent derivationEvent = getDerivationEvent(CREATE);\r
-                       // fieldUnit\r
-                       Set<FieldUnit> fieldOriginals = getFieldUnitOriginals(derivationEvent, null);\r
-                       if (fieldOriginals.size() > 1) {\r
-                               throw new DerivedUnitFacadeNotSupportedException(\r
-                                               "Specimen must not have more than 1 derivation event");\r
-                       } else if (fieldOriginals.size() == 0) {\r
-                               // fieldUnit = FieldUnit.NewInstance();\r
-                       } else if (fieldOriginals.size() == 1) {\r
-                               fieldUnit = fieldOriginals.iterator().next();\r
-                               // ###fieldUnit =\r
-                               // getInitializedFieldUnit(fieldUnit);\r
-                               if (config.isFirePropertyChangeEvents()){\r
-                                       addNewEventPropagationListener(fieldUnit);\r
-                               }\r
-                       } else {\r
-                               throw new IllegalStateException("Illegal state");\r
-                       }\r
-               }\r
-\r
-               this.derivedUnitMediaTextData = inititializeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);\r
-\r
-               fieldObjectMediaTextData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), false, true);\r
-\r
-\r
-//direct media have been removed from specimenorobservationbase #3597\r
-//             // handle derivedUnit.getMedia()\r
-//             if (derivedUnit.getMedia().size() > 0) {\r
-//                     // TODO better changed model here to allow only one place for images\r
-//                     if (this.config.isMoveDerivedUnitMediaToGallery()) {\r
-//                             Set<Media> mediaSet = derivedUnit.getMedia();\r
-//                             for (Media media : mediaSet) {\r
-//                                     this.addDerivedUnitMedia(media);\r
-//                             }\r
-//                             mediaSet.removeAll(getDerivedUnitMedia());\r
-//                     } else {\r
-//                             throw new DerivedUnitFacadeNotSupportedException(\r
-//                                             "Specimen may not have direct media. Only (one) image gallery is allowed");\r
-//                     }\r
-//             }\r
-//\r
-//             // handle fieldUnit.getMedia()\r
-//             if (fieldUnit != null && fieldUnit.getMedia() != null\r
-//                             && fieldUnit.getMedia().size() > 0) {\r
-//                     // TODO better changed model here to allow only one place for images\r
-//                     if (this.config.isMoveFieldObjectMediaToGallery()) {\r
-//                             Set<Media> mediaSet = fieldUnit.getMedia();\r
-//                             for (Media media : mediaSet) {\r
-//                                     this.addFieldObjectMedia(media);\r
-//                             }\r
-//                             mediaSet.removeAll(getFieldObjectMedia());\r
-//                     } else {\r
-//                             throw new DerivedUnitFacadeNotSupportedException(\r
-//                                             "Field object may not have direct media. Only (one) image gallery is allowed");\r
-//                     }\r
-//             }\r
-\r
-               // test if descriptions are supported\r
-               ecology = initializeFieldObjectTextDataWithSupportTest(\r
-                               Feature.ECOLOGY(), false, false);\r
-               plantDescription = initializeFieldObjectTextDataWithSupportTest(\r
-                               Feature.DESCRIPTION(), false, false);\r
-\r
-               setCacheStrategy();\r
-\r
-       }\r
-\r
-       private DerivedUnit getInitializedDerivedUnit(\r
-                       DerivedUnit derivedUnit) {\r
-               IOccurrenceService occurrenceService = this.config\r
-                               .getOccurrenceService();\r
-               if (occurrenceService == null) {\r
-                       return derivedUnit;\r
-               }\r
-               List<String> propertyPaths = this.config.getPropertyPaths();\r
-               if (propertyPaths == null) {\r
-                       return derivedUnit;\r
-               }\r
-               propertyPaths = getDerivedUnitPropertyPaths(propertyPaths);\r
-               DerivedUnit result = (DerivedUnit) occurrenceService.load(\r
-                               derivedUnit.getUuid(), propertyPaths);\r
-               return result;\r
-       }\r
-\r
-       /**\r
-        * Initializes the derived unit according to the configuartions property\r
-        * path. If the property path is <code>null</code> or no occurrence service\r
-        * is given the returned object is the same as the input parameter.\r
-        *\r
-        * @param fieldUnit\r
-        * @return\r
-        */\r
-       private FieldUnit getInitializedFieldUnit(FieldUnit fieldUnit) {\r
-               IOccurrenceService occurrenceService = this.config\r
-                               .getOccurrenceService();\r
-               if (occurrenceService == null) {\r
-                       return fieldUnit;\r
-               }\r
-               List<String> propertyPaths = this.config.getPropertyPaths();\r
-               if (propertyPaths == null) {\r
-                       return fieldUnit;\r
-               }\r
-               propertyPaths = getFieldObjectPropertyPaths(propertyPaths);\r
-               FieldUnit result = (FieldUnit) occurrenceService.load(\r
-                               fieldUnit.getUuid(), propertyPaths);\r
-               return result;\r
-       }\r
-\r
-       /**\r
-        * Transforms the property paths in a way that the facade is handled just\r
-        * like an ordinary CdmBase object.<BR>\r
-        * E.g. a property path "collectinAreas" will be translated into\r
-        * gatheringEvent.collectingAreas\r
-        *\r
-        * @param propertyPaths\r
-        * @return\r
-        */\r
-       private List<String> getFieldObjectPropertyPaths(List<String> propertyPaths) {\r
-               List<String> result = new ArrayList<String>();\r
-               for (String facadePath : propertyPaths) {\r
-                       // collecting areas (named area)\r
-                       if (facadePath.startsWith("collectingAreas")) {\r
-                               facadePath = "gatheringEvent." + facadePath;\r
-                               result.add(facadePath);\r
-                       }\r
-                       // collector (agentBase)\r
-                       else if (facadePath.startsWith("collector")) {\r
-                               facadePath = facadePath.replace("collector",\r
-                                               "gatheringEvent.actor");\r
-                               result.add(facadePath);\r
-                       }\r
-                       // exactLocation (agentBase)\r
-                       else if (facadePath.startsWith("exactLocation")) {\r
-                               facadePath = "gatheringEvent." + facadePath;\r
-                               result.add(facadePath);\r
-                       }\r
-                       // gatheringPeriod (TimePeriod)\r
-                       else if (facadePath.startsWith("gatheringPeriod")) {\r
-                               facadePath = facadePath.replace("gatheringPeriod",\r
-                                               "gatheringEvent.timeperiod");\r
-                               result.add(facadePath);\r
-                       }\r
-                       // (locality/ localityLanguage , LanguageString)\r
-                       else if (facadePath.startsWith("locality")) {\r
-                               facadePath = "gatheringEvent." + facadePath;\r
-                               result.add(facadePath);\r
-                       }\r
-\r
-                       // *********** FIELD OBJECT ************\r
-                       // fieldObjectDefinitions (Map<language, languageString)\r
-                       else if (facadePath.startsWith("fieldObjectDefinitions")) {\r
-                               // TODO or definition ???\r
-                               facadePath = facadePath.replace("fieldObjectDefinitions",\r
-                                               "description");\r
-                               result.add(facadePath);\r
-                       }\r
-                       // fieldObjectMedia (Media)\r
-                       else if (facadePath.startsWith("fieldObjectMedia")) {\r
-                               // TODO ???\r
-                               facadePath = facadePath.replace("fieldObjectMedia",\r
-                                               "descriptions.elements.media");\r
-                               result.add(facadePath);\r
-                       }\r
-\r
-                       // Gathering Event will always be added\r
-                       result.add("gatheringEvent");\r
-\r
-               }\r
-\r
-               /*\r
-                * Gathering Event ==================== - gatheringEvent\r
-                * (GatheringEvent)\r
-                *\r
-                * Field Object ================= - ecology/ ecologyAll (String) ??? -\r
-                * plant description (like ecology)\r
-                *\r
-                * - fieldObjectImageGallery (SpecimenDescription) - is automatically\r
-                * initialized via fieldObjectMedia\r
-                */\r
-\r
-               return result;\r
-       }\r
-\r
-       /**\r
-        * Transforms the property paths in a way that the facade is handled just\r
-        * like an ordinary CdmBase object.<BR>\r
-        * E.g. a property path "collectinAreas" will be translated into\r
-        * gatheringEvent.collectingAreas\r
-        *\r
-        * Not needed (?) as the facade works with REST service property paths\r
-        * without using this method.\r
-        *\r
-        * @param propertyPaths\r
-        * @return\r
-        */\r
-       private List<String> getDerivedUnitPropertyPaths(List<String> propertyPaths) {\r
-               List<String> result = new ArrayList<String>();\r
-               for (String facadePath : propertyPaths) {\r
-                       // determinations (DeterminationEvent)\r
-                       if (facadePath.startsWith("determinations")) {\r
-                               facadePath = "" + facadePath; // no change\r
-                               result.add(facadePath);\r
-                       }\r
-                       // storedUnder (TaxonNameBase)\r
-                       else if (facadePath.startsWith("storedUnder")) {\r
-                               facadePath = "" + facadePath; // no change\r
-                               result.add(facadePath);\r
-                       }\r
-                       // sources (IdentifiableSource)\r
-                       else if (facadePath.startsWith("sources")) {\r
-                               facadePath = "" + facadePath; // no change\r
-                               result.add(facadePath);\r
-                       }\r
-                       // collection (Collection)\r
-                       else if (facadePath.startsWith("collection")) {\r
-                               facadePath = "" + facadePath; // no change\r
-                               result.add(facadePath);\r
-                       }\r
-                       // (locality/ localityLanguage , LanguageString)\r
-                       else if (facadePath.startsWith("locality")) {\r
-                               facadePath = "gatheringEvent." + facadePath;\r
-                               result.add(facadePath);\r
-                       }\r
-\r
-                       // *********** FIELD OBJECT ************\r
-                       // derivedUnitDefinitions (Map<language, languageString)\r
-                       else if (facadePath.startsWith("derivedUnitDefinitions")) {\r
-                               // TODO or definition ???\r
-                               facadePath = facadePath.replace("derivedUnitDefinitions",\r
-                                               "description");\r
-                               result.add(facadePath);\r
-                       }\r
-\r
-                       // derivedUnitMedia (Media)\r
-                       else if (facadePath.startsWith("derivedUnitMedia")) {\r
-                               // TODO ???\r
-                               facadePath = facadePath.replace("derivedUnitMedia",\r
-                                               "descriptions.elements.media");\r
-                               result.add(facadePath);\r
-                       }\r
-\r
-               }\r
-\r
-               /*\r
-                * //TODO Derived Unit =====================\r
-                *\r
-                * - derivedUnitImageGallery (SpecimenDescription) - is automatically\r
-                * initialized via derivedUnitMedia\r
-                *\r
-                * - derivationEvent (DerivationEvent) - will always be initialized -\r
-                * duplicates (??? Specimen???) ???\r
-                */\r
-\r
-               return result;\r
-       }\r
-\r
-       /**\r
-        *\r
-        */\r
-       private void setCacheStrategy() {\r
-               if (derivedUnit == null) {\r
-                       throw new NullPointerException(\r
-                                       "Facade's derviedUnit must not be null to set cache strategy");\r
-               }else{\r
-                       derivedUnit.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());\r
-                       setFieldUnitCacheStrategy();\r
-               }\r
-       }\r
-\r
-       private void setFieldUnitCacheStrategy() {\r
-               if (this.hasFieldObject()){\r
-                       DerivedUnitFacadeFieldUnitCacheStrategy strategy = new DerivedUnitFacadeFieldUnitCacheStrategy();\r
-                       this.fieldUnit.setCacheStrategy(strategy);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * @param feature\r
-        * @param createIfNotExists\r
-        * @param isImageGallery\r
-        * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private TextData initializeFieldObjectTextDataWithSupportTest(\r
-                       Feature feature, boolean createIfNotExists, boolean isImageGallery)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               // field object\r
-               FieldUnit fieldObject = getFieldUnit(createIfNotExists);\r
-               if (fieldObject == null) {\r
-                       return null;\r
-               }\r
-               return inititializeTextDataWithSupportTest(feature, fieldObject,\r
-                               createIfNotExists, isImageGallery);\r
-       }\r
-\r
-       /**\r
-        * @param feature\r
-        * @param specimen\r
-        * @param createIfNotExists\r
-        * @param isImageGallery\r
-        * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private TextData inititializeTextDataWithSupportTest(Feature feature,\r
-                       SpecimenOrObservationBase specimen, boolean createIfNotExists,\r
-                       boolean isImageGallery)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               if (feature == null) {\r
-                       return null;\r
-               }\r
-               TextData textData = null;\r
-               if (createIfNotExists) {\r
-                       textData = TextData.NewInstance(feature);\r
-               }\r
-\r
-               Set<SpecimenDescription> descriptions;\r
-               if (isImageGallery) {\r
-                       descriptions = specimen.getSpecimenDescriptionImageGallery();\r
-               } else {\r
-                       descriptions = specimen.getSpecimenDescriptions(false);\r
-               }\r
-               // no description exists yet for this specimen\r
-               if (descriptions.size() == 0) {\r
-                       if (createIfNotExists) {\r
-                               SpecimenDescription newSpecimenDescription = SpecimenDescription\r
-                                               .NewInstance(specimen);\r
-                               newSpecimenDescription.addElement(textData);\r
-                               newSpecimenDescription.setImageGallery(isImageGallery);\r
-                               return textData;\r
-                       } else {\r
-                               return null;\r
-                       }\r
-               }\r
-               // description already exists\r
-               Set<DescriptionElementBase> existingTextData = new HashSet<DescriptionElementBase>();\r
-               for (SpecimenDescription description : descriptions) {\r
-                       // collect all existing text data\r
-                       for (DescriptionElementBase element : description.getElements()) {\r
-                               if (element.isInstanceOf(TextData.class)\r
-                                               && (feature.equals(element.getFeature()) || isImageGallery)) {\r
-                                       existingTextData.add(element);\r
-                               }\r
-                       }\r
-               }\r
-               // use existing text data if exactly one exists\r
-               if (existingTextData.size() > 1) {\r
-                       throw new DerivedUnitFacadeNotSupportedException(\r
-                                       "Specimen facade does not support more than one description text data of type "\r
-                                                       + feature.getLabel());\r
-\r
-               } else if (existingTextData.size() == 1) {\r
-                       return CdmBase.deproxy(existingTextData.iterator().next(),\r
-                                       TextData.class);\r
-               } else {\r
-                       if (createIfNotExists) {\r
-                               SpecimenDescription description = descriptions.iterator()\r
-                                               .next();\r
-                               description.addElement(textData);\r
-                       }\r
-                       return textData;\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Tests if a given image gallery is supported by the derived unit facade.\r
-        * It returns the only text data attached to the given image gallery. If the\r
-        * given image gallery does not have text data attached, it is created and\r
-        * attached.\r
-        *\r
-        * @param imageGallery\r
-        * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private TextData testImageGallery(SpecimenDescription imageGallery)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               if (imageGallery.isImageGallery() == false) {\r
-                       throw new DerivedUnitFacadeNotSupportedException(\r
-                                       "Image gallery needs to have image gallery flag set");\r
-               }\r
-               if (imageGallery.getElements().size() > 1) {\r
-                       throw new DerivedUnitFacadeNotSupportedException(\r
-                                       "Image gallery must not have more then one description element");\r
-               }\r
-               TextData textData;\r
-               if (imageGallery.getElements().size() == 0) {\r
-                       textData = TextData.NewInstance(Feature.IMAGE());\r
-                       imageGallery.addElement(textData);\r
-               } else {\r
-                       if (!imageGallery.getElements().iterator().next()\r
-                                       .isInstanceOf(TextData.class)) {\r
-                               throw new DerivedUnitFacadeNotSupportedException(\r
-                                               "Image gallery must only have TextData as element");\r
-                       } else {\r
-                               textData = CdmBase.deproxy(imageGallery.getElements()\r
-                                               .iterator().next(), TextData.class);\r
-                       }\r
-               }\r
-               return textData;\r
-       }\r
-\r
-       // ************************** METHODS\r
-       // *****************************************\r
-\r
-       private TextData getDerivedUnitImageGalleryTextData(\r
-                       boolean createIfNotExists)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               if (this.derivedUnitMediaTextData == null && createIfNotExists) {\r
-                       this.derivedUnitMediaTextData = getImageGalleryTextData(\r
-                                       derivedUnit, "Specimen");\r
-               }\r
-               return this.derivedUnitMediaTextData;\r
-       }\r
-\r
-       private TextData getObservationImageGalleryTextData(\r
-                       boolean createIfNotExists)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               if (this.fieldObjectMediaTextData == null && createIfNotExists) {\r
-                       this.fieldObjectMediaTextData = getImageGalleryTextData(fieldUnit, "Field unit");\r
-               }\r
-               return this.fieldObjectMediaTextData;\r
-       }\r
-\r
-       /**\r
-        * @param derivationEvent\r
-        * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private Set<FieldUnit> getFieldUnitOriginals(\r
-                       DerivationEvent derivationEvent,\r
-                       Set<SpecimenOrObservationBase> recursionAvoidSet)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               if (recursionAvoidSet == null) {\r
-                       recursionAvoidSet = new HashSet<SpecimenOrObservationBase>();\r
-               }\r
-               Set<FieldUnit> result = new HashSet<FieldUnit>();\r
-               Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();\r
-               for (SpecimenOrObservationBase original : originals) {\r
-                       if (original.isInstanceOf(FieldUnit.class)) {\r
-                               result.add(CdmBase.deproxy(original, FieldUnit.class));\r
-                       } else if (original.isInstanceOf(DerivedUnit.class)) {\r
-                               // if specimen has already been tested exclude it from further\r
-                               // recursion\r
-                               if (recursionAvoidSet.contains(original)) {\r
-                                       continue;\r
-                               }\r
-                               DerivedUnit derivedUnit = CdmBase.deproxy(original,     DerivedUnit.class);\r
-                               DerivationEvent originalDerivation = derivedUnit.getDerivedFrom();\r
-                               // Set<DerivationEvent> derivationEvents =\r
-                               // original.getDerivationEvents();\r
-                               // for (DerivationEvent originalDerivation : derivationEvents){\r
-                               if(originalDerivation!=null){\r
-                                   Set<FieldUnit> fieldUnits = getFieldUnitOriginals(\r
-                                           originalDerivation, recursionAvoidSet);\r
-                                   result.addAll(fieldUnits);\r
-                               }\r
-                               // }\r
-                       } else {\r
-                               throw new DerivedUnitFacadeNotSupportedException(\r
-                                               "Unhandled specimen or observation base type: "\r
-                                                               + original.getClass().getName());\r
-                       }\r
-\r
-               }\r
-               return result;\r
-       }\r
-\r
-       // *********** MEDIA METHODS ******************************\r
-\r
-       // /**\r
-       // * Returns the media list for a specimen. Throws an exception if the\r
-       // existing specimen descriptions\r
-       // * are not supported by this facade.\r
-       // * @param specimen the specimen the media belongs to\r
-       // * @param specimenExceptionText text describing the specimen for exception\r
-       // messages\r
-       // * @return\r
-       // * @throws DerivedUnitFacadeNotSupportedException\r
-       // */\r
-       // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase\r
-       // specimen, String specimenExceptionText) throws\r
-       // DerivedUnitFacadeNotSupportedException{\r
-       // List<Media> result;\r
-       // SpecimenDescription imageGallery =\r
-       // getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);\r
-       // TextData textData = getImageTextDataWithSupportTest(imageGallery,\r
-       // specimenExceptionText);\r
-       // result = textData.getMedia();\r
-       // return result;\r
-       // }\r
-\r
-       /**\r
-        * Returns the media list for a specimen. Throws an exception if the\r
-        * existing specimen descriptions are not supported by this facade.\r
-        *\r
-        * @param specimen\r
-        *            the specimen the media belongs to\r
-        * @param specimenExceptionText\r
-        *            text describing the specimen for exception messages\r
-        * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private TextData getImageGalleryTextData(SpecimenOrObservationBase specimen, String specimenExceptionText)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               TextData result;\r
-               SpecimenDescription imageGallery = getImageGalleryWithSupportTest(\r
-                               specimen, specimenExceptionText, true);\r
-               result = getImageTextDataWithSupportTest(imageGallery,\r
-                               specimenExceptionText);\r
-               return result;\r
-       }\r
-\r
-       /**\r
-        * Returns the image gallery of the according specimen. Throws an exception\r
-        * if the attached image gallerie(s) are not supported by this facade. If no\r
-        * image gallery exists a new one is created if\r
-        * <code>createNewIfNotExists</code> is true and if specimen is not\r
-        * <code>null</code>.\r
-        *\r
-        * @param specimen\r
-        * @param specimenText\r
-        * @param createNewIfNotExists\r
-        * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private SpecimenDescription getImageGalleryWithSupportTest(\r
-                       SpecimenOrObservationBase<?> specimen, String specimenText,\r
-                       boolean createNewIfNotExists)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               if (specimen == null) {\r
-                       return null;\r
-               }\r
-               SpecimenDescription imageGallery;\r
-               if (hasMultipleImageGalleries(specimen)) {\r
-                       throw new DerivedUnitFacadeNotSupportedException(specimenText\r
-                                       + " must not have more than 1 image gallery");\r
-               } else {\r
-                       imageGallery = getImageGallery(specimen, createNewIfNotExists);\r
-                       getImageTextDataWithSupportTest(imageGallery, specimenText);\r
-               }\r
-               return imageGallery;\r
-       }\r
-\r
-       /**\r
-        * Returns the media holding text data element of the image gallery. Throws\r
-        * an exception if multiple such text data already exist. Creates a new text\r
-        * data if none exists and adds it to the image gallery. If image gallery is\r
-        * <code>null</code> nothing happens.\r
-        *\r
-        * @param imageGallery\r
-        * @param textData\r
-        * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private TextData getImageTextDataWithSupportTest(\r
-                       SpecimenDescription imageGallery, String specimenText)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               if (imageGallery == null) {\r
-                       return null;\r
-               }\r
-               TextData textData = null;\r
-               for (DescriptionElementBase element : imageGallery.getElements()) {\r
-                       if (element.isInstanceOf(TextData.class)\r
-                                       && element.getFeature().equals(Feature.IMAGE())) {\r
-                               if (textData != null) {\r
-                                       throw new DerivedUnitFacadeNotSupportedException(\r
-                                                       specimenText\r
-                                                                       + " must not have more than 1 image text data element in image gallery");\r
-                               }\r
-                               textData = CdmBase.deproxy(element, TextData.class);\r
-                       }\r
-               }\r
-               if (textData == null) {\r
-                       textData = TextData.NewInstance(Feature.IMAGE());\r
-                       imageGallery.addElement(textData);\r
-               }\r
-               return textData;\r
-       }\r
-\r
-       /**\r
-        * Checks, if a specimen belongs to more than one description that is an\r
-        * image gallery\r
-        *\r
-        * @param derivedUnit\r
-        * @return\r
-        */\r
-       private boolean hasMultipleImageGalleries(\r
-                       SpecimenOrObservationBase<?> derivedUnit) {\r
-               int count = 0;\r
-               Set<SpecimenDescription> descriptions = derivedUnit\r
-                               .getSpecimenDescriptions();\r
-               for (SpecimenDescription description : descriptions) {\r
-                       if (description.isImageGallery()) {\r
-                               count++;\r
-                       }\r
-               }\r
-               return (count > 1);\r
-       }\r
-\r
-       /**\r
-        * Returns the image gallery for a specimen. If there are multiple specimen\r
-        * descriptions marked as image galleries an arbitrary one is chosen. If no\r
-        * image gallery exists, a new one is created if\r
-        * <code>createNewIfNotExists</code> is <code>true</code>.<Br>\r
-        * If specimen is <code>null</code> a null pointer exception is thrown.\r
-        *\r
-        * @param createNewIfNotExists\r
-        * @return\r
-        */\r
-       private SpecimenDescription getImageGallery(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) {\r
-               SpecimenDescription result = null;\r
-               Set<SpecimenDescription> descriptions = specimen.getSpecimenDescriptions();\r
-               for (SpecimenDescription description : descriptions) {\r
-                       if (description.isImageGallery()) {\r
-                               result = description;\r
-                               break;\r
-                       }\r
-               }\r
-               if (result == null && createIfNotExists) {\r
-                       result = SpecimenDescription.NewInstance(specimen);\r
-                       result.setImageGallery(true);\r
-               }\r
-               return result;\r
-       }\r
-\r
-       /**\r
-        * Adds a media to the specimens image gallery. If media is\r
-        * <code>null</code> nothing happens.\r
-        *\r
-        * @param media\r
-        * @param specimen\r
-        * @return true if media is not null (as specified by\r
-        *         {@link java.util.Collection#add(Object) Collection.add(E e)}\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private boolean addMedia(Media media, SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {\r
-               if (media != null) {\r
-                       List<Media> mediaList = getMediaList(specimen, true);\r
-                       if (! mediaList.contains(media)){\r
-                               return mediaList.add(media);\r
-                       }else{\r
-                               return true;\r
-                       }\r
-               } else {\r
-                       return false;\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Removes a media from the specimens image gallery.\r
-        *\r
-        * @param media\r
-        * @param specimen\r
-        * @return true if an element was removed as a result of this call (as\r
-        *         specified by {@link java.util.Collection#remove(Object)\r
-        *         Collection.remove(E e)}\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private boolean removeMedia(Media media,\r
-                       SpecimenOrObservationBase<?> specimen)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               List<Media> mediaList = getMediaList(specimen, true);\r
-               return mediaList == null ? null : mediaList.remove(media);\r
-       }\r
-\r
-       private List<Media> getMediaList(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               TextData textData = getMediaTextData(specimen, createIfNotExists);\r
-               return textData == null ? null : textData.getMedia();\r
-       }\r
-\r
-       /**\r
-        * Returns the one media list of a specimen which is part of the only image\r
-        * gallery that this specimen is part of.<BR>\r
-        * If these conditions are not hold an exception is thrwon.\r
-        *\r
-        * @param specimen\r
-        * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)\r
-       // throws DerivedUnitFacadeNotSupportedException {\r
-       // if (specimen == null){\r
-       // return null;\r
-       // }\r
-       // if (specimen == this.derivedUnit){\r
-       // return getDerivedUnitImageGalleryMedia();\r
-       // }else if (specimen == this.fieldUnit){\r
-       // return getObservationImageGalleryTextData();\r
-       // }else{\r
-       // return getImageGalleryMedia(specimen, "Undefined specimen ");\r
-       // }\r
-       // }\r
-\r
-       /**\r
-        * Returns the one media list of a specimen which is part of the only image\r
-        * gallery that this specimen is part of.<BR>\r
-        * If these conditions are not hold an exception is thrown.\r
-        *\r
-        * @param specimen\r
-        * @return\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private TextData getMediaTextData(SpecimenOrObservationBase<?> specimen,\r
-                       boolean createIfNotExists)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               if (specimen == null) {\r
-                       return null;\r
-               }\r
-               if (specimen == this.derivedUnit) {\r
-                       return getDerivedUnitImageGalleryTextData(createIfNotExists);\r
-               } else if (specimen == this.fieldUnit) {\r
-                       return getObservationImageGalleryTextData(createIfNotExists);\r
-               } else {\r
-                       return getImageGalleryTextData(specimen, "Undefined specimen ");\r
-               }\r
-       }\r
-\r
-       // ****************** GETTER / SETTER / ADDER / REMOVER\r
-       // ***********************/\r
-\r
-       // ****************** Gathering Event *********************************/\r
-\r
-       // country\r
-       @Transient\r
-       public NamedArea getCountry() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getCountry()\r
-                               : null);\r
-       }\r
-\r
-       public void setCountry(NamedArea country) {\r
-               getGatheringEvent(true).setCountry(country);\r
-       }\r
-\r
-       // Collecting area\r
-       public void addCollectingArea(NamedArea area) {\r
-               getGatheringEvent(true).addCollectingArea(area);\r
-       }\r
-\r
-       public void addCollectingAreas(java.util.Collection<NamedArea> areas) {\r
-               for (NamedArea area : areas) {\r
-                       getGatheringEvent(true).addCollectingArea(area);\r
-               }\r
-       }\r
-\r
-       @Transient\r
-       public Set<NamedArea> getCollectingAreas() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true)\r
-                               .getCollectingAreas() : null);\r
-       }\r
-\r
-       public void removeCollectingArea(NamedArea area) {\r
-               if (hasGatheringEvent()) {\r
-                       getGatheringEvent(true).removeCollectingArea(area);\r
-               }\r
-       }\r
-\r
-       static final String ALTITUDE_POSTFIX = " m";\r
-\r
-       /**\r
-        * Returns the correctly formatted <code>absolute elevation</code> information.\r
-        * If absoluteElevationText is set, this will be returned,\r
-        * otherwise we absoluteElevation will be returned, followed by absoluteElevationMax\r
-        * if existing, separated by " - "\r
-        * @return\r
-        */\r
-       @Transient\r
-       public String absoluteElevationToString() {\r
-               if (! hasGatheringEvent()){\r
-                       return null;\r
-               }else{\r
-                       GatheringEvent ev = getGatheringEvent(true);\r
-                       if (StringUtils.isNotBlank(ev.getAbsoluteElevationText())){\r
-                               return ev.getAbsoluteElevationText();\r
-                       }else{\r
-                               String text = ev.getAbsoluteElevationText();\r
-                               Integer min = getAbsoluteElevation();\r
-                               Integer max = getAbsoluteElevationMaximum();\r
-                               return distanceString(min, max, text, METER);\r
-                       }\r
-               }\r
-       }\r
-\r
-\r
-       /**\r
-        * meter above/below sea level of the surface\r
-        *\r
-        * @see #getAbsoluteElevationError()\r
-        * @see #getAbsoluteElevationRange()\r
-        **/\r
-       @Transient\r
-       public Integer getAbsoluteElevation() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevation() : null);\r
-       }\r
-\r
-       public void setAbsoluteElevation(Integer absoluteElevation) {\r
-               getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);\r
-       }\r
-\r
-       public void setAbsoluteElevationMax(Integer absoluteElevationMax) {\r
-               getGatheringEvent(true).setAbsoluteElevationMax(absoluteElevationMax);\r
-       }\r
-\r
-       public void setAbsoluteElevationText(String absoluteElevationText) {\r
-               getGatheringEvent(true).setAbsoluteElevationText(absoluteElevationText);\r
-       }\r
-\r
-       /**\r
-        * @see #getAbsoluteElevation()\r
-        * @see #getAbsoluteElevationError()\r
-        * @see #setAbsoluteElevationRange(Integer, Integer)\r
-        * @see #getAbsoluteElevationMinimum()\r
-        */\r
-       @Transient\r
-       public Integer getAbsoluteElevationMaximum() {\r
-               if (!hasGatheringEvent()) {\r
-                       return null;\r
-               }else{\r
-                       return getGatheringEvent(true).getAbsoluteElevationMax();\r
-               }\r
-       }\r
-\r
-       /**\r
-        * @see #getAbsoluteElevation()\r
-        * @see #getAbsoluteElevationError()\r
-        * @see #setAbsoluteElevationRange(Integer, Integer)\r
-        * @see #getAbsoluteElevationMinimum()\r
-        */\r
-       @Transient\r
-       public String getAbsoluteElevationText() {\r
-               if (!hasGatheringEvent()) {\r
-                       return null;\r
-               }else{\r
-                       return getGatheringEvent(true).getAbsoluteElevationText();\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Convenience method to set absolute elevation minimum and maximum.\r
-        *\r
-        * @see #setAbsoluteElevation(Integer)\r
-        * @see #setAbsoluteElevationMax(Integer)\r
-        * @param minimumElevation minimum of the range\r
-        * @param maximumElevation maximum of the range\r
-        */\r
-       public void setAbsoluteElevationRange(Integer minimumElevation, Integer maximumElevation) {\r
-               getGatheringEvent(true).setAbsoluteElevation(minimumElevation);\r
-               getGatheringEvent(true).setAbsoluteElevationMax(maximumElevation);\r
-       }\r
-\r
-       // collector\r
-       @Transient\r
-       public AgentBase getCollector() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getCollector()\r
-                               : null);\r
-       }\r
-\r
-       public void setCollector(AgentBase collector) {\r
-               getGatheringEvent(true).setCollector(collector);\r
-       }\r
-\r
-       // collecting method\r
-       @Transient\r
-       public String getCollectingMethod() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getCollectingMethod() : null);\r
-       }\r
-\r
-       public void setCollectingMethod(String collectingMethod) {\r
-               getGatheringEvent(true).setCollectingMethod(collectingMethod);\r
-       }\r
-\r
-       // distance to ground\r
-\r
-       /**\r
-        * Returns the correctly formatted <code>distance to ground</code> information.\r
-        * If distanceToGroundText is not blank, it will be returned,\r
-        * otherwise distanceToGround will be returned, followed by distanceToGroundMax\r
-        * if existing, separated by " - "\r
-        * @return\r
-        */\r
-       @Transient\r
-       public String distanceToGroundToString() {\r
-               if (! hasGatheringEvent()){\r
-                       return null;\r
-               }else{\r
-                       GatheringEvent ev = getGatheringEvent(true);\r
-                       String text = ev.getDistanceToGroundText();\r
-                       Double min = getDistanceToGround();\r
-                       Double max = getDistanceToGroundMax();\r
-                       return distanceString(min, max, text, METER);\r
-               }\r
-       }\r
-\r
-       @Transient\r
-       public Double getDistanceToGround() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToGround() : null);\r
-       }\r
-\r
-       public void setDistanceToGround(Double distanceToGround) {\r
-               getGatheringEvent(true).setDistanceToGround(distanceToGround);\r
-       }\r
-\r
-       /**\r
-        * @see #getDistanceToGround()\r
-        * @see #getDistanceToGroundRange(Integer, Integer)\r
-        */\r
-       @Transient\r
-       public Double getDistanceToGroundMax() {\r
-               if (!hasGatheringEvent()) {\r
-                       return null;\r
-               }else{\r
-                       return getGatheringEvent(true).getDistanceToGroundMax();\r
-               }\r
-       }\r
-\r
-       public void setDistanceToGroundMax(Double distanceToGroundMax) {\r
-               getGatheringEvent(true).setDistanceToGroundMax(distanceToGroundMax);\r
-       }\r
-\r
-       /**\r
-        * @see #getDistanceToGround()\r
-        * @see #setDistanceToGroundRange(Integer, Integer)\r
-        */\r
-       @Transient\r
-       public String getDistanceToGroundText() {\r
-               if (!hasGatheringEvent()) {\r
-                       return null;\r
-               }else{\r
-                       return getGatheringEvent(true).getDistanceToGroundText();\r
-               }\r
-       }\r
-       public void setDistanceToGroundText(String distanceToGroundText) {\r
-               getGatheringEvent(true).setDistanceToGroundText(distanceToGroundText);\r
-       }\r
-\r
-       /**\r
-        * Convenience method to set distance to ground minimum and maximum.\r
-        *\r
-        * @see #getDistanceToGround()\r
-        * @see #getDistanceToGroundMax()\r
-        * @param minimumDistance minimum of the range\r
-        * @param maximumDistance maximum of the range\r
-        */\r
-       public void setDistanceToGroundRange(Double minimumDistance, Double maximumDistance) throws IllegalArgumentException{\r
-               getGatheringEvent(true).setDistanceToGround(minimumDistance);\r
-               getGatheringEvent(true).setDistanceToGroundMax(maximumDistance);\r
-       }\r
-\r
-\r
-       /**\r
-        * Returns the correctly formatted <code>distance to water surface</code> information.\r
-        * If distanceToWaterSurfaceText is not blank, it will be returned,\r
-        * otherwise distanceToWaterSurface will be returned, followed by distanceToWatersurfaceMax\r
-        * if existing, separated by " - "\r
-        * @return\r
-        */\r
-       @Transient\r
-       public String distanceToWaterSurfaceToString() {\r
-               if (! hasGatheringEvent()){\r
-                       return null;\r
-               }else{\r
-                       GatheringEvent ev = getGatheringEvent(true);\r
-                       String text = ev.getDistanceToWaterSurfaceText();\r
-                       Double min = getDistanceToWaterSurface();\r
-                       Double max = getDistanceToWaterSurfaceMax();\r
-                       return distanceString(min, max, text, METER);\r
-               }\r
-       }\r
-\r
-       // distance to water surface\r
-       @Transient\r
-       public Double getDistanceToWaterSurface() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToWaterSurface() : null);\r
-       }\r
-\r
-       public void setDistanceToWaterSurface(Double distanceToWaterSurface) {\r
-               getGatheringEvent(true).setDistanceToWaterSurface(distanceToWaterSurface);\r
-       }\r
-\r
-       /**\r
-        * @see #getDistanceToWaterSurface()\r
-        * @see #getDistanceToWaterSurfaceRange(Double, Double)\r
-        */\r
-       @Transient\r
-       public Double getDistanceToWaterSurfaceMax() {\r
-               if (!hasGatheringEvent()) {\r
-                       return null;\r
-               }else{\r
-                       return getGatheringEvent(true).getDistanceToWaterSurfaceMax();\r
-               }\r
-       }\r
-\r
-       public void setDistanceToWaterSurfaceMax(Double distanceToWaterSurfaceMax) {\r
-               getGatheringEvent(true).setDistanceToWaterSurfaceMax(distanceToWaterSurfaceMax);\r
-       }\r
-\r
-       /**\r
-        * @see #getDistanceToWaterSurface()\r
-        * @see #getDistanceToWaterSurfaceRange(Double, Double)\r
-        */\r
-       @Transient\r
-       public String getDistanceToWaterSurfaceText() {\r
-               if (!hasGatheringEvent()) {\r
-                       return null;\r
-               }else{\r
-                       return getGatheringEvent(true).getDistanceToWaterSurfaceText();\r
-               }\r
-       }\r
-       public void setDistanceToWaterSurfaceText(String distanceToWaterSurfaceText) {\r
-               getGatheringEvent(true).setDistanceToWaterSurfaceText(distanceToWaterSurfaceText);\r
-       }\r
-\r
-       /**\r
-        * Convenience method to set distance to ground minimum and maximum.\r
-        *\r
-        * @see #getDistanceToWaterSurface()\r
-        * @see #getDistanceToWaterSurfaceMax()\r
-        * @param minimumDistance minimum of the range, this is the distance which is closer to the water surface\r
-        * @param maximumDistance maximum of the range, this is the distance which is farer to the water surface\r
-        */\r
-       public void setDistanceToWaterSurfaceRange(Double minimumDistance, Double maximumDistance) throws IllegalArgumentException{\r
-               getGatheringEvent(true).setDistanceToWaterSurface(minimumDistance);\r
-               getGatheringEvent(true).setDistanceToWaterSurfaceMax(maximumDistance);\r
-       }\r
-\r
-\r
-       // exact location\r
-       @Transient\r
-       public Point getExactLocation() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getExactLocation() : null);\r
-       }\r
-\r
-       /**\r
-        * Returns a sexagesimal representation of the exact location (e.g.\r
-        * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty\r
-        * string is returned.\r
-        *\r
-        * @param includeEmptySeconds\r
-        * @param includeReferenceSystem\r
-        * @return\r
-        */\r
-       public String getExactLocationText(boolean includeEmptySeconds,\r
-                       boolean includeReferenceSystem) {\r
-               return (this.getExactLocation() == null ? "" : this.getExactLocation()\r
-                               .toSexagesimalString(includeEmptySeconds,\r
-                                               includeReferenceSystem));\r
-       }\r
-\r
-       public void setExactLocation(Point exactLocation) {\r
-               getGatheringEvent(true).setExactLocation(exactLocation);\r
-       }\r
-\r
-       public void setExactLocationByParsing(String longitudeToParse,\r
-                       String latitudeToParse, ReferenceSystem referenceSystem,\r
-                       Integer errorRadius) throws ParseException {\r
-               Point point = Point.NewInstance(null, null, referenceSystem,\r
-                               errorRadius);\r
-               point.setLongitudeByParsing(longitudeToParse);\r
-               point.setLatitudeByParsing(latitudeToParse);\r
-               setExactLocation(point);\r
-       }\r
-\r
-       // gathering event description\r
-       @Transient\r
-       public String getGatheringEventDescription() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getDescription()\r
-                               : null);\r
-       }\r
-\r
-       public void setGatheringEventDescription(String description) {\r
-               getGatheringEvent(true).setDescription(description);\r
-       }\r
-\r
-       // gatering period\r
-       @Transient\r
-       public TimePeriod getGatheringPeriod() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getTimeperiod()\r
-                               : null);\r
-       }\r
-\r
-       public void setGatheringPeriod(TimePeriod timeperiod) {\r
-               getGatheringEvent(true).setTimeperiod(timeperiod);\r
-       }\r
-\r
-       // locality\r
-       @Transient\r
-       public LanguageString getLocality() {\r
-               return (hasGatheringEvent() ? getGatheringEvent(true).getLocality()\r
-                               : null);\r
-       }\r
-\r
-       /**\r
-        * convienience method for {@link #getLocality()}.\r
-        * {@link LanguageString#getText() getText()}\r
-        *\r
-        * @return\r
-        */\r
-       @Transient\r
-       public String getLocalityText() {\r
-               LanguageString locality = getLocality();\r
-               if (locality != null) {\r
-                       return locality.getText();\r
-               }\r
-               return null;\r
-       }\r
-\r
-       /**\r
-        * convienience method for {@link #getLocality()}.\r
-        * {@link LanguageString#getLanguage() getLanguage()}\r
-        *\r
-        * @return\r
-        */\r
-       @Transient\r
-       public Language getLocalityLanguage() {\r
-               LanguageString locality = getLocality();\r
-               if (locality != null) {\r
-                       return locality.getLanguage();\r
-               }\r
-               return null;\r
-       }\r
-\r
-       /**\r
-        * Sets the locality string in the default language\r
-        *\r
-        * @param locality\r
-        */\r
-       public void setLocality(String locality) {\r
-               Language language = Language.DEFAULT();\r
-               setLocality(locality, language);\r
-       }\r
-\r
-       public void setLocality(String locality, Language language) {\r
-               LanguageString langString = LanguageString.NewInstance(locality, language);\r
-               setLocality(langString);\r
-       }\r
-\r
-       public void setLocality(LanguageString locality) {\r
-               getGatheringEvent(true).setLocality(locality);\r
-       }\r
-\r
-       /**\r
-        * The gathering event will be used for the field object instead of the old\r
-        * gathering event.<BR>\r
-        * <B>This method will override all gathering values (see below).</B>\r
-        *\r
-        * @see #getAbsoluteElevation()\r
-        * @see #getAbsoluteElevationError()\r
-        * @see #getDistanceToGround()\r
-        * @see #getDistanceToWaterSurface()\r
-        * @see #getExactLocation()\r
-        * @see #getGatheringEventDescription()\r
-        * @see #getGatheringPeriod()\r
-        * @see #getCollectingAreas()\r
-        * @see #getCollectingMethod()\r
-        * @see #getLocality()\r
-        * @see #getCollector()\r
-        * @param gatheringEvent\r
-        */\r
-       public void setGatheringEvent(GatheringEvent gatheringEvent) {\r
-               getFieldUnit(true).setGatheringEvent(gatheringEvent);\r
-       }\r
-\r
-       public boolean hasGatheringEvent() {\r
-               return (getGatheringEvent(false) != null);\r
-       }\r
-\r
-       public GatheringEvent innerGatheringEvent() {\r
-               return getGatheringEvent(false);\r
-       }\r
-\r
-       public GatheringEvent getGatheringEvent(boolean createIfNotExists) {\r
-               if (!hasFieldUnit() && !createIfNotExists) {\r
-                       return null;\r
-               }\r
-               if (createIfNotExists && getFieldUnit(true).getGatheringEvent() == null) {\r
-                       GatheringEvent gatheringEvent = GatheringEvent.NewInstance();\r
-                       getFieldUnit(true).setGatheringEvent(gatheringEvent);\r
-               }\r
-               return getFieldUnit(true).getGatheringEvent();\r
-       }\r
-\r
-       // ****************** Field Object ************************************/\r
-\r
-       /**\r
-        * Returns true if a field unit exists (even if all attributes are\r
-        * empty or <code>null<code>.\r
-        *\r
-        * @return\r
-        */\r
-       public boolean hasFieldObject() {\r
-               return this.fieldUnit != null;\r
-       }\r
-\r
-       // ecology\r
-       @Transient\r
-       public String getEcology() {\r
-               return getEcology(Language.DEFAULT());\r
-       }\r
-\r
-       public String getEcology(Language language) {\r
-               LanguageString languageString = getEcologyAll().get(language);\r
-               return (languageString == null ? null : languageString.getText());\r
-       }\r
-\r
-       // public String getEcologyPreferred(List<Language> languages){\r
-       // LanguageString languageString =\r
-       // getEcologyAll().getPreferredLanguageString(languages);\r
-       // return languageString.getText();\r
-       // }\r
-       /**\r
-        * Returns a copy of the multilanguage text holding the ecology data.\r
-        *\r
-        * @see {@link TextData#getMultilanguageText()}\r
-        * @return\r
-        */\r
-       @Transient\r
-       public Map<Language, LanguageString> getEcologyAll() {\r
-               if (ecology == null) {\r
-                       try {\r
-                               ecology = initializeFieldObjectTextDataWithSupportTest(\r
-                                               Feature.ECOLOGY(), false, false);\r
-                       } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                               throw new IllegalStateException(notSupportMessage, e);\r
-                       }\r
-                       if (ecology == null) {\r
-                               return new HashMap<Language, LanguageString>();\r
-                       }\r
-               }\r
-               return ecology.getMultilanguageText();\r
-       }\r
-\r
-       public void setEcology(String ecology) {\r
-               setEcology(ecology, null);\r
-       }\r
-\r
-       public void setEcology(String ecologyText, Language language) {\r
-               if (language == null) {\r
-                       language = Language.DEFAULT();\r
-               }\r
-               boolean isEmpty = StringUtils.isBlank(ecologyText);\r
-               if (ecology == null) {\r
-                       try {\r
-                               ecology = initializeFieldObjectTextDataWithSupportTest(\r
-                                               Feature.ECOLOGY(), !isEmpty, false);\r
-                       } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                               throw new IllegalStateException(notSupportMessage, e);\r
-                       }\r
-               }\r
-               if (ecology != null){\r
-                       if (ecologyText == null) {\r
-                               ecology.removeText(language);\r
-                       } else {\r
-                               ecology.putText(language, ecologyText);\r
-                       }\r
-               }\r
-       }\r
-\r
-       public void removeEcology(Language language) {\r
-               setEcology(null, language);\r
-       }\r
-\r
-       /**\r
-        * Removes ecology for the default language\r
-        */\r
-       public void removeEcology() {\r
-               setEcology(null, null);\r
-       }\r
-\r
-       public void removeEcologyAll() {\r
-\r
-       }\r
-\r
-       // plant description\r
-       @Transient\r
-       public String getPlantDescription() {\r
-               return getPlantDescription(null);\r
-       }\r
-\r
-       public String getPlantDescription(Language language) {\r
-               if (language == null) {\r
-                       language = Language.DEFAULT();\r
-               }\r
-               LanguageString languageString = getPlantDescriptionAll().get(language);\r
-               return (languageString == null ? null : languageString.getText());\r
-       }\r
-\r
-       // public String getPlantDescriptionPreferred(List<Language> languages){\r
-       // LanguageString languageString =\r
-       // getPlantDescriptionAll().getPreferredLanguageString(languages);\r
-       // return languageString.getText();\r
-       // }\r
-       /**\r
-        * Returns a copy of the multilanguage text holding the description data.\r
-        *\r
-        * @see {@link TextData#getMultilanguageText()}\r
-        * @return\r
-        */\r
-       @Transient\r
-       public Map<Language, LanguageString> getPlantDescriptionAll() {\r
-               if (plantDescription == null) {\r
-                       try {\r
-                               plantDescription = initializeFieldObjectTextDataWithSupportTest(\r
-                                               Feature.DESCRIPTION(), false, false);\r
-                       } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                               throw new IllegalStateException(notSupportMessage, e);\r
-                       }\r
-                       if (plantDescription == null) {\r
-                               return new HashMap<Language, LanguageString>();\r
-                       }\r
-               }\r
-               return plantDescription.getMultilanguageText();\r
-       }\r
-\r
-       public void setPlantDescription(String plantDescription) {\r
-               setPlantDescription(plantDescription, null);\r
-       }\r
-\r
-       public void setPlantDescription(String plantDescriptionText, Language language) {\r
-               if (language == null) {\r
-                       language = Language.DEFAULT();\r
-               }\r
-               boolean isEmpty = StringUtils.isBlank(plantDescriptionText);\r
-               if (plantDescription == null) {\r
-                       try {\r
-                               plantDescription = initializeFieldObjectTextDataWithSupportTest(\r
-                                               Feature.DESCRIPTION(), !isEmpty, false);\r
-                       } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                               throw new IllegalStateException(notSupportMessage, e);\r
-                       }\r
-               }\r
-               if (plantDescription != null){\r
-                       if (plantDescriptionText == null) {\r
-                               plantDescription.removeText(language);\r
-                       } else {\r
-                               plantDescription.putText(language, plantDescriptionText);\r
-                       }\r
-               }\r
-       }\r
-\r
-       public void removePlantDescription(Language language) {\r
-               setPlantDescription(null, language);\r
-       }\r
-\r
-       // field object definition\r
-       public void addFieldObjectDefinition(String text, Language language) {\r
-               getFieldUnit(true).putDefinition(language, text);\r
-       }\r
-\r
-       @Transient\r
-       public Map<Language, LanguageString> getFieldObjectDefinition() {\r
-               if (!hasFieldUnit()) {\r
-                       return new HashMap<Language, LanguageString>();\r
-               } else {\r
-                       return getFieldUnit(true).getDefinition();\r
-               }\r
-       }\r
-\r
-       public String getFieldObjectDefinition(Language language) {\r
-               Map<Language, LanguageString> map = getFieldObjectDefinition();\r
-               LanguageString languageString = (map == null ? null : map.get(language));\r
-               if (languageString != null) {\r
-                       return languageString.getText();\r
-               } else {\r
-                       return null;\r
-               }\r
-       }\r
-\r
-       public void removeFieldObjectDefinition(Language lang) {\r
-               if (hasFieldUnit()) {\r
-                       getFieldUnit(true).removeDefinition(lang);\r
-               }\r
-       }\r
-\r
-       // media\r
-       public boolean addFieldObjectMedia(Media media) {\r
-               try {\r
-                       return addMedia(media, getFieldUnit(true));\r
-               } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                       throw new IllegalStateException(notSupportMessage, e);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Returns true, if an image gallery for the field object exists.<BR>\r
-        * Returns also <code>true</code> if the image gallery is empty.\r
-        *\r
-        * @return\r
-        */\r
-       public boolean hasFieldObjectImageGallery() {\r
-               if (!hasFieldObject()) {\r
-                       return false;\r
-               } else {\r
-                       return (getImageGallery(fieldUnit, false) != null);\r
-               }\r
-       }\r
-\r
-       public void setFieldObjectImageGallery(SpecimenDescription imageGallery)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               SpecimenDescription existingGallery = getFieldObjectImageGallery(false);\r
-\r
-               // test attached specimens contain this.derivedUnit\r
-               SpecimenOrObservationBase<?> facadeFieldUnit = innerFieldUnit();\r
-               testSpecimenInImageGallery(imageGallery, facadeFieldUnit);\r
-\r
-               if (existingGallery != null) {\r
-                       if (existingGallery != imageGallery) {\r
-                               throw new DerivedUnitFacadeNotSupportedException(\r
-                                               "DerivedUnitFacade does not allow more than one image gallery");\r
-                       } else {\r
-                               // do nothing\r
-                       }\r
-               } else {\r
-                       TextData textData = testImageGallery(imageGallery);\r
-                       this.fieldObjectMediaTextData = textData;\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Returns the field object image gallery. If no such image gallery exists\r
-        * and createIfNotExists is true an new one is created. Otherwise null is\r
-        * returned.\r
-        *\r
-        * @param createIfNotExists\r
-        * @return\r
-        */\r
-       public SpecimenDescription getFieldObjectImageGallery(\r
-                       boolean createIfNotExists) {\r
-               TextData textData;\r
-               try {\r
-                       textData = initializeFieldObjectTextDataWithSupportTest(\r
-                                       Feature.IMAGE(), createIfNotExists, true);\r
-               } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                       throw new IllegalStateException(notSupportMessage, e);\r
-               }\r
-               if (textData != null) {\r
-                       return CdmBase.deproxy(textData.getInDescription(),\r
-                                       SpecimenDescription.class);\r
-               } else {\r
-                       return null;\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Returns the media for the field object.<BR>\r
-        *\r
-        * @return\r
-        */\r
-       @Transient\r
-       public List<Media> getFieldObjectMedia() {\r
-               try {\r
-                       List<Media> result = getMediaList(getFieldUnit(false), false);\r
-                       return result == null ? new ArrayList<Media>() : result;\r
-               } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                       throw new IllegalStateException(notSupportMessage, e);\r
-               }\r
-       }\r
-\r
-       public boolean removeFieldObjectMedia(Media media) {\r
-               try {\r
-                       return removeMedia(media, getFieldUnit(false));\r
-               } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                       throw new IllegalStateException(notSupportMessage, e);\r
-               }\r
-       }\r
-\r
-       // field number\r
-       @Transient\r
-       public String getFieldNumber() {\r
-               if (!hasFieldUnit()) {\r
-                       return null;\r
-               } else {\r
-                       return getFieldUnit(true).getFieldNumber();\r
-               }\r
-       }\r
-\r
-       public void setFieldNumber(String fieldNumber) {\r
-               getFieldUnit(true).setFieldNumber(fieldNumber);\r
-       }\r
-\r
-       // primary collector\r
-       @Transient\r
-       public Person getPrimaryCollector() {\r
-               if (!hasFieldUnit()) {\r
-                       return null;\r
-               } else {\r
-                       return getFieldUnit(true).getPrimaryCollector();\r
-               }\r
-       }\r
-\r
-       public void setPrimaryCollector(Person primaryCollector) {\r
-               getFieldUnit(true).setPrimaryCollector(primaryCollector);\r
-       }\r
-\r
-       // field notes\r
-       @Transient\r
-       public String getFieldNotes() {\r
-               if (!hasFieldUnit()) {\r
-                       return null;\r
-               } else {\r
-                       return getFieldUnit(true).getFieldNotes();\r
-               }\r
-       }\r
-\r
-       public void setFieldNotes(String fieldNotes) {\r
-               getFieldUnit(true).setFieldNotes(fieldNotes);\r
-       }\r
-\r
-       // individual counts\r
-       @Transient\r
-       public Integer getIndividualCount() {\r
-               return (hasFieldUnit() ? getFieldUnit(true).getIndividualCount() : null);\r
-       }\r
-\r
-       public void setIndividualCount(Integer individualCount) {\r
-               getFieldUnit(true).setIndividualCount(individualCount);\r
-       }\r
-\r
-       // life stage\r
-       @Transient\r
-       public DefinedTerm getLifeStage() {\r
-               return (hasFieldUnit() ? getFieldUnit(true).getLifeStage() : null);\r
-       }\r
-\r
-       public void setLifeStage(DefinedTerm lifeStage) {\r
-               getFieldUnit(true).setLifeStage(lifeStage);\r
-       }\r
-\r
-       // sex\r
-       @Transient\r
-       public DefinedTerm getSex() {\r
-               return (hasFieldUnit() ? getFieldUnit(true).getSex(): null);\r
-       }\r
-\r
-       public void setSex(DefinedTerm sex) {\r
-               getFieldUnit(true).setSex(sex);\r
-       }\r
-\r
-       // kind of Unit\r
-       @Transient\r
-       public DefinedTerm getKindOfUnit() {\r
-               return (hasFieldUnit() ? getFieldUnit(true).getKindOfUnit() : null);\r
-       }\r
-\r
-       public void setKindOfUnit(DefinedTerm kindOfUnit) {\r
-               getFieldUnit(true).setKindOfUnit(kindOfUnit);\r
-       }\r
-\r
-\r
-       // field unit\r
-       public boolean hasFieldUnit() {\r
-               return (getFieldUnit(CREATE_NOT) != null);\r
-       }\r
-\r
-       /**\r
-        * Returns the field unit as an object.\r
-        *\r
-        * @return\r
-        */\r
-       public FieldUnit innerFieldUnit() {\r
-               return getFieldUnit(CREATE_NOT);\r
-       }\r
-\r
-       /**\r
-        * Returns the field unit as an object.\r
-        *\r
-        * @return\r
-        */\r
-       public FieldUnit getFieldUnit(boolean createIfNotExists) {\r
-               if (fieldUnit == null && createIfNotExists) {\r
-                       setFieldUnit(FieldUnit.NewInstance());\r
-               }\r
-               return this.fieldUnit;\r
-       }\r
-\r
-\r
-       private void setFieldUnit(FieldUnit fieldUnit) {\r
-               this.fieldUnit = fieldUnit;\r
-               if (fieldUnit != null){\r
-                       if (config.isFirePropertyChangeEvents()){\r
-                               addNewEventPropagationListener(fieldUnit);\r
-                       }\r
-                       if (derivedUnit != null){\r
-                               DerivationEvent derivationEvent = getDerivationEvent(CREATE);\r
-                               derivationEvent.addOriginal(fieldUnit);\r
-                       }\r
-                       setFieldUnitCacheStrategy();\r
-               }\r
-       }\r
-\r
-       // ****************** Specimen *******************************************\r
-\r
-       // Definition\r
-       public void addDerivedUnitDefinition(String text, Language language) {\r
-               innerDerivedUnit().putDefinition(language, text);\r
-       }\r
-\r
-       @Transient\r
-       public Map<Language, LanguageString> getDerivedUnitDefinitions() {\r
-               return ! checkDerivedUnit()? null : this.derivedUnit.getDefinition();\r
-       }\r
-\r
-\r
-       public String getDerivedUnitDefinition(Language language) {\r
-               if (! checkDerivedUnit()){\r
-                       return null;\r
-               }\r
-               Map<Language, LanguageString> languageMap = derivedUnit.getDefinition();\r
-               LanguageString languageString = languageMap.get(language);\r
-               if (languageString != null) {\r
-                       return languageString.getText();\r
-               } else {\r
-                       return null;\r
-               }\r
-       }\r
-\r
-       public void removeDerivedUnitDefinition(Language lang) {\r
-               testDerivedUnit();\r
-               derivedUnit.removeDefinition(lang);\r
-       }\r
-\r
-       // Determination\r
-       public void addDetermination(DeterminationEvent determination) {\r
-               //TODO implement correct bidirectional mapping in model classes\r
-               determination.setIdentifiedUnit(baseUnit());\r
-               baseUnit().addDetermination(determination);\r
-       }\r
-\r
-       @Transient\r
-       public DeterminationEvent getPreferredDetermination() {\r
-               Set<DeterminationEvent> events = baseUnit().getDeterminations();\r
-               for (DeterminationEvent event : events){\r
-                       if (event.getPreferredFlag() == true){\r
-                               return event;\r
-                       }\r
-               }\r
-               return null;\r
-       }\r
-\r
-       /**\r
-        * This method returns the preferred determination.\r
-        * @see #getOtherDeterminations()\r
-        * @see #getDeterminations()\r
-        * @return\r
-        */\r
-       @Transient\r
-       public void setPreferredDetermination(DeterminationEvent newEvent) {\r
-               Set<DeterminationEvent> events = baseUnit().getDeterminations();\r
-               for (DeterminationEvent event : events){\r
-                       if (event.getPreferredFlag() == true){\r
-                               event.setPreferredFlag(false);\r
-                       }\r
-               }\r
-               newEvent.setPreferredFlag(true);\r
-               events.add(newEvent);\r
-       }\r
-\r
-       /**\r
-        * This method returns all determinations except for the preferred one.\r
-        * @see #getPreferredDetermination()\r
-        * @see #getDeterminations()\r
-        * @return\r
-        */\r
-       @Transient\r
-       public Set<DeterminationEvent> getOtherDeterminations() {\r
-               Set<DeterminationEvent> events = baseUnit().getDeterminations();\r
-               Set<DeterminationEvent> result = new HashSet<DeterminationEvent>();\r
-               for (DeterminationEvent event : events){\r
-                       if (event.getPreferredFlag() != true){\r
-                               result.add(event);\r
-                       }\r
-               }\r
-               return result;\r
-       }\r
-\r
-       /**\r
-        * This method returns all determination events. The preferred one {@link #getPreferredDetermination()}\r
-        * and all others {@link #getOtherDeterminations()}.\r
-        * @return\r
-        */\r
-       @Transient\r
-       public Set<DeterminationEvent> getDeterminations() {\r
-               return baseUnit().getDeterminations();\r
-       }\r
-\r
-       public void removeDetermination(DeterminationEvent determination) {\r
-               baseUnit().removeDetermination(determination);\r
-       }\r
-\r
-       // Media\r
-       public boolean addDerivedUnitMedia(Media media) {\r
-               testDerivedUnit();\r
-               try {\r
-                       return addMedia(media, derivedUnit);\r
-               } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                       throw new IllegalStateException(notSupportMessage, e);\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Returns true, if an image gallery exists for the specimen.<BR>\r
-        * Returns also <code>true</code> if the image gallery is empty.\r
-        */\r
-       public boolean hasDerivedUnitImageGallery() {\r
-               return (getImageGallery(derivedUnit, false) != null);\r
-       }\r
-\r
-       public SpecimenDescription getDerivedUnitImageGallery(boolean createIfNotExists) {\r
-               if (!checkDerivedUnit()){\r
-                       return null;\r
-               }\r
-               TextData textData;\r
-               try {\r
-                       textData = inititializeTextDataWithSupportTest(Feature.IMAGE(),\r
-                                       derivedUnit, createIfNotExists, true);\r
-               } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                       throw new IllegalStateException(notSupportMessage, e);\r
-               }\r
-               if (textData != null) {\r
-                       return CdmBase.deproxy(textData.getInDescription(),\r
-                                       SpecimenDescription.class);\r
-               } else {\r
-                       return null;\r
-               }\r
-       }\r
-\r
-       public void setDerivedUnitImageGallery(SpecimenDescription imageGallery)\r
-                       throws DerivedUnitFacadeNotSupportedException {\r
-               testDerivedUnit();\r
-               SpecimenDescription existingGallery = getDerivedUnitImageGallery(false);\r
-\r
-               // test attached specimens contain this.derivedUnit\r
-               SpecimenOrObservationBase facadeDerivedUnit = innerDerivedUnit();\r
-               testSpecimenInImageGallery(imageGallery, facadeDerivedUnit);\r
-\r
-               if (existingGallery != null) {\r
-                       if (existingGallery != imageGallery) {\r
-                               throw new DerivedUnitFacadeNotSupportedException(\r
-                                               "DerivedUnitFacade does not allow more than one image gallery");\r
-                       } else {\r
-                               // do nothing\r
-                       }\r
-               } else {\r
-                       TextData textData = testImageGallery(imageGallery);\r
-                       this.derivedUnitMediaTextData = textData;\r
-               }\r
-       }\r
-\r
-       /**\r
-        * @param imageGallery\r
-        * @throws DerivedUnitFacadeNotSupportedException\r
-        */\r
-       private void testSpecimenInImageGallery(SpecimenDescription imageGallery, SpecimenOrObservationBase specimen)\r
-                               throws DerivedUnitFacadeNotSupportedException {\r
-               SpecimenOrObservationBase imageGallerySpecimen = imageGallery.getDescribedSpecimenOrObservation();\r
-               if (imageGallerySpecimen == null) {\r
-                       throw new DerivedUnitFacadeNotSupportedException(\r
-                                       "Image Gallery has no Specimen attached. Please attache according specimen or field unit.");\r
-               }\r
-               if (! imageGallerySpecimen.equals(specimen)) {\r
-                       throw new DerivedUnitFacadeNotSupportedException(\r
-                                       "Image Gallery has not the facade's field object attached. Please add field object first " +\r
-                                       "to image gallery specimenOrObservation list.");\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Returns the media for the specimen.<BR>\r
-        *\r
-        * @return\r
-        */\r
-       @Transient\r
-       public List<Media> getDerivedUnitMedia() {\r
-               if (! checkDerivedUnit()){\r
-                       return new ArrayList<Media>();\r
-               }\r
-               try {\r
-                       List<Media> result = getMediaList(derivedUnit, false);\r
-                       return result == null ? new ArrayList<Media>() : result;\r
-               } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                       throw new IllegalStateException(notSupportMessage, e);\r
-               }\r
-       }\r
-\r
-       public boolean removeDerivedUnitMedia(Media media) {\r
-               testDerivedUnit();\r
-               try {\r
-                       return removeMedia(media, derivedUnit);\r
-               } catch (DerivedUnitFacadeNotSupportedException e) {\r
-                       throw new IllegalStateException(notSupportMessage, e);\r
-               }\r
-       }\r
-\r
-       // Accession Number\r
-       @Transient\r
-       public String getAccessionNumber() {\r
-               return ! checkDerivedUnit()? null : derivedUnit.getAccessionNumber();\r
-       }\r
-\r
-       public void setAccessionNumber(String accessionNumber) {\r
-               testDerivedUnit();\r
-               derivedUnit.setAccessionNumber(accessionNumber);\r
-       }\r
-\r
-       @Transient\r
-       public String getCatalogNumber() {\r
-               return ! checkDerivedUnit()? null : derivedUnit.getCatalogNumber();\r
-       }\r
-\r
-       public void setCatalogNumber(String catalogNumber) {\r
-               testDerivedUnit();\r
-               derivedUnit.setCatalogNumber(catalogNumber);\r
-       }\r
-\r
-       @Transient\r
-       public String getBarcode() {\r
-               return ! checkDerivedUnit()? null : derivedUnit.getBarcode();\r
-       }\r
-\r
-       public void setBarcode(String barcode) {\r
-               testDerivedUnit();\r
-               derivedUnit.setBarcode(barcode);\r
-       }\r
-\r
-       // Preservation Method\r
-\r
-       /**\r
-        * Only supported by specimen and fossils\r
-        *\r
-        * @see #DerivedUnitType\r
-        * @return\r
-        */\r
-       @Transient\r
-       public PreservationMethod getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException {\r
-               if (derivedUnit!=null && derivedUnit.getRecordBasis().isPreservedSpecimen()) {\r
-                       return CdmBase.deproxy(derivedUnit, DerivedUnit.class).getPreservation();\r
-               } else {\r
-                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {\r
-                               throw new MethodNotSupportedByDerivedUnitTypeException(\r
-                                               "A preservation method is only available in derived units of type 'Preserved Specimen' or one of its specializations like 'Fossil Specimen' ");\r
-                       } else {\r
-                               return null;\r
-                       }\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Only supported by specimen and fossils\r
-        *\r
-        * @see #DerivedUnitType\r
-        * @return\r
-        */\r
-       public void setPreservationMethod(PreservationMethod preservation)\r
-                       throws MethodNotSupportedByDerivedUnitTypeException {\r
-               if (derivedUnit!=null && derivedUnit.getRecordBasis().isPreservedSpecimen()) {\r
-                       CdmBase.deproxy(derivedUnit, DerivedUnit.class).setPreservation(preservation);\r
-               } else {\r
-                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {\r
-                               throw new MethodNotSupportedByDerivedUnitTypeException(\r
-                                               "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");\r
-                       } else {\r
-                               return;\r
-                       }\r
-               }\r
-       }\r
-\r
-       // Stored under name\r
-       @Transient\r
-       public TaxonNameBase getStoredUnder() {\r
-               return ! checkDerivedUnit()? null : derivedUnit.getStoredUnder();\r
-       }\r
-\r
-       public void setStoredUnder(TaxonNameBase storedUnder) {\r
-               testDerivedUnit();\r
-               derivedUnit.setStoredUnder(storedUnder);\r
-       }\r
-\r
-       // title cache\r
-       public String getTitleCache() {\r
-               SpecimenOrObservationBase<?> titledUnit = baseUnit();\r
-\r
-               if (!titledUnit.isProtectedTitleCache()) {\r
-                       // always compute title cache anew as long as there are no property\r
-                       // change listeners on\r
-                       // field unit, gathering event etc\r
-                       titledUnit.setTitleCache(null, false);\r
-               }\r
-               return titledUnit.getTitleCache();\r
-       }\r
-\r
-       public boolean isProtectedTitleCache() {\r
-               return baseUnit().isProtectedTitleCache();\r
-       }\r
-\r
-       public void setTitleCache(String titleCache, boolean isProtected) {\r
-               this.baseUnit().setTitleCache(titleCache, isProtected);\r
-       }\r
-\r
-       /**\r
-        * Returns the derived unit itself.\r
-        *\r
-        * @return the derived unit\r
-        */\r
-       public DerivedUnit innerDerivedUnit() {\r
-               return this.derivedUnit;\r
-       }\r
-\r
-//     /**\r
-//      * Returns the derived unit itself.\r
-//      *\r
-//      * @return the derived unit\r
-//      */\r
-//     public DerivedUnit innerDerivedUnit(boolean createIfNotExists) {\r
-//             DerivedUnit result = this.derivedUnit;\r
-//             if (result == null && createIfNotExists){\r
-//                     if (this.fieldUnit == null){\r
-//                             String message = "Field unit must exist to create derived unit.";\r
-//                             throw new IllegalStateException(message);\r
-//                     }else{\r
-//                             DerivedUnit =\r
-//                             DerivationEvent derivationEvent = getDerivationEvent(true);\r
-//                             derivationEvent.addOriginal(fieldUnit);\r
-//                             return this.derivedUnit;\r
-//                     }\r
-//             }\r
-//     }\r
-\r
-       private boolean hasDerivationEvent() {\r
-               return getDerivationEvent() == null ? false : true;\r
-       }\r
-\r
-       private DerivationEvent getDerivationEvent() {\r
-               return getDerivationEvent(CREATE_NOT);\r
-       }\r
-\r
-       /**\r
-        * Returns the derivation event. If no derivation event exists and <code>createIfNotExists</code>\r
-        * is <code>true</code> a new derivation event is created and returned.\r
-        * Otherwise <code>null</code> is returned.\r
-        * @param createIfNotExists\r
-        */\r
-       private DerivationEvent getDerivationEvent(boolean createIfNotExists) {\r
-               DerivationEvent result = null;\r
-               if (derivedUnit != null){\r
-                       result = derivedUnit.getDerivedFrom();\r
-               }else{\r
-                       return null;\r
-               }\r
-               if (result == null && createIfNotExists) {\r
-                       DerivationEventType type = null;\r
-                       if (isAccessioned(derivedUnit)){\r
-                               type = DerivationEventType.ACCESSIONING();\r
-                       }\r
-\r
-                       result = DerivationEvent.NewInstance(type);\r
-                       derivedUnit.setDerivedFrom(result);\r
-               }\r
-               return result;\r
-       }\r
-\r
-       /**\r
-        * TODO still unclear which classes do definetly require accessioning.\r
-        * Only return true for those classes which are clear.\r
-        * @param derivedUnit\r
-        * @return\r
-        */\r
-       private boolean isAccessioned(DerivedUnit derivedUnit) {\r
-               if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen) ){\r
-                       return true;   //maybe also subtypes should be true\r
-               }else{\r
-                       return false;\r
-               }\r
-       }\r
-\r
-       @Transient\r
-       public String getExsiccatum() throws MethodNotSupportedByDerivedUnitTypeException {\r
-               testDerivedUnit();\r
-               if (derivedUnit.getRecordBasis().isPreservedSpecimen()) {\r
-                       return derivedUnit.getExsiccatum();\r
-               } else {\r
-                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {\r
-                               throw new MethodNotSupportedByDerivedUnitTypeException(\r
-                                               "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");\r
-                       } else {\r
-                               return null;\r
-                       }\r
-               }\r
-       }\r
-\r
-       public void setExsiccatum(String exsiccatum) throws Exception {\r
-               testDerivedUnit();\r
-               if (derivedUnit.getRecordBasis().isPreservedSpecimen()) {\r
-                       derivedUnit.setExsiccatum(exsiccatum);\r
-               } else {\r
-                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {\r
-                               throw new MethodNotSupportedByDerivedUnitTypeException(\r
-                                               "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");\r
-                       } else {\r
-                               return;\r
-                       }\r
-               }\r
-       }\r
-\r
-       /**\r
-        * Returns the original label information of the derived unit.\r
-        * @return\r
-        */\r
-       @Transient\r
-       public String getOriginalLabelInfo() {\r
-               return ! checkDerivedUnit()? null : derivedUnit.getOriginalLabelInfo();\r
-       }\r
-       public void setOriginalLabelInfo(String originalLabelInfo) {\r
-               testDerivedUnit();\r
-               derivedUnit.setOriginalLabelInfo(originalLabelInfo);\r
-       }\r
-\r
-       // **** sources **/\r
-       public void addSource(IdentifiableSource source) {\r
-               this.baseUnit().addSource(source);\r
-       }\r
-\r
-       /**\r
-        * Creates an {@link IOriginalSource orignal source} or type ,\r
-        * adds it to the specimen and returns it.\r
-        *\r
-        * @param reference\r
-        * @param microReference\r
-        * @param originalNameString\r
-        * @return\r
-        */\r
-       public IdentifiableSource addSource(OriginalSourceType type, Reference reference, String microReference, String originalNameString) {\r
-               IdentifiableSource source = IdentifiableSource.NewInstance(type, null, null, reference, microReference);\r
-               source.setOriginalNameString(originalNameString);\r
-               addSource(source);\r
-               return source;\r
-       }\r
-\r
-       @Transient\r
-       public Set<IdentifiableSource> getSources() {\r
-               return baseUnit().getSources();\r
-       }\r
-\r
-       public void removeSource(IdentifiableSource source) {\r
-               this.baseUnit().removeSource(source);\r
-       }\r
-\r
-       //*** identifiers ***/\r
-\r
-\r
-    public void addIdentifier(Identifier identifier) {\r
-        this.baseUnit().addIdentifier(identifier);\r
-    }\r
-\r
-       @Transient\r
-       public List<Identifier> getIdentifiers() {\r
-           return baseUnit().getIdentifiers();\r
-       }\r
-\r
-       public void removeIdentifier(Identifier identifier) {\r
-           this.baseUnit().removeIdentifier(identifier);\r
-       }\r
-\r
-       @Transient\r
-       public Set<Rights> getRights() {\r
-               return baseUnit().getRights();\r
-       }\r
-\r
-       /**\r
-        * @return the collection\r
-        */\r
-       @Transient\r
-       public Collection getCollection() {\r
-               return ! checkDerivedUnit()? null :  derivedUnit.getCollection();\r
-       }\r
-\r
-       /**\r
-        * @param collection\r
-        *            the collection to set\r
-        */\r
-       public void setCollection(Collection collection) {\r
-               testDerivedUnit();\r
-               derivedUnit.setCollection(collection);\r
-       }\r
-\r
-       // annotation\r
-       public void addAnnotation(Annotation annotation) {\r
-               this.baseUnit().addAnnotation(annotation);\r
-       }\r
-\r
-       @Transient\r
-       public void getAnnotations() {\r
-               this.baseUnit().getAnnotations();\r
-       }\r
-\r
-       public void removeAnnotation(Annotation annotation) {\r
-               this.baseUnit().removeAnnotation(annotation);\r
-       }\r
-\r
-       // ******************************* Events ***************************\r
-\r
-       //set of events that were currently fired by this facades field unit\r
-       //to avoid recursive fireing of the same event\r
-       private final Set<PropertyChangeEvent> fireingEvents = new HashSet<PropertyChangeEvent>();\r
-\r
-       /**\r
-        * @return\r
-        */\r
-       private void addNewEventPropagationListener(CdmBase listeningObject) {\r
-               //if there is already a listener, don't do anything\r
-               for (PropertyChangeListener listener : this.listeners.keySet()){\r
-                       if (listeners.get(listener) == listeningObject){\r
-                               return;\r
-                       }\r
-               }\r
-               //create new listener\r
-               PropertyChangeListener listener = new PropertyChangeListener() {\r
-                       @Override\r
-                       public void propertyChange(PropertyChangeEvent event) {\r
-                               if (derivedUnit != null){\r
-                                       derivedUnit.firePropertyChange(event);\r
-                               }else{\r
-                                       if (! event.getSource().equals(fieldUnit) && ! fireingEvents.contains(event)  ){\r
-                                               fireingEvents.add(event);\r
-                                               fieldUnit.firePropertyChange(event);\r
-                                               fireingEvents.remove(event);\r
-                                       }\r
-                               }\r
-                       }\r
-               };\r
-               //add listener to listening object and to list of listeners\r
-               listeningObject.addPropertyChangeListener(listener);\r
-               listeners.put(listener, listeningObject);\r
-       }\r
-\r
-       // **************** Other Collections ********************************\r
-\r
-       /**\r
-        * Creates a duplicate specimen which derives from the same derivation event\r
-        * as the facade specimen and adds collection data to it (all data available\r
-        * in DerivedUnit and Specimen. Data from SpecimenOrObservationBase and\r
-        * above are not yet shared at the moment.\r
-        *\r
-        * @param collection\r
-        * @param catalogNumber\r
-        * @param accessionNumber\r
-        * @param storedUnder\r
-        * @param preservation\r
-        * @return\r
-        */\r
-       public DerivedUnit addDuplicate(Collection collection, String catalogNumber,\r
-                       String accessionNumber, TaxonNameBase storedUnder, PreservationMethod preservation){\r
-               testDerivedUnit();\r
-               DerivedUnit duplicate = DerivedUnit.NewPreservedSpecimenInstance();\r
-               duplicate.setDerivedFrom(getDerivationEvent(CREATE));\r
-               duplicate.setCollection(collection);\r
-               duplicate.setCatalogNumber(catalogNumber);\r
-               duplicate.setAccessionNumber(accessionNumber);\r
-               duplicate.setStoredUnder(storedUnder);\r
-               duplicate.setPreservation(preservation);\r
-               return duplicate;\r
-       }\r
-\r
-       public void addDuplicate(DerivedUnit duplicateSpecimen) {\r
-               testDerivedUnit();\r
-               getDerivationEvent(CREATE).addDerivative(duplicateSpecimen);\r
-       }\r
-\r
-       @Transient\r
-       public Set<DerivedUnit> getDuplicates() {\r
-               if (! checkDerivedUnit()){\r
-                       return new HashSet<DerivedUnit>();\r
-               }\r
-               Set<DerivedUnit> result = new HashSet<DerivedUnit>();\r
-               if (hasDerivationEvent()) {\r
-                       for (DerivedUnit derivedUnit : getDerivationEvent(CREATE)\r
-                                       .getDerivatives()) {\r
-                               if (derivedUnit.isInstanceOf(DerivedUnit.class)\r
-                                               && !derivedUnit.equals(this.derivedUnit)) {\r
-                                       result.add(CdmBase.deproxy(derivedUnit, DerivedUnit.class));\r
-                               }\r
-                       }\r
-               }\r
-               return result;\r
-       }\r
-\r
-       public void removeDuplicate(DerivedUnit duplicateSpecimen) {\r
-               testDerivedUnit();\r
-               if (hasDerivationEvent()) {\r
-                       getDerivationEvent(CREATE).removeDerivative(duplicateSpecimen);\r
-               }\r
-       }\r
-\r
-       public SpecimenOrObservationBase<?> baseUnit(){\r
-           if(derivedUnit!=null){\r
-               return derivedUnit;\r
-           }\r
-           else if(fieldUnit!=null){\r
-               return fieldUnit;\r
-           }\r
-           else{\r
-               throw new IllegalStateException("A DerivedUnitFacade must always have either a field unit or a derived unit");\r
-           }\r
-       }\r
-\r
-\r
-       private boolean checkDerivedUnit()  {\r
-               if (derivedUnit == null){\r
-                       return false;\r
-               }else{\r
-                       return true;\r
-               }\r
-       }\r
-       \r
-       private void testDerivedUnit() /* throws MethodNotSupportedByDerivedUnitTypeException */ {\r
-               if (derivedUnit == null){\r
-                       throw new IllegalStateException("This method is not allowed for this specimen or observation type. Probably you have tried to add specimen(derived unit) information to a field unit");\r
-               }\r
-       }\r
-\r
-       public void setType(SpecimenOrObservationType type) {\r
-               if (type == null){\r
-                       throw new IllegalArgumentException("The type of a specimen or observation may not be null");\r
-               }\r
-               SpecimenOrObservationBase<?> baseUnit = baseUnit();\r
-               if(baseUnit.isInstanceOf(FieldUnit.class) && !type.isFieldUnit()){\r
-                   throw new IllegalArgumentException("A FieldUnit may only be of type FieldUnit") ;\r
-               }\r
-               else if(baseUnit.isInstanceOf(DerivedUnit.class) && type.isFieldUnit()){\r
-                   throw new IllegalArgumentException("A derived unit may not be of type FieldUnit") ;\r
-               }\r
-               baseUnit.setRecordBasis(type);\r
-       }\r
-\r
-       public SpecimenOrObservationType getType() {\r
-           return baseUnit().getRecordBasis();\r
-       }\r
-\r
-       /**\r
-        * Closes this facade. As a minimum this method removes all listeners created by this facade from their\r
-        * listening objects.\r
-        */\r
-       public void close(){\r
-               for (PropertyChangeListener listener : this.listeners.keySet()){\r
-                       CdmBase listeningObject = listeners.get(listener);\r
-                       listeningObject.removePropertyChangeListener(listener);\r
-               }\r
-       }\r
-\r
-\r
-       /**\r
-        * Computes the correct distance string for given values for min, max and text.\r
-        * If text is not blank, text is returned, otherwise "min - max" or a single value is returned.\r
-        * @param min min value as number\r
-        * @param max max value as number\r
-        * @param text text representation of distance\r
-        * @return the formatted distance string\r
-        */\r
-       private String distanceString(Number min, Number max, String text, String unit) {\r
-               if (StringUtils.isNotBlank(text)){\r
-                       return text;\r
-               }else{\r
-                       String minStr = min == null? null : String.valueOf(min);\r
-                       String maxStr = max == null? null : String.valueOf(max);\r
-                       String result = CdmUtils.concat(UTF8.EN_DASH_SPATIUM.toString(), minStr, maxStr);\r
-                       if (StringUtils.isNotBlank(result) && StringUtils.isNotBlank(unit)){\r
-                               result = result + " " + unit;\r
-                       }\r
-                       return result;\r
-               }\r
-       }\r
-\r
-       /**\r
-        * First checks the inner field unit for the publish flag. If set to <code>true</code>\r
-        * then <code>true</code> is returned. If the field unit is <code>null</code> the inner derived unit\r
-        * is checked.\r
-        * @return <code>true</code> if this facade can be published\r
-        */\r
-       public boolean isPublish(){\r
-           if(fieldUnit!=null){\r
-               return fieldUnit.isPublish();\r
-           }\r
-           if(derivedUnit!=null){\r
-               return derivedUnit.isPublish();\r
-           }\r
-           return false;\r
-       }\r
-}\r
+/**
+ * Copyright (C) 2007 EDIT
+ * European Distributed Institute of Taxonomy
+ * http://www.e-taxonomy.eu
+ *
+ * The contents of this file are subject to the Mozilla Public License Version 1.1
+ * See LICENSE.TXT at the top of this package for the full license terms.
+ */
+
+package eu.etaxonomy.cdm.api.facade;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.persistence.Transient;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.log4j.Logger;
+
+import eu.etaxonomy.cdm.api.service.IOccurrenceService;
+import eu.etaxonomy.cdm.common.CdmUtils;
+import eu.etaxonomy.cdm.common.UTF8;
+import eu.etaxonomy.cdm.model.agent.AgentBase;
+import eu.etaxonomy.cdm.model.agent.Person;
+import eu.etaxonomy.cdm.model.common.Annotation;
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.common.DefinedTerm;
+import eu.etaxonomy.cdm.model.common.IOriginalSource;
+import eu.etaxonomy.cdm.model.common.IdentifiableSource;
+import eu.etaxonomy.cdm.model.common.Identifier;
+import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.model.common.LanguageString;
+import eu.etaxonomy.cdm.model.common.OriginalSourceType;
+import eu.etaxonomy.cdm.model.common.TimePeriod;
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+import eu.etaxonomy.cdm.model.description.Feature;
+import eu.etaxonomy.cdm.model.description.SpecimenDescription;
+import eu.etaxonomy.cdm.model.description.TextData;
+import eu.etaxonomy.cdm.model.location.NamedArea;
+import eu.etaxonomy.cdm.model.location.Point;
+import eu.etaxonomy.cdm.model.location.ReferenceSystem;
+import eu.etaxonomy.cdm.model.media.Media;
+import eu.etaxonomy.cdm.model.media.Rights;
+import eu.etaxonomy.cdm.model.name.TaxonNameBase;
+import eu.etaxonomy.cdm.model.occurrence.Collection;
+import eu.etaxonomy.cdm.model.occurrence.DerivationEvent;
+import eu.etaxonomy.cdm.model.occurrence.DerivationEventType;
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
+import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
+import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
+import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
+import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
+import eu.etaxonomy.cdm.model.reference.Reference;
+
+/**
+ * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from
+ * a specimen based view. It does not support all functionality available in the
+ * occurrence package.<BR>
+ * The most significant restriction is that a specimen may derive only from one
+ * direct derivation event and there must be only one field unit
+ * (gathering event) it derives from.<BR>
+ *
+ * @author a.mueller
+ * @date 14.05.2010
+ */
+public class DerivedUnitFacade {
+       private static final String METER = "m";
+
+       @SuppressWarnings("unused")
+       private static final Logger logger = Logger.getLogger(DerivedUnitFacade.class);
+
+       private static final String notSupportMessage = "A specimen facade not supported exception has occurred at a place where this should not have happened. The developer should implement not support check properly during class initialization ";
+
+       private static final boolean CREATE = true;
+       private static final boolean CREATE_NOT = false;
+
+       private final DerivedUnitFacadeConfigurator config;
+
+       private final Map<PropertyChangeListener, CdmBase> listeners = new HashMap<PropertyChangeListener, CdmBase>();
+
+       // Either fieldUnit or derivedUnit must not be null.
+       private FieldUnit fieldUnit;
+       private final DerivedUnit derivedUnit;
+
+       // media - the text data holding the media
+       private TextData derivedUnitMediaTextData;
+       private TextData fieldObjectMediaTextData;
+
+       private TextData ecology;
+       private TextData plantDescription;
+
+       /**
+        * Creates a derived unit facade for a new derived unit of type
+        * <code>type</code>.
+        *
+        * @param type
+        * @return
+        */
+       public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type) {
+               return new DerivedUnitFacade(type, null, null);
+       }
+
+       /**
+        * Creates a derived unit facade for a new derived unit of type
+        * <code>type</code>.
+        *
+        * @param type
+        * @return
+        */
+       public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type, FieldUnit fieldUnit) {
+               return new DerivedUnitFacade(type, fieldUnit, null);
+       }
+
+       /**
+        * Creates a derived unit facade for a new derived unit of type
+        * <code>type</code>.
+        *
+        * @param type
+        * @param fieldUnit the field unit to use
+        * @param config the facade configurator to use
+        * //TODO are there any ambiguities to solve with defining a field unit or a configurator
+        * @return
+        */
+       public static DerivedUnitFacade NewInstance(SpecimenOrObservationType type, FieldUnit fieldUnit, DerivedUnitFacadeConfigurator config) {
+               return new DerivedUnitFacade(type, fieldUnit, config);
+       }
+
+
+       /**
+        * Creates a derived unit facade for a given derived unit using the default
+        * configuration.
+        *
+        * @param derivedUnit
+        * @return
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit)
+                       throws DerivedUnitFacadeNotSupportedException {
+               return new DerivedUnitFacade(derivedUnit, null);
+       }
+
+       public static DerivedUnitFacade NewInstance(DerivedUnit derivedUnit,
+                       DerivedUnitFacadeConfigurator config)
+                       throws DerivedUnitFacadeNotSupportedException {
+               return new DerivedUnitFacade(derivedUnit, config);
+       }
+
+       // ****************** CONSTRUCTOR ******************************************
+
+       private DerivedUnitFacade(SpecimenOrObservationType type, FieldUnit fieldUnit, DerivedUnitFacadeConfigurator config) {
+               if (config == null){
+                       config = DerivedUnitFacadeConfigurator.NewInstance();
+               }
+               this.config = config;
+               // derivedUnit
+               derivedUnit = getNewDerivedUnitInstance(type);
+               //TODO parameter checking should be solved in a more generic way if we start using other entity facades
+               if(derivedUnit==null && fieldUnit==null && type.isFieldUnit()){
+                   this.fieldUnit = getFieldUnit(CREATE);
+               }
+               setFieldUnit(fieldUnit);
+               if (derivedUnit != null){
+                       setCacheStrategy();
+               }else{
+                       setFieldUnitCacheStrategy();
+               }
+       }
+
+       private DerivedUnit getNewDerivedUnitInstance(SpecimenOrObservationType type) {
+               if (type.isFieldUnit()){
+                       return null;
+               }else if(type.isAnyDerivedUnit()){
+                       return DerivedUnit.NewInstance(type);
+               } else {
+                       String message = "Unknown specimen or observation type %s";
+                       message = String.format(message, type.getMessage());
+                       throw new IllegalStateException(message);
+               }
+       }
+
+       private DerivedUnitFacade(DerivedUnit derivedUnit, DerivedUnitFacadeConfigurator config)
+                       throws DerivedUnitFacadeNotSupportedException {
+
+           if(derivedUnit==null){
+               throw new IllegalArgumentException("DerivedUnit must not be null");
+           }
+
+               if (config == null) {
+                       config = DerivedUnitFacadeConfigurator.NewInstance();
+               }
+               this.config = config;
+
+               // derived unit
+               this.derivedUnit = derivedUnit;
+
+               // derivation event
+               if (this.derivedUnit.getDerivedFrom() != null) {
+                       DerivationEvent derivationEvent = getDerivationEvent(CREATE);
+                       // fieldUnit
+                       Set<FieldUnit> fieldOriginals = getFieldUnitOriginals(derivationEvent, null);
+                       if (fieldOriginals.size() > 1) {
+                               throw new DerivedUnitFacadeNotSupportedException(
+                                               "Specimen must not have more than 1 derivation event");
+                       } else if (fieldOriginals.size() == 0) {
+                               // fieldUnit = FieldUnit.NewInstance();
+                       } else if (fieldOriginals.size() == 1) {
+                               fieldUnit = fieldOriginals.iterator().next();
+                               // ###fieldUnit =
+                               // getInitializedFieldUnit(fieldUnit);
+                               if (config.isFirePropertyChangeEvents()){
+                                       addNewEventPropagationListener(fieldUnit);
+                               }
+                       } else {
+                               throw new IllegalStateException("Illegal state");
+                       }
+               }
+
+               this.derivedUnitMediaTextData = inititializeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);
+
+               fieldObjectMediaTextData = initializeFieldObjectTextDataWithSupportTest(Feature.IMAGE(), false, true);
+
+
+//direct media have been removed from specimenorobservationbase #3597
+//             // handle derivedUnit.getMedia()
+//             if (derivedUnit.getMedia().size() > 0) {
+//                     // TODO better changed model here to allow only one place for images
+//                     if (this.config.isMoveDerivedUnitMediaToGallery()) {
+//                             Set<Media> mediaSet = derivedUnit.getMedia();
+//                             for (Media media : mediaSet) {
+//                                     this.addDerivedUnitMedia(media);
+//                             }
+//                             mediaSet.removeAll(getDerivedUnitMedia());
+//                     } else {
+//                             throw new DerivedUnitFacadeNotSupportedException(
+//                                             "Specimen may not have direct media. Only (one) image gallery is allowed");
+//                     }
+//             }
+//
+//             // handle fieldUnit.getMedia()
+//             if (fieldUnit != null && fieldUnit.getMedia() != null
+//                             && fieldUnit.getMedia().size() > 0) {
+//                     // TODO better changed model here to allow only one place for images
+//                     if (this.config.isMoveFieldObjectMediaToGallery()) {
+//                             Set<Media> mediaSet = fieldUnit.getMedia();
+//                             for (Media media : mediaSet) {
+//                                     this.addFieldObjectMedia(media);
+//                             }
+//                             mediaSet.removeAll(getFieldObjectMedia());
+//                     } else {
+//                             throw new DerivedUnitFacadeNotSupportedException(
+//                                             "Field object may not have direct media. Only (one) image gallery is allowed");
+//                     }
+//             }
+
+               // test if descriptions are supported
+               ecology = initializeFieldObjectTextDataWithSupportTest(
+                               Feature.ECOLOGY(), false, false);
+               plantDescription = initializeFieldObjectTextDataWithSupportTest(
+                               Feature.DESCRIPTION(), false, false);
+
+               setCacheStrategy();
+
+       }
+
+       private DerivedUnit getInitializedDerivedUnit(
+                       DerivedUnit derivedUnit) {
+               IOccurrenceService occurrenceService = this.config
+                               .getOccurrenceService();
+               if (occurrenceService == null) {
+                       return derivedUnit;
+               }
+               List<String> propertyPaths = this.config.getPropertyPaths();
+               if (propertyPaths == null) {
+                       return derivedUnit;
+               }
+               propertyPaths = getDerivedUnitPropertyPaths(propertyPaths);
+               DerivedUnit result = (DerivedUnit) occurrenceService.load(
+                               derivedUnit.getUuid(), propertyPaths);
+               return result;
+       }
+
+       /**
+        * Initializes the derived unit according to the configuartions property
+        * path. If the property path is <code>null</code> or no occurrence service
+        * is given the returned object is the same as the input parameter.
+        *
+        * @param fieldUnit
+        * @return
+        */
+       private FieldUnit getInitializedFieldUnit(FieldUnit fieldUnit) {
+               IOccurrenceService occurrenceService = this.config
+                               .getOccurrenceService();
+               if (occurrenceService == null) {
+                       return fieldUnit;
+               }
+               List<String> propertyPaths = this.config.getPropertyPaths();
+               if (propertyPaths == null) {
+                       return fieldUnit;
+               }
+               propertyPaths = getFieldObjectPropertyPaths(propertyPaths);
+               FieldUnit result = (FieldUnit) occurrenceService.load(
+                               fieldUnit.getUuid(), propertyPaths);
+               return result;
+       }
+
+       /**
+        * Transforms the property paths in a way that the facade is handled just
+        * like an ordinary CdmBase object.<BR>
+        * E.g. a property path "collectinAreas" will be translated into
+        * gatheringEvent.collectingAreas
+        *
+        * @param propertyPaths
+        * @return
+        */
+       private List<String> getFieldObjectPropertyPaths(List<String> propertyPaths) {
+               List<String> result = new ArrayList<String>();
+               for (String facadePath : propertyPaths) {
+                       // collecting areas (named area)
+                       if (facadePath.startsWith("collectingAreas")) {
+                               facadePath = "gatheringEvent." + facadePath;
+                               result.add(facadePath);
+                       }
+                       // collector (agentBase)
+                       else if (facadePath.startsWith("collector")) {
+                               facadePath = facadePath.replace("collector",
+                                               "gatheringEvent.actor");
+                               result.add(facadePath);
+                       }
+                       // exactLocation (agentBase)
+                       else if (facadePath.startsWith("exactLocation")) {
+                               facadePath = "gatheringEvent." + facadePath;
+                               result.add(facadePath);
+                       }
+                       // gatheringPeriod (TimePeriod)
+                       else if (facadePath.startsWith("gatheringPeriod")) {
+                               facadePath = facadePath.replace("gatheringPeriod",
+                                               "gatheringEvent.timeperiod");
+                               result.add(facadePath);
+                       }
+                       // (locality/ localityLanguage , LanguageString)
+                       else if (facadePath.startsWith("locality")) {
+                               facadePath = "gatheringEvent." + facadePath;
+                               result.add(facadePath);
+                       }
+
+                       // *********** FIELD OBJECT ************
+                       // fieldObjectDefinitions (Map<language, languageString)
+                       else if (facadePath.startsWith("fieldObjectDefinitions")) {
+                               // TODO or definition ???
+                               facadePath = facadePath.replace("fieldObjectDefinitions",
+                                               "description");
+                               result.add(facadePath);
+                       }
+                       // fieldObjectMedia (Media)
+                       else if (facadePath.startsWith("fieldObjectMedia")) {
+                               // TODO ???
+                               facadePath = facadePath.replace("fieldObjectMedia",
+                                               "descriptions.elements.media");
+                               result.add(facadePath);
+                       }
+
+                       // Gathering Event will always be added
+                       result.add("gatheringEvent");
+
+               }
+
+               /*
+                * Gathering Event ==================== - gatheringEvent
+                * (GatheringEvent)
+                *
+                * Field Object ================= - ecology/ ecologyAll (String) ??? -
+                * plant description (like ecology)
+                *
+                * - fieldObjectImageGallery (SpecimenDescription) - is automatically
+                * initialized via fieldObjectMedia
+                */
+
+               return result;
+       }
+
+       /**
+        * Transforms the property paths in a way that the facade is handled just
+        * like an ordinary CdmBase object.<BR>
+        * E.g. a property path "collectinAreas" will be translated into
+        * gatheringEvent.collectingAreas
+        *
+        * Not needed (?) as the facade works with REST service property paths
+        * without using this method.
+        *
+        * @param propertyPaths
+        * @return
+        */
+       private List<String> getDerivedUnitPropertyPaths(List<String> propertyPaths) {
+               List<String> result = new ArrayList<String>();
+               for (String facadePath : propertyPaths) {
+                       // determinations (DeterminationEvent)
+                       if (facadePath.startsWith("determinations")) {
+                               facadePath = "" + facadePath; // no change
+                               result.add(facadePath);
+                       }
+                       // storedUnder (TaxonNameBase)
+                       else if (facadePath.startsWith("storedUnder")) {
+                               facadePath = "" + facadePath; // no change
+                               result.add(facadePath);
+                       }
+                       // sources (IdentifiableSource)
+                       else if (facadePath.startsWith("sources")) {
+                               facadePath = "" + facadePath; // no change
+                               result.add(facadePath);
+                       }
+                       // collection (Collection)
+                       else if (facadePath.startsWith("collection")) {
+                               facadePath = "" + facadePath; // no change
+                               result.add(facadePath);
+                       }
+                       // (locality/ localityLanguage , LanguageString)
+                       else if (facadePath.startsWith("locality")) {
+                               facadePath = "gatheringEvent." + facadePath;
+                               result.add(facadePath);
+                       }
+
+                       // *********** FIELD OBJECT ************
+                       // derivedUnitDefinitions (Map<language, languageString)
+                       else if (facadePath.startsWith("derivedUnitDefinitions")) {
+                               // TODO or definition ???
+                               facadePath = facadePath.replace("derivedUnitDefinitions",
+                                               "description");
+                               result.add(facadePath);
+                       }
+
+                       // derivedUnitMedia (Media)
+                       else if (facadePath.startsWith("derivedUnitMedia")) {
+                               // TODO ???
+                               facadePath = facadePath.replace("derivedUnitMedia",
+                                               "descriptions.elements.media");
+                               result.add(facadePath);
+                       }
+
+               }
+
+               /*
+                * //TODO Derived Unit =====================
+                *
+                * - derivedUnitImageGallery (SpecimenDescription) - is automatically
+                * initialized via derivedUnitMedia
+                *
+                * - derivationEvent (DerivationEvent) - will always be initialized -
+                * duplicates (??? Specimen???) ???
+                */
+
+               return result;
+       }
+
+       /**
+        *
+        */
+       private void setCacheStrategy() {
+               if (derivedUnit == null) {
+                       throw new NullPointerException(
+                                       "Facade's derviedUnit must not be null to set cache strategy");
+               }else{
+                       derivedUnit.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());
+                       setFieldUnitCacheStrategy();
+               }
+       }
+
+       private void setFieldUnitCacheStrategy() {
+               if (this.hasFieldObject()){
+                       DerivedUnitFacadeFieldUnitCacheStrategy strategy = new DerivedUnitFacadeFieldUnitCacheStrategy();
+                       this.fieldUnit.setCacheStrategy(strategy);
+               }
+       }
+
+       /**
+        * @param feature
+        * @param createIfNotExists
+        * @param isImageGallery
+        * @return
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private TextData initializeFieldObjectTextDataWithSupportTest(
+                       Feature feature, boolean createIfNotExists, boolean isImageGallery)
+                       throws DerivedUnitFacadeNotSupportedException {
+               // field object
+               FieldUnit fieldObject = getFieldUnit(createIfNotExists);
+               if (fieldObject == null) {
+                       return null;
+               }
+               return inititializeTextDataWithSupportTest(feature, fieldObject,
+                               createIfNotExists, isImageGallery);
+       }
+
+       /**
+        * @param feature
+        * @param specimen
+        * @param createIfNotExists
+        * @param isImageGallery
+        * @return
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private TextData inititializeTextDataWithSupportTest(Feature feature,
+                       SpecimenOrObservationBase<?> specimen, boolean createIfNotExists,
+                       boolean isImageGallery)
+                       throws DerivedUnitFacadeNotSupportedException {
+               if (feature == null) {
+                       return null;
+               }
+               TextData textData = null;
+               if (createIfNotExists) {
+                       textData = TextData.NewInstance(feature);
+               }
+
+               Set<SpecimenDescription> descriptions;
+               if (isImageGallery) {
+                       descriptions = specimen.getSpecimenDescriptionImageGallery();
+               } else {
+                       descriptions = specimen.getSpecimenDescriptions(false);
+               }
+               // no description exists yet for this specimen
+               if (descriptions.size() == 0) {
+                       if (createIfNotExists) {
+                               SpecimenDescription newSpecimenDescription = SpecimenDescription
+                                               .NewInstance(specimen);
+                               newSpecimenDescription.addElement(textData);
+                               newSpecimenDescription.setImageGallery(isImageGallery);
+                               return textData;
+                       } else {
+                               return null;
+                       }
+               }
+               // description already exists
+               Set<DescriptionElementBase> existingTextData = new HashSet<DescriptionElementBase>();
+               for (SpecimenDescription description : descriptions) {
+                       // collect all existing text data
+                       for (DescriptionElementBase element : description.getElements()) {
+                               if (element.isInstanceOf(TextData.class)
+                                               && (feature.equals(element.getFeature()) || isImageGallery)) {
+                                       existingTextData.add(element);
+                               }
+                       }
+               }
+               // use existing text data if exactly one exists
+               if (existingTextData.size() > 1) {
+                       throw new DerivedUnitFacadeNotSupportedException(
+                                       "Specimen facade does not support more than one description text data of type "
+                                                       + feature.getLabel());
+
+               } else if (existingTextData.size() == 1) {
+                       return CdmBase.deproxy(existingTextData.iterator().next(),
+                                       TextData.class);
+               } else {
+                       if (createIfNotExists) {
+                               SpecimenDescription description = descriptions.iterator()
+                                               .next();
+                               description.addElement(textData);
+                       }
+                       return textData;
+               }
+       }
+
+       /**
+        * Tests if a given image gallery is supported by the derived unit facade.
+        * It returns the only text data attached to the given image gallery. If the
+        * given image gallery does not have text data attached, it is created and
+        * attached.
+        *
+        * @param imageGallery
+        * @return
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private TextData testImageGallery(SpecimenDescription imageGallery)
+                       throws DerivedUnitFacadeNotSupportedException {
+               if (imageGallery.isImageGallery() == false) {
+                       throw new DerivedUnitFacadeNotSupportedException(
+                                       "Image gallery needs to have image gallery flag set");
+               }
+               if (imageGallery.getElements().size() > 1) {
+                       throw new DerivedUnitFacadeNotSupportedException(
+                                       "Image gallery must not have more then one description element");
+               }
+               TextData textData;
+               if (imageGallery.getElements().size() == 0) {
+                       textData = TextData.NewInstance(Feature.IMAGE());
+                       imageGallery.addElement(textData);
+               } else {
+                       if (!imageGallery.getElements().iterator().next()
+                                       .isInstanceOf(TextData.class)) {
+                               throw new DerivedUnitFacadeNotSupportedException(
+                                               "Image gallery must only have TextData as element");
+                       } else {
+                               textData = CdmBase.deproxy(imageGallery.getElements()
+                                               .iterator().next(), TextData.class);
+                       }
+               }
+               return textData;
+       }
+
+       // ************************** METHODS
+       // *****************************************
+
+       private TextData getDerivedUnitImageGalleryTextData(
+                       boolean createIfNotExists)
+                       throws DerivedUnitFacadeNotSupportedException {
+               if (this.derivedUnitMediaTextData == null && createIfNotExists) {
+                       this.derivedUnitMediaTextData = getImageGalleryTextData(
+                                       derivedUnit, "Specimen");
+               }
+               return this.derivedUnitMediaTextData;
+       }
+
+       private TextData getObservationImageGalleryTextData(
+                       boolean createIfNotExists)
+                       throws DerivedUnitFacadeNotSupportedException {
+               if (this.fieldObjectMediaTextData == null && createIfNotExists) {
+                       this.fieldObjectMediaTextData = getImageGalleryTextData(fieldUnit, "Field unit");
+               }
+               return this.fieldObjectMediaTextData;
+       }
+
+       /**
+        * @param derivationEvent
+        * @return
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private Set<FieldUnit> getFieldUnitOriginals(
+                       DerivationEvent derivationEvent,
+                       Set<SpecimenOrObservationBase> recursionAvoidSet)
+                       throws DerivedUnitFacadeNotSupportedException {
+               if (recursionAvoidSet == null) {
+                       recursionAvoidSet = new HashSet<SpecimenOrObservationBase>();
+               }
+               Set<FieldUnit> result = new HashSet<FieldUnit>();
+               Set<SpecimenOrObservationBase> originals = derivationEvent.getOriginals();
+               for (SpecimenOrObservationBase original : originals) {
+                       if (original.isInstanceOf(FieldUnit.class)) {
+                               result.add(CdmBase.deproxy(original, FieldUnit.class));
+                       } else if (original.isInstanceOf(DerivedUnit.class)) {
+                               // if specimen has already been tested exclude it from further
+                               // recursion
+                               if (recursionAvoidSet.contains(original)) {
+                                       continue;
+                               }
+                               DerivedUnit derivedUnit = CdmBase.deproxy(original,     DerivedUnit.class);
+                               DerivationEvent originalDerivation = derivedUnit.getDerivedFrom();
+                               // Set<DerivationEvent> derivationEvents =
+                               // original.getDerivationEvents();
+                               // for (DerivationEvent originalDerivation : derivationEvents){
+                               if(originalDerivation!=null){
+                                   Set<FieldUnit> fieldUnits = getFieldUnitOriginals(
+                                           originalDerivation, recursionAvoidSet);
+                                   result.addAll(fieldUnits);
+                               }
+                               // }
+                       } else {
+                               throw new DerivedUnitFacadeNotSupportedException(
+                                               "Unhandled specimen or observation base type: "
+                                                               + original.getClass().getName());
+                       }
+
+               }
+               return result;
+       }
+
+       // *********** MEDIA METHODS ******************************
+
+       // /**
+       // * Returns the media list for a specimen. Throws an exception if the
+       // existing specimen descriptions
+       // * are not supported by this facade.
+       // * @param specimen the specimen the media belongs to
+       // * @param specimenExceptionText text describing the specimen for exception
+       // messages
+       // * @return
+       // * @throws DerivedUnitFacadeNotSupportedException
+       // */
+       // private List<Media> getImageGalleryMedia(SpecimenOrObservationBase
+       // specimen, String specimenExceptionText) throws
+       // DerivedUnitFacadeNotSupportedException{
+       // List<Media> result;
+       // SpecimenDescription imageGallery =
+       // getImageGalleryWithSupportTest(specimen, specimenExceptionText, true);
+       // TextData textData = getImageTextDataWithSupportTest(imageGallery,
+       // specimenExceptionText);
+       // result = textData.getMedia();
+       // return result;
+       // }
+
+       /**
+        * Returns the media list for a specimen. Throws an exception if the
+        * existing specimen descriptions are not supported by this facade.
+        *
+        * @param specimen
+        *            the specimen the media belongs to
+        * @param specimenExceptionText
+        *            text describing the specimen for exception messages
+        * @return
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private TextData getImageGalleryTextData(SpecimenOrObservationBase specimen, String specimenExceptionText)
+                       throws DerivedUnitFacadeNotSupportedException {
+               TextData result;
+               SpecimenDescription imageGallery = getImageGalleryWithSupportTest(
+                               specimen, specimenExceptionText, true);
+               result = getImageTextDataWithSupportTest(imageGallery,
+                               specimenExceptionText);
+               return result;
+       }
+
+       /**
+        * Returns the image gallery of the according specimen. Throws an exception
+        * if the attached image gallerie(s) are not supported by this facade. If no
+        * image gallery exists a new one is created if
+        * <code>createNewIfNotExists</code> is true and if specimen is not
+        * <code>null</code>.
+        *
+        * @param specimen
+        * @param specimenText
+        * @param createNewIfNotExists
+        * @return
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private SpecimenDescription getImageGalleryWithSupportTest(
+                       SpecimenOrObservationBase<?> specimen, String specimenText,
+                       boolean createNewIfNotExists)
+                       throws DerivedUnitFacadeNotSupportedException {
+               if (specimen == null) {
+                       return null;
+               }
+               SpecimenDescription imageGallery;
+               if (hasMultipleImageGalleries(specimen)) {
+                       throw new DerivedUnitFacadeNotSupportedException(specimenText
+                                       + " must not have more than 1 image gallery");
+               } else {
+                       imageGallery = getImageGallery(specimen, createNewIfNotExists);
+                       getImageTextDataWithSupportTest(imageGallery, specimenText);
+               }
+               return imageGallery;
+       }
+
+       /**
+        * Returns the media holding text data element of the image gallery. Throws
+        * an exception if multiple such text data already exist. Creates a new text
+        * data if none exists and adds it to the image gallery. If image gallery is
+        * <code>null</code> nothing happens.
+        *
+        * @param imageGallery
+        * @param textData
+        * @return
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private TextData getImageTextDataWithSupportTest(
+                       SpecimenDescription imageGallery, String specimenText)
+                       throws DerivedUnitFacadeNotSupportedException {
+               if (imageGallery == null) {
+                       return null;
+               }
+               TextData textData = null;
+               for (DescriptionElementBase element : imageGallery.getElements()) {
+                       if (element.isInstanceOf(TextData.class)
+                                       && element.getFeature().equals(Feature.IMAGE())) {
+                               if (textData != null) {
+                                       throw new DerivedUnitFacadeNotSupportedException(
+                                                       specimenText
+                                                                       + " must not have more than 1 image text data element in image gallery");
+                               }
+                               textData = CdmBase.deproxy(element, TextData.class);
+                       }
+               }
+               if (textData == null) {
+                       textData = TextData.NewInstance(Feature.IMAGE());
+                       imageGallery.addElement(textData);
+               }
+               return textData;
+       }
+
+       /**
+        * Checks, if a specimen belongs to more than one description that is an
+        * image gallery
+        *
+        * @param derivedUnit
+        * @return
+        */
+       private boolean hasMultipleImageGalleries(
+                       SpecimenOrObservationBase<?> derivedUnit) {
+               int count = 0;
+               Set<SpecimenDescription> descriptions = derivedUnit
+                               .getSpecimenDescriptions();
+               for (SpecimenDescription description : descriptions) {
+                       if (description.isImageGallery()) {
+                               count++;
+                       }
+               }
+               return (count > 1);
+       }
+
+       /**
+        * Returns the image gallery for a specimen. If there are multiple specimen
+        * descriptions marked as image galleries an arbitrary one is chosen. If no
+        * image gallery exists, a new one is created if
+        * <code>createNewIfNotExists</code> is <code>true</code>.<Br>
+        * If specimen is <code>null</code> a null pointer exception is thrown.
+        *
+        * @param createNewIfNotExists
+        * @return
+        */
+       private SpecimenDescription getImageGallery(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists) {
+               SpecimenDescription result = null;
+               Set<SpecimenDescription> descriptions = specimen.getSpecimenDescriptions();
+               for (SpecimenDescription description : descriptions) {
+                       if (description.isImageGallery()) {
+                               result = description;
+                               break;
+                       }
+               }
+               if (result == null && createIfNotExists) {
+                       result = SpecimenDescription.NewInstance(specimen);
+                       result.setImageGallery(true);
+               }
+               return result;
+       }
+
+       /**
+        * Adds a media to the specimens image gallery. If media is
+        * <code>null</code> nothing happens.
+        *
+        * @param media
+        * @param specimen
+        * @return true if media is not null (as specified by
+        *         {@link java.util.Collection#add(Object) Collection.add(E e)}
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private boolean addMedia(Media media, SpecimenOrObservationBase<?> specimen) throws DerivedUnitFacadeNotSupportedException {
+               if (media != null) {
+                       List<Media> mediaList = getMediaList(specimen, true);
+                       if (! mediaList.contains(media)){
+                               return mediaList.add(media);
+                       }else{
+                               return true;
+                       }
+               } else {
+                       return false;
+               }
+       }
+
+       /**
+        * Removes a media from the specimens image gallery.
+        *
+        * @param media
+        * @param specimen
+        * @return true if an element was removed as a result of this call (as
+        *         specified by {@link java.util.Collection#remove(Object)
+        *         Collection.remove(E e)}
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private boolean removeMedia(Media media,
+                       SpecimenOrObservationBase<?> specimen)
+                       throws DerivedUnitFacadeNotSupportedException {
+               List<Media> mediaList = getMediaList(specimen, true);
+               return mediaList == null ? null : mediaList.remove(media);
+       }
+
+       private List<Media> getMediaList(SpecimenOrObservationBase<?> specimen, boolean createIfNotExists)
+                       throws DerivedUnitFacadeNotSupportedException {
+               TextData textData = getMediaTextData(specimen, createIfNotExists);
+               return textData == null ? null : textData.getMedia();
+       }
+
+       /**
+        * Returns the one media list of a specimen which is part of the only image
+        * gallery that this specimen is part of.<BR>
+        * If these conditions are not hold an exception is thrwon.
+        *
+        * @param specimen
+        * @return
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       // private List<Media> getMedia(SpecimenOrObservationBase<?> specimen)
+       // throws DerivedUnitFacadeNotSupportedException {
+       // if (specimen == null){
+       // return null;
+       // }
+       // if (specimen == this.derivedUnit){
+       // return getDerivedUnitImageGalleryMedia();
+       // }else if (specimen == this.fieldUnit){
+       // return getObservationImageGalleryTextData();
+       // }else{
+       // return getImageGalleryMedia(specimen, "Undefined specimen ");
+       // }
+       // }
+
+       /**
+        * Returns the one media list of a specimen which is part of the only image
+        * gallery that this specimen is part of.<BR>
+        * If these conditions are not hold an exception is thrown.
+        *
+        * @param specimen
+        * @return
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private TextData getMediaTextData(SpecimenOrObservationBase<?> specimen,
+                       boolean createIfNotExists)
+                       throws DerivedUnitFacadeNotSupportedException {
+               if (specimen == null) {
+                       return null;
+               }
+               if (specimen == this.derivedUnit) {
+                       return getDerivedUnitImageGalleryTextData(createIfNotExists);
+               } else if (specimen == this.fieldUnit) {
+                       return getObservationImageGalleryTextData(createIfNotExists);
+               } else {
+                       return getImageGalleryTextData(specimen, "Undefined specimen ");
+               }
+       }
+
+       // ****************** GETTER / SETTER / ADDER / REMOVER
+       // ***********************/
+
+       // ****************** Gathering Event *********************************/
+
+       // country
+       @Transient
+       public NamedArea getCountry() {
+               return (hasGatheringEvent() ? getGatheringEvent(true).getCountry()
+                               : null);
+       }
+
+       public void setCountry(NamedArea country) {
+               getGatheringEvent(true).setCountry(country);
+       }
+
+       // Collecting area
+       public void addCollectingArea(NamedArea area) {
+               getGatheringEvent(true).addCollectingArea(area);
+       }
+
+       public void addCollectingAreas(java.util.Collection<NamedArea> areas) {
+               for (NamedArea area : areas) {
+                       getGatheringEvent(true).addCollectingArea(area);
+               }
+       }
+
+       @Transient
+       public Set<NamedArea> getCollectingAreas() {
+               return (hasGatheringEvent() ? getGatheringEvent(true)
+                               .getCollectingAreas() : null);
+       }
+
+       public void removeCollectingArea(NamedArea area) {
+               if (hasGatheringEvent()) {
+                       getGatheringEvent(true).removeCollectingArea(area);
+               }
+       }
+
+       static final String ALTITUDE_POSTFIX = " m";
+
+       /**
+        * Returns the correctly formatted <code>absolute elevation</code> information.
+        * If absoluteElevationText is set, this will be returned,
+        * otherwise we absoluteElevation will be returned, followed by absoluteElevationMax
+        * if existing, separated by " - "
+        * @return
+        */
+       @Transient
+       public String absoluteElevationToString() {
+               if (! hasGatheringEvent()){
+                       return null;
+               }else{
+                       GatheringEvent ev = getGatheringEvent(true);
+                       if (StringUtils.isNotBlank(ev.getAbsoluteElevationText())){
+                               return ev.getAbsoluteElevationText();
+                       }else{
+                               String text = ev.getAbsoluteElevationText();
+                               Integer min = getAbsoluteElevation();
+                               Integer max = getAbsoluteElevationMaximum();
+                               return distanceString(min, max, text, METER);
+                       }
+               }
+       }
+
+
+       /**
+        * meter above/below sea level of the surface
+        *
+        * @see #getAbsoluteElevationError()
+        * @see #getAbsoluteElevationRange()
+        **/
+       @Transient
+       public Integer getAbsoluteElevation() {
+               return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevation() : null);
+       }
+
+       public void setAbsoluteElevation(Integer absoluteElevation) {
+               getGatheringEvent(true).setAbsoluteElevation(absoluteElevation);
+       }
+
+       public void setAbsoluteElevationMax(Integer absoluteElevationMax) {
+               getGatheringEvent(true).setAbsoluteElevationMax(absoluteElevationMax);
+       }
+
+       public void setAbsoluteElevationText(String absoluteElevationText) {
+               getGatheringEvent(true).setAbsoluteElevationText(absoluteElevationText);
+       }
+
+       /**
+        * @see #getAbsoluteElevation()
+        * @see #getAbsoluteElevationError()
+        * @see #setAbsoluteElevationRange(Integer, Integer)
+        * @see #getAbsoluteElevationMinimum()
+        */
+       @Transient
+       public Integer getAbsoluteElevationMaximum() {
+               if (!hasGatheringEvent()) {
+                       return null;
+               }else{
+                       return getGatheringEvent(true).getAbsoluteElevationMax();
+               }
+       }
+
+       /**
+        * @see #getAbsoluteElevation()
+        * @see #getAbsoluteElevationError()
+        * @see #setAbsoluteElevationRange(Integer, Integer)
+        * @see #getAbsoluteElevationMinimum()
+        */
+       @Transient
+       public String getAbsoluteElevationText() {
+               if (!hasGatheringEvent()) {
+                       return null;
+               }else{
+                       return getGatheringEvent(true).getAbsoluteElevationText();
+               }
+       }
+
+       /**
+        * Convenience method to set absolute elevation minimum and maximum.
+        *
+        * @see #setAbsoluteElevation(Integer)
+        * @see #setAbsoluteElevationMax(Integer)
+        * @param minimumElevation minimum of the range
+        * @param maximumElevation maximum of the range
+        */
+       public void setAbsoluteElevationRange(Integer minimumElevation, Integer maximumElevation) {
+               getGatheringEvent(true).setAbsoluteElevation(minimumElevation);
+               getGatheringEvent(true).setAbsoluteElevationMax(maximumElevation);
+       }
+
+       // collector
+       @Transient
+       public AgentBase getCollector() {
+               return (hasGatheringEvent() ? getGatheringEvent(true).getCollector()
+                               : null);
+       }
+
+       public void setCollector(AgentBase collector) {
+               getGatheringEvent(true).setCollector(collector);
+       }
+
+       // collecting method
+       @Transient
+       public String getCollectingMethod() {
+               return (hasGatheringEvent() ? getGatheringEvent(true).getCollectingMethod() : null);
+       }
+
+       public void setCollectingMethod(String collectingMethod) {
+               getGatheringEvent(true).setCollectingMethod(collectingMethod);
+       }
+
+       // distance to ground
+
+       /**
+        * Returns the correctly formatted <code>distance to ground</code> information.
+        * If distanceToGroundText is not blank, it will be returned,
+        * otherwise distanceToGround will be returned, followed by distanceToGroundMax
+        * if existing, separated by " - "
+        * @return
+        */
+       @Transient
+       public String distanceToGroundToString() {
+               if (! hasGatheringEvent()){
+                       return null;
+               }else{
+                       GatheringEvent ev = getGatheringEvent(true);
+                       String text = ev.getDistanceToGroundText();
+                       Double min = getDistanceToGround();
+                       Double max = getDistanceToGroundMax();
+                       return distanceString(min, max, text, METER);
+               }
+       }
+
+       @Transient
+       public Double getDistanceToGround() {
+               return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToGround() : null);
+       }
+
+       public void setDistanceToGround(Double distanceToGround) {
+               getGatheringEvent(true).setDistanceToGround(distanceToGround);
+       }
+
+       /**
+        * @see #getDistanceToGround()
+        * @see #getDistanceToGroundRange(Integer, Integer)
+        */
+       @Transient
+       public Double getDistanceToGroundMax() {
+               if (!hasGatheringEvent()) {
+                       return null;
+               }else{
+                       return getGatheringEvent(true).getDistanceToGroundMax();
+               }
+       }
+
+       public void setDistanceToGroundMax(Double distanceToGroundMax) {
+               getGatheringEvent(true).setDistanceToGroundMax(distanceToGroundMax);
+       }
+
+       /**
+        * @see #getDistanceToGround()
+        * @see #setDistanceToGroundRange(Integer, Integer)
+        */
+       @Transient
+       public String getDistanceToGroundText() {
+               if (!hasGatheringEvent()) {
+                       return null;
+               }else{
+                       return getGatheringEvent(true).getDistanceToGroundText();
+               }
+       }
+       public void setDistanceToGroundText(String distanceToGroundText) {
+               getGatheringEvent(true).setDistanceToGroundText(distanceToGroundText);
+       }
+
+       /**
+        * Convenience method to set distance to ground minimum and maximum.
+        *
+        * @see #getDistanceToGround()
+        * @see #getDistanceToGroundMax()
+        * @param minimumDistance minimum of the range
+        * @param maximumDistance maximum of the range
+        */
+       public void setDistanceToGroundRange(Double minimumDistance, Double maximumDistance) throws IllegalArgumentException{
+               getGatheringEvent(true).setDistanceToGround(minimumDistance);
+               getGatheringEvent(true).setDistanceToGroundMax(maximumDistance);
+       }
+
+
+       /**
+        * Returns the correctly formatted <code>distance to water surface</code> information.
+        * If distanceToWaterSurfaceText is not blank, it will be returned,
+        * otherwise distanceToWaterSurface will be returned, followed by distanceToWatersurfaceMax
+        * if existing, separated by " - "
+        * @return
+        */
+       @Transient
+       public String distanceToWaterSurfaceToString() {
+               if (! hasGatheringEvent()){
+                       return null;
+               }else{
+                       GatheringEvent ev = getGatheringEvent(true);
+                       String text = ev.getDistanceToWaterSurfaceText();
+                       Double min = getDistanceToWaterSurface();
+                       Double max = getDistanceToWaterSurfaceMax();
+                       return distanceString(min, max, text, METER);
+               }
+       }
+
+       // distance to water surface
+       @Transient
+       public Double getDistanceToWaterSurface() {
+               return (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToWaterSurface() : null);
+       }
+
+       public void setDistanceToWaterSurface(Double distanceToWaterSurface) {
+               getGatheringEvent(true).setDistanceToWaterSurface(distanceToWaterSurface);
+       }
+
+       /**
+        * @see #getDistanceToWaterSurface()
+        * @see #getDistanceToWaterSurfaceRange(Double, Double)
+        */
+       @Transient
+       public Double getDistanceToWaterSurfaceMax() {
+               if (!hasGatheringEvent()) {
+                       return null;
+               }else{
+                       return getGatheringEvent(true).getDistanceToWaterSurfaceMax();
+               }
+       }
+
+       public void setDistanceToWaterSurfaceMax(Double distanceToWaterSurfaceMax) {
+               getGatheringEvent(true).setDistanceToWaterSurfaceMax(distanceToWaterSurfaceMax);
+       }
+
+       /**
+        * @see #getDistanceToWaterSurface()
+        * @see #getDistanceToWaterSurfaceRange(Double, Double)
+        */
+       @Transient
+       public String getDistanceToWaterSurfaceText() {
+               if (!hasGatheringEvent()) {
+                       return null;
+               }else{
+                       return getGatheringEvent(true).getDistanceToWaterSurfaceText();
+               }
+       }
+       public void setDistanceToWaterSurfaceText(String distanceToWaterSurfaceText) {
+               getGatheringEvent(true).setDistanceToWaterSurfaceText(distanceToWaterSurfaceText);
+       }
+
+       /**
+        * Convenience method to set distance to ground minimum and maximum.
+        *
+        * @see #getDistanceToWaterSurface()
+        * @see #getDistanceToWaterSurfaceMax()
+        * @param minimumDistance minimum of the range, this is the distance which is closer to the water surface
+        * @param maximumDistance maximum of the range, this is the distance which is farer to the water surface
+        */
+       public void setDistanceToWaterSurfaceRange(Double minimumDistance, Double maximumDistance) throws IllegalArgumentException{
+               getGatheringEvent(true).setDistanceToWaterSurface(minimumDistance);
+               getGatheringEvent(true).setDistanceToWaterSurfaceMax(maximumDistance);
+       }
+
+
+       // exact location
+       @Transient
+       public Point getExactLocation() {
+               return (hasGatheringEvent() ? getGatheringEvent(true).getExactLocation() : null);
+       }
+
+       /**
+        * Returns a sexagesimal representation of the exact location (e.g.
+        * 12°59'N, 35°23E). If the exact location is <code>null</code> the empty
+        * string is returned.
+        *
+        * @param includeEmptySeconds
+        * @param includeReferenceSystem
+        * @return
+        */
+       public String getExactLocationText(boolean includeEmptySeconds,
+                       boolean includeReferenceSystem) {
+               return (this.getExactLocation() == null ? "" : this.getExactLocation()
+                               .toSexagesimalString(includeEmptySeconds,
+                                               includeReferenceSystem));
+       }
+
+       public void setExactLocation(Point exactLocation) {
+               getGatheringEvent(true).setExactLocation(exactLocation);
+       }
+
+       public void setExactLocationByParsing(String longitudeToParse,
+                       String latitudeToParse, ReferenceSystem referenceSystem,
+                       Integer errorRadius) throws ParseException {
+               Point point = Point.NewInstance(null, null, referenceSystem,
+                               errorRadius);
+               point.setLongitudeByParsing(longitudeToParse);
+               point.setLatitudeByParsing(latitudeToParse);
+               setExactLocation(point);
+       }
+
+       // gathering event description
+       @Transient
+       public String getGatheringEventDescription() {
+               return (hasGatheringEvent() ? getGatheringEvent(true).getDescription()
+                               : null);
+       }
+
+       public void setGatheringEventDescription(String description) {
+               getGatheringEvent(true).setDescription(description);
+       }
+
+       // gatering period
+       @Transient
+       public TimePeriod getGatheringPeriod() {
+               return (hasGatheringEvent() ? getGatheringEvent(true).getTimeperiod()
+                               : null);
+       }
+
+       public void setGatheringPeriod(TimePeriod timeperiod) {
+               getGatheringEvent(true).setTimeperiod(timeperiod);
+       }
+
+       // locality
+       @Transient
+       public LanguageString getLocality() {
+               return (hasGatheringEvent() ? getGatheringEvent(true).getLocality()
+                               : null);
+       }
+
+       /**
+        * convienience method for {@link #getLocality()}.
+        * {@link LanguageString#getText() getText()}
+        *
+        * @return
+        */
+       @Transient
+       public String getLocalityText() {
+               LanguageString locality = getLocality();
+               if (locality != null) {
+                       return locality.getText();
+               }
+               return null;
+       }
+
+       /**
+        * convienience method for {@link #getLocality()}.
+        * {@link LanguageString#getLanguage() getLanguage()}
+        *
+        * @return
+        */
+       @Transient
+       public Language getLocalityLanguage() {
+               LanguageString locality = getLocality();
+               if (locality != null) {
+                       return locality.getLanguage();
+               }
+               return null;
+       }
+
+       /**
+        * Sets the locality string in the default language
+        *
+        * @param locality
+        */
+       public void setLocality(String locality) {
+               Language language = Language.DEFAULT();
+               setLocality(locality, language);
+       }
+
+       public void setLocality(String locality, Language language) {
+               LanguageString langString = LanguageString.NewInstance(locality, language);
+               setLocality(langString);
+       }
+
+       public void setLocality(LanguageString locality) {
+               getGatheringEvent(true).setLocality(locality);
+       }
+
+       /**
+        * The gathering event will be used for the field object instead of the old
+        * gathering event.<BR>
+        * <B>This method will override all gathering values (see below).</B>
+        *
+        * @see #getAbsoluteElevation()
+        * @see #getAbsoluteElevationError()
+        * @see #getDistanceToGround()
+        * @see #getDistanceToWaterSurface()
+        * @see #getExactLocation()
+        * @see #getGatheringEventDescription()
+        * @see #getGatheringPeriod()
+        * @see #getCollectingAreas()
+        * @see #getCollectingMethod()
+        * @see #getLocality()
+        * @see #getCollector()
+        * @param gatheringEvent
+        */
+       public void setGatheringEvent(GatheringEvent gatheringEvent) {
+               getFieldUnit(true).setGatheringEvent(gatheringEvent);
+       }
+
+       public boolean hasGatheringEvent() {
+               return (getGatheringEvent(false) != null);
+       }
+
+       public GatheringEvent innerGatheringEvent() {
+               return getGatheringEvent(false);
+       }
+
+       public GatheringEvent getGatheringEvent(boolean createIfNotExists) {
+               if (!hasFieldUnit() && !createIfNotExists) {
+                       return null;
+               }
+               if (createIfNotExists && getFieldUnit(true).getGatheringEvent() == null) {
+                       GatheringEvent gatheringEvent = GatheringEvent.NewInstance();
+                       getFieldUnit(true).setGatheringEvent(gatheringEvent);
+               }
+               return getFieldUnit(true).getGatheringEvent();
+       }
+
+       // ****************** Field Object ************************************/
+
+       /**
+        * Returns true if a field unit exists (even if all attributes are
+        * empty or <code>null<code>.
+        *
+        * @return
+        */
+       public boolean hasFieldObject() {
+               return this.fieldUnit != null;
+       }
+
+       // ecology
+       @Transient
+       public String getEcology() {
+               return getEcology(Language.DEFAULT());
+       }
+
+       public String getEcology(Language language) {
+               LanguageString languageString = getEcologyAll().get(language);
+               return (languageString == null ? null : languageString.getText());
+       }
+
+       // public String getEcologyPreferred(List<Language> languages){
+       // LanguageString languageString =
+       // getEcologyAll().getPreferredLanguageString(languages);
+       // return languageString.getText();
+       // }
+       /**
+        * Returns a copy of the multilanguage text holding the ecology data.
+        *
+        * @see {@link TextData#getMultilanguageText()}
+        * @return
+        */
+       @Transient
+       public Map<Language, LanguageString> getEcologyAll() {
+               if (ecology == null) {
+                       try {
+                               ecology = initializeFieldObjectTextDataWithSupportTest(
+                                               Feature.ECOLOGY(), false, false);
+                       } catch (DerivedUnitFacadeNotSupportedException e) {
+                               throw new IllegalStateException(notSupportMessage, e);
+                       }
+                       if (ecology == null) {
+                               return new HashMap<Language, LanguageString>();
+                       }
+               }
+               return ecology.getMultilanguageText();
+       }
+
+       public void setEcology(String ecology) {
+               setEcology(ecology, null);
+       }
+
+       public void setEcology(String ecologyText, Language language) {
+               if (language == null) {
+                       language = Language.DEFAULT();
+               }
+               boolean isEmpty = StringUtils.isBlank(ecologyText);
+               if (ecology == null) {
+                       try {
+                               ecology = initializeFieldObjectTextDataWithSupportTest(
+                                               Feature.ECOLOGY(), !isEmpty, false);
+                       } catch (DerivedUnitFacadeNotSupportedException e) {
+                               throw new IllegalStateException(notSupportMessage, e);
+                       }
+               }
+               if (ecology != null){
+                       if (ecologyText == null) {
+                               ecology.removeText(language);
+                       } else {
+                               ecology.putText(language, ecologyText);
+                       }
+               }
+       }
+
+       public void removeEcology(Language language) {
+               setEcology(null, language);
+       }
+
+       /**
+        * Removes ecology for the default language
+        */
+       public void removeEcology() {
+               setEcology(null, null);
+       }
+
+       public void removeEcologyAll() {
+
+       }
+
+       // plant description
+       @Transient
+       public String getPlantDescription() {
+               return getPlantDescription(null);
+       }
+
+       public String getPlantDescription(Language language) {
+               if (language == null) {
+                       language = Language.DEFAULT();
+               }
+               LanguageString languageString = getPlantDescriptionAll().get(language);
+               return (languageString == null ? null : languageString.getText());
+       }
+
+       // public String getPlantDescriptionPreferred(List<Language> languages){
+       // LanguageString languageString =
+       // getPlantDescriptionAll().getPreferredLanguageString(languages);
+       // return languageString.getText();
+       // }
+       /**
+        * Returns a copy of the multilanguage text holding the description data.
+        *
+        * @see {@link TextData#getMultilanguageText()}
+        * @return
+        */
+       @Transient
+       public Map<Language, LanguageString> getPlantDescriptionAll() {
+               if (plantDescription == null) {
+                       try {
+                               plantDescription = initializeFieldObjectTextDataWithSupportTest(
+                                               Feature.DESCRIPTION(), false, false);
+                       } catch (DerivedUnitFacadeNotSupportedException e) {
+                               throw new IllegalStateException(notSupportMessage, e);
+                       }
+                       if (plantDescription == null) {
+                               return new HashMap<Language, LanguageString>();
+                       }
+               }
+               return plantDescription.getMultilanguageText();
+       }
+
+       public void setPlantDescription(String plantDescription) {
+               setPlantDescription(plantDescription, null);
+       }
+
+       public void setPlantDescription(String plantDescriptionText, Language language) {
+               if (language == null) {
+                       language = Language.DEFAULT();
+               }
+               boolean isEmpty = StringUtils.isBlank(plantDescriptionText);
+               if (plantDescription == null) {
+                       try {
+                               plantDescription = initializeFieldObjectTextDataWithSupportTest(
+                                               Feature.DESCRIPTION(), !isEmpty, false);
+                       } catch (DerivedUnitFacadeNotSupportedException e) {
+                               throw new IllegalStateException(notSupportMessage, e);
+                       }
+               }
+               if (plantDescription != null){
+                       if (plantDescriptionText == null) {
+                               plantDescription.removeText(language);
+                       } else {
+                               plantDescription.putText(language, plantDescriptionText);
+                       }
+               }
+       }
+
+       public void removePlantDescription(Language language) {
+               setPlantDescription(null, language);
+       }
+
+       // field object definition
+       public void addFieldObjectDefinition(String text, Language language) {
+               getFieldUnit(true).putDefinition(language, text);
+       }
+
+       @Transient
+       public Map<Language, LanguageString> getFieldObjectDefinition() {
+               if (!hasFieldUnit()) {
+                       return new HashMap<Language, LanguageString>();
+               } else {
+                       return getFieldUnit(true).getDefinition();
+               }
+       }
+
+       public String getFieldObjectDefinition(Language language) {
+               Map<Language, LanguageString> map = getFieldObjectDefinition();
+               LanguageString languageString = (map == null ? null : map.get(language));
+               if (languageString != null) {
+                       return languageString.getText();
+               } else {
+                       return null;
+               }
+       }
+
+       public void removeFieldObjectDefinition(Language lang) {
+               if (hasFieldUnit()) {
+                       getFieldUnit(true).removeDefinition(lang);
+               }
+       }
+
+       // media
+       public boolean addFieldObjectMedia(Media media) {
+               try {
+                       return addMedia(media, getFieldUnit(true));
+               } catch (DerivedUnitFacadeNotSupportedException e) {
+                       throw new IllegalStateException(notSupportMessage, e);
+               }
+       }
+
+       /**
+        * Returns true, if an image gallery for the field object exists.<BR>
+        * Returns also <code>true</code> if the image gallery is empty.
+        *
+        * @return
+        */
+       public boolean hasFieldObjectImageGallery() {
+               if (!hasFieldObject()) {
+                       return false;
+               } else {
+                       return (getImageGallery(fieldUnit, false) != null);
+               }
+       }
+
+       public void setFieldObjectImageGallery(SpecimenDescription imageGallery)
+                       throws DerivedUnitFacadeNotSupportedException {
+               SpecimenDescription existingGallery = getFieldObjectImageGallery(false);
+
+               // test attached specimens contain this.derivedUnit
+               SpecimenOrObservationBase<?> facadeFieldUnit = innerFieldUnit();
+               testSpecimenInImageGallery(imageGallery, facadeFieldUnit);
+
+               if (existingGallery != null) {
+                       if (existingGallery != imageGallery) {
+                               throw new DerivedUnitFacadeNotSupportedException(
+                                               "DerivedUnitFacade does not allow more than one image gallery");
+                       } else {
+                               // do nothing
+                       }
+               } else {
+                       TextData textData = testImageGallery(imageGallery);
+                       this.fieldObjectMediaTextData = textData;
+               }
+       }
+
+       /**
+        * Returns the field object image gallery. If no such image gallery exists
+        * and createIfNotExists is true an new one is created. Otherwise null is
+        * returned.
+        *
+        * @param createIfNotExists
+        * @return
+        */
+       public SpecimenDescription getFieldObjectImageGallery(
+                       boolean createIfNotExists) {
+               TextData textData;
+               try {
+                       textData = initializeFieldObjectTextDataWithSupportTest(
+                                       Feature.IMAGE(), createIfNotExists, true);
+               } catch (DerivedUnitFacadeNotSupportedException e) {
+                       throw new IllegalStateException(notSupportMessage, e);
+               }
+               if (textData != null) {
+                       return CdmBase.deproxy(textData.getInDescription(),
+                                       SpecimenDescription.class);
+               } else {
+                       return null;
+               }
+       }
+
+       /**
+        * Returns the media for the field object.<BR>
+        *
+        * @return
+        */
+       @Transient
+       public List<Media> getFieldObjectMedia() {
+               try {
+                       List<Media> result = getMediaList(getFieldUnit(false), false);
+                       return result == null ? new ArrayList<Media>() : result;
+               } catch (DerivedUnitFacadeNotSupportedException e) {
+                       throw new IllegalStateException(notSupportMessage, e);
+               }
+       }
+
+       public boolean removeFieldObjectMedia(Media media) {
+               try {
+                       return removeMedia(media, getFieldUnit(false));
+               } catch (DerivedUnitFacadeNotSupportedException e) {
+                       throw new IllegalStateException(notSupportMessage, e);
+               }
+       }
+
+       // field number
+       @Transient
+       public String getFieldNumber() {
+               if (!hasFieldUnit()) {
+                       return null;
+               } else {
+                       return getFieldUnit(true).getFieldNumber();
+               }
+       }
+
+       public void setFieldNumber(String fieldNumber) {
+               getFieldUnit(true).setFieldNumber(fieldNumber);
+       }
+
+       // primary collector
+       @Transient
+       public Person getPrimaryCollector() {
+               if (!hasFieldUnit()) {
+                       return null;
+               } else {
+                       return getFieldUnit(true).getPrimaryCollector();
+               }
+       }
+
+       public void setPrimaryCollector(Person primaryCollector) {
+               getFieldUnit(true).setPrimaryCollector(primaryCollector);
+       }
+
+       // field notes
+       @Transient
+       public String getFieldNotes() {
+               if (!hasFieldUnit()) {
+                       return null;
+               } else {
+                       return getFieldUnit(true).getFieldNotes();
+               }
+       }
+
+       public void setFieldNotes(String fieldNotes) {
+               getFieldUnit(true).setFieldNotes(fieldNotes);
+       }
+
+       // individual counts
+       @Transient
+       public Integer getIndividualCount() {
+               return (hasFieldUnit() ? getFieldUnit(true).getIndividualCount() : null);
+       }
+
+       public void setIndividualCount(Integer individualCount) {
+               getFieldUnit(true).setIndividualCount(individualCount);
+       }
+
+       // life stage
+       @Transient
+       public DefinedTerm getLifeStage() {
+               return (hasFieldUnit() ? getFieldUnit(true).getLifeStage() : null);
+       }
+
+       public void setLifeStage(DefinedTerm lifeStage) {
+               getFieldUnit(true).setLifeStage(lifeStage);
+       }
+
+       // sex
+       @Transient
+       public DefinedTerm getSex() {
+               return (hasFieldUnit() ? getFieldUnit(true).getSex(): null);
+       }
+
+       public void setSex(DefinedTerm sex) {
+               getFieldUnit(true).setSex(sex);
+       }
+
+       // kind of Unit
+       @Transient
+       public DefinedTerm getKindOfUnit() {
+               return (hasFieldUnit() ? getFieldUnit(true).getKindOfUnit() : null);
+       }
+
+       public void setKindOfUnit(DefinedTerm kindOfUnit) {
+               getFieldUnit(true).setKindOfUnit(kindOfUnit);
+       }
+
+
+       // field unit
+       public boolean hasFieldUnit() {
+               return (getFieldUnit(CREATE_NOT) != null);
+       }
+
+       /**
+        * Returns the field unit as an object.
+        *
+        * @return
+        */
+       public FieldUnit innerFieldUnit() {
+               return getFieldUnit(CREATE_NOT);
+       }
+
+       /**
+        * Returns the field unit as an object.
+        *
+        * @return
+        */
+       public FieldUnit getFieldUnit(boolean createIfNotExists) {
+               if (fieldUnit == null && createIfNotExists) {
+                       setFieldUnit(FieldUnit.NewInstance());
+               }
+               return this.fieldUnit;
+       }
+
+
+       private void setFieldUnit(FieldUnit fieldUnit) {
+               this.fieldUnit = fieldUnit;
+               if (fieldUnit != null){
+                       if (config.isFirePropertyChangeEvents()){
+                               addNewEventPropagationListener(fieldUnit);
+                       }
+                       if (derivedUnit != null){
+                               DerivationEvent derivationEvent = getDerivationEvent(CREATE);
+                               derivationEvent.addOriginal(fieldUnit);
+                       }
+                       setFieldUnitCacheStrategy();
+               }
+       }
+
+       // ****************** Specimen *******************************************
+
+       // Definition
+       public void addDerivedUnitDefinition(String text, Language language) {
+               innerDerivedUnit().putDefinition(language, text);
+       }
+
+       @Transient
+       public Map<Language, LanguageString> getDerivedUnitDefinitions() {
+               return ! checkDerivedUnit()? null : this.derivedUnit.getDefinition();
+       }
+
+
+       public String getDerivedUnitDefinition(Language language) {
+               if (! checkDerivedUnit()){
+                       return null;
+               }
+               Map<Language, LanguageString> languageMap = derivedUnit.getDefinition();
+               LanguageString languageString = languageMap.get(language);
+               if (languageString != null) {
+                       return languageString.getText();
+               } else {
+                       return null;
+               }
+       }
+
+       public void removeDerivedUnitDefinition(Language lang) {
+               testDerivedUnit();
+               derivedUnit.removeDefinition(lang);
+       }
+
+       // Determination
+       public void addDetermination(DeterminationEvent determination) {
+               //TODO implement correct bidirectional mapping in model classes
+               determination.setIdentifiedUnit(baseUnit());
+               baseUnit().addDetermination(determination);
+       }
+
+       @Transient
+       public DeterminationEvent getPreferredDetermination() {
+               Set<DeterminationEvent> events = baseUnit().getDeterminations();
+               for (DeterminationEvent event : events){
+                       if (event.getPreferredFlag() == true){
+                               return event;
+                       }
+               }
+               return null;
+       }
+
+       /**
+        * This method returns the preferred determination.
+        * @see #getOtherDeterminations()
+        * @see #getDeterminations()
+        * @return
+        */
+       @Transient
+       public void setPreferredDetermination(DeterminationEvent newEvent) {
+               Set<DeterminationEvent> events = baseUnit().getDeterminations();
+               for (DeterminationEvent event : events){
+                       if (event.getPreferredFlag() == true){
+                               event.setPreferredFlag(false);
+                       }
+               }
+               newEvent.setPreferredFlag(true);
+               events.add(newEvent);
+       }
+
+       /**
+        * This method returns all determinations except for the preferred one.
+        * @see #getPreferredDetermination()
+        * @see #getDeterminations()
+        * @return
+        */
+       @Transient
+       public Set<DeterminationEvent> getOtherDeterminations() {
+               Set<DeterminationEvent> events = baseUnit().getDeterminations();
+               Set<DeterminationEvent> result = new HashSet<DeterminationEvent>();
+               for (DeterminationEvent event : events){
+                       if (event.getPreferredFlag() != true){
+                               result.add(event);
+                       }
+               }
+               return result;
+       }
+
+       /**
+        * This method returns all determination events. The preferred one {@link #getPreferredDetermination()}
+        * and all others {@link #getOtherDeterminations()}.
+        * @return
+        */
+       @Transient
+       public Set<DeterminationEvent> getDeterminations() {
+               return baseUnit().getDeterminations();
+       }
+
+       public void removeDetermination(DeterminationEvent determination) {
+               baseUnit().removeDetermination(determination);
+       }
+
+       // Media
+       public boolean addDerivedUnitMedia(Media media) {
+               testDerivedUnit();
+               try {
+                       return addMedia(media, derivedUnit);
+               } catch (DerivedUnitFacadeNotSupportedException e) {
+                       throw new IllegalStateException(notSupportMessage, e);
+               }
+       }
+
+       /**
+        * Returns true, if an image gallery exists for the specimen.<BR>
+        * Returns also <code>true</code> if the image gallery is empty.
+        */
+       public boolean hasDerivedUnitImageGallery() {
+               return (getImageGallery(derivedUnit, false) != null);
+       }
+
+       public SpecimenDescription getDerivedUnitImageGallery(boolean createIfNotExists) {
+               if (!checkDerivedUnit()){
+                       return null;
+               }
+               TextData textData;
+               try {
+                       textData = inititializeTextDataWithSupportTest(Feature.IMAGE(),
+                                       derivedUnit, createIfNotExists, true);
+               } catch (DerivedUnitFacadeNotSupportedException e) {
+                       throw new IllegalStateException(notSupportMessage, e);
+               }
+               if (textData != null) {
+                       return CdmBase.deproxy(textData.getInDescription(),
+                                       SpecimenDescription.class);
+               } else {
+                       return null;
+               }
+       }
+
+       public void setDerivedUnitImageGallery(SpecimenDescription imageGallery)
+                       throws DerivedUnitFacadeNotSupportedException {
+               testDerivedUnit();
+               SpecimenDescription existingGallery = getDerivedUnitImageGallery(false);
+
+               // test attached specimens contain this.derivedUnit
+               SpecimenOrObservationBase facadeDerivedUnit = innerDerivedUnit();
+               testSpecimenInImageGallery(imageGallery, facadeDerivedUnit);
+
+               if (existingGallery != null) {
+                       if (existingGallery != imageGallery) {
+                               throw new DerivedUnitFacadeNotSupportedException(
+                                               "DerivedUnitFacade does not allow more than one image gallery");
+                       } else {
+                               // do nothing
+                       }
+               } else {
+                       TextData textData = testImageGallery(imageGallery);
+                       this.derivedUnitMediaTextData = textData;
+               }
+       }
+
+       /**
+        * @param imageGallery
+        * @throws DerivedUnitFacadeNotSupportedException
+        */
+       private void testSpecimenInImageGallery(SpecimenDescription imageGallery, SpecimenOrObservationBase specimen)
+                               throws DerivedUnitFacadeNotSupportedException {
+               SpecimenOrObservationBase imageGallerySpecimen = imageGallery.getDescribedSpecimenOrObservation();
+               if (imageGallerySpecimen == null) {
+                       throw new DerivedUnitFacadeNotSupportedException(
+                                       "Image Gallery has no Specimen attached. Please attache according specimen or field unit.");
+               }
+               if (! imageGallerySpecimen.equals(specimen)) {
+                       throw new DerivedUnitFacadeNotSupportedException(
+                                       "Image Gallery has not the facade's field object attached. Please add field object first " +
+                                       "to image gallery specimenOrObservation list.");
+               }
+       }
+
+       /**
+        * Returns the media for the specimen.<BR>
+        *
+        * @return
+        */
+       @Transient
+       public List<Media> getDerivedUnitMedia() {
+               if (! checkDerivedUnit()){
+                       return new ArrayList<Media>();
+               }
+               try {
+                       List<Media> result = getMediaList(derivedUnit, false);
+                       return result == null ? new ArrayList<Media>() : result;
+               } catch (DerivedUnitFacadeNotSupportedException e) {
+                       throw new IllegalStateException(notSupportMessage, e);
+               }
+       }
+
+       public boolean removeDerivedUnitMedia(Media media) {
+               testDerivedUnit();
+               try {
+                       return removeMedia(media, derivedUnit);
+               } catch (DerivedUnitFacadeNotSupportedException e) {
+                       throw new IllegalStateException(notSupportMessage, e);
+               }
+       }
+
+       // Accession Number
+       @Transient
+       public String getAccessionNumber() {
+               return ! checkDerivedUnit()? null : derivedUnit.getAccessionNumber();
+       }
+
+       public void setAccessionNumber(String accessionNumber) {
+               testDerivedUnit();
+               derivedUnit.setAccessionNumber(accessionNumber);
+       }
+
+       @Transient
+       public String getCatalogNumber() {
+               return ! checkDerivedUnit()? null : derivedUnit.getCatalogNumber();
+       }
+
+       public void setCatalogNumber(String catalogNumber) {
+               testDerivedUnit();
+               derivedUnit.setCatalogNumber(catalogNumber);
+       }
+
+       @Transient
+       public String getBarcode() {
+               return ! checkDerivedUnit()? null : derivedUnit.getBarcode();
+       }
+
+       public void setBarcode(String barcode) {
+               testDerivedUnit();
+               derivedUnit.setBarcode(barcode);
+       }
+
+       // Preservation Method
+
+       /**
+        * Only supported by specimen and fossils
+        *
+        * @see #DerivedUnitType
+        * @return
+        */
+       @Transient
+       public PreservationMethod getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException {
+               if (derivedUnit!=null && derivedUnit.getRecordBasis().isPreservedSpecimen()) {
+                       return CdmBase.deproxy(derivedUnit, DerivedUnit.class).getPreservation();
+               } else {
+                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
+                               throw new MethodNotSupportedByDerivedUnitTypeException(
+                                               "A preservation method is only available in derived units of type 'Preserved Specimen' or one of its specializations like 'Fossil Specimen' ");
+                       } else {
+                               return null;
+                       }
+               }
+       }
+
+       /**
+        * Only supported by specimen and fossils
+        *
+        * @see #DerivedUnitType
+        * @return
+        */
+       public void setPreservationMethod(PreservationMethod preservation)
+                       throws MethodNotSupportedByDerivedUnitTypeException {
+               if (derivedUnit!=null && derivedUnit.getRecordBasis().isPreservedSpecimen()) {
+                       CdmBase.deproxy(derivedUnit, DerivedUnit.class).setPreservation(preservation);
+               } else {
+                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
+                               throw new MethodNotSupportedByDerivedUnitTypeException(
+                                               "A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");
+                       } else {
+                               return;
+                       }
+               }
+       }
+
+       // Stored under name
+       @Transient
+       public TaxonNameBase getStoredUnder() {
+               return ! checkDerivedUnit()? null : derivedUnit.getStoredUnder();
+       }
+
+       public void setStoredUnder(TaxonNameBase storedUnder) {
+               testDerivedUnit();
+               derivedUnit.setStoredUnder(storedUnder);
+       }
+
+       // title cache
+       public String getTitleCache() {
+               SpecimenOrObservationBase<?> titledUnit = baseUnit();
+
+               if (!titledUnit.isProtectedTitleCache()) {
+                       // always compute title cache anew as long as there are no property
+                       // change listeners on
+                       // field unit, gathering event etc
+                       titledUnit.setTitleCache(null, false);
+               }
+               return titledUnit.getTitleCache();
+       }
+
+       public boolean isProtectedTitleCache() {
+               return baseUnit().isProtectedTitleCache();
+       }
+
+       public void setTitleCache(String titleCache, boolean isProtected) {
+               this.baseUnit().setTitleCache(titleCache, isProtected);
+       }
+
+       /**
+        * Returns the derived unit itself.
+        *
+        * @return the derived unit
+        */
+       public DerivedUnit innerDerivedUnit() {
+               return this.derivedUnit;
+       }
+
+//     /**
+//      * Returns the derived unit itself.
+//      *
+//      * @return the derived unit
+//      */
+//     public DerivedUnit innerDerivedUnit(boolean createIfNotExists) {
+//             DerivedUnit result = this.derivedUnit;
+//             if (result == null && createIfNotExists){
+//                     if (this.fieldUnit == null){
+//                             String message = "Field unit must exist to create derived unit.";
+//                             throw new IllegalStateException(message);
+//                     }else{
+//                             DerivedUnit =
+//                             DerivationEvent derivationEvent = getDerivationEvent(true);
+//                             derivationEvent.addOriginal(fieldUnit);
+//                             return this.derivedUnit;
+//                     }
+//             }
+//     }
+
+       private boolean hasDerivationEvent() {
+               return getDerivationEvent() == null ? false : true;
+       }
+
+       private DerivationEvent getDerivationEvent() {
+               return getDerivationEvent(CREATE_NOT);
+       }
+
+       /**
+        * Returns the derivation event. If no derivation event exists and <code>createIfNotExists</code>
+        * is <code>true</code> a new derivation event is created and returned.
+        * Otherwise <code>null</code> is returned.
+        * @param createIfNotExists
+        */
+       private DerivationEvent getDerivationEvent(boolean createIfNotExists) {
+               DerivationEvent result = null;
+               if (derivedUnit != null){
+                       result = derivedUnit.getDerivedFrom();
+               }else{
+                       return null;
+               }
+               if (result == null && createIfNotExists) {
+                       DerivationEventType type = null;
+                       if (isAccessioned(derivedUnit)){
+                               type = DerivationEventType.ACCESSIONING();
+                       }
+
+                       result = DerivationEvent.NewInstance(type);
+                       derivedUnit.setDerivedFrom(result);
+               }
+               return result;
+       }
+
+       /**
+        * TODO still unclear which classes do definetly require accessioning.
+        * Only return true for those classes which are clear.
+        * @param derivedUnit
+        * @return
+        */
+       private boolean isAccessioned(DerivedUnit derivedUnit) {
+               if (derivedUnit.getRecordBasis().equals(SpecimenOrObservationType.PreservedSpecimen) ){
+                       return true;   //maybe also subtypes should be true
+               }else{
+                       return false;
+               }
+       }
+
+       @Transient
+       public String getExsiccatum() throws MethodNotSupportedByDerivedUnitTypeException {
+               testDerivedUnit();
+               if (derivedUnit.getRecordBasis().isPreservedSpecimen()) {
+                       return derivedUnit.getExsiccatum();
+               } else {
+                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
+                               throw new MethodNotSupportedByDerivedUnitTypeException(
+                                               "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
+                       } else {
+                               return null;
+                       }
+               }
+       }
+
+       public void setExsiccatum(String exsiccatum) throws Exception {
+               testDerivedUnit();
+               if (derivedUnit.getRecordBasis().isPreservedSpecimen()) {
+                       derivedUnit.setExsiccatum(exsiccatum);
+               } else {
+                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()) {
+                               throw new MethodNotSupportedByDerivedUnitTypeException(
+                                               "An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");
+                       } else {
+                               return;
+                       }
+               }
+       }
+
+       /**
+        * Returns the original label information of the derived unit.
+        * @return
+        */
+       @Transient
+       public String getOriginalLabelInfo() {
+               return ! checkDerivedUnit()? null : derivedUnit.getOriginalLabelInfo();
+       }
+       public void setOriginalLabelInfo(String originalLabelInfo) {
+               testDerivedUnit();
+               derivedUnit.setOriginalLabelInfo(originalLabelInfo);
+       }
+
+       // **** sources **/
+       public void addSource(IdentifiableSource source) {
+               this.baseUnit().addSource(source);
+       }
+
+       /**
+        * Creates an {@link IOriginalSource orignal source} or type ,
+        * adds it to the specimen and returns it.
+        *
+        * @param reference
+        * @param microReference
+        * @param originalNameString
+        * @return
+        */
+       public IdentifiableSource addSource(OriginalSourceType type, Reference reference, String microReference, String originalNameString) {
+               IdentifiableSource source = IdentifiableSource.NewInstance(type, null, null, reference, microReference);
+               source.setOriginalNameString(originalNameString);
+               addSource(source);
+               return source;
+       }
+
+       @Transient
+       public Set<IdentifiableSource> getSources() {
+               return baseUnit().getSources();
+       }
+
+       public void removeSource(IdentifiableSource source) {
+               this.baseUnit().removeSource(source);
+       }
+
+       //*** identifiers ***/
+
+
+    public void addIdentifier(Identifier identifier) {
+        this.baseUnit().addIdentifier(identifier);
+    }
+
+       @Transient
+       public List<Identifier> getIdentifiers() {
+           return baseUnit().getIdentifiers();
+       }
+
+       public void removeIdentifier(Identifier identifier) {
+           this.baseUnit().removeIdentifier(identifier);
+       }
+
+       @Transient
+       public Set<Rights> getRights() {
+               return baseUnit().getRights();
+       }
+
+       /**
+        * @return the collection
+        */
+       @Transient
+       public Collection getCollection() {
+               return ! checkDerivedUnit()? null :  derivedUnit.getCollection();
+       }
+
+       /**
+        * @param collection
+        *            the collection to set
+        */
+       public void setCollection(Collection collection) {
+               testDerivedUnit();
+               derivedUnit.setCollection(collection);
+       }
+
+       // annotation
+       public void addAnnotation(Annotation annotation) {
+               this.baseUnit().addAnnotation(annotation);
+       }
+
+       @Transient
+       public void getAnnotations() {
+               this.baseUnit().getAnnotations();
+       }
+
+       public void removeAnnotation(Annotation annotation) {
+               this.baseUnit().removeAnnotation(annotation);
+       }
+
+       // ******************************* Events ***************************
+
+       //set of events that were currently fired by this facades field unit
+       //to avoid recursive fireing of the same event
+       private final Set<PropertyChangeEvent> fireingEvents = new HashSet<PropertyChangeEvent>();
+
+       /**
+        * @return
+        */
+       private void addNewEventPropagationListener(CdmBase listeningObject) {
+               //if there is already a listener, don't do anything
+               for (PropertyChangeListener listener : this.listeners.keySet()){
+                       if (listeners.get(listener) == listeningObject){
+                               return;
+                       }
+               }
+               //create new listener
+               PropertyChangeListener listener = new PropertyChangeListener() {
+                       @Override
+                       public void propertyChange(PropertyChangeEvent event) {
+                               if (derivedUnit != null){
+                                       derivedUnit.firePropertyChange(event);
+                               }else{
+                                       if (! event.getSource().equals(fieldUnit) && ! fireingEvents.contains(event)  ){
+                                               fireingEvents.add(event);
+                                               fieldUnit.firePropertyChange(event);
+                                               fireingEvents.remove(event);
+                                       }
+                               }
+                       }
+               };
+               //add listener to listening object and to list of listeners
+               listeningObject.addPropertyChangeListener(listener);
+               listeners.put(listener, listeningObject);
+       }
+
+       // **************** Other Collections ********************************
+
+       /**
+        * Creates a duplicate specimen which derives from the same derivation event
+        * as the facade specimen and adds collection data to it (all data available
+        * in DerivedUnit and Specimen. Data from SpecimenOrObservationBase and
+        * above are not yet shared at the moment.
+        *
+        * @param collection
+        * @param catalogNumber
+        * @param accessionNumber
+        * @param storedUnder
+        * @param preservation
+        * @return
+        */
+       public DerivedUnit addDuplicate(Collection collection, String catalogNumber,
+                       String accessionNumber, TaxonNameBase storedUnder, PreservationMethod preservation){
+               testDerivedUnit();
+               DerivedUnit duplicate = DerivedUnit.NewPreservedSpecimenInstance();
+               duplicate.setDerivedFrom(getDerivationEvent(CREATE));
+               duplicate.setCollection(collection);
+               duplicate.setCatalogNumber(catalogNumber);
+               duplicate.setAccessionNumber(accessionNumber);
+               duplicate.setStoredUnder(storedUnder);
+               duplicate.setPreservation(preservation);
+               return duplicate;
+       }
+
+       public void addDuplicate(DerivedUnit duplicateSpecimen) {
+               testDerivedUnit();
+               getDerivationEvent(CREATE).addDerivative(duplicateSpecimen);
+       }
+
+       @Transient
+       public Set<DerivedUnit> getDuplicates() {
+               if (! checkDerivedUnit()){
+                       return new HashSet<DerivedUnit>();
+               }
+               Set<DerivedUnit> result = new HashSet<DerivedUnit>();
+               if (hasDerivationEvent()) {
+                       for (DerivedUnit derivedUnit : getDerivationEvent(CREATE)
+                                       .getDerivatives()) {
+                               if (derivedUnit.isInstanceOf(DerivedUnit.class)
+                                               && !derivedUnit.equals(this.derivedUnit)) {
+                                       result.add(CdmBase.deproxy(derivedUnit, DerivedUnit.class));
+                               }
+                       }
+               }
+               return result;
+       }
+
+       public void removeDuplicate(DerivedUnit duplicateSpecimen) {
+               testDerivedUnit();
+               if (hasDerivationEvent()) {
+                       getDerivationEvent(CREATE).removeDerivative(duplicateSpecimen);
+               }
+       }
+
+       public SpecimenOrObservationBase<?> baseUnit(){
+           if(derivedUnit!=null){
+               return derivedUnit;
+           }
+           else if(fieldUnit!=null){
+               return fieldUnit;
+           }
+           else{
+               throw new IllegalStateException("A DerivedUnitFacade must always have either a field unit or a derived unit");
+           }
+       }
+
+
+       private boolean checkDerivedUnit()  {
+               if (derivedUnit == null){
+                       return false;
+               }else{
+                       return true;
+               }
+       }
+
+       private void testDerivedUnit() /* throws MethodNotSupportedByDerivedUnitTypeException */ {
+               if (derivedUnit == null){
+                       throw new IllegalStateException("This method is not allowed for this specimen or observation type. Probably you have tried to add specimen(derived unit) information to a field unit");
+               }
+       }
+
+       public void setType(SpecimenOrObservationType type) {
+               if (type == null){
+                       throw new IllegalArgumentException("The type of a specimen or observation may not be null");
+               }
+               SpecimenOrObservationBase<?> baseUnit = baseUnit();
+               if(baseUnit.isInstanceOf(FieldUnit.class) && !type.isFieldUnit()){
+                   throw new IllegalArgumentException("A FieldUnit may only be of type FieldUnit") ;
+               }
+               else if(baseUnit.isInstanceOf(DerivedUnit.class) && type.isFieldUnit()){
+                   throw new IllegalArgumentException("A derived unit may not be of type FieldUnit") ;
+               }
+               baseUnit.setRecordBasis(type);
+       }
+
+       public SpecimenOrObservationType getType() {
+           return baseUnit().getRecordBasis();
+       }
+
+       /**
+        * Closes this facade. As a minimum this method removes all listeners created by this facade from their
+        * listening objects.
+        */
+       public void close(){
+               for (PropertyChangeListener listener : this.listeners.keySet()){
+                       CdmBase listeningObject = listeners.get(listener);
+                       listeningObject.removePropertyChangeListener(listener);
+               }
+       }
+
+
+       /**
+        * Computes the correct distance string for given values for min, max and text.
+        * If text is not blank, text is returned, otherwise "min - max" or a single value is returned.
+        * @param min min value as number
+        * @param max max value as number
+        * @param text text representation of distance
+        * @return the formatted distance string
+        */
+       private String distanceString(Number min, Number max, String text, String unit) {
+               if (StringUtils.isNotBlank(text)){
+                       return text;
+               }else{
+                       String minStr = min == null? null : String.valueOf(min);
+                       String maxStr = max == null? null : String.valueOf(max);
+                       String result = CdmUtils.concat(UTF8.EN_DASH_SPATIUM.toString(), minStr, maxStr);
+                       if (StringUtils.isNotBlank(result) && StringUtils.isNotBlank(unit)){
+                               result = result + " " + unit;
+                       }
+                       return result;
+               }
+       }
+
+       /**
+        * First checks the inner field unit for the publish flag. If set to <code>true</code>
+        * then <code>true</code> is returned. If the field unit is <code>null</code> the inner derived unit
+        * is checked.
+        * @return <code>true</code> if this facade can be published
+        */
+       public boolean isPublish(){
+           if(fieldUnit!=null){
+               return fieldUnit.isPublish();
+           }
+           if(derivedUnit!=null){
+               return derivedUnit.isPublish();
+           }
+           return false;
+       }
+}
index e80cb8dc042ee48a6b9777a17a1c978e21ade252..6f7cd7b8b24d251ed6533242a95b999132a2125a 100644 (file)
 package eu.etaxonomy.cdm.api.facade;
 
 import java.io.FileNotFoundException;
+import java.util.UUID;
 
 import org.apache.log4j.Logger;
+import org.hibernate.proxy.HibernateProxy;
 import org.junit.Assert;
 import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
 import org.junit.Test;
+import org.unitils.dbunit.annotation.DataSet;
+import org.unitils.spring.annotation.SpringBeanByType;
 
+import eu.etaxonomy.cdm.api.service.IDescriptionService;
+import eu.etaxonomy.cdm.api.service.IOccurrenceService;
+import eu.etaxonomy.cdm.api.service.ITaxonService;
 import eu.etaxonomy.cdm.model.agent.Person;
 import eu.etaxonomy.cdm.model.agent.Team;
+import eu.etaxonomy.cdm.model.common.CdmBase;
+import eu.etaxonomy.cdm.model.common.DefaultTermInitializer;
 import eu.etaxonomy.cdm.model.common.DefinedTerm;
 import eu.etaxonomy.cdm.model.common.Language;
 import eu.etaxonomy.cdm.model.common.LanguageString;
 import eu.etaxonomy.cdm.model.common.TimePeriod;
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
 import eu.etaxonomy.cdm.model.location.Country;
 import eu.etaxonomy.cdm.model.location.NamedArea;
 import eu.etaxonomy.cdm.model.location.Point;
@@ -37,8 +50,10 @@ import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
 import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
 import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
+import eu.etaxonomy.cdm.model.taxon.Taxon;
 import eu.etaxonomy.cdm.strategy.parser.TimePeriodParser;
-import eu.etaxonomy.cdm.test.integration.CdmIntegrationTest;
+import eu.etaxonomy.cdm.test.integration.CdmTransactionalIntegrationTest;
 
 /**
  * NOTE: The only reason for having this test is to test if injection of the cache strategy into a
@@ -49,66 +64,75 @@ import eu.etaxonomy.cdm.test.integration.CdmIntegrationTest;
  * @date 03.06.2010
  *
  */
-public class DerivedUnitFacadeCacheStrategyInjectionTest extends CdmIntegrationTest {
+public class DerivedUnitFacadeCacheStrategyInjectionTest extends CdmTransactionalIntegrationTest {
        @SuppressWarnings("unused")
        private static final Logger logger = Logger.getLogger(DerivedUnitFacadeCacheStrategyInjectionTest.class);
 
-       DerivedUnit specimen;
-       DerivationEvent derivationEvent;
-       FieldUnit fieldUnit;
-       GatheringEvent gatheringEvent;
-       Integer absoluteElevation = 40;
-       Integer absoluteElevationError = 2;
-       Team collector = Team.NewInstance();
-       String collectingMethod = "Collection Method";
-       Double distanceToGround = 22.0;
-       Double distanceToSurface = 50.0;
-       ReferenceSystem referenceSystem = ReferenceSystem.WGS84();
-       Point exactLocation = Point.NewInstance(12.3, 10.567, referenceSystem, 22);
-       String gatheringEventDescription = "A nice gathering description";
-       TimePeriod gatheringPeriod = TimePeriodParser.parseString("03.05.2005");
-       String ecology = "sand dunes";
-       String plantDescription = "flowers blue";
-
-       String fieldNumber = "5678";
-       String fieldNotes = "such a beautiful specimen";
-       Person primaryCollector;
-
-       Integer individualCount = 1;
-       DefinedTerm lifeStage = DefinedTerm.NewStageInstance("A wonderful stage", "stage", "st");
-       DefinedTerm sex = DefinedTerm.NewSexInstance("FemaleMale", "FM", "FM");
-       LanguageString locality = LanguageString.NewInstance("Berlin-Dahlem, E side of Englerallee", Language.DEFAULT());
-       NamedArea country = Country.GERMANY();
-
-       String exsiccatum = "Greuter, Pl. Dahlem. 456";
-       String accessionNumber = "8909756";
-       String catalogNumber = "UU879873590";
-       TaxonNameBase<?,?> taxonName = BotanicalName.NewInstance(Rank.GENUS(), "Abies", null, null, null, null, null, null, null);
-       String collectorsNumber = "234589913A34";
-       Collection collection = Collection.NewInstance();
-
-       PreservationMethod preservationMethod = PreservationMethod.NewInstance(null, "my prservation");
-
-//     DerivedUnitFacade specimenFacade;
-
-       DerivedUnit collectionSpecimen;
-       GatheringEvent existingGatheringEvent;
-       DerivationEvent firstDerivationEvent;
-       FieldUnit firstFieldObject;
-       Media media1 = Media.NewInstance();
+    @SpringBeanByType
+    private IOccurrenceService occService;
+    @SpringBeanByType
+    private ITaxonService taxonService;
+    @SpringBeanByType
+    private IDescriptionService descService;
+
+    private static final UUID taxonUuid = UUID.fromString("10cfb372-0b1a-4d82-9707-c5ffd2b93a55");
+
+
+    private DerivedUnit specimen;
+    private DerivationEvent derivationEvent;
+    private FieldUnit fieldUnit;
+    private GatheringEvent gatheringEvent;
+    private final Integer absoluteElevation = 40;
+    private final Integer absoluteElevationError = 2;
+    private final Team collector = Team.NewInstance();
+    private final String collectingMethod = "Collection Method";
+    private final Double distanceToGround = 22.0;
+    private final Double distanceToSurface = 50.0;
+       private final ReferenceSystem referenceSystem = ReferenceSystem.WGS84();
+    private final Point exactLocation = Point.NewInstance(12.3, 10.567, referenceSystem, 22);
+    private final String gatheringEventDescription = "A nice gathering description";
+    private final TimePeriod gatheringPeriod = TimePeriodParser.parseString("03.05.2005");
+    private final String ecology = "sand dunes";
+    private final String plantDescription = "flowers blue";
+
+    private final String fieldNumber = "5678";
+    private final String fieldNotes = "such a beautiful specimen";
+    private Person primaryCollector;
+
+    private final Integer individualCount = 1;
+       private final DefinedTerm lifeStage = DefinedTerm.NewStageInstance("A wonderful stage", "stage", "st");
+       private final DefinedTerm sex = DefinedTerm.NewSexInstance("FemaleMale", "FM", "FM");
+       private final NamedArea country = Country.GERMANY();
+    private final LanguageString locality = LanguageString.NewInstance("Berlin-Dahlem, E side of Englerallee", Language.DEFAULT());
+
+    private final String exsiccatum = "Greuter, Pl. Dahlem. 456";
+    private final String accessionNumber = "8909756";
+    private final String catalogNumber = "UU879873590";
+    private final TaxonNameBase<?,?> taxonName = BotanicalName.NewInstance(Rank.GENUS(), "Abies", null, null, null, null, null, null, null);
+    private final String collectorsNumber = "234589913A34";
+    private final Collection collection = Collection.NewInstance();
+
+    private final PreservationMethod preservationMethod = PreservationMethod.NewInstance(null, "my prservation");
+
+    private DerivedUnit collectionSpecimen;
+    private GatheringEvent existingGatheringEvent;
+    private DerivationEvent firstDerivationEvent;
+    private FieldUnit firstFieldObject;
+    private final Media media1 = Media.NewInstance();
 
 
 //****************************** SET UP *****************************************/
 
-// /**
-//  * @throws java.lang.Exception
-//  */
-// @BeforeClass
-// public static void setUpBeforeClass() throws Exception {
-//     // FIXME maybe this will cause problems in other tests
-//     // INDEED !!!! it causes problems thus this is replaced by making this test a  CdmIntegrationTest !!!
-//     new DefaultTermInitializer().initialize();
-// }
+     /**
+      * @throws java.lang.Exception
+      */
+     @BeforeClass
+     public static void setUpBeforeClass() throws Exception {
+         // FIXME maybe this will cause problems in other tests
+         // INDEED !!!! it causes problems thus this is replaced by making this test a  CdmIntegrationTest !!!
+         new DefaultTermInitializer().initialize();
+     }
+
        /**
         * @throws java.lang.Exception
         */
@@ -157,8 +181,6 @@ public class DerivedUnitFacadeCacheStrategyInjectionTest extends CdmIntegrationT
                specimen.setPreservation(preservationMethod);
                specimen.setExsiccatum(exsiccatum);
 
-//             specimenFacade = DerivedUnitFacade.NewInstance(specimen);
-
                //existing specimen with 2 derivation events in line
                collectionSpecimen = DerivedUnit.NewPreservedSpecimenInstance();
                DerivedUnit middleSpecimen = DerivedUnit.NewPreservedSpecimenInstance();
@@ -215,11 +237,75 @@ public class DerivedUnitFacadeCacheStrategyInjectionTest extends CdmIntegrationT
 //        DescriptionElementBase plantDescItem = TextData.NewInstance(Feature.DESCRIPTION(), plantDescription, Language.DEFAULT(), null);
 //        fieldUnitDescription.addElement(plantDescItem);
 
-        collection.setCode("B");
+        collection.setCode("B");  //no effect
         Assert.assertEquals(correctCache, fieldUnit.getTitleCache());
     }
 
+    //Test if even a hibernate proxy (javassist) class correctly loads the DerivedUnitCacheStrategy.
+    @Test
+    @DataSet
+    public void testPersistedDerivedUnit(){
+        Taxon taxon = (Taxon)this.taxonService.find(taxonUuid);
+
+        IndividualsAssociation indivAssoc = getDescriptionElement(taxon, 5000);
+        SpecimenOrObservationBase<?> specimen = indivAssoc.getAssociatedSpecimenOrObservation();
+        Assert.assertTrue("Specimen should be proxy otherwise the test does not test what it should", specimen instanceof HibernateProxy);
+        String expectedDerivedUnitCache = "Berlin-Dahlem, E side of Englerallee, alt. 40 m, 10°34'1.2\"N, 12°18'E, 3.5.2005, Kilian 5678, A. Muller & Kohlbecker; Greuter, Pl. Dahlem. 456 (8909756).";
+        Assert.assertEquals(expectedDerivedUnitCache, specimen.getTitleCache());
+    }
+
+    @Test
+    @DataSet
+    public void testPersistedFieldUnit(){
+        Taxon taxon = (Taxon)this.taxonService.find(taxonUuid);
+        IndividualsAssociation indivAssoc = getDescriptionElement(taxon, 5001);
+        SpecimenOrObservationBase<?> fieldUnit = indivAssoc.getAssociatedSpecimenOrObservation();
+        Assert.assertTrue("FieldUnit should be proxy otherwise the test does not test what it should", fieldUnit instanceof HibernateProxy);
+        FieldUnit myFieldUnit = CdmBase.deproxy(fieldUnit, FieldUnit.class);
+        myFieldUnit.setTitleCache(null);
+        String expectedFieldUnitCache = "Berlin-Dahlem, E side of Englerallee, alt. 40 m, 10°34'1.2\"N, 12°18'E, 3.5.2005, Kilian 5678, A. Muller & Kohlbecker.";
+        Assert.assertEquals(expectedFieldUnitCache, fieldUnit.getTitleCache());
+    }
+
+
+
+    /**
+     * @param taxon
+     * @param i
+     * @return
+     */
+    private IndividualsAssociation getDescriptionElement(Taxon taxon, int id) {
+        for (DescriptionElementBase el : taxon.getDescriptions().iterator().next().getElements()){
+            if (el.getId() == id) {
+                return (IndividualsAssociation)el;
+            }
+        }
+        return null;
+    }
+
     @Override
-    public void createTestDataSet() throws FileNotFoundException {}
+    @Test
+    @Ignore
+    public void createTestDataSet() throws FileNotFoundException {
+//        specimen.setUuid(derivedUnitUuid);
+//        fieldUnit.setUuid(fieldUnitUuid);
+//        Taxon taxon = Taxon.NewInstance(null, null);
+//        taxon.setUuid(taxonUuid);
+//        TaxonDescription desc = TaxonDescription.NewInstance(taxon);
+//
+//        IndividualsAssociation indAssoc = IndividualsAssociation.NewInstance(specimen);
+//        desc.addElement(indAssoc);
+//
+//        this.taxonService.saveOrUpdate(taxon);
+//
+//        setComplete();
+//        endTransaction();
+//
+//        writeDbUnitDataSetFile(new String[]{"SpecimenOrObservationBase",
+//                "DerivationEvent", "DescriptionBase","DescriptionElementBase",
+//                "GatheringEvent","AgentBase","LanguageString","TaxonNameBase",
+//                "TaxonBase","Collection",
+//                "MaterialOrMethodEvent","SpecimenOrObservationBase_DerivationEvent"});
+    }
 
 }
index f0e755620a96724fcf732f8e061a7c74741cfa5b..96d0331feefeb78afb11404d64d4a60603fea904 100644 (file)
@@ -189,5 +189,4 @@ public class DerivedUnitFacadeCacheStrategyTest extends CdmIntegrationTest {
 
     @Override
     public void createTestDataSet() throws FileNotFoundException {}
-
 }
diff --git a/cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/facade/DerivedUnitFacadeCacheStrategyInjectionTest.xml b/cdmlib-services/src/test/resources/eu/etaxonomy/cdm/api/facade/DerivedUnitFacadeCacheStrategyInjectionTest.xml
new file mode 100644 (file)
index 0000000..32ca714
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version='1.0' encoding='UTF-8'?>
+<dataset>
+  <SPECIMENOROBSERVATIONBASE DTYPE="DerivedUnit" ID="5000" CREATED="2016-02-12 10:10:56.0" UUID="3391fab9-7feb-4c64-992f-363ceee1e06b" UPDATED="[null]" RECORDBASIS="PS" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Berlin-Dahlem, E side of Englerallee, alt. 40 m, 10&#176;34&apos;1.2&quot;N, 12&#176;18&apos;E, 3.5.2005, Kilian 5678, A. Muller &amp; Kohlbecker; Greuter, Pl. Dahlem. 456 (8909756)." PUBLISH="true" INDIVIDUALCOUNT="[null]" ACCESSIONNUMBER="8909756" BARCODE="[null]" CATALOGNUMBER="UU879873590" COLLECTORSNUMBER="[null]" EXSICCATUM="Greuter, Pl. Dahlem. 456" ORIGINALLABELINFO="[null]" FIELDNOTES="[null]" FIELDNUMBER="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" LIFESTAGE_ID="[null]" SEX_ID="[null]" KINDOFUNIT_ID="[null]" COLLECTION_ID="5000" DERIVEDFROM_ID="5000" STOREDUNDER_ID="5000" PRESERVATION_ID="5000" GATHERINGEVENT_ID="[null]" PRIMARYCOLLECTOR_ID="[null]" MEDIASPECIMEN_ID="[null]" DNAQUALITY_ID="[null]"/>
+  <SPECIMENOROBSERVATIONBASE DTYPE="FieldUnit" ID="5001" CREATED="2016-02-12 10:10:56.0" UUID="a730ba6d-049d-4288-814a-499b1cff8eff" UPDATED="[null]" RECORDBASIS="FU" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Berlin-Dahlem, E side of Englerallee, alt. 40 m, 10&#176;34&apos;1.2&quot;N, 12&#176;18&apos;E, 3.5.2005, Kilian 5678, A. Muller &amp; Kohlbecker." PUBLISH="true" INDIVIDUALCOUNT="1" ACCESSIONNUMBER="[null]" BARCODE="[null]" CATALOGNUMBER="[null]" COLLECTORSNUMBER="[null]" EXSICCATUM="[null]" ORIGINALLABELINFO="[null]" FIELDNOTES="such a beautiful specimen" FIELDNUMBER="5678" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" LIFESTAGE_ID="[null]" SEX_ID="[null]" KINDOFUNIT_ID="[null]" COLLECTION_ID="[null]" DERIVEDFROM_ID="[null]" STOREDUNDER_ID="[null]" PRESERVATION_ID="[null]" GATHERINGEVENT_ID="5000" PRIMARYCOLLECTOR_ID="5001" MEDIASPECIMEN_ID="[null]" DNAQUALITY_ID="[null]"/>
+  <DERIVATIONEVENT ID="5000" CREATED="2016-02-12 10:10:56.0" UUID="c3bad832-2bc7-41c8-85eb-a526977072e1" UPDATED="[null]" DESCRIPTION="[null]" TIMEPERIOD_END="[null]" TIMEPERIOD_FREETEXT="[null]" TIMEPERIOD_START="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" ACTOR_ID="[null]" INSTITUTION_ID="[null]" TYPE_ID="2004"/>
+  <DESCRIPTIONBASE DTYPE="TaxonDescription" ID="5000" CREATED="2016-02-12 10:10:56.0" UUID="f705a8ba-2f9f-43a4-a97e-a67c1e2a7630" UPDATED="[null]" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Taxon description for Taxon#0&lt;10cfb372-0b1a-4d82-9707-c5ffd2b93a55&gt;" IMAGEGALLERY="false" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" SPECIMEN_ID="[null]" TAXON_ID="5000" TAXONNAME_ID="[null]"/>
+  <DESCRIPTIONELEMENTBASE DTYPE="IndividualsAssociation" ID="5000" CREATED="2016-02-12 10:10:56.0" UUID="d4892659-4c71-43e8-99dc-b12d862ffbfa" UPDATED="[null]" NAME="[null]" TIMEPERIOD_START="[null]" TIMEPERIOD_END="[null]" TIMEPERIOD_FREETEXT="[null]" ORDERRELEVANT="[null]" UNKNOWNDATA="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" FEATURE_ID="2052" INDESCRIPTION_ID="5000" AREA_ID="[null]" LANGUAGE_ID="[null]" STATUS_ID="[null]" ASSOCIATEDSPECIMENOROBSERVATION_ID="5000" UNIT_ID="[null]" TAXON2_ID="[null]" FORMAT_ID="[null]"/>
+  <DESCRIPTIONELEMENTBASE DTYPE="IndividualsAssociation" ID="5001" CREATED="2016-02-12 10:10:56.0" UUID="747e9b8b-4d7f-44b6-8081-84cd55657627" UPDATED="[null]" NAME="[null]" TIMEPERIOD_START="[null]" TIMEPERIOD_END="[null]" TIMEPERIOD_FREETEXT="[null]" ORDERRELEVANT="[null]" UNKNOWNDATA="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" FEATURE_ID="2052" INDESCRIPTION_ID="5000" AREA_ID="[null]" LANGUAGE_ID="[null]" STATUS_ID="[null]" ASSOCIATEDSPECIMENOROBSERVATION_ID="5001" UNIT_ID="[null]" TAXON2_ID="[null]" FORMAT_ID="[null]"/>
+  <GATHERINGEVENT ID="5000" CREATED="2016-02-12 10:10:56.0" UUID="b552c719-c618-4f1c-81ba-f73ed94fa743" UPDATED="[null]" DESCRIPTION="A nice gathering description" TIMEPERIOD_END="[null]" TIMEPERIOD_FREETEXT="[null]" TIMEPERIOD_START="20050503" ABSOLUTEELEVATION="40" ABSOLUTEELEVATIONMAX="[null]" ABSOLUTEELEVATIONTEXT="[null]" COLLECTINGMETHOD="Collection Method" DISTANCETOGROUND="22.0" DISTANCETOGROUNDMAX="[null]" DISTANCETOGROUNDTEXT="[null]" DISTANCETOWATERSURFACE="50.0" DISTANCETOWATERSURFACEMAX="[null]" DISTANCETOWATERSURFACETEXT="[null]" EXACTLOCATION_ERRORRADIUS="22" EXACTLOCATION_LATITUDE="10.567" EXACTLOCATION_LONGITUDE="12.3" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" ACTOR_ID="5000" COUNTRY_ID="[null]" EXACTLOCATION_REFERENCESYSTEM_ID="[null]" LOCALITY_ID="5000"/>
+  <AGENTBASE DTYPE="Team" ID="5000" CREATED="2016-02-12 10:10:14.0" UUID="64730212-0fdf-4caf-8e3e-b0945b6697ea" UPDATED="[null]" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Kilian, Andreas Muller &amp; Kohlbecker" PROTECTEDCOLLECTORTITLECACHE="false" COLLECTORTITLE="[null]" PROTECTEDNOMENCLATURALTITLECACHE="false" NOMENCLATURALTITLE="Kilian, Andreas Muller &amp; Kohlbecker" CODE="[null]" NAME="[null]" FIRSTNAME="[null]" LASTNAME="[null]" LIFESPAN_END="[null]" LIFESPAN_FREETEXT="[null]" LIFESPAN_START="[null]" PREFIX="[null]" SUFFIX="[null]" HASMOREMEMBERS="false" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" ISPARTOF_ID="[null]"/>
+  <AGENTBASE DTYPE="Person" ID="5001" CREATED="2016-02-12 10:10:56.0" UUID="277cf878-461c-4722-a995-8952ef79b238" UPDATED="[null]" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="true" TITLECACHE="Kilian" PROTECTEDCOLLECTORTITLECACHE="[null]" COLLECTORTITLE="[null]" PROTECTEDNOMENCLATURALTITLECACHE="[null]" NOMENCLATURALTITLE="Kilian" CODE="[null]" NAME="[null]" FIRSTNAME="[null]" LASTNAME="[null]" LIFESPAN_END="[null]" LIFESPAN_FREETEXT="[null]" LIFESPAN_START="[null]" PREFIX="[null]" SUFFIX="[null]" HASMOREMEMBERS="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" ISPARTOF_ID="[null]"/>
+  <AGENTBASE DTYPE="Person" ID="5002" CREATED="2016-02-12 10:10:56.0" UUID="b6e3c3c0-2488-4aa5-ad5d-addea20fca23" UPDATED="[null]" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Andreas Muller" PROTECTEDCOLLECTORTITLECACHE="[null]" COLLECTORTITLE="[null]" PROTECTEDNOMENCLATURALTITLECACHE="[null]" NOMENCLATURALTITLE="Andreas Muller" CODE="[null]" NAME="[null]" FIRSTNAME="Andreas" LASTNAME="Muller" LIFESPAN_END="[null]" LIFESPAN_FREETEXT="[null]" LIFESPAN_START="[null]" PREFIX="[null]" SUFFIX="[null]" HASMOREMEMBERS="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" ISPARTOF_ID="[null]"/>
+  <AGENTBASE DTYPE="Person" ID="5003" CREATED="2016-02-12 10:10:56.0" UUID="0015d68b-9117-4aaf-8c67-a06e9fe618a4" UPDATED="[null]" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="true" TITLECACHE="Kohlbecker" PROTECTEDCOLLECTORTITLECACHE="[null]" COLLECTORTITLE="[null]" PROTECTEDNOMENCLATURALTITLECACHE="[null]" NOMENCLATURALTITLE="Kohlbecker" CODE="[null]" NAME="[null]" FIRSTNAME="[null]" LASTNAME="[null]" LIFESPAN_END="[null]" LIFESPAN_FREETEXT="[null]" LIFESPAN_START="[null]" PREFIX="[null]" SUFFIX="[null]" HASMOREMEMBERS="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" ISPARTOF_ID="[null]"/>
+  <LANGUAGESTRING ID="5000" CREATED="2016-02-12 10:10:14.0" UUID="2bf751e0-75f0-489d-810e-49be03e2d999" UPDATED="[null]" TEXT="Berlin-Dahlem, E side of Englerallee" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" LANGUAGE_ID="[null]"/>
+  <TAXONNAMEBASE DTYPE="BotanicalName" ID="5000" CREATED="2016-02-12 10:10:14.0" UUID="11151b8d-1657-4842-af8e-5b285a094717" UPDATED="[null]" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Abies" APPENDEDPHRASE="[null]" FULLTITLECACHE="Abies" NOMENCLATURALMICROREFERENCE="[null]" PARSINGPROBLEM="0" PROBLEMENDS="-1" PROBLEMSTARTS="-1" PROTECTEDFULLTITLECACHE="false" AUTHORSHIPCACHE="" BINOMHYBRID="false" GENUSORUNINOMIAL="Abies" HYBRIDFORMULA="false" INFRAGENERICEPITHET="[null]" INFRASPECIFICEPITHET="[null]" MONOMHYBRID="false" NAMECACHE="Abies" PROTECTEDAUTHORSHIPCACHE="false" PROTECTEDNAMECACHE="false" SPECIFICEPITHET="[null]" TRINOMHYBRID="false" NAMEAPPROBATION="[null]" SUBGENUSAUTHORSHIP="[null]" ANAMORPHIC="false" CULTIVARNAME="[null]" ACRONYM="[null]" BREED="[null]" ORIGINALPUBLICATIONYEAR="[null]" PUBLICATIONYEAR="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" HOMOTYPICALGROUP_ID="5000" NOMENCLATURALREFERENCE_ID="[null]" RANK_ID="[null]" BASIONYMAUTHORSHIP_ID="[null]" COMBINATIONAUTHORSHIP_ID="[null]" EXBASIONYMAUTHORSHIP_ID="[null]" EXCOMBINATIONAUTHORSHIP_ID="[null]"/>
+  <TAXONBASE DTYPE="Taxon" ID="5000" CREATED="2016-02-12 10:10:56.0" UUID="10cfb372-0b1a-4d82-9707-c5ffd2b93a55" UPDATED="[null]" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="Taxon#0&lt;10cfb372-0b1a-4d82-9707-c5ffd2b93a55&gt;" APPENDEDPHRASE="[null]" DOUBTFUL="false" PUBLISH="true" USENAMECACHE="false" EXCLUDED="false" TAXONSTATUSUNKNOWN="false" TAXONOMICCHILDRENCOUNT="0" UNPLACED="false" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" NAME_ID="[null]" SEC_ID="[null]" TAXONOMICPARENTCACHE_ID="[null]"/>
+  <COLLECTION ID="5000" CREATED="2016-02-12 10:10:14.0" UUID="ce9ee6f6-4ea4-45d5-835a-b942e430e8ef" UPDATED="[null]" LSID_AUTHORITY="[null]" LSID_LSID="[null]" LSID_NAMESPACE="[null]" LSID_OBJECT="[null]" LSID_REVISION="[null]" PROTECTEDTITLECACHE="false" TITLECACHE="" CODE="[null]" CODESTANDARD="[null]" NAME="[null]" TOWNORLOCATION="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" INSTITUTE_ID="[null]" SUPERCOLLECTION_ID="[null]"/>
+  <MATERIALORMETHODEVENT ID="5000" DTYPE="PreservationMethod" CREATED="2016-02-12 10:10:14.0" UUID="6cb85d0e-e604-4a9e-acc2-55049bc5167a" UPDATED="[null]" MATERIALMETHODTEXT="[null]" DESCRIPTION="my prservation" TIMEPERIOD_END="[null]" TIMEPERIOD_FREETEXT="[null]" TIMEPERIOD_START="[null]" STRAIN="[null]" TEMPERATURE="[null]" CREATEDBY_ID="[null]" UPDATEDBY_ID="[null]" DEFINEDMATERIALORMETHOD_ID="[null]" ACTOR_ID="[null]" FORWARDPRIMER_ID="[null]" REVERSEPRIMER_ID="[null]" MEDIUM_ID="[null]"/>
+  <SPECIMENOROBSERVATIONBASE_DERIVATIONEVENT ORIGINALS_ID="5001" DERIVATIONEVENTS_ID="5000"/>
+</dataset>
index 1c741cf3a6c0350eb3528cd6fe16bc68cb4761fe..c9452b5fa5bac61884d30e977c799e3346ac2a6b 100644 (file)
@@ -2477,7 +2477,9 @@ CREATE TABLE MATERIALORMETHODEVENT (
 
 CREATE TABLE MATERIALORMETHODEVENT_AUD (
     ID INTEGER NOT NULL,
-    DTYPE VARCHAR(255),
+    REV INTEGER NOT NULL,
+    REVTYPE TINYINT,
+       DTYPE VARCHAR(255),
     CREATED TIMESTAMP,
     UUID VARCHAR(36),
     UPDATED TIMESTAMP,