2 * Copyright (C) 2017 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.taxeditor
.editor
.descriptiveDataSet
.matrix
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Collection
;
14 import java
.util
.HashMap
;
15 import java
.util
.List
;
17 import java
.util
.Properties
;
19 import java
.util
.UUID
;
20 import java
.util
.stream
.Collectors
;
22 import javax
.inject
.Inject
;
24 import org
.apache
.commons
.collections4
.map
.LinkedMap
;
25 import org
.eclipse
.core
.runtime
.ICoreRunnable
;
26 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
27 import org
.eclipse
.core
.runtime
.SubMonitor
;
28 import org
.eclipse
.core
.runtime
.jobs
.IJobChangeEvent
;
29 import org
.eclipse
.core
.runtime
.jobs
.Job
;
30 import org
.eclipse
.core
.runtime
.jobs
.JobChangeAdapter
;
31 import org
.eclipse
.e4
.ui
.di
.UISynchronize
;
32 import org
.eclipse
.e4
.ui
.services
.EMenuService
;
33 import org
.eclipse
.jface
.layout
.GridDataFactory
;
34 import org
.eclipse
.jface
.viewers
.ComboViewer
;
35 import org
.eclipse
.jface
.viewers
.IStructuredSelection
;
36 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
37 import org
.eclipse
.nebula
.widgets
.nattable
.NatTable
;
38 import org
.eclipse
.nebula
.widgets
.nattable
.config
.AbstractUiBindingConfiguration
;
39 import org
.eclipse
.nebula
.widgets
.nattable
.config
.ConfigRegistry
;
40 import org
.eclipse
.nebula
.widgets
.nattable
.config
.DefaultNatTableStyleConfiguration
;
41 import org
.eclipse
.nebula
.widgets
.nattable
.coordinate
.PositionCoordinate
;
42 import org
.eclipse
.nebula
.widgets
.nattable
.coordinate
.Range
;
43 import org
.eclipse
.nebula
.widgets
.nattable
.copy
.command
.InternalCopyDataCommandHandler
;
44 import org
.eclipse
.nebula
.widgets
.nattable
.copy
.command
.InternalPasteDataCommandHandler
;
45 import org
.eclipse
.nebula
.widgets
.nattable
.data
.IDataProvider
;
46 import org
.eclipse
.nebula
.widgets
.nattable
.data
.ListDataProvider
;
47 import org
.eclipse
.nebula
.widgets
.nattable
.export
.command
.ExportCommandHandler
;
48 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.e4
.selection
.E4SelectionListener
;
49 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.GlazedListsEventLayer
;
50 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.GlazedListsSortModel
;
51 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.tree
.GlazedListTreeData
;
52 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.tree
.GlazedListTreeRowModel
;
53 import org
.eclipse
.nebula
.widgets
.nattable
.freeze
.CompositeFreezeLayer
;
54 import org
.eclipse
.nebula
.widgets
.nattable
.freeze
.FreezeHelper
;
55 import org
.eclipse
.nebula
.widgets
.nattable
.freeze
.FreezeLayer
;
56 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.GridRegion
;
57 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.command
.ClientAreaResizeCommand
;
58 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.DefaultColumnHeaderDataProvider
;
59 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.DefaultCornerDataProvider
;
60 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.DefaultRowHeaderDataProvider
;
61 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.FixedSummaryRowHeaderLayer
;
62 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.ColumnHeaderLayer
;
63 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.CornerLayer
;
64 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.DefaultRowHeaderDataLayer
;
65 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.GridLayer
;
66 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.AbstractLayer
;
67 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.CompositeLayer
;
68 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.DataLayer
;
69 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.ILayer
;
70 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.cell
.ColumnOverrideLabelAccumulator
;
71 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.stack
.DefaultBodyLayerStack
;
72 import org
.eclipse
.nebula
.widgets
.nattable
.selection
.SelectionLayer
;
73 import org
.eclipse
.nebula
.widgets
.nattable
.sort
.SortHeaderLayer
;
74 import org
.eclipse
.nebula
.widgets
.nattable
.sort
.config
.SingleClickSortConfiguration
;
75 import org
.eclipse
.nebula
.widgets
.nattable
.style
.HorizontalAlignmentEnum
;
76 import org
.eclipse
.nebula
.widgets
.nattable
.style
.theme
.ModernNatTableThemeConfiguration
;
77 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.FixedSummaryRowLayer
;
78 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.SummaryRowLayer
;
79 import org
.eclipse
.nebula
.widgets
.nattable
.tree
.ITreeRowModel
;
80 import org
.eclipse
.nebula
.widgets
.nattable
.tree
.TreeLayer
;
81 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.binding
.UiBindingRegistry
;
82 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.matcher
.MouseEventMatcher
;
83 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.menu
.PopupMenuAction
;
84 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.menu
.PopupMenuBuilder
;
85 import org
.eclipse
.nebula
.widgets
.nattable
.util
.GUIHelper
;
86 import org
.eclipse
.swt
.SWT
;
87 import org
.eclipse
.swt
.layout
.GridData
;
88 import org
.eclipse
.swt
.layout
.GridLayout
;
89 import org
.eclipse
.swt
.widgets
.Button
;
90 import org
.eclipse
.swt
.widgets
.Composite
;
91 import org
.eclipse
.swt
.widgets
.Menu
;
93 import ca
.odell
.glazedlists
.BasicEventList
;
94 import ca
.odell
.glazedlists
.EventList
;
95 import ca
.odell
.glazedlists
.SortedList
;
96 import ca
.odell
.glazedlists
.TreeList
;
97 import eu
.etaxonomy
.cdm
.api
.application
.CdmApplicationState
;
98 import eu
.etaxonomy
.cdm
.api
.service
.dto
.RowWrapperDTO
;
99 import eu
.etaxonomy
.cdm
.api
.service
.dto
.SpecimenRowWrapperDTO
;
100 import eu
.etaxonomy
.cdm
.common
.monitor
.IRemotingProgressMonitor
;
101 import eu
.etaxonomy
.cdm
.model
.description
.Character
;
102 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
103 import eu
.etaxonomy
.cdm
.model
.description
.DescriptiveDataSet
;
104 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
105 import eu
.etaxonomy
.cdm
.model
.description
.MeasurementUnit
;
106 import eu
.etaxonomy
.cdm
.model
.description
.State
;
107 import eu
.etaxonomy
.cdm
.model
.term
.FeatureNode
;
108 import eu
.etaxonomy
.cdm
.model
.term
.FeatureTree
;
109 import eu
.etaxonomy
.cdm
.persistence
.dto
.SpecimenNodeWrapper
;
110 import eu
.etaxonomy
.taxeditor
.editor
.internal
.TaxeditorEditorPlugin
;
111 import eu
.etaxonomy
.taxeditor
.editor
.l10n
.Messages
;
112 import eu
.etaxonomy
.taxeditor
.model
.MessagingUtils
;
113 import eu
.etaxonomy
.taxeditor
.session
.ICdmEntitySession
;
114 import eu
.etaxonomy
.taxeditor
.store
.CdmStore
;
115 import eu
.etaxonomy
.taxeditor
.workbench
.WorkbenchUtility
;
118 * Character matrix editor for editing specimen/taxon descriptions in a table
120 * @since Nov 26, 2017
123 public class CharacterMatrix
extends Composite
{
125 private static final String CHARACTER_MATRIX_STATE_PROPERTIES
= "characterMatrixState.properties"; //$NON-NLS-1$
127 static final int LEADING_COLUMN_COUNT
= 4;
128 static final String TAXON_COLUMN
= "taxon_column"; //$NON-NLS-1$
129 static final String COLLECTOR_COLUMN
= "collector_column"; //$NON-NLS-1$
130 static final String IDENTIFIER_COLUMN
= "identifier_column"; //$NON-NLS-1$
131 static final String COUNTRY_COLUMN
= "country_column"; //$NON-NLS-1$
133 static final String LABEL_TAXON_ROW
= "TAXON_ROW"; //$NON-NLS-1$
134 static final String LABEL_TAXON_AGGREGATED_DESCRIPTION
= "TAXON_AGGREGATED_DESCRIPTION"; //$NON-NLS-1$
135 static final String LABEL_TAXON_AGGREGATED_DESCRIPTION_ICON
= "TAXON_AGGREGATED_DESCRIPTION_ICON"; //$NON-NLS-1$
136 static final String LABEL_TAXON_DEFAULT_DESCRIPTION
= "TAXON_DEFAULT_DESCRIPTION"; //$NON-NLS-1$
137 static final String LABEL_TAXON_DEFAULT_DESCRIPTION_ICON
= "TAXON_DEFAULT_DESCRIPTION_ICON"; //$NON-NLS-1$
138 static final String LABEL_TAXON_LITERATURE_DESCRIPTION
= "TAXON_LITERATURE_DESCRIPTION"; //$NON-NLS-1$
139 static final String LABEL_TAXON_LITERATURE_DESCRIPTION_ICON
= "TAXON_LITERATURE_DESCRIPTION_ICON"; //$NON-NLS-1$
140 static final String LABEL_TAXON_DESCRIPTION
= "LABEL_TAXON_DESCRIPTION"; //$NON-NLS-1$
141 static final String LABEL_DESCRIPTION_HAS_SUPPLEMENTAL_DATA
= "LABEL_DESCRIPTION_HAS_SUPPLEMENTAL_DATA"; //$NON-NLS-1$
144 private UISynchronize sync
;
147 private EMenuService menuService
;
149 private DescriptiveDataSet descriptiveDataSet
;
151 private NatTable natTable
;
153 private Map
<Integer
, Character
> indexToCharacterMap
= new HashMap
<>();
155 private Map
<Feature
, List
<State
>> categoricalFeatureToStateMap
= new HashMap
<>();
157 private LinkedMap
<String
, String
> propertyToLabelMap
= new LinkedMap
<>();
159 private EventList
<Object
> descriptions
;
161 private Collection
<SpecimenNodeWrapper
> specimenCache
= null;
163 private Map
<Feature
, CategoricalDataHistogram
> featureToHistogramMap
= new HashMap
<>();
165 private Map
<Feature
, QuantitativeDataStatistics
> featureToQuantDataStatisticsMap
= new HashMap
<>();
167 private ListDataProvider
<Object
> bodyDataProvider
;
169 private FreezeLayer freezeLayer
;
171 private List
<Character
> characters
;
173 private CharacterMatrixPart part
;
175 private AbstractLayer topMostLayer
;
177 private FixedSummaryRowLayer summaryRowLayer
;
179 private ConfigRegistry configRegistry
;
181 private DefaultBodyLayerStack bodyLayer
;
183 private boolean isTreeView
= true;
185 private CharacterMatrixToolbar toolbar
;
187 private boolean isShowTooltips
= true;
189 public CharacterMatrix(Composite parent
, CharacterMatrixPart part
) {
190 super(parent
, SWT
.NONE
);
192 this.setLayout(new GridLayout());
196 natTable
= new NatTable(this, false);
198 createBottomToolbar();
202 private void createToolBar(){
203 toolbar
= new CharacterMatrixToolbar(this, SWT
.NONE
);
206 @SuppressWarnings("unused")
207 private void createBottomToolbar() {
208 new CharacterMatrixBottomToolbar(this, SWT
.NONE
);
212 private void applyStyles(){
213 ModernNatTableThemeConfiguration configuration
= new ModernNatTableThemeConfiguration();
214 configuration
.summaryRowHAlign
= HorizontalAlignmentEnum
.CENTER
;
215 // NOTE: Getting the colors and fonts from the GUIHelper ensures that
216 // they are disposed properly (required by SWT)
217 configuration
.cHeaderBgColor
= GUIHelper
.getColor(211, 211, 211);
218 configuration
.rHeaderBgColor
= GUIHelper
.getColor(211, 211, 211);
219 natTable
.addConfiguration(configuration
);
223 void toggleTreeFlat(boolean isTree
, Button btnToggleFlat
, Button btnToggleTree
, Button btnCollapseAll
, Button btnExpandAll
, Button btnFreezeSuppInfo
) {
225 createTable(isTree
, freezeLayer
.isFrozen());
226 btnToggleFlat
.setEnabled(isTree
);
227 btnToggleTree
.setEnabled(!isTree
);
228 btnCollapseAll
.setEnabled(isTree
);
229 btnExpandAll
.setEnabled(isTree
);
232 public boolean isTreeView() {
236 public void createTable(boolean treeView
, boolean freezeSupplementalColumns
){
240 createLayers(treeView
);
245 configureNatTable(treeView
, configRegistry
, topMostLayer
, summaryRowLayer
);
248 * handlers and listeners
250 registerHandlersAndListeners(topMostLayer
);
253 GridDataFactory
.fillDefaults().grab(true, true).applyTo(natTable
);
255 //update label to current data set
256 toolbar
.getWsLabel().setText(descriptiveDataSet
.getLabel());
257 toolbar
.getWsLabel().setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, true, false));
258 toolbar
.getWsLabel().getParent().layout();
260 //initial freeze of supplemental columns
261 freezeSupplementalColumns(freezeSupplementalColumns
);
264 //add tooltip to table
265 new CategoricalChartTooltip(this);
266 new QuantitativeChartTooltip(this);
269 natTable
.doCommand(new ClientAreaResizeCommand(natTable
));
272 private List
<Character
> initCharacterList(FeatureNode
<Character
> node
){
273 List
<Character
> characters
= new ArrayList
<>();
274 node
.getChildNodes().forEach(childNode
->
276 characters
.add(childNode
.getTerm());
277 characters
.addAll(initCharacterList(childNode
));
282 public void initDescriptiveDataSet(DescriptiveDataSet descriptiveDataSet
){
283 this.descriptiveDataSet
= descriptiveDataSet
;
284 //get features/columns stored in descriptive data set
285 FeatureTree
<Character
> tree
= descriptiveDataSet
.getDescriptiveSystem();
286 characters
= initCharacterList(tree
.getRoot());
288 //init state data for categorical features
289 characters
.forEach(character
->
291 if(character
.isSupportsCategoricalData()){
292 List
<State
> supportedStates
= new ArrayList
<>();
293 character
.getSupportedCategoricalEnumerations().forEach(voc
->supportedStates
.addAll(voc
.getTerms()));
294 categoricalFeatureToStateMap
.put(character
, supportedStates
);
297 descriptions
= new BasicEventList
<>();
301 private void createLayers(boolean treeView
) {
302 SortedList
<Object
> sortedList
= new SortedList
<>(descriptions
, new MatrixRowComparator());
303 // wrap the SortedList with the TreeList
304 TreeList
<Object
> treeList
= new TreeList(sortedList
, new DescriptionTreeFormat(descriptiveDataSet
), TreeList
.NODES_START_EXPANDED
);
308 SpecimenColumnPropertyAccessor columnPropertyAccessor
= new SpecimenColumnPropertyAccessor(this);
309 bodyDataProvider
= treeView?
new ListDataProvider
<>(treeList
, columnPropertyAccessor
):new ListDataProvider
<>(sortedList
, columnPropertyAccessor
);
311 configRegistry
= new ConfigRegistry();
320 - (top) SummaryRowLayer
321 - (bottom) ViewportLayer
327 TreeLayer (default visible)
353 DataLayer bodyDataLayer
= new DataLayer(bodyDataProvider
);
354 bodyDataLayer
.registerCommandHandler(new CopyPasteUpdateDataCommandHandler(bodyDataLayer
));
357 CharacterMatrixConfigLabelAccumulator labelAccumulator
= new CharacterMatrixConfigLabelAccumulator(this);
358 bodyDataLayer
.setConfigLabelAccumulator(labelAccumulator
);
361 propertyToLabelMap
.put(TAXON_COLUMN
, Messages
.CharacterMatrix_TAXON
);
362 propertyToLabelMap
.put(COLLECTOR_COLUMN
, Messages
.CharacterMatrix_COLLECTOR_NO
);
363 propertyToLabelMap
.put(IDENTIFIER_COLUMN
, Messages
.CharacterMatrix_IDENTIFIER
);
364 propertyToLabelMap
.put(COUNTRY_COLUMN
, Messages
.CharacterMatrix_COUNTRY
);
365 for(int i
=0;i
<characters
.size();i
++){
366 Character character
= characters
.get(i
);
367 initLabels(i
, character
);
370 // layer for event handling of GlazedLists and PropertyChanges
371 GlazedListsEventLayer eventLayer
= new GlazedListsEventLayer
<>(bodyDataLayer
, treeList
);
372 GlazedListTreeData treeData
= new GlazedListTreeData
<>(treeList
);
373 ITreeRowModel treeRowModel
= new GlazedListTreeRowModel
<>(treeData
);
375 bodyLayer
= new DefaultBodyLayerStack(eventLayer
);
376 final SelectionLayer selectionLayer
= bodyLayer
.getSelectionLayer();
377 freezeLayer
= new FreezeLayer(selectionLayer
);
378 final CompositeFreezeLayer compositeFreezeLayer
= new CompositeFreezeLayer(
379 freezeLayer
, bodyLayer
.getViewportLayer(), selectionLayer
);
380 TreeLayer treeLayer
= new TreeLayer(compositeFreezeLayer
, treeRowModel
);
382 topMostLayer
= treeView?treeLayer
:compositeFreezeLayer
;
384 summaryRowLayer
= new FixedSummaryRowLayer(bodyDataLayer
, topMostLayer
, configRegistry
, false);
385 //regoster labels with summary prefix for summary layer
386 ColumnOverrideLabelAccumulator summaryColumnLabelAccumulator
=new ColumnOverrideLabelAccumulator(bodyDataLayer
);
387 summaryRowLayer
.setConfigLabelAccumulator(summaryColumnLabelAccumulator
);
388 for(int i
=0;i
<characters
.size();i
++){
389 Character character
= characters
.get(i
);
390 summaryColumnLabelAccumulator
.registerColumnOverrides(
391 i
+LEADING_COLUMN_COUNT
,
392 SummaryRowLayer
.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX
+MatrixUtility
.getProperty(character
));
394 // because the horizontal dependency is the ViewportLayer
395 // we need to set the composite dependency to false
396 summaryRowLayer
.setHorizontalCompositeDependency(false);
398 CompositeLayer composite
= new CompositeLayer(1, 2);
399 composite
.setChildLayer("SUMMARY", summaryRowLayer
, 0, 0); //$NON-NLS-1$
400 composite
.setChildLayer(GridRegion
.BODY
, topMostLayer
, 0, 1);
404 * column header layer
406 IDataProvider columnHeaderDataProvider
= new DefaultColumnHeaderDataProvider(
407 propertyToLabelMap
.values().toArray(new String
[] {}), propertyToLabelMap
);
408 DataLayer columnHeaderDataLayer
= new DataLayer(columnHeaderDataProvider
);
409 ColumnHeaderLayer columnHeaderLayer
= new ColumnHeaderLayer(columnHeaderDataLayer
, topMostLayer
, selectionLayer
);
411 // add the SortHeaderLayer to the column header layer stack
412 // as we use GlazedLists, we use the GlazedListsSortModel which
413 // delegates the sorting to the SortedList
414 final SortHeaderLayer
<DescriptionBase
> sortHeaderLayer
= new SortHeaderLayer
<>(
416 new GlazedListsSortModel
<>(
418 columnPropertyAccessor
,
420 columnHeaderDataLayer
));
426 IDataProvider rowHeaderDataProvider
= new DefaultRowHeaderDataProvider(bodyDataProvider
);
427 DefaultRowHeaderDataLayer rowHeaderDataLayer
= new DefaultRowHeaderDataLayer(rowHeaderDataProvider
);
428 FixedSummaryRowHeaderLayer fixedSummaryRowHeaderLayer
= new FixedSummaryRowHeaderLayer(rowHeaderDataLayer
,
429 composite
, selectionLayer
);
430 fixedSummaryRowHeaderLayer
.setSummaryRowLabel("\u2211"); //$NON-NLS-1$
436 ILayer cornerLayer
= new CornerLayer(
437 new DataLayer(new DefaultCornerDataProvider(columnHeaderDataProvider
, rowHeaderDataProvider
)),
438 fixedSummaryRowHeaderLayer
, sortHeaderLayer
);
442 * GRID layer (composition of all other layers)
444 GridLayer gridLayer
= new GridLayer(composite
, sortHeaderLayer
, fixedSummaryRowHeaderLayer
, cornerLayer
);
446 natTable
.setLayer(gridLayer
);
450 private void registerHandlersAndListeners(AbstractLayer topMostLayer
) {
451 // add the ExportCommandHandler to the ViewportLayer in order to make
453 topMostLayer
.registerCommandHandler(new ExportCommandHandler(topMostLayer
));
456 E4SelectionListener selectionListener
= new CellSelectionListener(part
.getSelectionService(),
457 bodyLayer
.getSelectionLayer(), bodyDataProvider
, part
);
458 bodyLayer
.getSelectionLayer().addLayerListener(selectionListener
);
459 selectionListener
.setFullySelectedRowsOnly(false);
461 //register handler for view configuration menu
462 natTable
.registerCommandHandler(toolbar
.getDisplayPersistenceDialogCommandHandler());
464 //register handlers for copy&paste
465 natTable
.registerCommandHandler(
466 new InternalPasteDataCommandHandler(bodyLayer
.getSelectionLayer(), natTable
.getInternalCellClipboard()));
467 natTable
.registerCommandHandler(
468 new InternalCopyDataCommandHandler(bodyLayer
.getSelectionLayer(), natTable
.getInternalCellClipboard()));
471 private void configureNatTable(boolean treeView
,
472 ConfigRegistry configRegistry
,
473 AbstractLayer topMostLayer
,
474 FixedSummaryRowLayer summaryRowLayer
) {
478 natTable
.setConfigRegistry(configRegistry
);
482 //add default configuration because autoconfigure is set to false in constructor
483 natTable
.addConfiguration(new DefaultNatTableStyleConfiguration());
485 // this is for DEBUG ONLY
486 // natTable.addConfiguration(new DebugMenuConfiguration(natTable));
488 // override the default sort configuration and change the mouse bindings
489 // to sort on a single click
491 natTable
.addConfiguration(new SingleClickSortConfiguration());
494 natTable
.addConfiguration(new CharacterMatrixLabelStyleConfiguration());
496 // add the header menu configuration for adding the column header menu
497 // with hide/show actions
498 natTable
.addConfiguration(new CharacterMatrixHeaderMenuConfiguration(natTable
));
500 // add custom configuration for data conversion and add column labels to viewport layer
501 topMostLayer
.addConfiguration(new CellEditorDataConversionConfiguration(this));
503 //register aggregation configuration
504 summaryRowLayer
.addConfiguration(new AggregationConfiguration(this));
506 //copy&paste configuration
507 natTable
.addConfiguration(new CopyPasteEditBindings(bodyLayer
.getSelectionLayer(), natTable
.getInternalCellClipboard()));
510 menuService
.registerContextMenu(natTable
, "eu.etaxonomy.taxeditor.editor.popupmenu.charactermatrix"); //$NON-NLS-1$
511 // get the menu registered by EMenuService
512 final Menu e4Menu
= natTable
.getMenu();
513 // remove the menu reference from NatTable instance
514 natTable
.setMenu(null);
515 natTable
.addConfiguration(
516 new AbstractUiBindingConfiguration() {
518 public void configureUiBindings(
519 UiBindingRegistry uiBindingRegistry
) {
520 // add e4 menu to NatTable
521 new PopupMenuBuilder(natTable
, e4Menu
)
524 // register the UI binding for header, corner and body region
525 uiBindingRegistry
.registerMouseDownBinding(
526 new MouseEventMatcher(
529 MouseEventMatcher
.RIGHT_BUTTON
),
530 new PopupMenuAction(e4Menu
));
534 natTable
.configure();
537 void freezeSupplementalColumns(boolean freeze
){
539 FreezeHelper
.freeze(freezeLayer
, bodyLayer
.getViewportLayer(),
540 new PositionCoordinate(bodyLayer
.getViewportLayer(), 0, 0),
541 new PositionCoordinate(bodyLayer
.getViewportLayer(), LEADING_COLUMN_COUNT
-1, -1));
544 FreezeHelper
.unfreeze(freezeLayer
, bodyLayer
.getViewportLayer());
548 void selectStateItem(ComboViewer comboStates
, String stateName
){
549 String
[] items
= comboStates
.getCombo().getItems();
550 for(int i
=0;i
<items
.length
;i
++){
551 if(items
[i
].equals(stateName
)){
552 comboStates
.getCombo().select(i
);
558 private void initLabels(int index
, Character character
) {
559 indexToCharacterMap
.put(index
+LEADING_COLUMN_COUNT
, character
);
561 String label
= character
.getLabel();
562 String property
= character
.getUuid().toString();
563 //show unit for quantitative data
564 if(character
.isSupportsQuantitativeData()){
565 Set
<MeasurementUnit
> recommendedMeasurementUnits
= character
.getRecommendedMeasurementUnits();
566 if(recommendedMeasurementUnits
.size()>1){
567 MessagingUtils
.warningDialog(Messages
.CharacterMatrix_INIT_PROBLEM
, CharacterMatrix
.class,
568 String
.format(Messages
.CharacterMatrix_INIT_PROBLEM_MESSAGE
, character
.getLabel()));
570 if(recommendedMeasurementUnits
.size()==1){
571 MeasurementUnit unit
= recommendedMeasurementUnits
.iterator().next();
572 label
+= " ["+unit
.getIdInVocabulary()+"]"; //$NON-NLS-1$ //$NON-NLS-2$
575 propertyToLabelMap
.put(property
, label
);
578 public void loadDescriptions(DescriptiveDataSet descriptiveDataSet
) {
579 UUID monitorUuid
= CdmApplicationState
.getLongRunningTasksService().monitGetRowWrapper(descriptiveDataSet
);
581 final Collection
<RowWrapperDTO
> wrappers
= new ArrayList
<>();
582 String jobLabel
= Messages
.CharacterMatrix_LOAD_CHARACTER_DATA
;
583 Job job
= Job
.create(jobLabel
, (ICoreRunnable
) monitor
-> {
584 SubMonitor subMonitor
= SubMonitor
.convert(monitor
);
585 subMonitor
.beginTask(jobLabel
, IProgressMonitor
.UNKNOWN
);
586 IRemotingProgressMonitor remotingMonitor
;
588 remotingMonitor
= CdmStore
.getProgressMonitorClientManager()
589 .pollMonitor(jobLabel
,
595 } catch (InterruptedException e
) {
596 MessagingUtils
.informationDialog(Messages
.CharacterMatrix_LOADING_FAILED_TITLE
,
597 Messages
.CharacterMatrix_LOADING_FAILED_MESSAGE
);
600 Object result
= remotingMonitor
.getResult();
601 if(result
instanceof Collection
){
602 wrappers
.addAll((Collection
<RowWrapperDTO
>) result
);
604 if(result
instanceof Exception
){
605 MessagingUtils
.errorDialog("Exception during description loading", this.getClass(), "An exception occured during loading", TaxeditorEditorPlugin
.PLUGIN_ID
, (Throwable
) result
, true);
607 else if(wrappers
.isEmpty()){
608 MessagingUtils
.informationDialog(Messages
.CharacterMatrix_NO_DESCRIPTION_TITLE
,
609 Messages
.CharacterMatrix_NO_DESCRIPTION_MESSAGE
);
613 job
.addJobChangeListener(new JobChangeAdapter(){
615 public void done(IJobChangeEvent event
) {
617 List
<RowWrapperDTO
> rowsWithoutTaxonNode
= wrappers
.stream().filter(row
->row
.getTaxonNode()==null).collect(Collectors
.toList());
618 if(!rowsWithoutTaxonNode
.isEmpty()){
619 String collect
= rowsWithoutTaxonNode
.stream().
620 map(row
->row
.getDescription().toString())
621 .collect(Collectors
.joining("\n\n - ")); //$NON-NLS-1$
622 MessagingUtils
.warningDialog(
623 Messages
.CharacterMatrix_NO_NODE_FOUND_TITLE
,
625 String
.format(Messages
.CharacterMatrix_NO_NODE_FOUND_MESSAGE
, collect
)
628 wrappers
.stream().filter(row
->row
.getTaxonNode()!=null).forEach(wrapper
->CharacterMatrix
.this.descriptions
.add(wrapper
));
636 public IStructuredSelection
getSelection(){
637 Set
<Range
> selectedRowPositions
= bodyLayer
.getSelectionLayer().getSelectedRowPositions();
638 List
<Object
> selectedObjects
= new ArrayList
<>();
639 for (Range range
: selectedRowPositions
) {
640 for(int i
=range
.start
;i
<range
.end
;i
++){
641 selectedObjects
.add(bodyDataProvider
.getRowObject(i
));
644 return new StructuredSelection(selectedObjects
);
647 private void loadingDone() {
648 this.part
.loadingDone();
649 createTable(isTreeView
, freezeLayer
.isFrozen());
652 public List
<State
> getSupportedStatesForCategoricalFeature(Feature feature
){
653 return categoricalFeatureToStateMap
.get(feature
);
656 public Map
<Integer
, Character
> getIndexToCharacterMap() {
657 return indexToCharacterMap
;
660 public LinkedMap
<String
, String
> getPropertyToLabelMap() {
661 return propertyToLabelMap
;
664 public void setDirty() {
668 public CharacterMatrixPart
getPart() {
672 public NatTable
getNatTable() {
676 public EventList
<Object
> getDescriptions() {
680 public DescriptiveDataSet
getDescriptiveDataSet() {
681 return descriptiveDataSet
;
684 public Collection
<SpecimenNodeWrapper
> getSpecimenCache() {
685 return specimenCache
;
688 public void setSpecimenCache(Collection
<SpecimenNodeWrapper
> specimenCache
) {
689 this.specimenCache
= specimenCache
.stream()
691 //map descriptions on a list of uuids of the described specimen
692 !this.descriptions
.stream()
693 .filter(rowWrapper
->rowWrapper
instanceof SpecimenRowWrapperDTO
)
694 .map(specimenRowWrapper
->((SpecimenRowWrapperDTO
) specimenRowWrapper
).getSpecimen().getUuid())
695 .collect(Collectors
.toList())
696 //an check if the specimen to add is already contained
697 .contains(wrapper
.getUuidAndTitleCache().getUuid())
699 .collect(Collectors
.toList());
702 public Properties
getNatTableState() {
703 return toolbar
.getNatTableState();
706 public ListDataProvider
<Object
> getBodyDataProvider() {
707 return bodyDataProvider
;
710 DefaultBodyLayerStack
getBodyLayer() {
714 File
getStatePropertiesFile() {
715 return new File(WorkbenchUtility
.getBaseLocation(), CHARACTER_MATRIX_STATE_PROPERTIES
);
718 public List
<Character
> getFeatures() {
722 public Map
<Feature
, CategoricalDataHistogram
> getFeatureToHistogramMap() {
723 return featureToHistogramMap
;
726 public Map
<Feature
, QuantitativeDataStatistics
> getFeatureToQuantDataStatisticsMap() {
727 return featureToQuantDataStatisticsMap
;
730 public void toogleIsShowTooltips() {
731 this.isShowTooltips
= !this.isShowTooltips
;
734 public boolean isShowTooltips() {
735 return isShowTooltips
;
738 public ICdmEntitySession
getCdmEntitiySession(){
739 return part
.getCdmEntitySession();