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
.workingSet
.matrix
;
12 import java
.io
.FileInputStream
;
13 import java
.io
.IOException
;
14 import java
.util
.ArrayList
;
15 import java
.util
.Arrays
;
16 import java
.util
.Collection
;
17 import java
.util
.HashMap
;
18 import java
.util
.HashSet
;
19 import java
.util
.List
;
21 import java
.util
.Properties
;
23 import java
.util
.stream
.Collectors
;
25 import org
.apache
.commons
.collections4
.map
.LinkedMap
;
26 import org
.eclipse
.core
.runtime
.ICoreRunnable
;
27 import org
.eclipse
.core
.runtime
.jobs
.Job
;
28 import org
.eclipse
.jface
.layout
.GridDataFactory
;
29 import org
.eclipse
.jface
.viewers
.ArrayContentProvider
;
30 import org
.eclipse
.jface
.viewers
.ComboViewer
;
31 import org
.eclipse
.jface
.viewers
.LabelProvider
;
32 import org
.eclipse
.jface
.viewers
.StructuredSelection
;
33 import org
.eclipse
.jface
.window
.Window
;
34 import org
.eclipse
.nebula
.widgets
.nattable
.NatTable
;
35 import org
.eclipse
.nebula
.widgets
.nattable
.command
.VisualRefreshCommand
;
36 import org
.eclipse
.nebula
.widgets
.nattable
.config
.AbstractRegistryConfiguration
;
37 import org
.eclipse
.nebula
.widgets
.nattable
.config
.CellConfigAttributes
;
38 import org
.eclipse
.nebula
.widgets
.nattable
.config
.ConfigRegistry
;
39 import org
.eclipse
.nebula
.widgets
.nattable
.config
.DefaultNatTableStyleConfiguration
;
40 import org
.eclipse
.nebula
.widgets
.nattable
.config
.IConfigRegistry
;
41 import org
.eclipse
.nebula
.widgets
.nattable
.config
.IEditableRule
;
42 import org
.eclipse
.nebula
.widgets
.nattable
.coordinate
.PositionCoordinate
;
43 import org
.eclipse
.nebula
.widgets
.nattable
.data
.IDataProvider
;
44 import org
.eclipse
.nebula
.widgets
.nattable
.data
.ListDataProvider
;
45 import org
.eclipse
.nebula
.widgets
.nattable
.edit
.EditConfigAttributes
;
46 import org
.eclipse
.nebula
.widgets
.nattable
.export
.command
.ExportCommand
;
47 import org
.eclipse
.nebula
.widgets
.nattable
.export
.command
.ExportCommandHandler
;
48 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.GlazedListsEventLayer
;
49 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.GlazedListsSortModel
;
50 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.tree
.GlazedListTreeData
;
51 import org
.eclipse
.nebula
.widgets
.nattable
.extension
.glazedlists
.tree
.GlazedListTreeRowModel
;
52 import org
.eclipse
.nebula
.widgets
.nattable
.freeze
.CompositeFreezeLayer
;
53 import org
.eclipse
.nebula
.widgets
.nattable
.freeze
.FreezeHelper
;
54 import org
.eclipse
.nebula
.widgets
.nattable
.freeze
.FreezeLayer
;
55 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.GridRegion
;
56 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.command
.ClientAreaResizeCommand
;
57 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.DefaultColumnHeaderDataProvider
;
58 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.DefaultCornerDataProvider
;
59 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.DefaultRowHeaderDataProvider
;
60 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.data
.FixedSummaryRowHeaderLayer
;
61 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.ColumnHeaderLayer
;
62 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.CornerLayer
;
63 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.DefaultRowHeaderDataLayer
;
64 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.GridLayer
;
65 import org
.eclipse
.nebula
.widgets
.nattable
.grid
.layer
.config
.DefaultRowStyleConfiguration
;
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
.ILayerListener
;
71 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.cell
.ColumnOverrideLabelAccumulator
;
72 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.cell
.ILayerCell
;
73 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.config
.DefaultColumnHeaderStyleConfiguration
;
74 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.config
.DefaultRowHeaderStyleConfiguration
;
75 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.event
.ILayerEvent
;
76 import org
.eclipse
.nebula
.widgets
.nattable
.layer
.stack
.DefaultBodyLayerStack
;
77 import org
.eclipse
.nebula
.widgets
.nattable
.persistence
.PersistenceHelper
;
78 import org
.eclipse
.nebula
.widgets
.nattable
.persistence
.command
.DisplayPersistenceDialogCommand
;
79 import org
.eclipse
.nebula
.widgets
.nattable
.persistence
.command
.DisplayPersistenceDialogCommandHandler
;
80 import org
.eclipse
.nebula
.widgets
.nattable
.persistence
.command
.IStateChangedListener
;
81 import org
.eclipse
.nebula
.widgets
.nattable
.persistence
.command
.StateChangeEvent
;
82 import org
.eclipse
.nebula
.widgets
.nattable
.persistence
.gui
.PersistenceDialog
;
83 import org
.eclipse
.nebula
.widgets
.nattable
.selection
.SelectionLayer
;
84 import org
.eclipse
.nebula
.widgets
.nattable
.selection
.config
.DefaultSelectionStyleConfiguration
;
85 import org
.eclipse
.nebula
.widgets
.nattable
.selection
.event
.CellSelectionEvent
;
86 import org
.eclipse
.nebula
.widgets
.nattable
.sort
.SortHeaderLayer
;
87 import org
.eclipse
.nebula
.widgets
.nattable
.sort
.config
.SingleClickSortConfiguration
;
88 import org
.eclipse
.nebula
.widgets
.nattable
.style
.CellStyleAttributes
;
89 import org
.eclipse
.nebula
.widgets
.nattable
.style
.DisplayMode
;
90 import org
.eclipse
.nebula
.widgets
.nattable
.style
.HorizontalAlignmentEnum
;
91 import org
.eclipse
.nebula
.widgets
.nattable
.style
.Style
;
92 import org
.eclipse
.nebula
.widgets
.nattable
.style
.VerticalAlignmentEnum
;
93 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.DefaultSummaryRowConfiguration
;
94 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.FixedSummaryRowLayer
;
95 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.ISummaryProvider
;
96 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.SummaryRowConfigAttributes
;
97 import org
.eclipse
.nebula
.widgets
.nattable
.summaryrow
.SummaryRowLayer
;
98 import org
.eclipse
.nebula
.widgets
.nattable
.tree
.ITreeRowModel
;
99 import org
.eclipse
.nebula
.widgets
.nattable
.tree
.TreeLayer
;
100 import org
.eclipse
.nebula
.widgets
.nattable
.tree
.command
.TreeCollapseAllCommand
;
101 import org
.eclipse
.nebula
.widgets
.nattable
.tree
.command
.TreeExpandAllCommand
;
102 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.menu
.AbstractHeaderMenuConfiguration
;
103 import org
.eclipse
.nebula
.widgets
.nattable
.ui
.menu
.PopupMenuBuilder
;
104 import org
.eclipse
.nebula
.widgets
.nattable
.util
.GUIHelper
;
105 import org
.eclipse
.nebula
.widgets
.nattable
.viewport
.ViewportLayer
;
106 import org
.eclipse
.swt
.SWT
;
107 import org
.eclipse
.swt
.events
.SelectionAdapter
;
108 import org
.eclipse
.swt
.events
.SelectionEvent
;
109 import org
.eclipse
.swt
.graphics
.Color
;
110 import org
.eclipse
.swt
.graphics
.FontData
;
111 import org
.eclipse
.swt
.layout
.GridData
;
112 import org
.eclipse
.swt
.layout
.GridLayout
;
113 import org
.eclipse
.swt
.layout
.RowLayout
;
114 import org
.eclipse
.swt
.widgets
.Button
;
115 import org
.eclipse
.swt
.widgets
.Composite
;
116 import org
.eclipse
.swt
.widgets
.Label
;
118 import ca
.odell
.glazedlists
.BasicEventList
;
119 import ca
.odell
.glazedlists
.EventList
;
120 import ca
.odell
.glazedlists
.SortedList
;
121 import ca
.odell
.glazedlists
.TreeList
;
122 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
123 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
124 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
125 import eu
.etaxonomy
.cdm
.model
.description
.FeatureNode
;
126 import eu
.etaxonomy
.cdm
.model
.description
.FeatureTree
;
127 import eu
.etaxonomy
.cdm
.model
.description
.MeasurementUnit
;
128 import eu
.etaxonomy
.cdm
.model
.description
.SpecimenDescription
;
129 import eu
.etaxonomy
.cdm
.model
.description
.State
;
130 import eu
.etaxonomy
.cdm
.model
.description
.WorkingSet
;
131 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
132 import eu
.etaxonomy
.taxeditor
.editor
.l10n
.Messages
;
133 import eu
.etaxonomy
.taxeditor
.editor
.workingSet
.matrix
.categorical
.CategoricalDataCellEditor
;
134 import eu
.etaxonomy
.taxeditor
.editor
.workingSet
.matrix
.categorical
.CategoricalDataDisplayConverter
;
135 import eu
.etaxonomy
.taxeditor
.editor
.workingSet
.matrix
.quantitative
.QuantitativeDataCellEditor
;
136 import eu
.etaxonomy
.taxeditor
.editor
.workingSet
.matrix
.quantitative
.QuantitativeDataDisplayConverter
;
137 import eu
.etaxonomy
.taxeditor
.editor
.workingSet
.matrix
.supplementalInfo
.SupplementalInfoDisplayConverter
;
138 import eu
.etaxonomy
.taxeditor
.model
.ColorResources
;
139 import eu
.etaxonomy
.taxeditor
.model
.DescriptionHelper
;
140 import eu
.etaxonomy
.taxeditor
.model
.ImageResources
;
141 import eu
.etaxonomy
.taxeditor
.model
.MessagingUtils
;
142 import eu
.etaxonomy
.taxeditor
.preference
.Resources
;
143 import eu
.etaxonomy
.taxeditor
.workbench
.WorkbenchUtility
;
146 * Character matrix editor for editing specimen/taxon descriptions in a table
148 * @since Nov 26, 2017
151 public class CharacterMatrix
extends Composite
{
153 private static final List
<String
> WS_PROPERTY_PATH
= Arrays
.asList(new String
[] {
154 "descriptions", //$NON-NLS-1$
155 "descriptions.describedSpecimenOrObservation", //$NON-NLS-1$
156 "descriptions.describedSpecimenOrObservation.gatheringEvent", //$NON-NLS-1$
157 "descriptions.describedSpecimenOrObservation.gatheringEvent.actor", //$NON-NLS-1$
158 "descriptions.descriptionElements", //$NON-NLS-1$
159 "descriptions.descriptionElements.stateData", //$NON-NLS-1$
160 "descriptions.descriptionElements.stateData.state", //$NON-NLS-1$
161 "descriptions.descriptionElements.inDescription", //$NON-NLS-1$
162 "descriptions.descriptionElements.inDescription.descriptionElements", //$NON-NLS-1$
163 "descriptions.descriptionElements.feature", //$NON-NLS-1$
164 "representations", //$NON-NLS-1$
165 "descriptiveSystem", //$NON-NLS-1$
166 "taxonSubtreeFilter", //$NON-NLS-1$
167 "taxonSubtreeFilter.parent", //$NON-NLS-1$
168 "taxonSubtreeFilter.classification", //$NON-NLS-1$
169 "taxonSubtreeFilter.taxon", //$NON-NLS-1$
170 "taxonSubtreeFilter.taxon.name", //$NON-NLS-1$
171 "taxonSubtreeFilter.taxon.name.rank", //$NON-NLS-1$
172 "geoFilter", //$NON-NLS-1$
173 "minRank", //$NON-NLS-1$
174 "maxRank", //$NON-NLS-1$
177 private static final String CHARACTER_MATRIX_STATE_PROPERTIES
= "characterMatrixState.properties"; //$NON-NLS-1$
179 private static final int LEADING_COLUMN_COUNT
= 4;
180 private static final String TAXON_COLUMN
= "taxon_column"; //$NON-NLS-1$
181 private static final String COLLECTOR_COLUMN
= "collector_column"; //$NON-NLS-1$
182 private static final String IDENTIFIER_COLUMN
= "identifier_column"; //$NON-NLS-1$
183 private static final String COUNTRY_COLUMN
= "country_column"; //$NON-NLS-1$
185 private WorkingSet workingSet
;
187 private NatTable natTable
;
189 private Map
<Integer
, Feature
> indexToFeatureMap
= new HashMap
<>();
191 private Map
<Feature
, List
<State
>> categoricalFeatureToStateMap
= new HashMap
<>();
193 private LinkedMap
<String
, String
> propertyToLabelMap
= new LinkedMap
<>();
195 private Properties natTableState
;
197 private EventList
<Object
> descriptions
;
199 private List
<SpecimenWrapper
> specimenCache
= null;
201 private ListDataProvider
<Object
> bodyDataProvider
;
203 private FreezeLayer freezeLayer
;
205 private ViewportLayer viewportLayer
;
207 private Label wsLabel
;
209 private List
<Feature
> features
;
211 private DisplayPersistenceDialogCommandHandler displayPersistenceDialogCommandHandler
;
213 private CharacterMatrixPart part
;
215 private AbstractLayer topMostLayer
;
217 private FixedSummaryRowLayer summaryRowLayer
;
219 private ConfigRegistry configRegistry
;
221 private DefaultBodyLayerStack bodyLayer
;
223 private boolean isTreeView
= true;
226 public CharacterMatrix(Composite parent
, CharacterMatrixPart part
) {
227 super(parent
, SWT
.NONE
);
229 this.setLayout(new GridLayout());
233 natTable
= new NatTable(this, false);
237 createBottomToolbar();
241 private void createToolBar(){
242 Composite toolbarComposite
= new Composite(this, SWT
.NONE
);
243 toolbarComposite
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, true, false));
244 toolbarComposite
.setLayout(new GridLayout(9, false));
246 wsLabel
= new Label(toolbarComposite
, SWT
.NONE
);
248 Button btnToggleTree
= new Button(toolbarComposite
, SWT
.PUSH
);
249 Button btnToggleFlat
= new Button(toolbarComposite
, SWT
.PUSH
);
250 Button btnCollapseAll
= new Button(toolbarComposite
, SWT
.PUSH
);
251 Button btnExpandAll
= new Button(toolbarComposite
, SWT
.PUSH
);
252 Button btnFreezeSuppInfo
= new Button(toolbarComposite
, SWT
.TOGGLE
);
253 ComboViewer comboStates
= new ComboViewer(toolbarComposite
, SWT
.DROP_DOWN
);
254 Button btnManageState
= new Button(toolbarComposite
, SWT
.PUSH
);
255 Button btnExcelExport
= new Button(toolbarComposite
, SWT
.PUSH
);
260 btnToggleTree
.setImage(ImageResources
.getImage(ImageResources
.HIERARCHICAL
));
261 btnToggleTree
.setToolTipText(Messages
.CharacterMatrix_SHOW_HIERARCHY
);
262 btnToggleTree
.setSelection(true);
263 btnToggleTree
.setEnabled(false);
264 btnToggleTree
.addSelectionListener(new SelectionAdapter() {
266 public void widgetSelected(SelectionEvent e
) {
267 toggleTreeFlat(true, btnToggleFlat
, btnToggleTree
, btnCollapseAll
, btnExpandAll
, btnFreezeSuppInfo
);
274 btnToggleFlat
.setImage(ImageResources
.getImage(ImageResources
.FLAT
));
275 btnToggleFlat
.setToolTipText(Messages
.CharacterMatrix_SHOW_FLAT_LIST
);
276 btnToggleFlat
.addSelectionListener(new SelectionAdapter() {
278 public void widgetSelected(SelectionEvent e
) {
279 toggleTreeFlat(false, btnToggleFlat
, btnToggleTree
, btnCollapseAll
, btnExpandAll
, btnFreezeSuppInfo
);
287 btnCollapseAll
.setImage(ImageResources
.getImage(ImageResources
.COLLAPSE_ALL
));
288 btnCollapseAll
.setToolTipText(Messages
.CharacterMatrix_COLLAPSE
);
289 btnCollapseAll
.addSelectionListener(new SelectionAdapter() {
291 public void widgetSelected(SelectionEvent e
) {
292 natTable
.doCommand(new TreeCollapseAllCommand());
299 btnExpandAll
.setImage(ImageResources
.getImage(ImageResources
.EXPAND_ALL
));
300 btnExpandAll
.setToolTipText(Messages
.CharacterMatrix_EXPAND
);
301 btnExpandAll
.addSelectionListener(new SelectionAdapter() {
303 public void widgetSelected(SelectionEvent e
) {
304 natTable
.doCommand(new TreeExpandAllCommand());
309 * Freeze supplemental info button
311 btnFreezeSuppInfo
.setImage(ImageResources
.getImage(ImageResources
.LOCK_ICON
));
312 btnFreezeSuppInfo
.setToolTipText(Messages
.CharacterMatrix_LOCK_COLUMNS
);
313 btnFreezeSuppInfo
.setSelection(true);
314 btnFreezeSuppInfo
.addSelectionListener(new SelectionAdapter() {
316 public void widgetSelected(SelectionEvent e
) {
317 boolean isSelected
= btnFreezeSuppInfo
.getSelection();
318 freezeSupplementalColumns(isSelected
);
319 btnFreezeSuppInfo
.setImage(isSelected?
320 ImageResources
.getImage(ImageResources
.LOCK_ICON
):
321 ImageResources
.getImage(ImageResources
.LOCK_OPEN_ICON
));
326 * Table state persistence
328 natTableState
= new Properties();
329 //load persisted state
330 File statePropertiesFile
= getStatePropertiesFile();
331 FileInputStream inputStream
;
333 inputStream
= new FileInputStream(statePropertiesFile
);
334 natTableState
.load(inputStream
);
335 } catch (IOException e
) {
336 MessagingUtils
.info("No initial state properties file found for character matrix"); //$NON-NLS-1$
339 // create a combobox for showing the available view states
340 Collection
<String
> availableStates
= PersistenceHelper
.getAvailableStates(natTableState
);
341 comboStates
.setLabelProvider(new LabelProvider(){
343 public String
getText(Object element
) {
344 if(element
instanceof String
&& ((String
) element
).isEmpty()){
345 return Messages
.CharacterMatrix_DEFAULT
;
347 return super.getText(element
);
350 comboStates
.setContentProvider(new ArrayContentProvider());
351 comboStates
.addSelectionChangedListener(e
->
353 int index
= comboStates
.getCombo().getSelectionIndex();
355 String selected
= comboStates
.getCombo().getItem(index
);
357 natTable
.loadState(selected
, natTableState
);
358 natTableState
.setProperty(PersistenceDialog
.ACTIVE_VIEW_CONFIGURATION_KEY
, selected
);
361 comboStates
.setInput(availableStates
);
362 if(comboStates
.getCombo().getItemCount()>0){
363 comboStates
.getCombo().select(0);
366 displayPersistenceDialogCommandHandler
= new DisplayPersistenceDialogCommandHandler(natTableState
, natTable
);
367 // add listener to update the combo on view state management changes
368 displayPersistenceDialogCommandHandler
.addStateChangeListener(new IStateChangedListener() {
370 public void handleStateChange(StateChangeEvent event
) {
371 comboStates
.setInput(PersistenceHelper
.getAvailableStates(natTableState
));
372 selectStateItem(comboStates
, event
.getViewConfigName());
376 // add button to show dialog
377 btnManageState
.setImage(ImageResources
.getImage(ImageResources
.SETTINGS
));
378 btnManageState
.setToolTipText(Messages
.CharacterMatrix_VIEW_CONFIG
);
379 btnManageState
.addSelectionListener(new SelectionAdapter() {
381 public void widgetSelected(SelectionEvent e
) {
382 natTable
.doCommand(new DisplayPersistenceDialogCommand(natTable
));
383 selectStateItem(comboStates
, natTableState
.get(PersistenceDialog
.ACTIVE_VIEW_CONFIGURATION_KEY
).toString());
390 btnExcelExport
.setToolTipText(Messages
.CharacterMatrix_EXPORT
);
391 btnExcelExport
.setImage(ImageResources
.getImage(ImageResources
.EXPORT
));
392 btnExcelExport
.addSelectionListener(new SelectionAdapter() {
394 public void widgetSelected(SelectionEvent e
) {
397 natTable
.getConfigRegistry(),
398 natTable
.getShell()));
403 private void createBottomToolbar() {
404 Composite buttonPanel
= new Composite(this, SWT
.NONE
);
406 buttonPanel
.setLayout(new RowLayout());
407 GridDataFactory
.fillDefaults().grab(true, false).applyTo(buttonPanel
);
410 * Add description button
412 Button btnAddDescription
= new Button(buttonPanel
, SWT
.PUSH
);
413 btnAddDescription
.setImage(ImageResources
.getImage(ImageResources
.ADD_ICON_GREEN
));
414 btnAddDescription
.addSelectionListener(new SelectionAdapter() {
416 public void widgetSelected(SelectionEvent e
) {
417 SpecimenSelectionDialog dialog
= new SpecimenSelectionDialog(natTable
.getShell(), CharacterMatrix
.this);
418 if(dialog
.open()==Window
.OK
){
419 Collection
<SpecimenOrObservationBase
> specimens
= dialog
.getSpecimen();
420 boolean hasAdded
= false;
421 for (SpecimenOrObservationBase specimen
: specimens
) {
422 SpecimenDescription description
= getDescriptionForWorkingSet(specimen
);
423 if(!workingSet
.getDescriptions().contains(description
)){
424 CharacterMatrix
.this.descriptions
.add(new RowWrapper(description
, workingSet
));
425 workingSet
.addDescription(description
);
436 * Remove description button
438 Button btnRemoveDescription
= new Button(buttonPanel
, SWT
.PUSH
);
439 btnRemoveDescription
.setImage(ImageResources
.getImage(ImageResources
.ACTIVE_DELETE_ICON
));
440 btnRemoveDescription
.addSelectionListener(new SelectionAdapter() {
442 public void widgetSelected(SelectionEvent e
) {
443 int[] fullySelectedRowPositions
= bodyLayer
.getSelectionLayer().getFullySelectedRowPositions();
444 for (int i
: fullySelectedRowPositions
) {
445 Object rowObject
= bodyDataProvider
.getRowObject(i
);
446 if(rowObject
instanceof RowWrapper
){
447 CharacterMatrix
.this.descriptions
.remove(rowObject
);
448 workingSet
.removeDescription(((RowWrapper
) rowObject
).getSpecimenDescription());
456 private void applyStyles(){
457 // NOTE: Getting the colors and fonts from the GUIHelper ensures that
458 // they are disposed properly (required by SWT)
459 DefaultNatTableStyleConfiguration natTableConfiguration
= new DefaultNatTableStyleConfiguration();
460 natTableConfiguration
.bgColor
= GUIHelper
.getColor(249, 172, 7);
461 natTableConfiguration
.fgColor
= GUIHelper
.getColor(30, 76, 19);
462 natTableConfiguration
.hAlign
= HorizontalAlignmentEnum
.LEFT
;
463 natTableConfiguration
.vAlign
= VerticalAlignmentEnum
.TOP
;
464 // natTableConfiguration.borderStyle = new BorderStyle(1, GUIHelper.getColor(249, 172, 7), LineStyleEnum.SOLID);
466 // Setup even odd row colors - row colors override the NatTable default
468 DefaultRowStyleConfiguration rowStyleConfiguration
= new DefaultRowStyleConfiguration();
469 rowStyleConfiguration
.oddRowBgColor
= ColorResources
.getColor(Resources
.COLOR_LIST_ODD
);
470 rowStyleConfiguration
.evenRowBgColor
= ColorResources
.getColor(Resources
.COLOR_LIST_EVEN
);
472 // Setup selection styling
473 DefaultSelectionStyleConfiguration selectionStyle
= new DefaultSelectionStyleConfiguration();
474 // selectionStyle.selectionFont = GUIHelper.getFont(new FontData("Verdana", 8, SWT.NORMAL));
475 // selectionStyle.selectionBgColor = GUIHelper.getColor(217, 232, 251);
476 // selectionStyle.selectionFgColor = GUIHelper.COLOR_BLACK;
477 // selectionStyle.anchorBorderStyle = new BorderStyle(1, GUIHelper.COLOR_DARK_GRAY, LineStyleEnum.SOLID);
478 // selectionStyle.anchorBgColor = GUIHelper.getColor(65, 113, 43);
479 selectionStyle
.selectedHeaderBgColor
= GUIHelper
.getColor(156, 209, 103);
481 // Add all style configurations to NatTable
482 natTable
.addConfiguration(natTableConfiguration
);
483 natTable
.addConfiguration(rowStyleConfiguration
);
484 natTable
.addConfiguration(selectionStyle
);
486 // Column/Row header style and custom painters
487 DefaultRowHeaderStyleConfiguration rowHeaderConfig
= new DefaultRowHeaderStyleConfiguration();
488 Color rowColumnColor
= GUIHelper
.getColor(230, 255, 255);
489 rowHeaderConfig
.bgColor
= rowColumnColor
;
490 natTable
.addConfiguration(rowHeaderConfig
);
491 DefaultColumnHeaderStyleConfiguration columnHeaderStyle
= new DefaultColumnHeaderStyleConfiguration();
492 columnHeaderStyle
.bgColor
= rowColumnColor
;
493 columnHeaderStyle
.font
= GUIHelper
.getFont(new FontData("Verdana", 9, SWT
.BOLD
)); //$NON-NLS-1$
494 natTable
.addConfiguration(columnHeaderStyle
);
498 private void toggleTreeFlat(boolean isTree
, Button btnToggleFlat
, Button btnToggleTree
, Button btnCollapseAll
, Button btnExpandAll
, Button btnFreezeSuppInfo
) {
500 createLayers(isTree
);
501 btnToggleFlat
.setEnabled(isTree
);
502 btnToggleTree
.setEnabled(!isTree
);
503 btnCollapseAll
.setEnabled(isTree
);
504 btnExpandAll
.setEnabled(isTree
);
505 natTable
.doCommand(new ClientAreaResizeCommand(natTable
));
506 natTable
.doCommand(new VisualRefreshCommand());
507 freezeSupplementalColumns(btnFreezeSuppInfo
.getSelection());
510 public boolean isTreeView() {
514 public void createTable(boolean treeView
){
518 createLayers(treeView
);
523 configureNatTable(treeView
, configRegistry
, topMostLayer
, summaryRowLayer
);
526 * handlers and listeners
528 registerHandlersAndListeners(topMostLayer
);
530 GridDataFactory
.fillDefaults().grab(true, true).applyTo(natTable
);
532 wsLabel
.setText(workingSet
.getLabel());
533 wsLabel
.setLayoutData(new GridData(SWT
.FILL
, SWT
.CENTER
, true, false));
534 wsLabel
.getParent().layout();
536 freezeSupplementalColumns(true);
541 private List
<Feature
> initFeatureList(FeatureNode node
){
542 List
<Feature
> features
= new ArrayList
<>();
543 node
.getChildNodes().forEach(childNode
->
545 features
.add(childNode
.getFeature());
546 features
.addAll(initFeatureList(childNode
));
551 public void initWorkingSet(WorkingSet workingSet
){
552 this.workingSet
= workingSet
;
553 //get features/columns stored in working set
554 FeatureTree tree
= workingSet
.getDescriptiveSystem();
555 features
= initFeatureList(tree
.getRoot());
557 //init state data for categorical features
558 features
.forEach(feature
->
560 if(feature
.isSupportsCategoricalData()){
561 List
<State
> supportedStates
= new ArrayList
<>();
562 feature
.getSupportedCategoricalEnumerations().forEach(voc
->supportedStates
.addAll(voc
.getTerms()));
563 categoricalFeatureToStateMap
.put(feature
, supportedStates
);
566 descriptions
= new BasicEventList
<>();
570 private void createLayers(boolean treeView
) {
571 // use the SortedList constructor with 'null' for the Comparator
572 // because the Comparator will be set by configuration
573 SortedList
<Object
> sortedList
= new SortedList
<>(descriptions
, new MatrixRowComparator());
574 // wrap the SortedList with the TreeList
575 TreeList
<Object
> treeList
= new TreeList(sortedList
, new DescriptionTreeFormat(workingSet
), TreeList
.NODES_START_EXPANDED
);
579 SpecimenColumnPropertyAccessor columnPropertyAccessor
= new SpecimenColumnPropertyAccessor(this);
580 bodyDataProvider
= treeView?
new ListDataProvider
<>(treeList
, columnPropertyAccessor
):new ListDataProvider
<>(sortedList
, columnPropertyAccessor
);
582 configRegistry
= new ConfigRegistry();
591 - (top) SummaryRowLayer
592 - (bottom) ViewportLayer
598 TreeLayer (default visible)
624 DataLayer bodyDataLayer
= new DataLayer(bodyDataProvider
);
626 //register labels for columns
627 ColumnOverrideLabelAccumulator bodyColumnLabelAccumulator
=new ColumnOverrideLabelAccumulator(bodyDataLayer
);
628 bodyDataLayer
.setConfigLabelAccumulator(bodyColumnLabelAccumulator
);
629 propertyToLabelMap
.put(TAXON_COLUMN
, Messages
.CharacterMatrix_TAXON
);
630 bodyColumnLabelAccumulator
.registerColumnOverrides(0, TAXON_COLUMN
);
631 propertyToLabelMap
.put(COLLECTOR_COLUMN
, Messages
.CharacterMatrix_COLLECTOR_NO
);
632 bodyColumnLabelAccumulator
.registerColumnOverrides(1, COLLECTOR_COLUMN
);
633 propertyToLabelMap
.put(IDENTIFIER_COLUMN
, Messages
.CharacterMatrix_IDENTIFIER
);
634 bodyColumnLabelAccumulator
.registerColumnOverrides(2, IDENTIFIER_COLUMN
);
635 propertyToLabelMap
.put(COUNTRY_COLUMN
, Messages
.CharacterMatrix_COUNTRY
);
636 bodyColumnLabelAccumulator
.registerColumnOverrides(3, COUNTRY_COLUMN
);
637 for(int i
=0;i
<features
.size();i
++){
638 Feature feature
= features
.get(i
);
639 initLabels(bodyColumnLabelAccumulator
, i
, feature
);
642 // layer for event handling of GlazedLists and PropertyChanges
643 GlazedListsEventLayer eventLayer
= new GlazedListsEventLayer
<>(bodyDataLayer
, treeList
);
644 GlazedListTreeData treeData
= new GlazedListTreeData
<>(treeList
);
645 ITreeRowModel treeRowModel
= new GlazedListTreeRowModel
<>(treeData
);
647 bodyLayer
= new DefaultBodyLayerStack(
649 viewportLayer
= bodyLayer
.getViewportLayer();
650 final SelectionLayer selectionLayer
= bodyLayer
.getSelectionLayer();
651 freezeLayer
= new FreezeLayer(selectionLayer
);
652 final CompositeFreezeLayer compositeFreezeLayer
= new CompositeFreezeLayer(
653 freezeLayer
, bodyLayer
.getViewportLayer(), selectionLayer
);
654 TreeLayer treeLayer
= new TreeLayer(compositeFreezeLayer
, treeRowModel
);
656 topMostLayer
= treeView?treeLayer
:compositeFreezeLayer
;
658 summaryRowLayer
= new FixedSummaryRowLayer(bodyDataLayer
, topMostLayer
, configRegistry
, false);
659 //regoster labels with summary prefix for summary layer
660 ColumnOverrideLabelAccumulator summaryColumnLabelAccumulator
=new ColumnOverrideLabelAccumulator(bodyDataLayer
);
661 summaryRowLayer
.setConfigLabelAccumulator(summaryColumnLabelAccumulator
);
662 for(int i
=0;i
<features
.size();i
++){
663 Feature feature
= features
.get(i
);
664 summaryColumnLabelAccumulator
.registerColumnOverrides(
665 i
+LEADING_COLUMN_COUNT
,
666 SummaryRowLayer
.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX
+MatrixUtility
.getProperty(feature
));
668 // because the horizontal dependency is the ViewportLayer
669 // we need to set the composite dependency to false
670 summaryRowLayer
.setHorizontalCompositeDependency(false);
672 CompositeLayer composite
= new CompositeLayer(1, 2);
673 composite
.setChildLayer("SUMMARY", summaryRowLayer
, 0, 0); //$NON-NLS-1$
674 composite
.setChildLayer(GridRegion
.BODY
, topMostLayer
, 0, 1);
678 * column header layer
680 IDataProvider columnHeaderDataProvider
= new DefaultColumnHeaderDataProvider(
681 propertyToLabelMap
.values().toArray(new String
[] {}), propertyToLabelMap
);
682 DataLayer columnHeaderDataLayer
= new DataLayer(columnHeaderDataProvider
);
683 ColumnHeaderLayer columnHeaderLayer
= new ColumnHeaderLayer(columnHeaderDataLayer
, topMostLayer
, selectionLayer
);
685 // add the SortHeaderLayer to the column header layer stack
686 // as we use GlazedLists, we use the GlazedListsSortModel which
687 // delegates the sorting to the SortedList
688 final SortHeaderLayer
<SpecimenDescription
> sortHeaderLayer
= new SortHeaderLayer
<>(
690 new GlazedListsSortModel
<>(
692 columnPropertyAccessor
,
694 columnHeaderDataLayer
));
700 IDataProvider rowHeaderDataProvider
= new DefaultRowHeaderDataProvider(bodyDataProvider
);
701 DefaultRowHeaderDataLayer rowHeaderDataLayer
= new DefaultRowHeaderDataLayer(rowHeaderDataProvider
);
702 FixedSummaryRowHeaderLayer fixedSummaryRowHeaderLayer
= new FixedSummaryRowHeaderLayer(rowHeaderDataLayer
,
703 composite
, selectionLayer
);
704 fixedSummaryRowHeaderLayer
.setSummaryRowLabel("\u2211"); //$NON-NLS-1$
710 ILayer cornerLayer
= new CornerLayer(
711 new DataLayer(new DefaultCornerDataProvider(columnHeaderDataProvider
, rowHeaderDataProvider
)),
712 fixedSummaryRowHeaderLayer
, sortHeaderLayer
);
716 * GRID layer (composition of all other layers)
718 GridLayer gridLayer
= new GridLayer(composite
, sortHeaderLayer
, fixedSummaryRowHeaderLayer
, cornerLayer
);
720 natTable
.setLayer(gridLayer
);
724 private void registerHandlersAndListeners(AbstractLayer topMostLayer
) {
725 // add the ExportCommandHandler to the ViewportLayer in order to make
727 topMostLayer
.registerCommandHandler(new ExportCommandHandler(topMostLayer
));
729 //propagate single cell selection
730 natTable
.addLayerListener(new ILayerListener() {
732 public void handleLayerEvent(ILayerEvent event
) {
733 if(event
instanceof CellSelectionEvent
){
734 CellSelectionEvent cellSelectionEvent
= (CellSelectionEvent
)event
;
735 int columnPosition
= cellSelectionEvent
.getColumnPosition();
736 if(columnPosition
>LEADING_COLUMN_COUNT
){
737 Collection
<ILayerCell
> selectedCells
= cellSelectionEvent
.getSelectionLayer().getSelectedCells();
738 StructuredSelection selection
= new StructuredSelection();
739 if(selectedCells
.size()==1){
740 ILayerCell cell
= selectedCells
.iterator().next();
741 Object dataValue
= cell
.getDataValue();
743 selection
= new StructuredSelection(dataValue
);
746 part
.getSelectionService().setSelection(selection
);
752 //register handler for view configuration menu
753 natTable
.registerCommandHandler(displayPersistenceDialogCommandHandler
);
756 private void configureNatTable(boolean treeView
, ConfigRegistry configRegistry
, AbstractLayer topMostLayer
,
757 FixedSummaryRowLayer summaryRowLayer
) {
761 natTable
.setConfigRegistry(configRegistry
);
763 //add default configuration because autoconfigure is set to false in constructor
764 natTable
.addConfiguration(new DefaultNatTableStyleConfiguration());
766 //FIXME: this is for DEBUG ONLY
767 // natTable.addConfiguration(new DebugMenuConfiguration(natTable));
769 // override the default sort configuration and change the mouse bindings
770 // to sort on a single click
772 natTable
.addConfiguration(new SingleClickSortConfiguration());
775 // add the header menu configuration for adding the column header menu
776 // with hide/show actions
777 natTable
.addConfiguration(new AbstractHeaderMenuConfiguration(natTable
) {
780 protected PopupMenuBuilder
createColumnHeaderMenu(NatTable natTable
) {
781 return super.createColumnHeaderMenu(natTable
)
782 .withHideColumnMenuItem()
783 .withShowAllColumnsMenuItem();
788 Style cellStyle
= new Style();
789 cellStyle
.setAttributeValue(CellStyleAttributes
.HORIZONTAL_ALIGNMENT
, HorizontalAlignmentEnum
.LEFT
);
791 // add custom configuration for data conversion and add column labels to viewport layer
792 topMostLayer
.addConfiguration(new AbstractRegistryConfiguration() {
794 public void configureRegistry(IConfigRegistry configRegistry
) {
795 //add display converter for string representation
796 configRegistry
.registerConfigAttribute(
797 CellConfigAttributes
.DISPLAY_CONVERTER
,
798 new SupplementalInfoDisplayConverter(CharacterMatrix
.this),
801 configRegistry
.registerConfigAttribute(
802 CellConfigAttributes
.CELL_STYLE
,
806 configRegistry
.registerConfigAttribute(
807 CellConfigAttributes
.DISPLAY_CONVERTER
,
808 new SupplementalInfoDisplayConverter(CharacterMatrix
.this),
811 configRegistry
.registerConfigAttribute(
812 CellConfigAttributes
.CELL_STYLE
,
816 configRegistry
.registerConfigAttribute(
817 CellConfigAttributes
.DISPLAY_CONVERTER
,
818 new SupplementalInfoDisplayConverter(CharacterMatrix
.this),
821 configRegistry
.registerConfigAttribute(
822 CellConfigAttributes
.CELL_STYLE
,
826 configRegistry
.registerConfigAttribute(
827 CellConfigAttributes
.DISPLAY_CONVERTER
,
828 new SupplementalInfoDisplayConverter(CharacterMatrix
.this),
831 configRegistry
.registerConfigAttribute(
832 CellConfigAttributes
.CELL_STYLE
,
836 features
.forEach(feature
->registerColumnConfiguration(feature
, configRegistry
));
841 //no summary for the supplemental columns
842 for(int i
=0;i
<LEADING_COLUMN_COUNT
;i
++){
844 summaryRowLayer
.addConfiguration(new DefaultSummaryRowConfiguration() {
846 public void addSummaryProviderConfig(IConfigRegistry configRegistry
) {
847 configRegistry
.registerConfigAttribute(
848 SummaryRowConfigAttributes
.SUMMARY_PROVIDER
,
849 new ISummaryProvider() {
852 public Object
summarize(int columnIndex
) {
857 SummaryRowLayer
.DEFAULT_SUMMARY_COLUMN_CONFIG_LABEL_PREFIX
+index
);
861 //register aggregation configuration for each feature
862 features
.forEach(feature
->summaryRowLayer
.addConfiguration(new AggregationConfiguration(bodyDataProvider
, feature
)));
864 natTable
.configure();
867 private void freezeSupplementalColumns(boolean freeze
){
869 FreezeHelper
.freeze(freezeLayer
, viewportLayer
,
870 new PositionCoordinate(viewportLayer
, 0, 0),
871 new PositionCoordinate(viewportLayer
, LEADING_COLUMN_COUNT
-1, -1));
874 FreezeHelper
.unfreeze(freezeLayer
, viewportLayer
);
878 private void selectStateItem(ComboViewer comboStates
, String stateName
){
879 String
[] items
= comboStates
.getCombo().getItems();
880 for(int i
=0;i
<items
.length
;i
++){
881 if(items
[i
].equals(stateName
)){
882 comboStates
.getCombo().select(i
);
888 private SpecimenDescription
getDescriptionForWorkingSet(SpecimenOrObservationBase specimen
){
889 Set
<Feature
> wsFeatures
= workingSet
.getDescriptiveSystem().getDistinctFeatures();
890 List
<DescriptionElementBase
> matchingDescriptionElements
= new ArrayList
<>();
892 for (SpecimenDescription specimenDescription
: (Set
<SpecimenDescription
>) specimen
.getDescriptions()) {
893 Set
<Feature
> specimenDescriptionFeatures
= new HashSet
<>();
894 //gather specimen description features and check for match with WS features
895 for (DescriptionElementBase specimenDescriptionElement
: specimenDescription
.getElements()) {
896 Feature feature
= specimenDescriptionElement
.getFeature();
897 specimenDescriptionFeatures
.add(feature
);
898 if(wsFeatures
.contains(feature
)){
899 matchingDescriptionElements
.add(specimenDescriptionElement
);
902 //if description with the exact same features is found return the description
903 if(specimenDescriptionFeatures
.equals(wsFeatures
)){
904 return specimenDescription
;
907 //Create new specimen description if no match was found
909 SpecimenDescription newDesription
= SpecimenDescription
.NewInstance(specimen
);
910 newDesription
.setTitleCache(Messages
.CharacterMatrix_WORKING_SET
+workingSet
.getLabel()+": "+newDesription
.generateTitle(), true); //$NON-NLS-2$
912 //check for equals description element (same feature and same values)
913 Map
<Feature
, List
<DescriptionElementBase
>> featureToElementMap
= new HashMap
<>();
914 for(DescriptionElementBase element
:matchingDescriptionElements
){
915 List
<DescriptionElementBase
> list
= featureToElementMap
.get(element
.getFeature());
917 list
= new ArrayList
<>();
920 featureToElementMap
.put(element
.getFeature(), list
);
922 Set
<DescriptionElementBase
> descriptionElementsToClone
= new HashSet
<>();
923 for(Feature feature
:featureToElementMap
.keySet()){
924 List
<DescriptionElementBase
> elements
= featureToElementMap
.get(feature
);
925 //no duplicate description elements found for this feature
926 if(elements
.size()==1){
927 descriptionElementsToClone
.add(elements
.get(0));
929 //duplicates found -> check if all are equal
931 DescriptionElementBase match
= null;
932 for (DescriptionElementBase descriptionElementBase
: elements
) {
934 match
= descriptionElementBase
;
936 else if(!new DescriptionElementCompareWrapper(match
).equals(new DescriptionElementCompareWrapper(descriptionElementBase
))){
938 MessagingUtils
.informationDialog(Messages
.CharacterMatrix_MULTIPLE_DATA
,
939 String
.format(Messages
.CharacterMatrix_MULTIPLE_DATA_MESSAGE
, feature
.getLabel()));
944 descriptionElementsToClone
.add(match
);
948 //clone matching descriptionElements
949 for (DescriptionElementBase descriptionElementBase
: descriptionElementsToClone
) {
950 DescriptionElementBase clone
;
952 clone
= descriptionElementBase
.clone(newDesription
);
953 clone
.getSources().forEach(source
-> source
.setOriginalNameString(DescriptionHelper
.getLabel(descriptionElementBase
)));
954 } catch (CloneNotSupportedException e
) {
955 MessagingUtils
.error(CharacterMatrix
.class, e
);
958 return newDesription
;
962 private void initLabels(final ColumnOverrideLabelAccumulator columnLabelAccumulator
,
963 int index
, Feature feature
) {
965 columnLabelAccumulator
.registerColumnOverrides(index
+LEADING_COLUMN_COUNT
, MatrixUtility
.getProperty(feature
));
966 indexToFeatureMap
.put(index
+LEADING_COLUMN_COUNT
, feature
);
968 String featureLabel
= feature
.getLabel();
969 String property
= featureLabel
;
970 //show unit for quantitative data
971 if(feature
.isSupportsQuantitativeData()){
972 Set
<MeasurementUnit
> recommendedMeasurementUnits
= feature
.getRecommendedMeasurementUnits();
973 if(recommendedMeasurementUnits
.size()>1){
974 MessagingUtils
.warningDialog(Messages
.CharacterMatrix_INIT_PROBLEM
, CharacterMatrix
.class,
975 String
.format(Messages
.CharacterMatrix_INIT_PROBLEM_MESSAGE
, feature
.getLabel()));
977 if(recommendedMeasurementUnits
.size()==1){
978 MeasurementUnit unit
= recommendedMeasurementUnits
.iterator().next();
979 featureLabel
+= " ["+unit
.getIdInVocabulary()+"]"; //$NON-NLS-1$ //$NON-NLS-2$
982 propertyToLabelMap
.put(property
, featureLabel
);
985 private void registerColumnConfiguration(Feature feature
, IConfigRegistry configRegistry
) {
987 configRegistry
.registerConfigAttribute(
988 EditConfigAttributes
.CELL_EDITABLE_RULE
,
989 IEditableRule
.ALWAYS_EDITABLE
,
991 MatrixUtility
.getProperty(feature
)
993 if(feature
.isSupportsQuantitativeData()){
994 //add display converter for string representation
995 configRegistry
.registerConfigAttribute(
996 CellConfigAttributes
.DISPLAY_CONVERTER
,
997 new QuantitativeDataDisplayConverter(),
999 MatrixUtility
.getProperty(feature
));
1000 //register quantitative editor
1001 configRegistry
.registerConfigAttribute(
1002 EditConfigAttributes
.CELL_EDITOR
,
1003 new QuantitativeDataCellEditor(feature
, this),
1005 MatrixUtility
.getProperty(feature
));
1007 else if(feature
.isSupportsCategoricalData()){
1008 //add display converter for string representation
1009 configRegistry
.registerConfigAttribute(
1010 CellConfigAttributes
.DISPLAY_CONVERTER
,
1011 new CategoricalDataDisplayConverter(),
1013 MatrixUtility
.getProperty(feature
));
1015 //add combo box cell editor
1017 configRegistry
.registerConfigAttribute(EditConfigAttributes
.CELL_EDITOR
,
1018 new CategoricalDataCellEditor(getSupportedStatesForCategoricalFeature(feature
), this, feature
),
1020 MatrixUtility
.getProperty(feature
));
1027 public void loadDescriptions(WorkingSet workingSet
) {
1028 String jobLabel
= "Load character data";
1029 Job job
= Job
.create(jobLabel
, (ICoreRunnable
) monitor
-> {
1030 monitor
.beginTask(jobLabel
, CharacterMatrix
.this.descriptions
.size());
1031 Set
<DescriptionBase
> wsDescriptions
= workingSet
.getDescriptions();
1032 for (DescriptionBase descriptionBase
: wsDescriptions
) {
1033 if(descriptionBase
instanceof SpecimenDescription
){
1034 CharacterMatrix
.this.descriptions
.add(new RowWrapper((SpecimenDescription
) descriptionBase
, workingSet
));
1042 public List
<State
> getSupportedStatesForCategoricalFeature(Feature feature
){
1043 return categoricalFeatureToStateMap
.get(feature
);
1046 public Map
<Integer
, Feature
> getIndexToFeatureMap() {
1047 return indexToFeatureMap
;
1050 public LinkedMap
<String
, String
> getPropertyToLabelMap() {
1051 return propertyToLabelMap
;
1054 public void setDirty() {
1058 public NatTable
getNatTable() {
1062 public WorkingSet
getWorkingSet() {
1066 public List
<SpecimenWrapper
> getSpecimenCache() {
1067 return specimenCache
;
1070 public void setSpecimenCache(List
<SpecimenWrapper
> specimenCache
) {
1071 this.specimenCache
=
1072 specimenCache
.stream().filter(wrapper
->
1073 wrapper
.getSpecimen().getDescriptions().stream().noneMatch(description
->
1074 workingSet
.getDescriptions().contains(description
)))
1075 .collect(Collectors
.toList());
1078 public Properties
getNatTableState() {
1079 return natTableState
;
1082 public ListDataProvider
<Object
> getBodyDataProvider() {
1083 return bodyDataProvider
;
1086 private File
getStatePropertiesFile() {
1087 return new File(WorkbenchUtility
.getBaseLocation(), CHARACTER_MATRIX_STATE_PROPERTIES
);