Project

General

Profile

Download (24.8 KB) Statistics
| Branch: | Tag: | Revision:
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.io.File;
12
import java.io.FileInputStream;
13
import java.io.FileOutputStream;
14
import java.io.IOException;
15
import java.util.ArrayList;
16
import java.util.Collection;
17
import java.util.Collections;
18
import java.util.HashMap;
19
import java.util.List;
20
import java.util.Map;
21
import java.util.Properties;
22
import java.util.Set;
23
import java.util.UUID;
24

    
25
import javax.annotation.PostConstruct;
26
import javax.annotation.PreDestroy;
27
import javax.inject.Inject;
28

    
29
import org.apache.commons.collections4.map.LinkedMap;
30
import org.eclipse.core.runtime.IProgressMonitor;
31
import org.eclipse.e4.ui.di.Focus;
32
import org.eclipse.e4.ui.di.Persist;
33
import org.eclipse.e4.ui.model.application.ui.MDirtyable;
34
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
35
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
36
import org.eclipse.jface.layout.GridDataFactory;
37
import org.eclipse.nebula.widgets.nattable.NatTable;
38
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
39
import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
40
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
41
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
42
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
43
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
44
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
45
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
46
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
47
import org.eclipse.nebula.widgets.nattable.edit.editor.IComboBoxDataProvider;
48
import org.eclipse.nebula.widgets.nattable.export.command.ExportCommand;
49
import org.eclipse.nebula.widgets.nattable.export.command.ExportCommandHandler;
50
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
51
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
52
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
53
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
54
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
55
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
56
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
57
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
58
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
59
import org.eclipse.nebula.widgets.nattable.hideshow.ColumnHideShowLayer;
60
import org.eclipse.nebula.widgets.nattable.hideshow.RowHideShowLayer;
61
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
62
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
63
import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;
64
import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnOverrideLabelAccumulator;
65
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
66
import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
67
import org.eclipse.nebula.widgets.nattable.persistence.PersistenceHelper;
68
import org.eclipse.nebula.widgets.nattable.persistence.command.DisplayPersistenceDialogCommand;
69
import org.eclipse.nebula.widgets.nattable.persistence.command.DisplayPersistenceDialogCommandHandler;
70
import org.eclipse.nebula.widgets.nattable.persistence.command.IStateChangedListener;
71
import org.eclipse.nebula.widgets.nattable.persistence.command.StateChangeEvent;
72
import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer;
73
import org.eclipse.nebula.widgets.nattable.reorder.RowReorderLayer;
74
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
75
import org.eclipse.nebula.widgets.nattable.selection.event.CellSelectionEvent;
76
import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
77
import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
78
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
79
import org.eclipse.nebula.widgets.nattable.ui.menu.AbstractHeaderMenuConfiguration;
80
import org.eclipse.nebula.widgets.nattable.ui.menu.PopupMenuBuilder;
81
import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
82
import org.eclipse.swt.SWT;
83
import org.eclipse.swt.events.SelectionAdapter;
84
import org.eclipse.swt.events.SelectionEvent;
85
import org.eclipse.swt.layout.GridLayout;
86
import org.eclipse.swt.widgets.Button;
87
import org.eclipse.swt.widgets.Combo;
88
import org.eclipse.swt.widgets.Composite;
89

    
90
import ca.odell.glazedlists.EventList;
91
import ca.odell.glazedlists.GlazedLists;
92
import ca.odell.glazedlists.SortedList;
93
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
94
import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
95
import eu.etaxonomy.cdm.api.service.IWorkingSetService;
96
import eu.etaxonomy.cdm.model.common.TermVocabulary;
97
import eu.etaxonomy.cdm.model.description.DescriptionBase;
98
import eu.etaxonomy.cdm.model.description.Feature;
99
import eu.etaxonomy.cdm.model.description.FeatureTree;
100
import eu.etaxonomy.cdm.model.description.MeasurementUnit;
101
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
102
import eu.etaxonomy.cdm.model.description.State;
103
import eu.etaxonomy.cdm.model.description.WorkingSet;
104
import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
105
import eu.etaxonomy.taxeditor.editor.workingSet.matrix.categorical.CategoricalDataCellEditor;
106
import eu.etaxonomy.taxeditor.editor.workingSet.matrix.categorical.CategoricalDataDisplayConverter;
107
import eu.etaxonomy.taxeditor.editor.workingSet.matrix.quantitative.QuantitativeDataCellEditor;
108
import eu.etaxonomy.taxeditor.editor.workingSet.matrix.quantitative.QuantitativeDataDisplayConverter;
109
import eu.etaxonomy.taxeditor.editor.workingSet.matrix.supplementalInfo.SupplementalInfoDisplayConverter;
110
import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
111
import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
112
import eu.etaxonomy.taxeditor.model.MessagingUtils;
113
import eu.etaxonomy.taxeditor.store.CdmStore;
114
import eu.etaxonomy.taxeditor.workbench.WorkbenchUtility;
115
import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
116

    
117
/**
118
 * @author pplitzner
119
 * @since Nov 26, 2017
120
 *
121
 */
