Project

General

Profile

« Previous | Next » 

Revision 6b8fbb9a

Added by Patrick Plitzner over 6 years ago

ref #7095 Implement combo box editor for categorical data

View differences:

eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/CategoricalDataCellEditor.java
1
/**
2
* Copyright (C) 2017 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.taxeditor.editor.workingSet.matrix;
10

  
11
import java.util.List;
12

  
13
import org.eclipse.nebula.widgets.nattable.edit.editor.IComboBoxDataProvider;
14
import org.eclipse.nebula.widgets.nattable.filterrow.combobox.FilterRowComboBoxCellEditor;
15

  
16
import eu.etaxonomy.cdm.model.description.CategoricalData;
17
import eu.etaxonomy.cdm.model.description.State;
18

  
19
/**
20
 * @author pplitzner
21
 * @since Dec 7, 2017
22
 *
23
 */
24
public class CategoricalDataCellEditor extends FilterRowComboBoxCellEditor{
25

  
26
    public CategoricalDataCellEditor(IComboBoxDataProvider dataProvider, int maxVisibleItems) {
27
        super(dataProvider, maxVisibleItems);
28
    }
29

  
30
    @Override
31
    public void setCanonicalValue(Object canonicalValue) {
32
        if (canonicalValue instanceof CategoricalData) {
33
            CategoricalData data = (CategoricalData)canonicalValue;
34
            List<State> states = data.getStatesOnly();
35
            String[] result = new String[states.size()];
36
            for (int i = 0; i < states.size(); i++) {
37
                result[i] = (String) this.displayConverter.canonicalToDisplayValue(
38
                        this.layerCell, this.configRegistry, states.get(i));
39
            }
40
            setEditorValue(result);
41
        }
42
    }
43

  
44
}
eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/CategoricalDataDisplayConverter.java
1
/**
2
* Copyright (C) 2017 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.taxeditor.editor.workingSet.matrix;
10

  
11
import org.eclipse.nebula.widgets.nattable.data.convert.DisplayConverter;
12

  
13
import eu.etaxonomy.cdm.model.description.CategoricalData;
14
import eu.etaxonomy.cdm.model.description.State;
15
import eu.etaxonomy.taxeditor.model.DescriptionHelper;
16

  
17
/**
18
 * @author pplitzner
19
 * @since Dec 1, 2017
20
 *
21
 */
22
public class CategoricalDataDisplayConverter extends DisplayConverter {
23

  
24
    /**
25
     * {@inheritDoc}
26
     */
27
    @Override
28
    public Object canonicalToDisplayValue(Object canonicalValue) {
29
        if(canonicalValue instanceof CategoricalData){
30
            return DescriptionHelper.getLabel(canonicalValue);
31
        }
32
        else if(canonicalValue instanceof State){
33
            return ((State) canonicalValue).getLabel();
34
        }
35
        return canonicalValue.toString();
36
    }
37

  
38
    /**
39
     * {@inheritDoc}
40
     */
41
    @Override
42
    public Object displayToCanonicalValue(Object displayValue) {
43
        return null;
44
    }
45

  
46
}
eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/CharacterMatrix.java
10 10

  
11 11
import java.util.ArrayList;
12 12
import java.util.Collection;
13
import java.util.Collections;
13 14
import java.util.List;
14 15
import java.util.Set;
15 16

  
......
30 31
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
31 32
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
32 33
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
34
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
33 35
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
34 36
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
37
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
38
import org.eclipse.nebula.widgets.nattable.edit.editor.IComboBoxDataProvider;
35 39
import org.eclipse.nebula.widgets.nattable.export.command.ExportCommand;
36 40
import org.eclipse.nebula.widgets.nattable.export.command.ExportCommandHandler;
37 41
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
38 42
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
39
import org.eclipse.nebula.widgets.nattable.grid.GridRegion;
40 43
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
41 44
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
42 45
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
......
47 50
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
48 51
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
49 52
import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;
53
import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnOverrideLabelAccumulator;
50 54
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
51 55
import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
52 56
import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer;
......
70 74
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
71 75
import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
72 76
import eu.etaxonomy.cdm.api.service.IWorkingSetService;
77
import eu.etaxonomy.cdm.model.common.TermVocabulary;
73 78
import eu.etaxonomy.cdm.model.description.DescriptionBase;
79
import eu.etaxonomy.cdm.model.description.Feature;
80
import eu.etaxonomy.cdm.model.description.FeatureTree;
74 81
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
82
import eu.etaxonomy.cdm.model.description.State;
75 83
import eu.etaxonomy.cdm.model.description.WorkingSet;
76 84
import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
77 85
import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
......
103 111

  
104 112
    private NatTable natTable;
