commit 4fbf8f875706b0f321e817a8827800be2210e149
Author: Patrick Plitzner <p.plitzner@bgbm.org>
Date:   Wed Dec 13 21:01:22 2017 +0100

    ref #7095 Add non-editable additional columns with extra info to matrix

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 f0070f5cc..a2023922c 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
@@ -11,7 +11,9 @@ package eu.etaxonomy.taxeditor.editor.workingSet.matrix;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 
@@ -19,6 +21,7 @@ import javax.annotation.PostConstruct;
 import javax.annotation.PreDestroy;
 import javax.inject.Inject;
 
+import org.apache.commons.collections4.map.LinkedMap;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.e4.ui.di.Focus;
 import org.eclipse.e4.ui.di.Persist;
@@ -79,6 +82,7 @@ import eu.etaxonomy.cdm.model.common.TermVocabulary;
 import eu.etaxonomy.cdm.model.description.DescriptionBase;
 import eu.etaxonomy.cdm.model.description.Feature;
 import eu.etaxonomy.cdm.model.description.FeatureTree;
+import eu.etaxonomy.cdm.model.description.MeasurementUnit;
 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
 import eu.etaxonomy.cdm.model.description.State;
 import eu.etaxonomy.cdm.model.description.WorkingSet;
@@ -89,6 +93,7 @@ import eu.etaxonomy.taxeditor.editor.workingSet.matrix.quantitative.Quantitative
 import eu.etaxonomy.taxeditor.editor.workingSet.matrix.quantitative.QuantitativeDataDisplayConverter;
 import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
+import eu.etaxonomy.taxeditor.model.MessagingUtils;
 import eu.etaxonomy.taxeditor.store.CdmStore;
 import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
 
@@ -99,6 +104,12 @@ import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
  */
 public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails, IConversationEnabled, IDirtyMarkable{
 
+    private static final int LEADING_COLUMN_COUNT = 4;
+    private static final String TAXON_COLUMN = "taxon_column";
+    private static final String COLLECTOR_COLUMN = "collector_column";
+    private static final String IDENTIFIER_COLUMN = "identifier_column";
+    private static final String COUNTRY_COLUMN = "country_column";
+
     private WorkingSet workingSet;
 
     private Composite parent;
@@ -116,7 +127,9 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
 
     private NatTable natTable;
 
-    private List<Feature> features;
+    private Map<Integer, Feature> indexToFeatureMap = new HashMap<>();
+
+    private LinkedMap<String, String> propertyToLabelMap = new LinkedMap<>();
 
     @PostConstruct
     public void create(Composite parent) {
@@ -137,7 +150,7 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
 
         //get features/columns stored in working set
         FeatureTree tree = workingSet.getDescriptiveSystem();
-        features = new ArrayList<>(tree.getDistinctFeatures());
+        List<Feature> features = new ArrayList<>(tree.getDistinctFeatures());
         Collections.sort(features);
 
 
@@ -147,7 +160,7 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
         /**
          * data provider
          */
-        SpecimenColumnPropertyAccessor columnPropertyAccessor = new SpecimenColumnPropertyAccessor(features);
+        SpecimenColumnPropertyAccessor columnPropertyAccessor = new SpecimenColumnPropertyAccessor(this);
         IDataProvider bodyDataProvider = new ListDataProvider<SpecimenDescription>(sortedList, columnPropertyAccessor);
 
         /**
@@ -156,7 +169,10 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
         DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
         final ColumnOverrideLabelAccumulator columnLabelAccumulator =new ColumnOverrideLabelAccumulator(bodyDataLayer);
         bodyDataLayer.setConfigLabelAccumulator(columnLabelAccumulator);
-        registerColumnLabels(columnLabelAccumulator);
+        for(int i=0;i<features.size();i++){
+            Feature feature = features.get(i);
+            initLabels(features, columnLabelAccumulator, i, feature);
+        }
         GlazedListsEventLayer<SpecimenDescription> eventLayer = new GlazedListsEventLayer<>(bodyDataLayer, sortedList);
 
         RowReorderLayer rowReorderLayer = new RowReorderLayer(eventLayer);
@@ -168,7 +184,7 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
          * column header layer
          */
         IDataProvider columnHeaderDataProvider = new DefaultColumnHeaderDataProvider(
-                columnPropertyAccessor.getPropertyToLabelMap().values().toArray(new String[] {}), columnPropertyAccessor.getPropertyToLabelMap());
+                propertyToLabelMap.values().toArray(new String[] {}), propertyToLabelMap);
         DataLayer columnHeaderDataLayer = new DataLayer(columnHeaderDataProvider);
         ILayer columnHeaderLayer = new ColumnHeaderLayer(columnHeaderDataLayer, viewportLayer, selectionLayer);
 
@@ -221,14 +237,14 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
         natTable.addConfiguration(new SingleClickSortConfiguration());
 
 
-        // add custom configuration for data conversion
+        // add custom configuration for data conversion and add column labels for each feature
         viewportLayer.addConfiguration(new AbstractRegistryConfiguration() {
             @Override
             public void configureRegistry(IConfigRegistry configRegistry) {
-                features.forEach(feature -> registerColumnConfiguration(feature, configRegistry));
-                }
+                features.forEach(feature->registerColumnConfiguration(feature, configRegistry));
             }
-        );
+
+        });
 
         // add the ExportCommandHandler to the ViewportLayer in order to make
         // exporting work
