Project

General

Profile

Download (16 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.util.ArrayList;
12
import java.util.Collection;
13
import java.util.Collections;
14
import java.util.List;
15
import java.util.Set;
16
import java.util.UUID;
17

    
18
import javax.annotation.PostConstruct;
19
import javax.annotation.PreDestroy;
20
import javax.inject.Inject;
21

    
22
import org.eclipse.core.runtime.IProgressMonitor;
23
import org.eclipse.e4.ui.di.Focus;
24
import org.eclipse.e4.ui.di.Persist;
25
import org.eclipse.e4.ui.model.application.ui.MDirtyable;
26
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
27
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
28
import org.eclipse.jface.layout.GridDataFactory;
29
import org.eclipse.nebula.widgets.nattable.NatTable;
30
import org.eclipse.nebula.widgets.nattable.config.AbstractRegistryConfiguration;
31
import org.eclipse.nebula.widgets.nattable.config.CellConfigAttributes;
32
import org.eclipse.nebula.widgets.nattable.config.ConfigRegistry;
33
import org.eclipse.nebula.widgets.nattable.config.DefaultNatTableStyleConfiguration;
34
import org.eclipse.nebula.widgets.nattable.config.IConfigRegistry;
35
import org.eclipse.nebula.widgets.nattable.config.IEditableRule;
36
import org.eclipse.nebula.widgets.nattable.data.IDataProvider;
37
import org.eclipse.nebula.widgets.nattable.data.ListDataProvider;
38
import org.eclipse.nebula.widgets.nattable.edit.EditConfigAttributes;
39
import org.eclipse.nebula.widgets.nattable.edit.editor.IComboBoxDataProvider;
40
import org.eclipse.nebula.widgets.nattable.export.command.ExportCommand;
41
import org.eclipse.nebula.widgets.nattable.export.command.ExportCommandHandler;
42
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsEventLayer;
43
import org.eclipse.nebula.widgets.nattable.extension.glazedlists.GlazedListsSortModel;
44
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultColumnHeaderDataProvider;
45
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultCornerDataProvider;
46
import org.eclipse.nebula.widgets.nattable.grid.data.DefaultRowHeaderDataProvider;
47
import org.eclipse.nebula.widgets.nattable.grid.layer.ColumnHeaderLayer;
48
import org.eclipse.nebula.widgets.nattable.grid.layer.CornerLayer;
49
import org.eclipse.nebula.widgets.nattable.grid.layer.GridLayer;
50
import org.eclipse.nebula.widgets.nattable.grid.layer.RowHeaderLayer;
51
import org.eclipse.nebula.widgets.nattable.layer.DataLayer;
52
import org.eclipse.nebula.widgets.nattable.layer.ILayer;
53
import org.eclipse.nebula.widgets.nattable.layer.ILayerListener;
54
import org.eclipse.nebula.widgets.nattable.layer.cell.ColumnOverrideLabelAccumulator;
55
import org.eclipse.nebula.widgets.nattable.layer.cell.ILayerCell;
56
import org.eclipse.nebula.widgets.nattable.layer.event.ILayerEvent;
57
import org.eclipse.nebula.widgets.nattable.reorder.ColumnReorderLayer;
58
import org.eclipse.nebula.widgets.nattable.reorder.RowReorderLayer;
59
import org.eclipse.nebula.widgets.nattable.selection.SelectionLayer;
60
import org.eclipse.nebula.widgets.nattable.selection.event.CellSelectionEvent;
61
import org.eclipse.nebula.widgets.nattable.sort.SortHeaderLayer;
62
import org.eclipse.nebula.widgets.nattable.sort.config.SingleClickSortConfiguration;
63
import org.eclipse.nebula.widgets.nattable.style.DisplayMode;
64
import org.eclipse.nebula.widgets.nattable.viewport.ViewportLayer;
65
import org.eclipse.swt.SWT;
66
import org.eclipse.swt.events.SelectionAdapter;
67
import org.eclipse.swt.events.SelectionEvent;
68
import org.eclipse.swt.layout.GridLayout;
69
import org.eclipse.swt.widgets.Button;
70
import org.eclipse.swt.widgets.Composite;
71

    
72
import ca.odell.glazedlists.EventList;
73
import ca.odell.glazedlists.GlazedLists;
74
import ca.odell.glazedlists.SortedList;
75
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
76
import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
77
import eu.etaxonomy.cdm.api.service.IWorkingSetService;
78
import eu.etaxonomy.cdm.model.common.TermVocabulary;
79
import eu.etaxonomy.cdm.model.description.DescriptionBase;
80
import eu.etaxonomy.cdm.model.description.Feature;
81
import eu.etaxonomy.cdm.model.description.FeatureTree;
82
import eu.etaxonomy.cdm.model.description.SpecimenDescription;
83
import eu.etaxonomy.cdm.model.description.State;
84
import eu.etaxonomy.cdm.model.description.WorkingSet;
85
import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
86
import eu.etaxonomy.taxeditor.editor.workingSet.matrix.categorical.CategoricalDataCellEditor;
87
import eu.etaxonomy.taxeditor.editor.workingSet.matrix.categorical.CategoricalDataDisplayConverter;
88
import eu.etaxonomy.taxeditor.editor.workingSet.matrix.quantitative.QuantitativeDataCellEditor;
89
import eu.etaxonomy.taxeditor.editor.workingSet.matrix.quantitative.QuantitativeDataDisplayConverter;
90
import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
91
import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
92
import eu.etaxonomy.taxeditor.store.CdmStore;
93
import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
94

    
95
/**
96
 * @author pplitzner
97
 * @since Nov 26, 2017
98
 *
99
 */
100
public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails, IConversationEnabled, IDirtyMarkable{
101

    
102
    private WorkingSet workingSet;
103

    
104
    private Composite parent;
105

    
106
    private ConversationHolder conversation;
107

    
108
    @Inject
109
    private ESelectionService selService;
110

    
111
    @Inject
112
    private MDirtyable dirty;
113

    
114
    @Inject
115
    private MPart thisPart;
116

    
117
    private NatTable natTable;
118

    
119
    private List<Feature> features;
120

    
121
    @PostConstruct
122
    public void create(Composite parent) {
123
        if(CdmStore.isActive() && conversation==null){
124
            conversation = CdmStore.createConversation();
125
        }
126
        else{
127
            return;
128
        }
129
        parent.setLayout(new GridLayout());
130
        this.parent = parent;
131
    }
132

    
133

    
134
    public void init(UUID workingSetUuid) {
135
        this.workingSet = CdmStore.getService(IWorkingSetService.class).load(workingSetUuid);
136
        thisPart.setLabel(workingSet.getLabel());
137

    
138
        //get features/columns stored in working set
139
        FeatureTree tree = workingSet.getDescriptiveSystem();
140
        features = new ArrayList<>(tree.getDistinctFeatures());
141
        Collections.sort(features);
142

    
143

    
144
        EventList<SpecimenDescription> descriptions = GlazedLists.eventList(getDescriptions(workingSet));
145
        SortedList<SpecimenDescription> sortedList = new SortedList<>(descriptions, null);
146

    
147
        /**
148
         * data provider
149
         */
150
        SpecimenColumnPropertyAccessor columnPropertyAccessor = new SpecimenColumnPropertyAccessor(features);
151
        IDataProvider bodyDataProvider = new ListDataProvider<SpecimenDescription>(sortedList, columnPropertyAccessor);
152

    
153
        /**
154
         * BODY layer
155
         */
156
        DataLayer bodyDataLayer = new DataLayer(bodyDataProvider);
157
        final ColumnOverrideLabelAccumulator columnLabelAccumulator =new ColumnOverrideLabelAccumulator(bodyDataLayer);
158
        bodyDataLayer.setConfigLabelAccumulator(columnLabelAccumulator);
159
        registerColumnLabels(columnLabelAccumulator);
160
        GlazedListsEventLayer<SpecimenDescription> eventLayer = new GlazedListsEventLayer<>(bodyDataLayer, sortedList);
161

    
162
        RowReorderLayer rowReorderLayer = new RowReorderLayer(eventLayer);
163
        ColumnReorderLayer columnReorderLayer = new ColumnReorderLayer(rowReorderLayer);
164
        SelectionLayer selectionLayer = new SelectionLayer(columnReorderLayer);
165
        ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
166

    
167
        /**
168
         * column header layer
169
         */
170
        IDataProvider columnHeaderDataProvider = new DefaultColumnHeaderDataProvider(
171
                columnPropertyAccessor.getPropertyToLabelMap().values().toArray(new String[] {}), columnPropertyAccessor.getPropertyToLabelMap());
172
        DataLayer columnHeaderDataLayer = new DataLayer(columnHeaderDataProvider);
173
        ILayer columnHeaderLayer = new ColumnHeaderLayer(columnHeaderDataLayer, viewportLayer, selectionLayer);
174

    
175
        ConfigRegistry configRegistry = new ConfigRegistry();
176

    
177
        // add the SortHeaderLayer to the column header layer stack
178
        // as we use GlazedLists, we use the GlazedListsSortModel which
179
        // delegates the sorting to the SortedList
180
        final SortHeaderLayer<SpecimenDescription> sortHeaderLayer = new SortHeaderLayer<>(
181
                        columnHeaderLayer,
182
                        new GlazedListsSortModel<>(
183
                                sortedList,
184
                                columnPropertyAccessor,
185
                                configRegistry,
186
                                columnHeaderDataLayer));
187

    
188

    
189
        /**
190
         * row header layer
191
         */
192
        IDataProvider rowHeaderDataProvider = new DefaultRowHeaderDataProvider(bodyDataProvider);
193
        DataLayer rowHeaderDataLayer = new DataLayer(rowHeaderDataProvider, 40, 20);
194
        ILayer rowHeaderLayer = new RowHeaderLayer(rowHeaderDataLayer, viewportLayer, selectionLayer);
195

    
196

    
197
        /**
198
         * corner layer
199
         */
200
        ILayer cornerLayer = new CornerLayer(
201
                new DataLayer(new DefaultCornerDataProvider(columnHeaderDataProvider, rowHeaderDataProvider)),
202
                rowHeaderLayer, sortHeaderLayer);
203

    
204

    
205
        /**
206
         * GRID layer (composition of all other layers)
207
         */
208
        GridLayer gridLayer = new GridLayer(viewportLayer, sortHeaderLayer, rowHeaderLayer, cornerLayer);
209

    
210

    
211
        natTable = new NatTable(parent, gridLayer, false);
212

    
213
        natTable.setConfigRegistry(configRegistry);
214

    
215

    
216
        //add default configuration because autoconfigure is set to false in constructor
217
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
218

    
219
        // override the default sort configuration and change the mouse bindings
220
        // to sort on a single click
221
        natTable.addConfiguration(new SingleClickSortConfiguration());
222

    
223

    
224
        // add custom configuration for data conversion
225
        viewportLayer.addConfiguration(new AbstractRegistryConfiguration() {
226
            @Override
227
            public void configureRegistry(IConfigRegistry configRegistry) {
228
                features.forEach(feature -> registerColumnConfiguration(feature, configRegistry));
229
                }
230
            }
231
        );
232

    
233
        // add the ExportCommandHandler to the ViewportLayer in order to make
234
        // exporting work
235
        viewportLayer.registerCommandHandler(new ExportCommandHandler(viewportLayer));
236

    
237
        //propagate single cell selection
238
        natTable.addLayerListener(new ILayerListener() {
239
            @Override
240
            public void handleLayerEvent(ILayerEvent event) {
241
                if(event instanceof CellSelectionEvent){
242
                    CellSelectionEvent cellSelectionEvent = (CellSelectionEvent)event;
243
                    Collection<ILayerCell> selectedCells = cellSelectionEvent.getSelectionLayer().getSelectedCells();
244
                    if(selectedCells.size()==1){
245
                        ILayerCell cell = selectedCells.iterator().next();
246
                        selService.setSelection(cell.getDataValue());
247
                    }
248
                }
249
            }
250
        });
251

    
252
        natTable.configure();
253

    
254
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
255

    
256
        //excel export
257
        Button addColumnButton = new Button(parent, SWT.PUSH);
258
        addColumnButton.setText("Export");
259
        addColumnButton.addSelectionListener(new SelectionAdapter() {
260
            @Override
261
            public void widgetSelected(SelectionEvent e) {
262
                natTable.doCommand(
263
                        new ExportCommand(
264
                                natTable.getConfigRegistry(),
265
                                natTable.getShell()));
266
            }
267
        });
268

    
269
        parent.layout();
270
    }
271

    
272
    private void registerColumnLabels(ColumnOverrideLabelAccumulator columnLabelAccumulator) {
273
        for(int i=0;i<features.size();i++){
274
            columnLabelAccumulator.registerColumnOverrides(i, getProperty(features.get(i)));
275
        }
276
    }
277

    
278
    private void registerColumnConfiguration(Feature feature, IConfigRegistry configRegistry) {
279
        //make cell editable
280
        configRegistry.registerConfigAttribute(
281
                EditConfigAttributes.CELL_EDITABLE_RULE,
282
                IEditableRule.ALWAYS_EDITABLE,
283
                DisplayMode.EDIT,
284
                getProperty(feature)
285
                );
286
        if(feature.isSupportsQuantitativeData()){
287
            //add display converter for string representation
288
            configRegistry.registerConfigAttribute(
289
                    CellConfigAttributes.DISPLAY_CONVERTER,
290
                    new QuantitativeDataDisplayConverter(),
291
                    DisplayMode.NORMAL,
292
                    getProperty(feature));
293
            //register quantitative editor
294
            configRegistry.registerConfigAttribute(
295
                    EditConfigAttributes.CELL_EDITOR,
296
                    new QuantitativeDataCellEditor(feature.getRecommendedStatisticalMeasures(), this),
297
                    DisplayMode.EDIT,
298
                    getProperty(feature));
299
        }
300
        else if(feature.isSupportsCategoricalData()){
301
            //add display converter for string representation
302
            configRegistry.registerConfigAttribute(
303
                    CellConfigAttributes.DISPLAY_CONVERTER,
304
                    new CategoricalDataDisplayConverter(),
305
                    DisplayMode.NORMAL,
306
                    getProperty(feature));
307

    
308
            //add combo box cell editor
309
            CategoricalDataCellEditor comboBoxCellEditor = new CategoricalDataCellEditor(new IComboBoxDataProvider() {
310

    
311
                @Override
312
                public List<?> getValues(int columnIndex, int rowIndex) {
313
                    List<State> states = new ArrayList<>();
314
                    Feature feature = features.get(columnIndex);
315
                    if(feature.isSupportsCategoricalData()){
316
                        Set<TermVocabulary<State>> stateVocs = feature.getSupportedCategoricalEnumerations();
317
                        for (TermVocabulary<State> voc : stateVocs) {
318
                            states.addAll(voc.getTerms());
319
                        }
320
                    }
321
                    return states;
322
                }
323
            }, 5, this);
324
            //register editor
325
            configRegistry.registerConfigAttribute(EditConfigAttributes.CELL_EDITOR,
326
                    comboBoxCellEditor,
327
                    DisplayMode.EDIT,
328
                    getProperty(feature));
329

    
330
        }
331

    
332
    }
333

    
334
    private List<SpecimenDescription> getDescriptions(WorkingSet workingSet) {
335
        List<SpecimenDescription> descriptions = new ArrayList<>();
336
        Set<DescriptionBase> wsDescriptions = workingSet.getDescriptions();
337
        for (DescriptionBase descriptionBase : wsDescriptions) {
338
            if(descriptionBase instanceof SpecimenDescription){
339
                descriptions.add((SpecimenDescription) descriptionBase);
340
            }
341
        }
342
        return descriptions;
343
    }
344

    
345
    public List<Feature> getFeatures() {
346
        return features;
347
    }
348

    
349
    private String getProperty(Feature feature){
350
        return feature.getLabel();
351
    }
352

    
353
    public void setDirty() {
354
        this.dirty.setDirty(true);
355
    }
356

    
357
    @Persist
358
    @Override
359
    public void save(IProgressMonitor monitor) {
360
        CdmStore.getService(IWorkingSetService.class).merge(workingSet, true);
361
        conversation.commit();
362
        dirty.setDirty(false);
363
    }
364

    
365
    @Focus
366
    public void setFocus(){
367
        if(conversation!=null){
368
            conversation.bind();
369
        }
370
    }
371

    
372
    @PreDestroy
373
    public void dispose(){
374
        if(conversation!=null){
375
            conversation.close();
376
            conversation = null;
377
        }
378
    }
379

    
380

    
381
    /**
382
     * {@inheritDoc}
383
     */
384
    @Override
385
    public void update(CdmDataChangeMap arg0) {
386
    }
387

    
388

    
389
    /**
390
     * {@inheritDoc}
391
     */
392
    @Override
393
    public ConversationHolder getConversationHolder() {
394
        return conversation;
395
    }
396

    
397

    
398
    /**
399
     * {@inheritDoc}
400
     */
401
    @Override
402
    public void changed(Object element) {
403
        dirty.setDirty(true);
404
        natTable.refresh();
405
    }
406

    
407

    
408
    /**
409
     * {@inheritDoc}
410
     */
411
    @Override
412
    public void forceDirty() {
413
        dirty.setDirty(true);
414
    }
415

    
416
}
(1-1/3)