122
public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails, IConversationEnabled, IDirtyMarkable{
123

    
124
    private static final String CHARACTER_MATRIX_STATE_PROPERTIES = "characterMatrixState.properties";
125

    
126
    private static final int LEADING_COLUMN_COUNT = 4;
127
    private static final String TAXON_COLUMN = "taxon_column";
128
    private static final String COLLECTOR_COLUMN = "collector_column";
129
    private static final String IDENTIFIER_COLUMN = "identifier_column";
130
    private static final String COUNTRY_COLUMN = "country_column";
131

    
132
    private WorkingSet workingSet;
133

    
134
    private Composite parent;
135

    
136
    private ConversationHolder conversation;
137

    
138
    @Inject
139
    private ESelectionService selService;
140

    
141
    @Inject
142
    private MDirtyable dirty;
143

    
144
    @Inject
145
    private MPart thisPart;
146

    
147
    private NatTable natTable;
148

    
149
    private Map<Integer, Feature> indexToFeatureMap = new HashMap<>();
150

    
151
    private LinkedMap<String, String> propertyToLabelMap = new LinkedMap<>();
152

    
153
    @PostConstruct
154
    public void create(Composite parent) {
155
        if(CdmStore.isActive() && conversation==null){
156
            conversation = CdmStore.createConversation();
157
        }
158
        else{
159
            return;
160
        }
161
        parent.setLayout(new GridLayout());
162
        this.parent = parent;
163
    }
164

    
165

    
166
    public void init(UUID workingSetUuid) {
167
        this.workingSet = CdmStore.getService(IWorkingSetService.class).load(workingSetUuid);
168
        thisPart.setLabel(workingSet.getLabel());
169

    
170
        //get features/columns stored in working set
171
        FeatureTree tree = workingSet.getDescriptiveSystem();
172
        List<Feature> features = new ArrayList<>(tree.getDistinctFeatures());
173
        Collections.sort(features);
174

    
175

    
176
        EventList<RowWrapper> descriptions = GlazedLists.eventList(getDescriptions(workingSet));
177
        SortedList<RowWrapper> sortedList = new SortedList<>(descriptions, null);
178

    
179
        /**
180
         * data provider
181
         */
182
        SpecimenColumnPropertyAccessor columnPropertyAccessor = new SpecimenColumnPropertyAccessor(this);
183
        IDataProvider bodyDataProvider = new ListDataProvider<RowWrapper>(sortedList, columnPropertyAccessor);
184

    
185
        /**
186
         * BODY layer
187
         */
188
        DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
189

    
190
        //register labels for columns
191
        final ColumnOverrideLabelAccumulator columnLabelAccumulator =new ColumnOverrideLabelAccumulator(bodyDataLayer);
192
        bodyDataLayer.setConfigLabelAccumulator(columnLabelAccumulator);
193
        propertyToLabelMap.put(TAXON_COLUMN, "Taxon");
194
        columnLabelAccumulator.registerColumnOverrides(0, TAXON_COLUMN);
195
        propertyToLabelMap.put(COLLECTOR_COLUMN, "Collector + No");
196
        columnLabelAccumulator.registerColumnOverrides(1, COLLECTOR_COLUMN);
197
        propertyToLabelMap.put(IDENTIFIER_COLUMN, "Identifier");
198
        columnLabelAccumulator.registerColumnOverrides(2, IDENTIFIER_COLUMN);
199
        propertyToLabelMap.put(COUNTRY_COLUMN, "Country");
200
        columnLabelAccumulator.registerColumnOverrides(3, COUNTRY_COLUMN);
201
        for(int i=0;i<features.size();i++){
202
            Feature feature = features.get(i);
203
            initLabels(columnLabelAccumulator, i, feature);
204
        }
205

    
206
        GlazedListsEventLayer<RowWrapper> eventLayer = new GlazedListsEventLayer<>(bodyDataLayer, sortedList);
207

    
208
        RowReorderLayer rowReorderLayer = new RowReorderLayer(eventLayer);
209
        ColumnReorderLayer columnReorderLayer = new ColumnReorderLayer(rowReorderLayer);
210
        ColumnHideShowLayer columnHideShowLayer = new ColumnHideShowLayer(columnReorderLayer);
211
        RowHideShowLayer rowHideShowLayer = new RowHideShowLayer(columnHideShowLayer);
212
        SelectionLayer selectionLayer = new SelectionLayer(rowHideShowLayer);
213
        ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
214

    
215
        /**
216
         * column header layer
217
         */
218
        IDataProvider columnHeaderDataProvider = new DefaultColumnHeaderDataProvider(
219
                propertyToLabelMap.values().toArray(new String[] {}), propertyToLabelMap);
220
        DataLayer columnHeaderDataLayer = new DataLayer(columnHeaderDataProvider);
221
        ColumnHeaderLayer columnHeaderLayer = new ColumnHeaderLayer(columnHeaderDataLayer, viewportLayer, selectionLayer);
222

    
223
        ConfigRegistry configRegistry = new ConfigRegistry();
224

    
225
        // add the SortHeaderLayer to the column header layer stack
226
        // as we use GlazedLists, we use the GlazedListsSortModel which
227
        // delegates the sorting to the SortedList
228
        final SortHeaderLayer<SpecimenDescription> sortHeaderLayer = new SortHeaderLayer<>(
229
                columnHeaderLayer,
230
                        new GlazedListsSortModel<>(
231
                                sortedList,
232
                                columnPropertyAccessor,
233
                                configRegistry,
234
                                columnHeaderDataLayer));
235

    
236

    
237
        /**
238
         * row header layer
239
         */
240
        IDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(bodyDataProvider);
241
        DataLayer rowHeaderDataLayer = new DataLayer(rowHeaderDataProvider, 40, 20);
242
        ILayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, viewportLayer, selectionLayer);
243

    
244

    
245
        /**
246
         * corner layer
247
         */
248
        ILayer cornerLayer = new CornerLayer(
249
                new DataLayer(new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider)),
250
                rowHeaderLayer, sortHeaderLayer);
