tests for image galleries in derived unit facade and bugfix for test in DescriptionSe...
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / facade / DerivedUnitFacade.java
index 8e3bfba9ff0863360dcb8b722b0ed95823cb6ff7..5ab077055e5f2424aed942ea70b6143c8b55adde 100644 (file)
@@ -9,6 +9,8 @@
 \r
 package eu.etaxonomy.cdm.api.facade;\r
 \r
+import java.beans.PropertyChangeEvent;\r
+import java.beans.PropertyChangeListener;\r
 import java.text.ParseException;\r
 import java.util.ArrayList;\r
 import java.util.HashMap;\r
@@ -17,11 +19,14 @@ import java.util.List;
 import java.util.Map;\r
 import java.util.Set;\r
 \r
-import javax.mail.MethodNotSupportedException;\r
+import javax.persistence.Transient;\r
 \r
 import org.apache.log4j.Logger;\r
 \r
+import eu.etaxonomy.cdm.api.service.IOccurrenceService;\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.IdentifiableSource;\r
 import eu.etaxonomy.cdm.model.common.Language;\r
@@ -47,7 +52,7 @@ import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
 import eu.etaxonomy.cdm.model.occurrence.PreservationMethod;\r
 import eu.etaxonomy.cdm.model.occurrence.Specimen;\r
 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;\r
-import eu.etaxonomy.cdm.model.reference.ReferenceBase;\r
+import eu.etaxonomy.cdm.model.reference.Reference;\r
 \r
 /**\r
  * This class is a facade to the eu.etaxonomy.cdm.model.occurrence package from\r
@@ -65,7 +70,6 @@ public class DerivedUnitFacade {
        \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
-       \r
        /**\r
         * Enum that defines the class the "Specimen" belongs to.\r
         * Some methods of the facade are not available for certain classes\r
@@ -126,18 +130,32 @@ public class DerivedUnitFacade {
        private TextData ecology;\r
        private TextData plantDescription;\r
        \r
+       \r
+       /**\r
+        * Creates a derived unit facade for a new derived unit of type <code>type</code>.\r
+        * @param type\r
+        * @return\r
+        */\r
        public static DerivedUnitFacade NewInstance(DerivedUnitType type){\r
                return new DerivedUnitFacade(type);\r
        }\r
 \r
+       /**\r
+        * Creates a derived unit facade for a given derived unit using the default configuration.\r
+        * @param derivedUnit\r
+        * @return\r
+        * @throws DerivedUnitFacadeNotSupportedException\r
+        */\r
        public static DerivedUnitFacade NewInstance(DerivedUnitBase derivedUnit) throws DerivedUnitFacadeNotSupportedException{\r
-               return new DerivedUnitFacade(derivedUnit, null, DerivedUnitType.Specimen);\r
+               return new DerivedUnitFacade(derivedUnit, null);\r
        }\r
        \r
        public static DerivedUnitFacade NewInstance(DerivedUnitBase derivedUnit, DerivedUnitFacadeConfigurator config) throws DerivedUnitFacadeNotSupportedException{\r
-               return new DerivedUnitFacade(derivedUnit, config, DerivedUnitType.Specimen);\r
+               return new DerivedUnitFacade(derivedUnit, config);\r
        }\r
 \r
