ref #7549 Implement historgram tooltip for categorical data
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / descriptiveDataSet / matrix / CharacterMatrixBottomToolbar.java
1 /**
2 * Copyright (C) 2018 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.descriptiveDataSet.matrix;
10
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.HashMap;
14 import java.util.HashSet;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.Set;
18 import java.util.UUID;
19 import java.util.stream.Collectors;
20
21 import org.eclipse.jface.layout.GridDataFactory;
22 import org.eclipse.jface.window.Window;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.events.SelectionAdapter;
25 import org.eclipse.swt.events.SelectionEvent;
26 import org.eclipse.swt.layout.RowLayout;
27 import org.eclipse.swt.widgets.Button;
28 import org.eclipse.swt.widgets.Composite;
29
30 import eu.etaxonomy.cdm.api.service.IDescriptionService;
31 import eu.etaxonomy.cdm.api.service.IDescriptiveDataSetService;
32 import eu.etaxonomy.cdm.api.service.IOccurrenceService;
33 import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
34 import eu.etaxonomy.cdm.api.service.UpdateResult;
35 import eu.etaxonomy.cdm.api.service.dto.RowWrapperDTO;
36 import eu.etaxonomy.cdm.model.description.CategoricalData;
37 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
38 import eu.etaxonomy.cdm.model.description.Feature;
39 import eu.etaxonomy.cdm.model.description.QuantitativeData;
40 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
41 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
42 import eu.etaxonomy.cdm.model.taxon.Taxon;
43 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
44 import eu.etaxonomy.cdm.persistence.dto.SpecimenNodeWrapper;
45 import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
46 import eu.etaxonomy.taxeditor.editor.l10n.Messages;
47 import eu.etaxonomy.taxeditor.model.DescriptionHelper;
48 import eu.etaxonomy.taxeditor.model.ImageResources;
49 import eu.etaxonomy.taxeditor.model.MessagingUtils;
50 import eu.etaxonomy.taxeditor.store.CdmStore;
51 import eu.etaxonomy.taxeditor.ui.dialog.selection.TaxonSelectionDialog;
52
53 /**
54 * @author pplitzner
55 * @since Jul 9, 2018
56 *
57 */
58 public class CharacterMatrixBottomToolbar extends Composite{
59
60 private CharacterMatrix matrix;
61
62 public CharacterMatrixBottomToolbar(CharacterMatrix matrix, int style) {
63 super(matrix, style);
64 this.matrix = matrix;
65
66 init();
67 }
68
69 private void init() {
70
71 setLayout(new RowLayout());
72 GridDataFactory.fillDefaults().grab(true, false).applyTo(this);
73
74 /**
75 * Add description button
76 */
77 Button btnAddDescription = new Button(this, SWT.PUSH);
78 btnAddDescription.setImage(ImageResources.getImage(ImageResources.ADD_ICON_GREEN));
79 btnAddDescription.addSelectionListener(new SelectionAdapter() {
80 @Override
81 public void widgetSelected(SelectionEvent e) {
82 SpecimenSelectionDialog dialog = new SpecimenSelectionDialog(matrix.getShell(), matrix);
83 if(dialog.open()==Window.OK){
84 Collection<SpecimenNodeWrapper> wrappers = dialog.getSpecimen();
85 for (SpecimenNodeWrapper wrapper : wrappers) {
86 SpecimenOrObservationBase specimen = CdmStore.getService(IOccurrenceService.class).load(wrapper.getUuidAndTitleCache().getUuid());
87 SpecimenDescription description = getDescriptionForDescriptiveDataSet(specimen);
88 //description elements
89 Map<Feature, DescriptionElementBase> featureToElementMap = new HashMap<>();
90 Set<DescriptionElementBase> elements = description.getElements();
91 for (DescriptionElementBase descriptionElementBase : elements) {
92 Feature feature = descriptionElementBase.getFeature();
93 featureToElementMap.put(feature, descriptionElementBase);
94 }
95 RowWrapperDTO rowWrapper = CdmStore.getService(IDescriptiveDataSetService.class).createRowWrapper(description, matrix.getDescriptiveDataSet());
96 matrix.getDescriptions().add(rowWrapper);
97 matrix.getDescriptiveDataSet().addDescription(description);
98 matrix.setDirty();
99 matrix.getSpecimenCache().remove(wrapper);
100 }
101 }
102 }
103 });
104 /**
105 * Remove description button
106 */
107 Button btnRemoveDescription = new Button(this, SWT.PUSH);
108 btnRemoveDescription.setImage(ImageResources.getImage(ImageResources.ACTIVE_DELETE_ICON));
109 btnRemoveDescription.addSelectionListener(new SelectionAdapter() {
110 @Override
111 public void widgetSelected(SelectionEvent e) {
112 int[] fullySelectedRowPositions = matrix.getBodyLayer().getSelectionLayer().getFullySelectedRowPositions();
113 List<RowWrapperDTO> toRemove = new ArrayList<>();
114 for (int i : fullySelectedRowPositions) {
115 Object rowObject = matrix.getBodyDataProvider().getRowObject(i);
116 if(rowObject instanceof RowWrapperDTO){
117 toRemove.add((RowWrapperDTO) rowObject);
118 }
119 }
120 toRemove.forEach(rowToRemove->{
121 matrix.getDescriptions().remove(rowToRemove);
122 matrix.getDescriptiveDataSet().removeDescription(rowToRemove.getSpecimenDescription());
123 matrix.setDirty();
124 });
125 }
126 });
127 /**
128 * Aggregate button
129 */
130 Button btnAggregate = new Button(this, SWT.PUSH);
131 btnAggregate.setText("Aggregate");
132 btnAggregate.addSelectionListener(new SelectionAdapter() {
133 @SuppressWarnings("unchecked")
134 @Override
135 public void widgetSelected(SelectionEvent e) {
136 Set<TaxonNode> taxonSubtreeFilter = matrix.getDescriptiveDataSet().getTaxonSubtreeFilter();
137 List<TaxonNodeDto> nodeDtos = taxonSubtreeFilter.stream()
138 .map(node -> new TaxonNodeDto(node)).collect(Collectors.toList());
139 TaxonNodeDto parentDto = CdmStore.getService(ITaxonNodeService.class).findCommonParentDto(nodeDtos);
140 UUID taxonUuid = parentDto.getTaxonUuid();
141 int response = MessagingUtils.confirmDialog(
142 "Choose location for the aggregated description",
143 String.format("The aggregated description will be stored at "
144 + "the common parent taxon of this data set:\n%s\n\n"
145 + "Do you want to use this taxon?"
146 , parentDto.getTaxonTitleCache()), "Yes", "Choose taxon", "Cancel");
147 if(response==2){
148 return;
149 }
150 else if(response==1){
151 Taxon taxon = TaxonSelectionDialog.selectTaxon(getShell(), null);
152 if(taxon==null){
153 return;
154 }
155 taxonUuid = taxon.getUuid();
156 }
157 UpdateResult result = CdmStore.getService(IDescriptionService.class).aggregateDescription(taxonUuid, null, matrix.getDescriptiveDataSet().getUuid());
158 matrix.addUpdateResult(result);
159 matrix.setDirty();
160
161 //aggregate histogram for categorical tooltip
162 Map<Feature, CategoricalDataHistogram> featureToHistogramMap = matrix.getFeatureToHistogramMap();
163 featureToHistogramMap.clear();
164 // matrix.getFeatures().forEach(feature->{
165 matrix.getDescriptions()
166 .forEach(o -> ((RowWrapperDTO) o).getSpecimenDescription().getElements().stream()
167 .filter(descriptionElement -> descriptionElement instanceof CategoricalData)
168 .forEach(categoricalData -> {
169 Feature feature = ((CategoricalData) categoricalData).getFeature();
170 CategoricalDataHistogram dataHistogram = featureToHistogramMap.get(feature);
171 if(dataHistogram==null){
172 dataHistogram = new CategoricalDataHistogram(feature);
173 }
174 featureToHistogramMap.put(feature, dataHistogram);
175 ((CategoricalData) categoricalData).getStateData()
176 .forEach(stateData -> featureToHistogramMap.get(feature).addState(stateData.getState()));
177 }));
178 }
179 });
180 }
181 private SpecimenDescription getDescriptionForDescriptiveDataSet(SpecimenOrObservationBase specimen){
182 Set<Feature> wsFeatures = matrix.getDescriptiveDataSet().getDescriptiveSystem().getDistinctFeatures();
183 List<DescriptionElementBase> matchingDescriptionElements = new ArrayList<>();
184
185 for (SpecimenDescription specimenDescription : (Set<SpecimenDescription>) specimen.getDescriptions()) {
186 specimenDescription = (SpecimenDescription) CdmStore.getService(IDescriptionService.class).load(specimenDescription.getUuid());
187 Set<Feature> specimenDescriptionFeatures = new HashSet<>();
188 //gather specimen description features and check for match with WS features
189 for (DescriptionElementBase specimenDescriptionElement : specimenDescription.getElements()) {
190 Feature feature = specimenDescriptionElement.getFeature();
191 specimenDescriptionFeatures.add(feature);
192 if(wsFeatures.contains(feature)){
193 matchingDescriptionElements.add(specimenDescriptionElement);
194 }
195 }
196 //if description with the exact same features is found return the description
197 if(specimenDescriptionFeatures.equals(wsFeatures)){
198 return specimenDescription;
199 }
200 }
201 //Create new specimen description if no match was found
202 matrix.setDirty();
203 SpecimenDescription newDesription = SpecimenDescription.NewInstance(specimen);
204 newDesription.setTitleCache(Messages.CharacterMatrix_DESCRIPTIVE_DATA_SET+matrix.getDescriptiveDataSet().getLabel()+": "+newDesription.generateTitle(), true); //$NON-NLS-2$
205
206 //check for equals description element (same feature and same values)
207 Map<Feature, List<DescriptionElementBase>> featureToElementMap = new HashMap<>();
208 for(DescriptionElementBase element:matchingDescriptionElements){
209 List<DescriptionElementBase> list = featureToElementMap.get(element.getFeature());
210 if(list==null){
211 list = new ArrayList<>();
212 }
213 list.add(element);
214 featureToElementMap.put(element.getFeature(), list);
215 }
216 Set<DescriptionElementBase> descriptionElementsToClone = new HashSet<>();
217 for(Feature feature:featureToElementMap.keySet()){
218 List<DescriptionElementBase> elements = featureToElementMap.get(feature);
219 //no duplicate description elements found for this feature
220 if(elements.size()==1){
221 descriptionElementsToClone.add(elements.get(0));
222 }
223 //duplicates found -> check if all are equal
224 else{
225 DescriptionElementBase match = null;
226 for (DescriptionElementBase descriptionElementBase : elements) {
227 if(match==null){
228 match = descriptionElementBase;
229 }
230 else if(!new DescriptionElementCompareWrapper(match).equals(new DescriptionElementCompareWrapper(descriptionElementBase))){
231 match = null;
232 MessagingUtils.informationDialog(Messages.CharacterMatrix_MULTIPLE_DATA,
233 String.format(Messages.CharacterMatrix_MULTIPLE_DATA_MESSAGE, feature.getLabel()));
234 break;
235 }
236 }
237 if(match!=null){
238 descriptionElementsToClone.add(match);
239 }
240 }
241 }
242 //clone matching descriptionElements
243 for (DescriptionElementBase descriptionElementBase : descriptionElementsToClone) {
244 DescriptionElementBase clone;
245 try {
246 clone = descriptionElementBase.clone(newDesription);
247 clone.getSources().forEach(source -> source.setOriginalNameString(DescriptionHelper.getLabel(descriptionElementBase)));
248 } catch (CloneNotSupportedException e) {
249 MessagingUtils.error(CharacterMatrix.class, e);
250 }
251 }
252
253 //add all remaining description elements to the new description
254 for(Feature wsFeature:wsFeatures){
255 boolean featureFound = false;
256 for(DescriptionElementBase element:newDesription.getElements()){
257 if(element.getFeature().equals(wsFeature)){
258 featureFound = true;
259 break;
260 }
261 }
262 if(!featureFound){
263 if(wsFeature.isSupportsCategoricalData()){
264 newDesription.addElement(CategoricalData.NewInstance(wsFeature));
265 }
266 else if(wsFeature.isSupportsQuantitativeData()){
267 newDesription.addElement(QuantitativeData.NewInstance(wsFeature));
268 }
269 }
270 }
271 return newDesription;
272
273 }
274 }