251

    
252

    
253
        /**
254
         * GRID layer (composition of all other layers)
255
         */
256
        GridLayer gridLayer = new GridLayer(viewportLayer, sortHeaderLayer, rowHeaderLayer, cornerLayer);
257

    
258

    
259
        natTable = new NatTable(parent, gridLayer, false);
260

    
261

    
262
        /**
263
         * CONFIGURATION
264
         */
265
        natTable.setConfigRegistry(configRegistry);
266

    
267

    
268
        //add default configuration because autoconfigure is set to false in constructor
269
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
270

    
271
        // override the default sort configuration and change the mouse bindings
272
        // to sort on a single click
273
        natTable.addConfiguration(new SingleClickSortConfiguration());
274

    
275
        // add the header menu configuration for adding the column header menu
276
        // with hide/show actions
277
        natTable.addConfiguration(new AbstractHeaderMenuConfiguration(natTable) {
278

    
279
            @Override
280
            protected PopupMenuBuilder createColumnHeaderMenu(NatTable natTable) {
281
                return super.createColumnHeaderMenu(natTable)
282
                        .withHideColumnMenuItem()
283
                        .withShowAllColumnsMenuItem()
284
                        .withStateManagerMenuItemProvider();
285
            }
286

    
287
            @Override
288
            protected PopupMenuBuilder createRowHeaderMenu(NatTable natTable) {
289
                return super.createRowHeaderMenu(natTable)
290
                        .withHideRowMenuItem()
291
                        .withShowAllRowsMenuItem()
292
                        .withStateManagerMenuItemProvider();
293
            }
294

    
295
            @Override
296
            protected PopupMenuBuilder createCornerMenu(NatTable natTable) {
297
                return super.createCornerMenu(natTable)
298
                        .withShowAllColumnsMenuItem().withShowAllRowsMenuItem()
299
                        .withStateManagerMenuItemProvider();
300
            }
301
        });