105 113

  
114
    private List<Feature> features;
115

  
106 116
    @PostConstruct
107 117
    public void create(Composite parent) {
108 118
        if(CdmStore.isActive() && conversation==null){
......
120 130
        this.workingSet = workingSet;
121 131
        thisPart.setLabel(workingSet.getLabel());
122 132

  
133
        //get features/columns stored in working set
134
        FeatureTree tree = workingSet.getDescriptiveSystem();
135
        features = new ArrayList<>(tree.getDistinctFeatures());
136
        Collections.sort(features);
137

  
138

  
123 139
        EventList<SpecimenDescription> descriptions = GlazedLists.eventList(getDescriptions(workingSet));
124 140
        SortedList<SpecimenDescription> sortedList = new SortedList<>(descriptions, null);
125 141

  
126
        // create the data provider
127
        SpecimenColumnPropertyAccessor columnPropertyAccessor = new SpecimenColumnPropertyAccessor(workingSet);
142
        /**
143
         * data provider
144
         */
145
        SpecimenColumnPropertyAccessor columnPropertyAccessor = new SpecimenColumnPropertyAccessor(features);
128 146
        IDataProvider bodyDataProvider = new ListDataProvider<SpecimenDescription>(sortedList, columnPropertyAccessor);
129 147

  
148
        /**
149
         * BODY layer
150
         */
130 151
        DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
152
        final ColumnOverrideLabelAccumulator columnLabelAccumulator =new ColumnOverrideLabelAccumulator(bodyDataLayer);
153
        bodyDataLayer.setConfigLabelAccumulator(columnLabelAccumulator);
154
        registerColumnLabels(columnLabelAccumulator);
131 155
        GlazedListsEventLayer<SpecimenDescription> eventLayer = new GlazedListsEventLayer<>(bodyDataLayer, sortedList);
132 156

  
133 157
        RowReorderLayer rowReorderLayer = new RowReorderLayer(eventLayer);
......
135 159
        SelectionLayer selectionLayer = new SelectionLayer(columnReorderLayer);
136 160
        ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
137 161

  
138
        // build the column header layer stack
162
        /**
163
         * column header layer
164
         */
139 165
        IDataProvider columnHeaderDataProvider = new DefaultColumnHeaderDataProvider(
140 166
                columnPropertyAccessor.getPropertyToLabelMap().values().toArray(new String[] {}), columnPropertyAccessor.getPropertyToLabelMap());
141 167
        DataLayer columnHeaderDataLayer = new DataLayer(columnHeaderDataProvider);
......
146 172
        // add the SortHeaderLayer to the column header layer stack
147 173
        // as we use GlazedLists, we use the GlazedListsSortModel which
148 174
        // delegates the sorting to the SortedList
149
        final SortHeaderLayer sortHeaderLayer =
150
                new SortHeaderLayer<>(
175
        final SortHeaderLayer<SpecimenDescription> sortHeaderLayer = new SortHeaderLayer<>(
151 176
                        columnHeaderLayer,
152 177
                        new GlazedListsSortModel<>(
153 178
                                sortedList,
......
155 180
                                configRegistry,
156 181
                                columnHeaderDataLayer));
157 182

  
158
        // build the row header layer stack
183

  
184
        /**
185
         * row header layer
186
         */
159 187
        IDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(bodyDataProvider);
160 188
        DataLayer rowHeaderDataLayer = new DataLayer(rowHeaderDataProvider, 40, 20);
161 189
        ILayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, viewportLayer, selectionLayer);
162 190

  
163
        // build the corner layer stack
191

  
192
        /**
193
         * corner layer
194
         */
164 195
        ILayer cornerLayer = new CornerLayer(
165 196
                new DataLayer(new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider)),
166 197
                rowHeaderLayer, sortHeaderLayer);
167 198

  
168
        // create the grid layer composed with the prior created layer stacks
199

  
200
        /**
201
         * GRID layer (composition of all other layers)
202
         */
169 203
        GridLayer gridLayer = new GridLayer(viewportLayer, sortHeaderLayer, rowHeaderLayer, cornerLayer);
170 204

  
171 205

  
......
181 215
        // to sort on a single click
182 216
        natTable.addConfiguration(new SingleClickSortConfiguration());
183 217

  
218

  
184 219
        // add custom configuration for data conversion
185 220
        viewportLayer.addConfiguration(new AbstractRegistryConfiguration() {
186

  
187 221
            @Override
188 222
            public void configureRegistry(IConfigRegistry configRegistry) {
189

  
190
                // apply for all columns of the data body region
191
                configRegistry.registerConfigAttribute(
192
                        // attribute to apply
193
                        CellConfigAttributes.DISPLAY_CONVERTER,
194
                        // value of the attribute
195
                        new DescriptionDisplayConverter(),
196
                        // apply during normal rendering i.e not
197
                        // during selection or edit
198
                        DisplayMode.NORMAL,
199
                        // apply the above for all cells with this label
200
                        GridRegion.BODY);
201

  
223
                features.forEach(feature -> registerColumnConfiguration(feature, configRegistry));
224
                }
202 225
            }
203
        });