@@ -269,10 +285,30 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
         parent.layout();
     }
 
-    private void registerColumnLabels(ColumnOverrideLabelAccumulator columnLabelAccumulator) {
-        for(int i=0;i<features.size();i++){
-            columnLabelAccumulator.registerColumnOverrides(i, getProperty(features.get(i)));
+    private void initLabels(List<Feature> features, final ColumnOverrideLabelAccumulator columnLabelAccumulator,
+            int index, Feature feature) {
+
+        propertyToLabelMap.put(TAXON_COLUMN, "Taxon");
+        propertyToLabelMap.put(COLLECTOR_COLUMN, "Collector + No");
+        propertyToLabelMap.put(IDENTIFIER_COLUMN, "Identifier");
+        propertyToLabelMap.put(COUNTRY_COLUMN, "Country");
+
+        columnLabelAccumulator.registerColumnOverrides(index+LEADING_COLUMN_COUNT, getProperty(features.get(index)));
+        indexToFeatureMap.put(index+LEADING_COLUMN_COUNT, feature);
+
+        String featureLabel = feature.getLabel();
+        String property = featureLabel;
+        //show unit for quantitative data
+        if(feature.isSupportsQuantitativeData()){
+            Set<MeasurementUnit> recommendedMeasurementUnits = feature.getRecommendedMeasurementUnits();
+            if(recommendedMeasurementUnits.size()!=1){
+                MessagingUtils.error(SpecimenColumnPropertyAccessor.class, "Only one unit is allowed for quantitative data", null);
+                return;
+            }
+            MeasurementUnit unit = recommendedMeasurementUnits.iterator().next();
+            featureLabel += " ["+unit.getLabel()+"]";
         }
+        propertyToLabelMap.put(property, featureLabel);
     }
 
     private void registerColumnConfiguration(Feature feature, IConfigRegistry configRegistry) {
@@ -311,7 +347,7 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
                 @Override
                 public List<?> getValues(int columnIndex, int rowIndex) {
                     List<State> states = new ArrayList<>();
-                    Feature feature = features.get(columnIndex);
+                    Feature feature = indexToFeatureMap.get(columnIndex);
                     if(feature.isSupportsCategoricalData()){
                         Set<TermVocabulary<State>> stateVocs = feature.getSupportedCategoricalEnumerations();
                         for (TermVocabulary<State> voc : stateVocs) {
@@ -342,14 +378,18 @@ public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails,
         return descriptions;
     }
 
-    public List<Feature> getFeatures() {
-        return features;
-    }
-
     private String getProperty(Feature feature){
         return feature.getLabel();
     }
 
+    public Map<Integer, Feature> getIndexToFeatureMap() {
+        return indexToFeatureMap;
+    }
+
+    public LinkedMap<String, String> getPropertyToLabelMap() {
+        return propertyToLabelMap;
+    }
+
     public void setDirty() {
         this.dirty.setDirty(true);
     }
diff --git a/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/SpecimenColumnPropertyAccessor.java b/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/SpecimenColumnPropertyAccessor.java
index fc0ea1148..6caf86ac9 100644
--- a/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/SpecimenColumnPropertyAccessor.java
+++ b/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/SpecimenColumnPropertyAccessor.java
@@ -11,20 +11,22 @@ package eu.etaxonomy.taxeditor.editor.workingSet.matrix;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
-import java.util.Map;
 import java.util.Set;
 
-import org.apache.commons.collections4.map.LinkedMap;
 import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
 
+import eu.etaxonomy.cdm.api.service.IOccurrenceService;
+import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.model.description.CategoricalData;
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
 import eu.etaxonomy.cdm.model.description.Feature;
-import eu.etaxonomy.cdm.model.description.MeasurementUnit;
 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
 import eu.etaxonomy.cdm.model.description.State;
 import eu.etaxonomy.cdm.model.description.StateData;
-import eu.etaxonomy.taxeditor.model.MessagingUtils;
+import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
+import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
+import eu.etaxonomy.taxeditor.store.CdmStore;
 
 /**
  * @author pplitzner
@@ -33,39 +35,48 @@ import eu.etaxonomy.taxeditor.model.MessagingUtils;
  */
 public class SpecimenColumnPropertyAccessor implements IColumnPropertyAccessor<SpecimenDescription>{
 
-    private final List<Feature> features;
-    private LinkedMap<String, String> propertyToLabelMap = new LinkedMap<>();
-
-
-    public SpecimenColumnPropertyAccessor(final List<Feature> features) {
-        this.features = features;
-        for (Feature feature : features) {
-            String featureLabel = feature.getLabel();
-            String property = featureLabel;
-            //show unit for quantitative data
-            if(feature.isSupportsQuantitativeData()){
-                Set<MeasurementUnit> recommendedMeasurementUnits = feature.getRecommendedMeasurementUnits();
-                if(recommendedMeasurementUnits.size()!=1){
-                    MessagingUtils.error(SpecimenColumnPropertyAccessor.class, "Only one unit is allowed for quantitative data", null);
-                    continue;
-                }
-                MeasurementUnit unit = recommendedMeasurementUnits.iterator().next();
-                featureLabel += " ["+unit.getLabel()+"]";
-            }
-            propertyToLabelMap.put(property, featureLabel);
-        }
-    }
+    private CharacterMatrix matrix;
 
-    public Map<String, String> getPropertyToLabelMap() {
-        return propertyToLabelMap;
+    public SpecimenColumnPropertyAccessor(CharacterMatrix matrix) {
+        this.matrix = matrix;
     }
 
+
     /**
      * {@inheritDoc}
      */
     @Override
-    public DescriptionElementBase getDataValue(SpecimenDescription rowObject, int columnIndex) {
-        Feature feature = features.get(columnIndex);
+    public Object getDataValue(SpecimenDescription rowObject, int columnIndex) {
+        IOccurrenceService occurrenceService = CdmStore.getService(IOccurrenceService.class);
+        SpecimenOrObservationBase<?> specimen = rowObject.getDescribedSpecimenOrObservation();
+        Collection<FieldUnit> fieldUnits = occurrenceService.getFieldUnits(specimen.getUuid());
+        switch (columnIndex) {
+        case 0:
+            return occurrenceService.listAssociatedTaxa(specimen, null, null, null, null);
+        case 1:
+            if(fieldUnits.size()!=1){
+                return "More than one or no field unit found for specimen";
+            }
+            return fieldUnits.iterator().next();
+        case 2:
+            if(specimen instanceof DerivedUnit){
+                return occurrenceService.getMostSignificantIdentifier(HibernateProxyHelper.deproxy(specimen, DerivedUnit.class));
+            }
+            return null;
+        case 3:
+            if(fieldUnits.size()!=1){
+                return "More than one or no field unit found for specimen";
+            }
+            FieldUnit fieldUnit = fieldUnits.iterator().next();
+            if(fieldUnit.getGatheringEvent()!=null){
+                return fieldUnit.getGatheringEvent().getCountry();
+            }
+            return null;
+
+        default:
+            break;
+        }
+        Feature feature = matrix.getIndexToFeatureMap().get(columnIndex);
         Set<DescriptionElementBase> elements = rowObject.getElements();
         for (DescriptionElementBase descriptionElementBase : elements) {
             if(descriptionElementBase.getFeature().equals(feature)){
@@ -80,17 +91,17 @@ public class SpecimenColumnPropertyAccessor implements IColumnPropertyAccessor<S
      */
     @Override
     public void setDataValue(SpecimenDescription rowObject, int columnIndex, Object newValue) {
-        Feature feature = features.get(columnIndex);
+        Feature feature = matrix.getIndexToFeatureMap().get(columnIndex);
         Set<DescriptionElementBase> elements = rowObject.getElements();
         for (DescriptionElementBase descriptionElementBase : elements) {
             if(descriptionElementBase.getFeature().equals(feature)){
-                setDescriptionElement(rowObject, descriptionElementBase, feature, newValue);
+                setDescriptionElement(descriptionElementBase, feature, newValue);
             }
         }
     }
 
 
-    private void setDescriptionElement(SpecimenDescription description, DescriptionElementBase descriptionElementBase, Feature feature, Object newValue) {
+    private void setDescriptionElement(DescriptionElementBase descriptionElementBase, Feature feature, Object newValue) {
         //FIXME move this to cdmlib service layer
         if(feature.isSupportsCategoricalData() && descriptionElementBase instanceof CategoricalData && newValue instanceof Collection){
             CategoricalData categoricalData = (CategoricalData)descriptionElementBase;
@@ -110,7 +121,7 @@ public class SpecimenColumnPropertyAccessor implements IColumnPropertyAccessor<S
      */
     @Override
     public int getColumnCount() {
-        return features.size();
+        return matrix.getPropertyToLabelMap().size();
     }
 
     /**
@@ -118,7 +129,7 @@ public class SpecimenColumnPropertyAccessor implements IColumnPropertyAccessor<S
      */
     @Override
     public String getColumnProperty(int columnIndex) {
-        return features.get(columnIndex).getLabel();
+        return matrix.getPropertyToLabelMap().get(columnIndex);
     }
 
     /**
@@ -126,7 +137,7 @@ public class SpecimenColumnPropertyAccessor implements IColumnPropertyAccessor<S
      */
     @Override
     public int getColumnIndex(String propertyName){
-        return propertyToLabelMap.indexOf(propertyName);
+        return matrix.getPropertyToLabelMap().indexOf(propertyName);
     }
 
 }
diff --git a/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/categorical/CategoricalDataDisplayConverter.java b/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/categorical/CategoricalDataDisplayConverter.java
index cf7de7180..db33052ac 100644
--- a/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/categorical/CategoricalDataDisplayConverter.java
+++ b/eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/categorical/CategoricalDataDisplayConverter.java
@@ -32,7 +32,10 @@ public class CategoricalDataDisplayConverter extends DisplayConverter {
         else if(canonicalValue instanceof State){
             return ((State) canonicalValue).getLabel();
         }
-        return canonicalValue.toString();
+        else if(canonicalValue!=null){
+            return canonicalValue.toString();
+        }
+        return null;
     }
 
     /**