302

    
303
        // add custom configuration for data conversion and add column labels
304
        viewportLayer.addConfiguration(new AbstractRegistryConfiguration() {
305
            @Override
306
            public void configureRegistry(IConfigRegistry configRegistry) {
307
                //add display converter for string representation
308
                configRegistry.registerConfigAttribute(
309
                        CellConfigAttributes.DISPLAY_CONVERTER,
310
                        new SupplementalInfoDisplayConverter(),
311
                        DisplayMode.NORMAL,
312
                        TAXON_COLUMN);
313
                configRegistry.registerConfigAttribute(
314
                        CellConfigAttributes.DISPLAY_CONVERTER,
315
                        new SupplementalInfoDisplayConverter(),
316
                        DisplayMode.NORMAL,
317
                        COLLECTOR_COLUMN);
318
                configRegistry.registerConfigAttribute(
319
                        CellConfigAttributes.DISPLAY_CONVERTER,
320
                        new SupplementalInfoDisplayConverter(),
321
                        DisplayMode.NORMAL,
322
                        IDENTIFIER_COLUMN);
323
                configRegistry.registerConfigAttribute(
324
                        CellConfigAttributes.DISPLAY_CONVERTER,
325
                        new SupplementalInfoDisplayConverter(),
326
                        DisplayMode.NORMAL,
327
                        COUNTRY_COLUMN);
328
                features.forEach(feature->registerColumnConfiguration(feature, configRegistry));
329
            }
330

    
331
        });
332

    
333
        // add the ExportCommandHandler to the ViewportLayer in order to make
334
        // exporting work
335
        viewportLayer.registerCommandHandler(new ExportCommandHandler(viewportLayer));
336

    
337
        //propagate single cell selection
338
        natTable.addLayerListener(new ILayerListener() {
339
            @Override
340
            public void handleLayerEvent(ILayerEvent event) {
341
                if(event instanceof CellSelectionEvent){
342
                    CellSelectionEvent cellSelectionEvent = (CellSelectionEvent)event;
343
                    Collection<ILayerCell> selectedCells = cellSelectionEvent.getSelectionLayer().getSelectedCells();
344
                    if(selectedCells.size()==1){
345
                        ILayerCell cell = selectedCells.iterator().next();
346
                        selService.setSelection(cell.getDataValue());
347
                    }
348
                }
349
            }
350
        });
351

    
352
        natTable.configure();
353

    
354
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
355

    
356
        /**
357
         * Table state persistence
358
         */
359
        Properties natTableState = new Properties();
360
        //load persisted state
361
        File statePropertiesFile = new File(WorkbenchUtility.getBaseLocation(), CHARACTER_MATRIX_STATE_PROPERTIES);
362
        FileInputStream inputStream;
363
        try {
364
            inputStream = new FileInputStream(statePropertiesFile);
365
            natTableState.load(inputStream);
366
            natTable.loadState("", natTableState);
367
        } catch (IOException e) {
368
            MessagingUtils.error(CharacterMatrix.class, e);;e.printStackTrace();
369
        }
370

    
371
        DisplayPersistenceDialogCommandHandler handler =
372
                new DisplayPersistenceDialogCommandHandler(natTableState, natTable);
373
        gridLayer.registerCommandHandler(handler);
374
     // create a combobox for showing the available view states
375
        Combo viewStates = new Combo(parent, SWT.DROP_DOWN);
376
        viewStates.setItems(PersistenceHelper
377
                .getAvailableStates(natTableState)
378
                .toArray(new String[] {}));