226
        );
204 227

  
205 228
        // add the ExportCommandHandler to the ViewportLayer in order to make
206 229
        // exporting work
......
208 231

  
209 232
        //propagate single cell selection
210 233
        natTable.addLayerListener(new ILayerListener() {
211

  
212 234
            @Override
213 235
            public void handleLayerEvent(ILayerEvent event) {
214 236
                if(event instanceof CellSelectionEvent){
......
242 264
        parent.layout();
243 265
    }
244 266

  
267
    private void registerColumnLabels(ColumnOverrideLabelAccumulator columnLabelAccumulator) {
268
        for(int i=0;i<features.size();i++){
269
            columnLabelAccumulator.registerColumnOverrides(i, getProperty(features.get(i)));
270
        }
271
    }
272

  
273
    private void registerColumnConfiguration(Feature feature, IConfigRegistry configRegistry) {
274
        //make cell editable
275
        configRegistry.registerConfigAttribute(
276
                EditConfigAttributes.CELL_EDITABLE_RULE,
277
                IEditableRule.ALWAYS_EDITABLE,
278
                DisplayMode.EDIT,
279
                getProperty(feature)
280
                );
281
        if(feature.isSupportsQuantitativeData()){
282
            //add display converter for string representation
283
            configRegistry.registerConfigAttribute(
284
                    CellConfigAttributes.DISPLAY_CONVERTER,
285
                    new QuantitativeDataDisplayConverter(),
286
                    DisplayMode.NORMAL,
287
                    getProperty(feature));
288
        }
289
        else if(feature.isSupportsCategoricalData()){
290
            //add display converter for string representation
291
            configRegistry.registerConfigAttribute(
292
                    CellConfigAttributes.DISPLAY_CONVERTER,
293
                    new CategoricalDataDisplayConverter(),
294
                    DisplayMode.NORMAL,
295
                    getProperty(feature));
296

  
297
            //add combo box cell editor
298
            CategoricalDataCellEditor comboBoxCellEditor = new CategoricalDataCellEditor(new IComboBoxDataProvider() {
299

  
300
                @Override
301
                public List<?> getValues(int columnIndex, int rowIndex) {
302
                    List<State> states = new ArrayList<>();
303
                    Feature feature = features.get(columnIndex);
304
                    if(feature.isSupportsCategoricalData()){
305
                        Set<TermVocabulary<State>> stateVocs = feature.getSupportedCategoricalEnumerations();
306
                        for (TermVocabulary<State> voc : stateVocs) {
307
                            states.addAll(voc.getTerms());
308
                        }
309
                    }
310
                    return states;
311
                }
312
            }, 5);
313
            //register editor
314
            configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR,
315
                    comboBoxCellEditor,
316
                    DisplayMode.EDIT,
317
                    getProperty(feature));
318
            configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR,
319
                    comboBoxCellEditor,
320
                    DisplayMode.NORMAL,
321
                    getProperty(feature));
322

  
323
        }
324

  
325
    }
326

  
245 327
    private List<SpecimenDescription> getDescriptions(WorkingSet workingSet) {
246 328
        List<SpecimenDescription> descriptions = new ArrayList<>();
247 329
        Set<DescriptionBase> wsDescriptions = workingSet.getDescriptions();
......
253 335
        return descriptions;
254 336
    }
255 337

  
338
    public List<Feature> getFeatures() {
339
        return features;
340
    }
341

  
342
    private String getProperty(Feature feature){
343
        return feature.getLabel();
344
    }
345

  
256 346
    @Persist
257 347
    @Override
258 348
    public void save(IProgressMonitor monitor) {
eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/DescriptionDisplayConverter.java
1
/**
2
* Copyright (C) 2017 EDIT
3
* European Distributed Institute of Taxonomy
4
* http://www.e-taxonomy.eu
5
*
6
* The contents of this file are subject to the Mozilla Public License Version 1.1
7
* See LICENSE.TXT at the top of this package for the full license terms.
8
*/
9
package eu.etaxonomy.taxeditor.editor.workingSet.matrix;
10

  
11
import org.eclipse.nebula.widgets.nattable.data.convert.DisplayConverter;
12

  
13
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
14
import eu.etaxonomy.taxeditor.model.DescriptionHelper;
15

  
16
/**
17
 * @author pplitzner
18
 * @since Dec 1, 2017
19
 *
20
 */
21
public class DescriptionDisplayConverter extends DisplayConverter {
22

  
23
    /**
24
     * {@inheritDoc}
25
     */
26
    @Override
27
    public Object canonicalToDisplayValue(Object canonicalValue) {
28
        if(canonicalValue instanceof DescriptionElementBase){
29
            return DescriptionHelper.getLabel(canonicalValue);
30
        }
31
        return null;
32
    }
33

  
34
    /**
35
     * {@inheritDoc}
36
     */
37
    @Override
38
    public Object displayToCanonicalValue(Object displayValue) {
39
        return null;
40
    }
41

  
42

  
43
}
eu.etaxonomy.taxeditor.editor/src/main/java/eu/etaxonomy/taxeditor/editor/workingSet/matrix/SpecimenColumnPropertyAccessor.java
9 9
package eu.etaxonomy.taxeditor.editor.workingSet.matrix;
10 10

  
11 11
import java.util.ArrayList;
12
import java.util.Collection;
12 13
import java.util.List;
13 14
import java.util.Map;
14 15
import java.util.Set;
......
16 17
import org.apache.commons.collections4.map.LinkedMap;
17 18
import org.eclipse.nebula.widgets.nattable.data.IColumnPropertyAccessor;
18 19

  
20
import eu.etaxonomy.cdm.model.description.CategoricalData;
19 21
import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
20 22
import eu.etaxonomy.cdm.model.description.Feature;
21
import eu.etaxonomy.cdm.model.description.FeatureTree;
22 23
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
23
import eu.etaxonomy.cdm.model.description.WorkingSet;
24
import eu.etaxonomy.cdm.model.description.State;
25
import eu.etaxonomy.cdm.model.description.StateData;
24 26

  
25 27
/**
26 28
 * @author pplitzner
......
29 31
 */
30 32
public class SpecimenColumnPropertyAccessor implements IColumnPropertyAccessor<SpecimenDescription>{
31 33

  
32
    private List<Feature> features = new ArrayList<>();
34
    private final List<Feature> features;
33 35
    private LinkedMap<String, String> propertyToLabelMap = new LinkedMap<>();
34 36

  
35 37

  
36
    public SpecimenColumnPropertyAccessor(WorkingSet workingSet) {
37
        FeatureTree tree = workingSet.getDescriptiveSystem();
38
        Set<Feature> distinctFeatures = tree.getDistinctFeatures();
39
        for (Feature feature : distinctFeatures) {
40
            if(feature!=null){
41
                features.add(feature);
42
                propertyToLabelMap.put(feature.getLabel(), feature.getLabel());
43
            }
38
    public SpecimenColumnPropertyAccessor(final List<Feature> features) {
39
        this.features = features;
40
        for (Feature feature : features) {
41
            propertyToLabelMap.put(feature.getLabel(), feature.getLabel());
44 42
        }
45 43
    }
46 44

  
......
68 66
     */
69 67
    @Override
70 68
    public void setDataValue(SpecimenDescription rowObject, int columnIndex, Object newValue) {
71
        features.remove(columnIndex);
72
        features.add(columnIndex, (Feature) newValue);
69
        Feature feature = features.get(columnIndex);
70
        Set<DescriptionElementBase> elements = rowObject.getElements();
71
        for (DescriptionElementBase descriptionElementBase : elements) {
72
            if(descriptionElementBase.getFeature().equals(feature)){
73
                setDescriptionElement(rowObject, descriptionElementBase, feature, newValue);
74
            }
75
        }
76
    }
77

  
78

  
79
    private void setDescriptionElement(SpecimenDescription description, DescriptionElementBase descriptionElementBase, Feature feature, Object newValue) {
80
        //FIXME move this to cdmlib service layer
81
        if(feature.isSupportsCategoricalData() && descriptionElementBase instanceof CategoricalData && newValue instanceof Collection){
82
            CategoricalData categoricalData = (CategoricalData)descriptionElementBase;
83
            List<StateData> stateData = new ArrayList<>(categoricalData.getStateData());
84
            for (StateData stateData2 : stateData) {
85
                categoricalData.removeStateData(stateData2);
86
            }
87
            Collection<State> states = (Collection<State>) newValue;
88
            for (State state : states) {
89
                categoricalData.addStateData(state);
90
            }
91
        }
73 92
    }
74 93

  
75 94
    /**

Also available in: Unified diff