Project

General

Profile

Download (15.2 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.model.IDirtyMarkable;
86
import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
87
import eu.etaxonomy.taxeditor.store.CdmStore;
88
import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
89

    
90
/**
91
 * @author pplitzner
92
 * @since Nov 26, 2017
93
 *
94
 */
95
public class CharacterMatrix implements IE4SavablePart, IPartContentHasDetails, IConversationEnabled, IDirtyMarkable{
96

    
97
    private WorkingSet workingSet;
98

    
99
    private Composite parent;
100

    
101
    private ConversationHolder conversation;
102

    
103
    @Inject
104
    private ESelectionService selService;
105

    
106
    @Inject
107
    private MDirtyable dirty;
108

    
109
    @Inject
110
    private MPart thisPart;
111

    
112
    private NatTable natTable;
113

    
114
    private List<Feature> features;
115

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

    
128

    
129
    public void init(WorkingSet workingSet) {
130
        this.workingSet = workingSet;
131
        thisPart.setLabel(workingSet.getLabel());
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

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

    
142
        /**
143
         * data provider
144
         */
145
        SpecimenColumnPropertyAccessor columnPropertyAccessor = new SpecimenColumnPropertyAccessor(features);
146
        IDataProvider bodyDataProvider = new ListDataProvider<SpecimenDescription>(sortedList, columnPropertyAccessor);
147

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

    
157
        RowReorderLayer rowReorderLayer = new RowReorderLayer(eventLayer);
158
        ColumnReorderLayer columnReorderLayer = new ColumnReorderLayer(rowReorderLayer);
159
        SelectionLayer selectionLayer = new SelectionLayer(columnReorderLayer);
160
        ViewportLayer viewportLayer = new ViewportLayer(selectionLayer);
161

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

    
170
        ConfigRegistry configRegistry = new ConfigRegistry();
171

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

    
183

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

    
191

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

    
199

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

    
205

    
206
        natTable = new NatTable(parent, gridLayer, false);
207

    
208
        natTable.setConfigRegistry(configRegistry);
209

    
210

    
211
        //add default configuration because autoconfigure is set to false in constructor
212
        natTable.addConfiguration(new DefaultNatTableStyleConfiguration());
213

    
214
        // override the default sort configuration and change the mouse bindings
215
        // to sort on a single click
216
        natTable.addConfiguration(new SingleClickSortConfiguration());
217

    
218

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

    
228
        // add the ExportCommandHandler to the ViewportLayer in order to make
229
        // exporting work
230
        viewportLayer.registerCommandHandler(new ExportCommandHandler(viewportLayer));
231

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

    
247
        natTable.configure();
248

    
249
        GridDataFactory.fillDefaults().grab(true, true).applyTo(natTable);
250

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

    
264
        parent.layout();
265
    }
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

    
319
        }
320

    
321
    }
322

    
323
    private List<SpecimenDescription> getDescriptions(WorkingSet workingSet) {
324
        List<SpecimenDescription> descriptions = new ArrayList<>();
325
        Set<DescriptionBase> wsDescriptions = workingSet.getDescriptions();
326
        for (DescriptionBase descriptionBase : wsDescriptions) {
327
            if(descriptionBase instanceof SpecimenDescription){
328
                descriptions.add((SpecimenDescription) descriptionBase);
329
            }
330
        }
331
        return descriptions;
332
    }
333

    
334
    public List<Feature> getFeatures() {
335
        return features;
336
    }
337

    
338
    private String getProperty(Feature feature){
339
        return feature.getLabel();
340
    }
341

    
342
    @Persist
343
    @Override
344
    public void save(IProgressMonitor monitor) {
345
        CdmStore.getService(IWorkingSetService.class).merge(workingSet, true);
346
        conversation.commit();
347
        dirty.setDirty(false);
348
    }
349

    
350
    @Focus
351
    public void setFocus(){
352
        if(conversation!=null){
353
            conversation.bind();
354
        }
355
    }
356

    
357
    @PreDestroy
358
    public void dispose(){
359
        if(conversation!=null){
360
            conversation.close();
361
            conversation = null;
362
        }
363
    }
364

    
365

    
366
    /**
367
     * {@inheritDoc}
368
     */
369
    @Override
370
    public void update(CdmDataChangeMap arg0) {
371
    }
372

    
373

    
374
    /**
375
     * {@inheritDoc}
376
     */
377
    @Override
378
    public ConversationHolder getConversationHolder() {
379
        return conversation;
380
    }
381

    
382

    
383
    /**
384
     * {@inheritDoc}
385
     */
386
    @Override
387
    public void changed(Object element) {
388
        dirty.setDirty(true);
389
        natTable.refresh();
390
    }
391

    
392

    
393
    /**
394
     * {@inheritDoc}
395
     */
396
    @Override
397
    public void forceDirty() {
398
        dirty.setDirty(true);
399
    }
400

    
401
}
(3-3/5)