379
        viewStates.addSelectionListener(new SelectionAdapter() {
380

    
381
            @Override
382
            public void widgetSelected(SelectionEvent e) {
383
                int index = viewStates.getSelectionIndex();
384
                if (index >= 0) {
385
                    String selected = viewStates.getItem(index);
386
                    // load the state
387
                    natTable.loadState(selected, natTableState);
388
                }
389
            }
390
        });
391

    
392
        // add listener to update the combo on view state management changes
393
        handler.addStateChangeListener(new IStateChangedListener() {
394

    
395
            @Override
396
            public void handleStateChange(StateChangeEvent event) {
397
                viewStates.setItems(PersistenceHelper
398
                        .getAvailableStates(natTableState)
399
                        .toArray(new String[] {}));
400
            }
401
        });
402

    
403
        // add button to show dialog
404
        Button manage = new Button(parent, SWT.PUSH);
405
        manage.setText("View Management");
406
        manage.addSelectionListener(new SelectionAdapter() {
407
            @Override
408
            public void widgetSelected(SelectionEvent e) {
409
                natTable.doCommand(new DisplayPersistenceDialogCommand(natTable));
410
            }
411
        });
412
        Button btnSave = new Button(parent, SWT.PUSH);
413
        btnSave.setText("Save State");
414
        btnSave.addSelectionListener(new SelectionAdapter() {
415
            @Override
416
            public void widgetSelected(SelectionEvent e) {
417
                natTable.saveState("", natTableState);
418
                try (FileOutputStream tableStateStream =
419
                        new FileOutputStream(new File(WorkbenchUtility.getBaseLocation(), CHARACTER_MATRIX_STATE_PROPERTIES))) {
420
                    natTableState.store(tableStateStream, null);
421
                } catch (IOException ioe) {
422
                    ioe.printStackTrace();
423
                }
424
            }
425
        });
426

    
427
        /**
428
         * excel export
429
         */
430
        Button addColumnButton = new Button(parent, SWT.PUSH);
431
        addColumnButton.setText("Export");
432
        addColumnButton.addSelectionListener(new SelectionAdapter() {
433
            @Override
434
            public void widgetSelected(SelectionEvent e) {
435
                natTable.doCommand(
436
                        new ExportCommand(
437
                                natTable.getConfigRegistry(),
438
                                natTable.getShell()));
439
            }
440
        });
441

    
442
        parent.layout();
443
    }
444

    
445
    private void initLabels(final ColumnOverrideLabelAccumulator columnLabelAccumulator,
446
            int index, Feature feature) {
447

    
448
        columnLabelAccumulator.registerColumnOverrides(index+LEADING_COLUMN_COUNT, getProperty(feature));
449
        indexToFeatureMap.put(index+LEADING_COLUMN_COUNT, feature);
450

    
451
        String featureLabel = feature.getLabel();
452
        String property = featureLabel;
453
        //show unit for quantitative data
454
        if(feature.isSupportsQuantitativeData()){
455
            Set<MeasurementUnit> recommendedMeasurementUnits = feature.getRecommendedMeasurementUnits();
456
            if(recommendedMeasurementUnits.size()!=1){
457
                MessagingUtils.error(SpecimenColumnPropertyAccessor.class, "Only one unit is allowed for quantitative data", null);
458
                return;
459
            }
460
            MeasurementUnit unit = recommendedMeasurementUnits.iterator().next();
461
            featureLabel += " ["+unit.getLabel()+"]";
462
        }
463
        propertyToLabelMap.put(property, featureLabel);
464
    }