+\r
+       \r
 // ****************** CONSTRUCTOR ****************************************************\r
        \r
        private DerivedUnitFacade(DerivedUnitType type){\r
@@ -148,8 +166,7 @@ public class DerivedUnitFacade {
                setCacheStrategy();\r
        }\r
        \r
-       private DerivedUnitFacade(DerivedUnitBase derivedUnit, DerivedUnitFacadeConfigurator config, DerivedUnitType type) throws DerivedUnitFacadeNotSupportedException{\r
-               //this.type = type;  ??\r
+       private DerivedUnitFacade(DerivedUnitBase derivedUnit, DerivedUnitFacadeConfigurator config) throws DerivedUnitFacadeNotSupportedException{\r
                \r
                if (config == null){\r
                        config = DerivedUnitFacadeConfigurator.NewInstance();\r
@@ -171,10 +188,13 @@ public class DerivedUnitFacade {
                                //fieldObservation = FieldObservation.NewInstance();\r
                        }else if (fieldOriginals.size() == 1){\r
                                fieldObservation = fieldOriginals.iterator().next();\r
+                               //###fieldObservation = getInitializedFieldObservation(fieldObservation);\r
+                               fieldObservation.addPropertyChangeListener(getNewEventPropagationListener());\r
                        }else{\r
                                throw new IllegalStateException("Illegal state");\r
                        }       \r
                }\r
+               // #### derivedUnit = getInitializedDerivedUnit(derivedUnit);\r
 \r
                //test if unsupported\r
                \r
@@ -183,7 +203,7 @@ public class DerivedUnitFacade {
 //             String objectTypeExceptionText = "Specimen";\r
 //             SpecimenDescription imageGallery = getImageGalleryWithSupportTest(derivedUnit, objectTypeExceptionText, false);\r
 //             getImageTextDataWithSupportTest(imageGallery, objectTypeExceptionText);\r
-               this.derivedUnitMediaTextData = inititialzeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);\r
+               this.derivedUnitMediaTextData = inititializeTextDataWithSupportTest(Feature.IMAGE(), this.derivedUnit, false, true);\r
                \r
                //field observation\r
 //             objectTypeExceptionText = "Field observation";\r
@@ -225,10 +245,189 @@ public class DerivedUnitFacade {
        }\r
        \r
 \r
+       private DerivedUnitBase getInitializedDerivedUnit(DerivedUnitBase derivedUnit) {\r
+               IOccurrenceService occurrenceService = this.config.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
+               DerivedUnitBase result = (DerivedUnitBase)occurrenceService.load(derivedUnit.getUuid(), propertyPaths);\r
+               return result;\r
+       }\r
+\r
+       /**\r
+        * Initializes the derived unit according to the configuartions property path.\r
+        * If the property path is <code>null</code> or no occurrence service is given the\r
+        * returned object is the same as the input parameter.\r
+        * @param fieldObservation2\r
+        * @return\r
+        */\r
+       private FieldObservation getInitializedFieldObservation(FieldObservation fieldObservation) {\r
+               IOccurrenceService occurrenceService = this.config.getOccurrenceService();\r
+               if (occurrenceService == null){\r
+                       return fieldObservation;\r
+               }\r
+               List<String> propertyPaths = this.config.getPropertyPaths();\r
+               if (propertyPaths == null){\r
+                       return fieldObservation;\r
+               }\r
+               propertyPaths = getFieldObjectPropertyPaths(propertyPaths);\r
+               FieldObservation result = (FieldObservation)occurrenceService.load(fieldObservation.getUuid(), propertyPaths);\r
+               return result;\r
+       }\r
+\r
+       /**\r
+        * Transforms the property paths in a way that the facade is handled just like an \r
+        * ordinary CdmBase object.<BR>\r
+        * E.g. a property path "collectinAreas" will be translated into gatheringEvent.collectingAreas\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", "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", "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", "description");\r
+                               result.add(facadePath);\r
+                       }\r
+                       // fieldObjectMedia  (Media)\r
+                       else if (facadePath.startsWith("fieldObjectMedia")){\r
+                               // TODO ??? \r
+                               facadePath = facadePath.replace("fieldObjectMedia", "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\r
+               ====================\r
+               - gatheringEvent (GatheringEvent)\r
+\r
+               Field Object\r
+               =================\r
+               - ecology/ ecologyAll (String)  ???\r
+               - plant description (like ecology)\r
+               \r
+               - fieldObjectImageGallery (SpecimenDescription)  - is automatically initialized via fieldObjectMedia\r
+\r
+*/\r
+               \r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Transforms the property paths in a way that the facade is handled just like an \r
+        * ordinary CdmBase object.<BR>\r
+        * E.g. a property path "collectinAreas" will be translated into gatheringEvent.collectingAreas\r
+        * \r
+        * Not needed (?) as the facade works with REST service property paths without using this method.\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", "description");\r
+                               result.add(facadePath);\r
+                       }\r
+                       \r
+                       // derivedUnitMedia  (Media)\r
+                       else if (facadePath.startsWith("derivedUnitMedia")){\r
+                               // TODO ??? \r
+                               facadePath = facadePath.replace("derivedUnitMedia", "descriptions.elements.media");\r
+                               result.add(facadePath);\r
+                       }\r
+                       \r
+               }\r
+               \r
+/*\r
+               //TODO\r
+               Derived Unit\r
+               =====================\r
+               \r
+               - derivedUnitImageGallery (SpecimenDescription)  - is automatically 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("Facade's derviedUnit must not be null to set cache strategy");\r
+               }\r
                derivedUnit.setCacheStrategy(new DerivedUnitFacadeCacheStrategy());\r
        }\r
 \r
@@ -246,7 +445,7 @@ public class DerivedUnitFacade {
                if (fieldObject == null){\r
                        return null;\r
                }\r
-               return inititialzeTextDataWithSupportTest(feature, fieldObject, createIfNotExists, isImageGallery);\r
+               return inititializeTextDataWithSupportTest(feature, fieldObject, createIfNotExists, isImageGallery);\r
        }\r
 \r
 \r
@@ -258,7 +457,7 @@ public class DerivedUnitFacade {
         * @return\r
         * @throws DerivedUnitFacadeNotSupportedException\r
         */\r
-       private TextData inititialzeTextDataWithSupportTest(Feature feature, SpecimenOrObservationBase specimen, boolean createIfNotExists, \r
+       private TextData inititializeTextDataWithSupportTest(Feature feature, SpecimenOrObservationBase specimen, boolean createIfNotExists, \r
                                boolean isImageGallery) throws DerivedUnitFacadeNotSupportedException {\r
                if (feature == null ){\r
                        return null;\r
@@ -302,6 +501,36 @@ public class DerivedUnitFacade {
                        return textData;\r
                }\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.\r
+        * If the given image gallery does not have text data attached, it is created and attached.\r
+        * @param imageGallery\r
+        * @return\r
+        * @throws DerivedUnitFacadeNotSupportedException\r
+        */\r
+       private TextData testImageGallery(SpecimenDescription imageGallery) throws DerivedUnitFacadeNotSupportedException {\r
+               if (imageGallery.isImageGallery() == false){\r
+                       throw new DerivedUnitFacadeNotSupportedException("Image gallery needs to have image gallery flag set");\r
+               }\r
+               if (imageGallery.getElements().size() > 1){\r
+                       throw new DerivedUnitFacadeNotSupportedException("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().isInstanceOf(TextData.class)){\r
+                               throw new DerivedUnitFacadeNotSupportedException("Image gallery must only have TextData as element");\r
+                       }else{\r
+                               textData = CdmBase.deproxy(imageGallery.getElements().iterator().next(), TextData.class);\r
+                       }\r
+               }\r
+               return textData;\r
+       }\r
 \r
 //************************** METHODS ***************************************** \r
 \r
@@ -566,6 +795,17 @@ public class DerivedUnitFacade {
        \r
 // ****************** Gathering Event *********************************/\r
        \r
+       //country\r
+       @Transient\r
+       public NamedArea getCountry(){\r
+               return  (hasGatheringEvent() ? getGatheringEvent(true).getCountry() : null);\r
+       }\r
+       \r
+       public void setCountry(NamedArea country){\r
+               getGatheringEvent(true).setCountry(country);\r
+       }\r
+       \r
+       \r
        //Collecting area\r
        public void addCollectingArea(NamedArea area) {\r
                getGatheringEvent(true).addCollectingArea(area);\r
@@ -575,6 +815,7 @@ public class DerivedUnitFacade {
                        getGatheringEvent(true).addCollectingArea(area);\r
                }\r
        }\r
+       @Transient\r
        public Set<NamedArea> getCollectingAreas() {\r
                return  (hasGatheringEvent() ? getGatheringEvent(true).getCollectingAreas() : null);\r
        }\r
@@ -589,6 +830,7 @@ public class DerivedUnitFacade {
         * @see #getAbsoluteElevationError()\r
         * @see #getAbsoluteElevationRange()\r
         **/\r
+       @Transient\r
        public Integer getAbsoluteElevation() {\r
                return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevation() : null);\r
        }\r
@@ -597,6 +839,7 @@ public class DerivedUnitFacade {
        }\r
 \r
        //absolute elevation error\r
+       @Transient\r
        public Integer getAbsoluteElevationError() {\r
                return (hasGatheringEvent() ? getGatheringEvent(true).getAbsoluteElevationError() : null);\r
        }\r
@@ -610,6 +853,7 @@ public class DerivedUnitFacade {
         * @see #setAbsoluteElevationRange(Integer, Integer)\r
         * @see #getAbsoluteElevationMaximum()\r
         */\r
+       @Transient\r
        public Integer getAbsoluteElevationMinimum(){\r
                if ( ! hasGatheringEvent() ){\r
                        return null;\r
@@ -626,6 +870,7 @@ public class DerivedUnitFacade {
         * @see #setAbsoluteElevationRange(Integer, Integer)\r
         * @see #getAbsoluteElevationMinimum()\r
         */\r
+       @Transient\r
        public Integer getAbsoluteElevationMaximum(){\r
                if ( ! hasGatheringEvent() ){\r
                        return null;\r
@@ -686,6 +931,7 @@ public class DerivedUnitFacade {
        }\r
 \r
        //collector\r
+       @Transient\r
        public AgentBase getCollector() {\r
                return  (hasGatheringEvent() ? getGatheringEvent(true).getCollector() : null);\r
        }\r
@@ -694,6 +940,7 @@ public class DerivedUnitFacade {
        }\r
 \r
        //collecting method\r
+       @Transient\r
        public String getCollectingMethod() {\r
                return  (hasGatheringEvent() ? getGatheringEvent(true).getCollectingMethod() : null);\r
        }\r
@@ -702,6 +949,7 @@ public class DerivedUnitFacade {
        }\r
 \r
        //distance to ground\r
+       @Transient\r
        public Integer getDistanceToGround() {\r
                return  (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToGround() : null);\r
        }\r
@@ -710,6 +958,7 @@ public class DerivedUnitFacade {
        }\r
 \r
        //distance to water surface\r
+       @Transient\r
        public Integer getDistanceToWaterSurface() {\r
                return  (hasGatheringEvent() ? getGatheringEvent(true).getDistanceToWaterSurface() : null);\r
        }\r
@@ -718,12 +967,13 @@ public class DerivedUnitFacade {
        }\r
 \r
        //exact location\r
+       @Transient\r
        public Point getExactLocation() {\r
                return  (hasGatheringEvent() ? getGatheringEvent(true).getExactLocation() : null );\r
        }\r
        \r
        /**\r
-        * Returns a sexagesimal representation of the exact location (e.g. 12°59'N, 35°23E).\r
+        * Returns a sexagesimal representation of the exact location (e.g. 12°59'N, 35°23E).\r
         * If the exact location is <code>null</code> the empty string is returned.\r
         * @param includeEmptySeconds\r
         * @param includeReferenceSystem\r
@@ -743,6 +993,7 @@ public class DerivedUnitFacade {
        }\r
        \r
        //gathering event description\r
+       @Transient\r
        public String getGatheringEventDescription() {\r
                return  (hasGatheringEvent() ? getGatheringEvent(true).getDescription() : null);\r
        }\r
@@ -751,6 +1002,7 @@ public class DerivedUnitFacade {
        }\r
 \r
        //gatering period\r
+       @Transient\r
        public TimePeriod getGatheringPeriod() {\r
                return (hasGatheringEvent() ? getGatheringEvent(true).getTimeperiod() : null);\r
        }\r
@@ -759,9 +1011,15 @@ public class DerivedUnitFacade {
        }\r
 \r
        //locality\r
+       @Transient\r
        public LanguageString getLocality(){\r
                return (hasGatheringEvent() ? getGatheringEvent(true).getLocality() : null);\r
        }\r
+       /**\r
+        * convienience method for {@link #getLocality()}.{@link LanguageString#getText() getText()}\r
+        * @return\r
+        */\r
+       @Transient\r
        public String getLocalityText(){\r
                LanguageString locality = getLocality();\r
                if(locality != null){\r
@@ -769,6 +1027,11 @@ public class DerivedUnitFacade {
                }\r
                return null;\r
        }\r
+       /**\r
+        * convienience method for {@link #getLocality()}.{@link LanguageString#getLanguage() getLanguage()}\r
+        * @return\r
+        */\r
+       @Transient\r
        public Language getLocalityLanguage(){\r
                LanguageString locality = getLocality();\r
                if(locality != null){\r
@@ -815,7 +1078,8 @@ public class DerivedUnitFacade {
        public boolean hasGatheringEvent(){\r
                return (getGatheringEvent(false) != null);\r
        }\r
-       public GatheringEvent getGatheringEvent() {\r
+       \r
+       public GatheringEvent innerGatheringEvent() {\r
                return getGatheringEvent(false);\r
        }\r
        \r
@@ -841,6 +1105,7 @@ public class DerivedUnitFacade {
        }\r
        \r
        //ecology\r
+       @Transient\r
        public String getEcology(){\r
                return getEcology(Language.DEFAULT());\r
        }\r
@@ -852,13 +1117,22 @@ public class DerivedUnitFacade {
 //             LanguageString languageString = getEcologyAll().getPreferredLanguageString(languages);\r
 //             return languageString.getText();\r
 //     }\r
+       /**\r
+        * Returns a copy of the multilanguage text holding the ecology data.\r
+        * @see {@link TextData#getMultilanguageText()}\r
+        * @return\r
+        */\r
+       @Transient\r
        public Map<Language, LanguageString> getEcologyAll(){\r
                if (ecology == null){\r
                        try {\r
-                               ecology = initializeFieldObjectTextDataWithSupportTest(Feature.ECOLOGY(), true, false);\r
+                               ecology = initializeFieldObjectTextDataWithSupportTest(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
@@ -880,7 +1154,7 @@ public class DerivedUnitFacade {
                if (ecologyText == null){\r
                        ecology.removeText(language);\r
                }else{\r
-                       ecology.putText(ecologyText, language);\r
+                       ecology.putText(language, ecologyText);\r
                }\r
        }\r
        public void removeEcology(Language language){\r
@@ -898,6 +1172,7 @@ public class DerivedUnitFacade {
 \r
        \r
        //plant description\r
+       @Transient\r
        public String getPlantDescription(){\r
                return getPlantDescription(null);\r
        }\r
@@ -912,13 +1187,22 @@ public class DerivedUnitFacade {
 //             LanguageString languageString = getPlantDescriptionAll().getPreferredLanguageString(languages);\r
 //             return languageString.getText();\r
 //     }\r
+       /**\r
+        * Returns a copy of the multilanguage text holding the description data.\r
+        * @see {@link TextData#getMultilanguageText()}\r
+        * @return\r
+        */\r
+       @Transient\r
        public Map<Language, LanguageString> getPlantDescriptionAll(){\r
                if (plantDescription == null){\r
                        try {\r
-                               plantDescription = initializeFieldObjectTextDataWithSupportTest(Feature.DESCRIPTION(), true, false);\r
+                               plantDescription = initializeFieldObjectTextDataWithSupportTest(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
@@ -939,19 +1223,18 @@ public class DerivedUnitFacade {
                if (plantDescriptionText == null){\r
                        plantDescription.removeText(language);\r
                }else{\r
-                       plantDescription.putText(plantDescriptionText, language);\r
+                       plantDescription.putText(language, plantDescriptionText);\r
                }\r
        }\r
        public void removePlantDescription(Language language){\r
                setPlantDescription(null, language);\r
        }\r
        \r
-\r
-       \r
        //field object definition\r
        public void addFieldObjectDefinition(String text, Language language) {\r
                getFieldObservation(true).addDefinition(text, language);\r
        }\r
+       @Transient\r
        public Map<Language, LanguageString> getFieldObjectDefinition() {\r
                if (! hasFieldObservation()){\r
                        return new HashMap<Language, LanguageString>();\r
@@ -996,7 +1279,29 @@ public class DerivedUnitFacade {
                }\r
        }\r
        \r
+       public void setFieldObjectImageGallery(SpecimenDescription imageGallery) throws DerivedUnitFacadeNotSupportedException{\r
+               SpecimenDescription existingGallery = getFieldObjectImageGallery(false);\r
+               \r
+               //test attached specimens contain this.derivedUnit\r
+               SpecimenOrObservationBase<?> facadeFieldObservation = innerFieldObservation();\r
+               testSpecimenInImageGallery(imageGallery, facadeFieldObservation);\r
+               \r
+               if (existingGallery != null){\r
+                       if (existingGallery != imageGallery){\r
+                               throw new DerivedUnitFacadeNotSupportedException("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
        /**\r
+        * Returns the field object image gallery. If no such image gallery exists and\r
+        * createIfNotExists is true an new one is created. Otherwise null is returned.\r
         * @param createIfNotExists\r
         * @return\r
         */\r
@@ -1017,6 +1322,7 @@ public class DerivedUnitFacade {
         * Returns the media for the field object.<BR>\r
         * @return\r
         */\r
+       @Transient\r
        public List<Media> getFieldObjectMedia() {\r
                try {\r
                        List<Media> result = getMedia(getFieldObservation(false), false);\r
@@ -1034,6 +1340,7 @@ public class DerivedUnitFacade {
        }\r
 \r
        //field number\r
+       @Transient\r
        public String getFieldNumber() {\r
                if (! hasFieldObservation()){\r
                        return null;\r
@@ -1045,8 +1352,23 @@ public class DerivedUnitFacade {
                getFieldObservation(true).setFieldNumber(fieldNumber);\r
        }\r
 \r
+       //primary collector\r
+       @Transient\r
+       public Person getPrimaryCollector() {\r
+               if (! hasFieldObservation()){\r
+                       return null;\r
+               }else{\r
+                       return getFieldObservation(true).getPrimaryCollector();\r
+               }\r
+       }\r
+       public void setPrimaryCollector(Person primaryCollector) {\r
+               getFieldObservation(true).setPrimaryCollector(primaryCollector);\r
+       }\r
+       \r
+       \r
        \r
        //field notes\r
+       @Transient\r
        public String getFieldNotes() {\r
                if (! hasFieldObservation()){\r
                        return null;\r
@@ -1060,6 +1382,7 @@ public class DerivedUnitFacade {
 \r
 \r
        //individual counts\r
+       @Transient\r
        public Integer getIndividualCount() {\r
                return (hasFieldObservation()? getFieldObservation(true).getIndividualCount() : null );\r
        }\r
@@ -1068,6 +1391,7 @@ public class DerivedUnitFacade {
        }\r
 \r
        //life stage\r
+       @Transient\r
        public Stage getLifeStage() {\r
                return (hasFieldObservation()? getFieldObservation(true).getLifeStage() : null );\r
        }\r
@@ -1076,6 +1400,7 @@ public class DerivedUnitFacade {
        }\r
 \r
        //sex\r
+       @Transient\r
        public Sex getSex() {\r
                return (hasFieldObservation()? getFieldObservation(true).getSex() : null );\r
        }\r
@@ -1093,7 +1418,7 @@ public class DerivedUnitFacade {
         * Returns the field observation as an object.\r
         * @return\r
         */\r
-       public FieldObservation getFieldObservation(){\r
+       public FieldObservation innerFieldObservation(){\r
                return getFieldObservation(false);\r
        }\r
 \r
@@ -1104,6 +1429,7 @@ public class DerivedUnitFacade {
        public FieldObservation getFieldObservation(boolean createIfNotExists){\r
                if (fieldObservation == null && createIfNotExists){\r
                        fieldObservation = FieldObservation.NewInstance();\r
+                       fieldObservation.addPropertyChangeListener(getNewEventPropagationListener());\r
                        DerivationEvent derivationEvent = getDerivationEvent(true);\r
                        derivationEvent.addOriginal(fieldObservation);\r
                }\r
@@ -1112,12 +1438,15 @@ public class DerivedUnitFacade {
 \r
        \r
 \r
+\r
+       \r
 //****************** Specimen **************************************************       \r
        \r
        //Definition\r
        public void addDerivedUnitDefinition(String text, Language language) {\r
                derivedUnit.addDefinition(text, language);\r
        }\r
+       @Transient\r
        public Map<Language, LanguageString> getDerivedUnitDefinitions(){\r
                return this.derivedUnit.getDefinition();\r
        }\r
@@ -1138,6 +1467,7 @@ public class DerivedUnitFacade {
        public void addDetermination(DeterminationEvent determination) {\r
                derivedUnit.addDetermination(determination);\r
        }\r
+       @Transient\r
        public Set<DeterminationEvent> getDeterminations() {\r
                return derivedUnit.getDeterminations();\r
        }\r
@@ -1164,7 +1494,7 @@ public class DerivedUnitFacade {
        public SpecimenDescription getDerivedUnitImageGallery(boolean createIfNotExists){\r
                TextData textData;\r
                try {\r
-                       textData = inititialzeTextDataWithSupportTest(Feature.IMAGE(), derivedUnit, createIfNotExists, true);\r
+                       textData = inititializeTextDataWithSupportTest(Feature.IMAGE(), derivedUnit, createIfNotExists, true);\r
                } catch (DerivedUnitFacadeNotSupportedException e) {\r
                        throw new IllegalStateException(notSupportMessage, e);\r
                }\r
@@ -1174,11 +1504,44 @@ public class DerivedUnitFacade {
                        return null;\r
                }\r
        }\r
+       public void setDerivedUnitImageGallery(SpecimenDescription imageGallery) throws DerivedUnitFacadeNotSupportedException{\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("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) throws DerivedUnitFacadeNotSupportedException {\r
+               Set<SpecimenOrObservationBase> imageGallerySpecimens = imageGallery.getDescribedSpecimenOrObservations();\r
+               if (imageGallerySpecimens.size() < 1){\r
+                       throw new DerivedUnitFacadeNotSupportedException("Image Gallery has no Specimen attached. Please attache according specimen or field observation.");\r
+               }\r
+               if (! imageGallerySpecimens.contains(specimen)){\r
+                       throw new DerivedUnitFacadeNotSupportedException("Image Gallery has not the facade's field object attached. Please add field object first to image gallery specimenOrObservation list.");\r
+               }\r
+       }\r
        \r
        /**\r
         * Returns the media for the specimen.<BR>\r
         * @return\r
         */\r
+       @Transient\r
        public List<Media> getDerivedUnitMedia() {\r
                try {\r
                        List<Media> result = getMedia(derivedUnit, false);\r
@@ -1197,6 +1560,7 @@ public class DerivedUnitFacade {
 \r
        \r
        //Accession Number\r
+       @Transient\r
        public String getAccessionNumber() {\r
                return derivedUnit.getAccessionNumber();\r
        }\r
@@ -1204,7 +1568,7 @@ public class DerivedUnitFacade {
                derivedUnit.setAccessionNumber(accessionNumber);\r
        }\r
 \r
-       //Catalog Number\r
+       @Transient\r
        public String getCatalogNumber() {\r
                return derivedUnit.getCatalogNumber();\r
        }\r
@@ -1212,6 +1576,15 @@ public class DerivedUnitFacade {
                derivedUnit.setCatalogNumber(catalogNumber);\r
        }\r
 \r
+       @Transient\r
+       public String getBarcode() {\r
+               return derivedUnit.getBarcode();\r
+       }\r
+       public void setBarcode(String barcode) {\r
+               derivedUnit.setBarcode(barcode);\r
+       }\r
+\r
+       \r
        //Preservation Method\r
        \r
        /**\r
@@ -1219,11 +1592,16 @@ public class DerivedUnitFacade {
         * @see #DerivedUnitType\r
         * @return\r
         */\r
+       @Transient\r
        public PreservationMethod getPreservationMethod() throws MethodNotSupportedByDerivedUnitTypeException {\r
                if (derivedUnit.isInstanceOf(Specimen.class)){\r
                        return CdmBase.deproxy(derivedUnit, Specimen.class).getPreservation();\r
                }else{\r
-                       throw new MethodNotSupportedByDerivedUnitTypeException("A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");\r
+                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()){\r
+                               throw new MethodNotSupportedByDerivedUnitTypeException("A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");\r
+                       }else{\r
+                               return null;\r
+                       }\r
                }\r
        }\r
        /**\r
@@ -1235,12 +1613,16 @@ public class DerivedUnitFacade {
                if (derivedUnit.isInstanceOf(Specimen.class)){\r
                        CdmBase.deproxy(derivedUnit, Specimen.class).setPreservation(preservation);\r
                }else{\r
-                       throw new MethodNotSupportedByDerivedUnitTypeException("A preservation method is only available in derived units of type 'Specimen' or 'Fossil'");\r
-                       \r
+                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()){\r
+                               throw new MethodNotSupportedByDerivedUnitTypeException("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 derivedUnit.getStoredUnder();\r
        }\r
@@ -1249,6 +1631,7 @@ public class DerivedUnitFacade {
        }\r
 \r
        //colletors number\r
+       @Transient\r
        public String getCollectorsNumber() {\r
                return derivedUnit.getCollectorsNumber();\r
        }\r
@@ -1265,6 +1648,9 @@ public class DerivedUnitFacade {
                }\r
                return this.derivedUnit.getTitleCache();\r
        }\r
+       public boolean isProtectedTitleCache(){\r
+               return derivedUnit.isProtectedTitleCache();\r
+       }\r
        public void setTitleCache(String titleCache, boolean isProtected) {\r
                this.derivedUnit.setTitleCache(titleCache, isProtected);\r
        }\r
@@ -1274,7 +1660,7 @@ public class DerivedUnitFacade {
         * Returns the derived unit itself.\r
         * @return the derived unit\r
         */\r
-       public DerivedUnitBase getDerivedUnit() {\r
+       public DerivedUnitBase innerDerivedUnit() {\r
                return this.derivedUnit;\r
        }\r
        \r
@@ -1292,14 +1678,29 @@ public class DerivedUnitFacade {
                }\r
                return result;\r
        }\r
-       \r
-       public String getExsiccatum() {\r
-               logger.warn("Exsiccatum method not yet supported. Needs model change");\r
-               return null;\r
+       @Transient\r
+       public String getExsiccatum() throws MethodNotSupportedByDerivedUnitTypeException {\r
+               if (derivedUnit.isInstanceOf(Specimen.class)){\r
+                       return CdmBase.deproxy(derivedUnit, Specimen.class).getExsiccatum();\r
+               }else{\r
+                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()){\r
+                               throw new MethodNotSupportedByDerivedUnitTypeException("An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");\r
+                       }else{\r
+                               return null;\r
+                       }\r
+               }\r
        }\r
        \r
-       public String setExsiccatum() throws MethodNotSupportedException{\r
-               throw new MethodNotSupportedException("Exsiccatum method not yet supported. Needs model change");\r
+       public void setExsiccatum(String exsiccatum) throws Exception{\r
+               if (derivedUnit.isInstanceOf(Specimen.class)){\r
+                       CdmBase.deproxy(derivedUnit, Specimen.class).setExsiccatum(exsiccatum);\r
+               }else{\r
+                       if (this.config.isThrowExceptionForNonSpecimenPreservationMethodRequest()){\r
+                               throw new MethodNotSupportedByDerivedUnitTypeException("An exsiccatum is only available in derived units of type 'Specimen' or 'Fossil'");\r
+                       }else{\r
+                               return;\r
+                       }\r
+               }\r
        }\r
        \r
        \r
@@ -1314,13 +1715,14 @@ public class DerivedUnitFacade {
         * @param originalNameString\r
         * @return\r
         */\r
-       public IdentifiableSource addSource(ReferenceBase reference, String microReference, String originalNameString){\r
+       public IdentifiableSource addSource(Reference reference, String microReference, String originalNameString){\r
                IdentifiableSource source = IdentifiableSource.NewInstance(reference, microReference);\r
                source.setOriginalNameString(originalNameString);\r
                derivedUnit.addSource(source);\r
                return source;\r
        }\r
-       \r
+\r
+       @Transient\r
        public Set<IdentifiableSource> getSources(){\r
                return derivedUnit.getSources();\r
        }\r
@@ -1333,6 +1735,7 @@ public class DerivedUnitFacade {
        /**\r
         * @return the collection\r
         */\r
+       @Transient\r
        public Collection getCollection() {\r
                return derivedUnit.getCollection();\r
        }\r
@@ -1345,8 +1748,38 @@ public class DerivedUnitFacade {
                derivedUnit.setCollection(collection);\r
        }\r
        \r
+       //annotation    \r
+       public void addAnnotation(Annotation annotation){\r
+               this.derivedUnit.addAnnotation(annotation);\r
+       }\r
+\r
+       @Transient\r
+       public void getAnnotations(){\r
+               this.derivedUnit.getAnnotations();\r
+       }\r
        \r
+       public void removeAnnotation(Annotation annotation){\r
+               this.derivedUnit.removeAnnotation(annotation);\r
+       }\r
+       \r
+       \r
+// ******************************* Events *********************************************\r
+       \r
+       /**\r
+        * @return\r
+        */\r
+       private PropertyChangeListener getNewEventPropagationListener() {\r
+               PropertyChangeListener listener = new PropertyChangeListener(){\r
+                       @Override\r
+                       public void propertyChange(PropertyChangeEvent event) {\r
+                               derivedUnit.firePropertyChange(event);\r
+                       }\r
+                       \r
+               };\r
+               return listener;\r
+       }\r
 \r
+               \r
        \r
 \r
 //**************** Other Collections ***************************************************       \r
@@ -1381,6 +1814,8 @@ public class DerivedUnitFacade {
                //TODO check derivedUnitType\r
                getDerivationEvent(true).addDerivative(duplicateSpecimen);  \r
        }\r
+       \r
+       @Transient\r
        public Set<Specimen> getDuplicates(){\r
                Set<Specimen> result = new HashSet<Specimen>();\r
                if (hasDerivationEvent()){\r