commit 5c025bd1e0c7679eb65a68fc75c8a32a026580ab
Author: Patrick Plitzner <p.plitzner@bgbm.org>
Date:   Thu Jan 18 13:41:57 2018 +0100

    ref #7196 Check existing data value-wise

diff --git a/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/CharacterMatrix.java b/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/CharacterMatrix.java
index da8539157..60c1ba151 100644
--- a/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/CharacterMatrix.java
+++ b/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/CharacterMatrix.java
@@ -1,11 +1,11 @@
 /**
-* Copyright (C) 2017 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.
-*/
+ * Copyright (C) 2017 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.taxeditor.editor.workingSet.matrix;
 
 import java.io.File;
@@ -140,7 +140,7 @@ import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
  *
  */
 public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails, IConversationEnabled, IDirtyMarkable,
-        ICdmEntitySessionEnabled{
+ICdmEntitySessionEnabled{
 
     private static final List<String> WS_PROPERTY_PATH = Arrays.asList(new String[] {
             "descriptions", //$NON-NLS-1$
@@ -257,7 +257,7 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
              ^
         DataLayer
 
-             *
+         *
 
          */
         DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
@@ -327,11 +327,11 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
         // delegates the sorting to the SortedList
         final SortHeaderLayer<SpecimenDescription> sortHeaderLayer = new SortHeaderLayer<>(
                 columnHeaderLayer,
-                        new GlazedListsSortModel<>(
-                                sortedList,
-                                columnPropertyAccessor,
-                                configRegistry,
-                                columnHeaderDataLayer));
+                new GlazedListsSortModel<>(
+                        sortedList,
+                        columnPropertyAccessor,
+                        configRegistry,
+                        columnHeaderDataLayer));
 
 
         /**
@@ -372,7 +372,7 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
         natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
 
         //FIXME: this is for DEBUG ONLY
-//        natTable.addConfiguration(new DebugMenuConfiguration(natTable));
+        //        natTable.addConfiguration(new DebugMenuConfiguration(natTable));
 
         // override the default sort configuration and change the mouse bindings
         // to sort on a single click
@@ -617,26 +617,53 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
         SpecimenDescription newDesription = SpecimenDescription.NewInstance(specimen);
         newDesription.setTitleCache("WorkingSet "+workingSet.getLabel()+": "+newDesription.generateTitle(), true);
 
-        //check for equals description element (same feature and TODO: same values)
-        matchingDescriptionElements.forEach(element ->
-        {
-            if(matchingDescriptionElements.contains(element)){
-                MessagingUtils.informationDialog("Multiple data found",
-                        String.format("Multiple description elements"
-                                //                             + " with different values "
-                                + "found for feature %s. "
-                                + "Data will not be copied to new specimen description", element.getFeature().getLabel()));
+        //check for equals description element (same feature and same values)
+        Map<Feature, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
+        for(DescriptionElementBase element:matchingDescriptionElements){
+            List<DescriptionElementBase> list = featureToElementMap.get(element.getFeature());
+            if(list==null){
+                list = new ArrayList<>();
             }
+            list.add(element);
+            featureToElementMap.put(element.getFeature(), list);
+        }
+        Set<DescriptionElementBase> descriptionElementsToClone = new HashSet<>();
+        for(Feature feature:featureToElementMap.keySet()){
+            List<DescriptionElementBase> elements = featureToElementMap.get(feature);
+            //no duplicate description elements found for this feature
+            if(elements.size()==1){
+                descriptionElementsToClone.add(elements.get(0));
+            }
+            //duplicates found -> check if all are equal
             else{
-                DescriptionElementBase clone;
-                try {
-                    clone = element.clone(newDesription);
-                    clone.getSources().forEach(source -> source.setOriginalNameString(DescriptionHelper.getLabel(element)));
-                } catch (CloneNotSupportedException e) {
-                    MessagingUtils.error(CharacterMatrix.class, e);
+                DescriptionElementBase match = null;
+                for (DescriptionElementBase descriptionElementBase : elements) {
+                    if(match==null){
+                        match = descriptionElementBase;
+                    }
+                    else if(!new DescriptionElementCompareWrapper(match).equals(new DescriptionElementCompareWrapper(descriptionElementBase))){
+                        match = null;
+                        MessagingUtils.informationDialog("Multiple data found",
+                                String.format("Multiple description elements with different values "
+                                        + "found for feature '%s'.\nData will not be copied to new specimen description.", feature.getLabel()));
+                        break;
+                    }
+                }
+                if(match!=null){
+                    descriptionElementsToClone.add(match);
                 }
             }
-        });
+        }
+        //clone matching descriptionElements
+        for (DescriptionElementBase descriptionElementBase : descriptionElementsToClone) {
+            DescriptionElementBase clone;
+            try {
+                clone = descriptionElementBase.clone(newDesription);
+                clone.getSources().forEach(source -> source.setOriginalNameString(DescriptionHelper.getLabel(descriptionElementBase)));
+            } catch (CloneNotSupportedException e) {
+                MessagingUtils.error(CharacterMatrix.class, e);
+            }
+        }
         return newDesription;
 
     }
diff --git a/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/DescriptionElementCompareWrapper.java b/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/DescriptionElementCompareWrapper.java
new file mode 100644
index 000000000..e2d5049b9
--- /dev/null
+++ b/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/DescriptionElementCompareWrapper.java
@@ -0,0 +1,70 @@
+/**
+ * Copyright (C) 2018 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.taxeditor.editor.workingSet.matrix;
+
+import eu.etaxonomy.cdm.model.description.CategoricalData;
+import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
+import eu.etaxonomy.cdm.model.description.QuantitativeData;
+
+/**
+ * @author pplitzner
+ * @since Jan 18, 2018
+ *
+ */
+public class DescriptionElementCompareWrapper {
+
+    private DescriptionElementBase element;
+
+    public DescriptionElementCompareWrapper(DescriptionElementBase element) {
+        this.element = element;
+    }
+
+    public DescriptionElementBase unwrap() {
+        return element;
+    }
+
+    public boolean equals(DescriptionElementCompareWrapper other) {
+        DescriptionElementBase otherElement = other.unwrap();
+        //TODO: Implement more elaborate equals() method
+        if(element.isInstanceOf(CategoricalData.class) && otherElement instanceof CategoricalData){
+            boolean equal = true;
+            CategoricalData otherData = (CategoricalData)otherElement;
+            CategoricalData elementData = (CategoricalData)element;
+            equal &= elementData.getStatesOnly().equals(otherData.getStatesOnly());
+            return equal;
+        }
+        if(element.isInstanceOf(QuantitativeData.class) && otherElement instanceof QuantitativeData){
+            boolean equal = true;
+            QuantitativeData otherData = (QuantitativeData)otherElement;
+            QuantitativeData elementData = (QuantitativeData)element;
+            if(elementData.getMin()!=null && otherData.getMin()==null){
+                return false;
+            }
+            if(elementData.getMin()==null && otherData.getMin()!=null){
+                return false;
+            }
+            if(elementData.getMax()==null && otherData.getMax()!=null){
+                return false;
+            }
+            if(elementData.getMax()!=null && otherData.getMax()==null){
+                return false;
+            }
+            if(elementData.getMin()!=null && otherData.getMin()!=null){
+                equal &= elementData.getMin().equals(otherData.getMin());
+            }
+            if(elementData.getMax()!=null && otherData.getMax()!=null){
+                equal &= elementData.getMax().equals(otherData.getMax());
+            }
+            return equal;
+        }
+        return false;
+    }
+
+}
+