465

    
466
    private void registerColumnConfiguration(Feature feature, IConfigRegistry configRegistry) {
467
        //make cell editable
468
        configRegistry.registerConfigAttribute(
469
                EditConfigAttributes.CELL_EDITABLE_RULE,
470
                IEditableRule.ALWAYS_EDITABLE,
471
                DisplayMode.EDIT,
472
                getProperty(feature)
473
                );
474
        if(feature.isSupportsQuantitativeData()){
475
            //add display converter for string representation
476
            configRegistry.registerConfigAttribute(
477
                    CellConfigAttributes.DISPLAY_CONVERTER,
478
                    new QuantitativeDataDisplayConverter(),
479
                    DisplayMode.NORMAL,
480
                    getProperty(feature));
481
            //register quantitative editor
482
            configRegistry.registerConfigAttribute(
483
                    EditConfigAttributes.CELL_EDITOR,
484
                    new QuantitativeDataCellEditor(feature.getRecommendedStatisticalMeasures(), this),
485
                    DisplayMode.EDIT,
486
                    getProperty(feature));
487
        }
488
        else if(feature.isSupportsCategoricalData()){
489
            //add display converter for string representation
490
            configRegistry.registerConfigAttribute(
491
                    CellConfigAttributes.DISPLAY_CONVERTER,
492
                    new CategoricalDataDisplayConverter(),
493
                    DisplayMode.NORMAL,
494
                    getProperty(feature));
495

    
496
            //add combo box cell editor
497
            CategoricalDataCellEditor comboBoxCellEditor = new CategoricalDataCellEditor(new IComboBoxDataProvider() {
498

    
499
                @Override
500
                public List<?> getValues(int columnIndex, int rowIndex) {
501
                    List<State> states = new ArrayList<>();
502
                    Feature feature = indexToFeatureMap.get(columnIndex);
503
                    if(feature.isSupportsCategoricalData()){
504
                        Set<TermVocabulary<State>> stateVocs = feature.getSupportedCategoricalEnumerations();
505
                        for (TermVocabulary<State> voc : stateVocs) {
506
                            states.addAll(voc.getTerms());
507
                        }
508
                    }
509
                    return states;
510
                }
511
            }, 5, this);
512
            //register editor
513
            configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR,
514
                    comboBoxCellEditor,
515
                    DisplayMode.EDIT,
516
                    getProperty(feature));
517

    
518
        }
519

    
520
    }
521

    
522
    private List<RowWrapper> getDescriptions(WorkingSet workingSet) {
523
        List<RowWrapper> rowWrappers = new ArrayList<>();
524
        Set<DescriptionBase> wsDescriptions = workingSet.getDescriptions();
525
        for (DescriptionBase descriptionBase : wsDescriptions) {
526
            if(descriptionBase instanceof SpecimenDescription){
527
                rowWrappers.add(new RowWrapper((SpecimenDescription) descriptionBase));
528
            }
529
        }
530
        return rowWrappers;
531
    }
532

    
533
    private String getProperty(Feature feature){
534
        return feature.getLabel();
535
    }
536

    
537
    public Map<Integer, Feature> getIndexToFeatureMap() {
538
        return indexToFeatureMap;
539
    }
540

    
541
    public LinkedMap<String, String> getPropertyToLabelMap() {
542
        return propertyToLabelMap;
543
    }
544

    
545
    public void setDirty() {
546
        this.dirty.setDirty(true);
547
    }
548

    
549
    @Persist
550
    @Override
551
    public void save(IProgressMonitor monitor) {
552
        CdmStore.getService(IWorkingSetService.class).merge(workingSet, true);
553
        conversation.commit();
554
        dirty.setDirty(false);
555
    }
556

    
557
    @Focus
558
    public void setFocus(){
559
        if(conversation!=null){
560
            conversation.bind();
561
        }
562
    }
563

    
564
    @PreDestroy
565
    public void dispose(){
566
        if(conversation!=null){
567
            conversation.close();
568
            conversation = null;
569
        }
570
    }
571

    
572

    
573
    /**
574
     * {@inheritDoc}
575
     */
576
    @Override
577
    public void update(CdmDataChangeMap arg0) {
578
    }
579

    
580

    
581
    /**
582
     * {@inheritDoc}
583
     */
584
    @Override
585
    public ConversationHolder getConversationHolder() {
586
        return conversation;
587
    }
588

    
589

    
590
    /**
591
     * {@inheritDoc}
592
     */
593
    @Override
594
    public void changed(Object element) {
595
        dirty.setDirty(true);
596
        natTable.refresh();
597
    }
598

    
599

    
600
    /**
601
     * {@inheritDoc}
602
     */
603
    @Override
604
    public void forceDirty() {
605
        dirty.setDirty(true);
606
    }
607

    
608
}
(1-1/4)