Project

General

Profile

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

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

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

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

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

    
101
    private WorkingSet workingSet;
102

    
103
    private Composite parent;
104

    
105
    private ConversationHolder conversation;
106

    
107
    @Inject
108
    private ESelectionService selService;
109

    
110
    @Inject
111
    private MDirtyable dirty;
112

    
113
    @Inject
114
    private MPart thisPart;
115

    
116
    private NatTable natTable;
117

    
118
    private List<Feature> features;
119

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

    
132

    
133
    public void init(WorkingSet workingSet) {
134
        this.workingSet = workingSet;
135
        thisPart.setLabel(workingSet.getLabel());
136

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

    
142

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

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

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

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

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

    
174
        ConfigRegistry configRegistry = new ConfigRegistry();
175

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

    
187

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

    
195

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

    
203

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

    
209

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

    
212
        natTable.setConfigRegistry(configRegistry);
213

    
214

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

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

    
222

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

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

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

    
251
        natTable.configure();
252

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

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

    
268
        parent.layout();
269
    }
270

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

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

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

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

    
329
        }
330

    
331
    }
332

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

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

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

    
352
    @Persist
353
    @Override
354
    public void save(IProgressMonitor monitor) {
355
        CdmStore.getService(IWorkingSetService.class).merge(workingSet, true);
356
        conversation.commit();
357
        dirty.setDirty(false);
358
    }
359

    
360
    @Focus
361
    public void setFocus(){
362
        if(conversation!=null){
363
            conversation.bind();
364
        }
365
    }
366

    
367
    @PreDestroy
368
    public void dispose(){
369
        if(conversation!=null){
370
            conversation.close();
371
            conversation = null;
372
        }
373
    }
374

    
375

    
376
    /**
377
     * {@inheritDoc}
378
     */
379
    @Override
380
    public void update(CdmDataChangeMap arg0) {
381
    }
382

    
383

    
384
    /**
385
     * {@inheritDoc}
386
     */
387
    @Override
388
    public ConversationHolder getConversationHolder() {
389
        return conversation;
390
    }
391

    
392

    
393
    /**
394
     * {@inheritDoc}
395
     */
396
    @Override
397
    public void changed(Object element) {
398
        dirty.setDirty(true);
399
        natTable.refresh();
400
    }
401

    
402

    
403
    /**
404
     * {@inheritDoc}
405
     */
406
    @Override
407
    public void forceDirty() {
408
        dirty.setDirty(true);
409
    }
410

    
411
}
(1-1/3)