commit 5e97d775905920aece00b80f8be65888353b4d69
Author: Andreas Kohlbecker <a.kohlbecker@bgbm.org>
Date:   Tue Nov 14 22:16:14 2017 +0100

    solving multiple representation probelms when adding source references to MediaSpecimen
      - extending PersistentContextAnalyzer
      - EntityCache support for more than one base entities

diff --git a/src/main/java/eu/etaxonomy/cdm/cache/CdmEntityCache.java b/src/main/java/eu/etaxonomy/cdm/cache/CdmEntityCache.java
index d161e28f..67c3ccaf 100644
--- a/src/main/java/eu/etaxonomy/cdm/cache/CdmEntityCache.java
+++ b/src/main/java/eu/etaxonomy/cdm/cache/CdmEntityCache.java
@@ -44,7 +44,7 @@ public class CdmEntityCache implements EntityCache {
 
     private static final String COPY_ENTITY = "!";
 
-    private CdmBase entity;
+    private Set<CdmBase> entities = new HashSet<>();
 
     private Map<EntityKey, CdmBase> entityyMap = new HashMap<>();
 
@@ -57,7 +57,7 @@ public class CdmEntityCache implements EntityCache {
     private Set<Object> objectsSeen = new HashSet<>();
 
     public CdmEntityCache(CdmBase entity){
-        this.entity = entity;
+        this.entities.add(entity);
         update();
     }
 
@@ -69,9 +69,9 @@ public class CdmEntityCache implements EntityCache {
         objectsSeen.clear();
         copyEntitiyKeys.clear();
 
-        String propertyPath = "";
-
-        analyzeEntity(entity, propertyPath);
+        for(CdmBase entity : entities){
+        analyzeEntity(entity, "");
+        }
 
         return copyEntitiyKeys.isEmpty();
     }
@@ -269,8 +269,11 @@ public class CdmEntityCache implements EntityCache {
      */
     @Override
     public <CDM extends CdmBase> CDM find(CDM value) {
-        EntityKey entityKey = new EntityKey(HibernateProxyHelper.deproxy(value));
-        return (CDM) entityyMap.get(entityKey);
+        if(value != null){
+            EntityKey entityKey = new EntityKey(HibernateProxyHelper.deproxy(value));
+            return (CDM) entityyMap.get(entityKey);
+        }
+        return null;
     }
 
     /**
@@ -282,5 +285,14 @@ public class CdmEntityCache implements EntityCache {
         return (CDM) entityyMap.get(entityKey);
     }
 
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public <CDM extends CdmBase> void add(CDM value) {
+        entities.add(value);
+        analyzeEntity(value, "");
+    }
+
 
 }
diff --git a/src/main/java/eu/etaxonomy/cdm/cache/EntityCache.java b/src/main/java/eu/etaxonomy/cdm/cache/EntityCache.java
index 82212362..80ff599b 100644
--- a/src/main/java/eu/etaxonomy/cdm/cache/EntityCache.java
+++ b/src/main/java/eu/etaxonomy/cdm/cache/EntityCache.java
@@ -25,6 +25,8 @@ public interface EntityCache {
 
     public boolean update();
 
+    public <CDM extends CdmBase> void add(CDM value);
+
     /**
      * @param type
      * @param id
diff --git a/src/main/java/eu/etaxonomy/cdm/debug/PersistentContextAnalyzer.java b/src/main/java/eu/etaxonomy/cdm/debug/PersistentContextAnalyzer.java
index fcb928ca..fe0b3cfb 100644
--- a/src/main/java/eu/etaxonomy/cdm/debug/PersistentContextAnalyzer.java
+++ b/src/main/java/eu/etaxonomy/cdm/debug/PersistentContextAnalyzer.java
@@ -40,6 +40,11 @@ import eu.etaxonomy.cdm.persistence.dao.initializer.AbstractBeanInitializer;
  */
 public class PersistentContextAnalyzer {
 
+    /**
+     *
+     */
+    private static final char HASH_SEPARATOR = '.';
+
     /**
      *
      */
@@ -66,6 +71,8 @@ public class PersistentContextAnalyzer {
 
     private Set<Object> objectsSeen = new HashSet<>();
 
+    private boolean showHashCodes = false;
+
 
     public PersistentContextAnalyzer(CdmBase entity, Session session){
         this.session = session;
@@ -107,8 +114,9 @@ public class PersistentContextAnalyzer {
      */
     protected void printLegend(PrintStream printStream) {
         printStream.println("PersistentContextAnalyzer legend: ");
-        printStream.println("    - '*': entity mapped in persistent context");
+        printStream.println("    - '.{objectHash}': unique copy entity, followed by object hash (only shown when showHashCodes is enabled)");
         printStream.println("    - '!{objectHash}': detected copy entity, followed by object hash");
+        printStream.println("    - '*': entity mapped in persistent context");
     }
 
     /**
@@ -122,12 +130,18 @@ public class PersistentContextAnalyzer {
         String flags = "";
         CdmBase mappedEntity = entityyMap.put(entityKey, bean);
 
+        boolean hashAdded = false;
+
         if(session != null && session.contains(bean)){
             flags += IN_PERSITENT_CONTEXT;
         }
         if(mappedEntity != null && mappedEntity != bean) {
             copyEntitiyKeys.add(entityKey);
             flags += COPY_ENTITY + bean.hashCode();
+            hashAdded = true;
+        }
+        if(showHashCodes && ! hashAdded){
+            flags += HASH_SEPARATOR + bean.hashCode();
         }
         if(!flags.isEmpty()){
             propertyPath += "(" + flags + ")";
@@ -207,6 +221,24 @@ public class PersistentContextAnalyzer {
         }
     }
 
+    /**
+     * @return the showHashCodes
+     */
+    public boolean isShowHashCodes() {
+        return showHashCodes;
+    }
+
+    /**
+     * @param showHashCodes the showHashCodes to set
+     */
+    public void setShowHashCodes(boolean showHashCodes) {
+        boolean runUpdate = this.showHashCodes != showHashCodes;
+        this.showHashCodes = showHashCodes;
+        if(runUpdate){
+            update();
+        }
+    }
+
     class EntityKey {
 
         Class type;
diff --git a/src/main/java/eu/etaxonomy/cdm/service/SpecimenTypeDesignationWorkingSetServiceImpl.java b/src/main/java/eu/etaxonomy/cdm/service/SpecimenTypeDesignationWorkingSetServiceImpl.java
index 04512c50..771a9c5e 100644
--- a/src/main/java/eu/etaxonomy/cdm/service/SpecimenTypeDesignationWorkingSetServiceImpl.java
+++ b/src/main/java/eu/etaxonomy/cdm/service/SpecimenTypeDesignationWorkingSetServiceImpl.java
@@ -22,6 +22,7 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import eu.etaxonomy.cdm.api.application.CdmRepository;
+import eu.etaxonomy.cdm.debug.PersistentContextAnalyzer;
 import eu.etaxonomy.cdm.model.common.VersionableEntity;
 import eu.etaxonomy.cdm.model.name.Registration;
 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
@@ -77,7 +78,7 @@ public class SpecimenTypeDesignationWorkingSetServiceImpl implements ISpecimenTy
         FieldUnit newfieldUnit = FieldUnit.NewInstance();
         Registration reg = repo.getRegistrationService().load(registrationId, RegistrationWorkingSetService.REGISTRATION_INIT_STRATEGY);
         TaxonName typifiedName = repo.getNameService().load(typifiedNameId, TAXON_NAME_INIT_STRATEGY);
-        Reference citation = repo.getReferenceService().load(registrationId, Arrays.asList("$"));
+        Reference citation = repo.getReferenceService().load(publicationId, Arrays.asList("$"));
         SpecimenTypeDesignationWorkingSetDTO<Registration> workingSetDto = new SpecimenTypeDesignationWorkingSetDTO<Registration>(reg, newfieldUnit, citation, typifiedName);
         return workingSetDto;
     }
@@ -157,8 +158,13 @@ public class SpecimenTypeDesignationWorkingSetServiceImpl implements ISpecimenTy
 
             Session session = repo.getSession();
 
+            PersistentContextAnalyzer regAnalyzer = new PersistentContextAnalyzer(dto.getOwner(), session);
+            regAnalyzer.printEntityGraph(System.out);
+            regAnalyzer.printCopyEntities(System.out);
+
             session.merge(dto.getOwner());
 
+
             session.flush();
         }
 
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/event/ToOneRelatedEntityReloader.java b/src/main/java/eu/etaxonomy/cdm/vaadin/event/ToOneRelatedEntityReloader.java
index 8fe15f7f..46826b3f 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/event/ToOneRelatedEntityReloader.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/event/ToOneRelatedEntityReloader.java
@@ -43,14 +43,20 @@ public class ToOneRelatedEntityReloader<CDM extends CdmBase> implements ValueCha
     @Override
     public void valueChange(ValueChangeEvent event) {
 
+
         CDM value = (CDM)event.getProperty().getValue();
+        if(value == null) {
+            return;
+        }
         value = HibernateProxyHelper.deproxy(value);
 
         EntityCache cache = cachingPresenter.getCache();
         if(cache != null){
             cache.update();
             CDM cachedEntity = cache.find(value);
-            if(cachedEntity != null && cachedEntity != value){
+            if(cachedEntity == null){
+                cache.add(value);
+            } else if(cachedEntity != value){
                 toOneRelatedEntityField.removeValueChangeListener(this);
                 toOneRelatedEntityField.setValue(null); // reset to trick equals check in vaadin
                 toOneRelatedEntityField.setValue(cachedEntity);
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/model/registration/SpecimenTypeDesignationWorkingSetDTO.java b/src/main/java/eu/etaxonomy/cdm/vaadin/model/registration/SpecimenTypeDesignationWorkingSetDTO.java
index 1dfccff2..2c5c4412 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/model/registration/SpecimenTypeDesignationWorkingSetDTO.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/model/registration/SpecimenTypeDesignationWorkingSetDTO.java
@@ -57,6 +57,12 @@ public class SpecimenTypeDesignationWorkingSetDTO<OWNER extends VersionableEntit
         super();
         this.owner = owner;
         this.baseEntity = baseEntity;
+        if(citation == null){
+            throw new NullPointerException("citation must not be null");
+        }
+        if(typifiedName == null){
+            throw new NullPointerException("typifiedName must not be null");
+        }
         this.citation = citation;
         this.typifiedName = typifiedName;
         if(baseEntity instanceof FieldUnit){
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/SpecimenTypeDesignationDTORow.java b/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/SpecimenTypeDesignationDTORow.java
index 6d55cc84..991a4343 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/SpecimenTypeDesignationDTORow.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/SpecimenTypeDesignationDTORow.java
@@ -82,7 +82,7 @@ public class SpecimenTypeDesignationDTORow {
 
         mediaSpecimenReference.setEnabled(publishedImageType);
         mediaSpecimenReferenceDetail.setEnabled(publishedImageType);
-        mediaUri.setEnabled(unPublishedImageType);
+        mediaUri.setEnabled(publishedImageType || unPublishedImageType);
 
     }
 }
\ No newline at end of file
diff --git a/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/SpecimenTypeDesignationWorkingsetEditorPresenter.java b/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/SpecimenTypeDesignationWorkingsetEditorPresenter.java
index 88c80916..e932ac6a 100644
--- a/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/SpecimenTypeDesignationWorkingsetEditorPresenter.java
+++ b/src/main/java/eu/etaxonomy/cdm/vaadin/view/name/SpecimenTypeDesignationWorkingsetEditorPresenter.java
@@ -154,7 +154,8 @@ public class SpecimenTypeDesignationWorkingsetEditorPresenter
                         );
                 row.collection.getSelect().setCaptionGenerator(new CdmTitleCacheCaptionGenerator<Collection>());
                 row.collection.getSelect().addValueChangeListener(new ToOneRelatedEntityButtonUpdater<Collection>(row.collection));
-                row.collection.getSelect().addValueChangeListener(new ToOneRelatedEntityReloader<Collection>(row.collection.getSelect(), SpecimenTypeDesignationWorkingsetEditorPresenter.this));
+                row.collection.getSelect().addValueChangeListener(new ToOneRelatedEntityReloader<Collection>(row.collection.getSelect(),
+                        SpecimenTypeDesignationWorkingsetEditorPresenter.this));
 
                 row.mediaSpecimenReference.loadFrom(
                         referencePagingProvider,
@@ -164,6 +165,8 @@ public class SpecimenTypeDesignationWorkingsetEditorPresenter
 
                 row.mediaSpecimenReference.getSelect().setCaptionGenerator(new CdmTitleCacheCaptionGenerator<Reference>());
                 row.mediaSpecimenReference.getSelect().addValueChangeListener(new ToOneRelatedEntityButtonUpdater<Reference>(row.mediaSpecimenReference));
+                row.mediaSpecimenReference.getSelect().addValueChangeListener(new ToOneRelatedEntityReloader<Reference>(row.mediaSpecimenReference.getSelect(),
+                        SpecimenTypeDesignationWorkingsetEditorPresenter.this));
 
                 getView().applyDefaultComponentStyle(row.components());
 
