Fix problem for characters with same name
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / descriptiveDataSet / DescriptiveDataSetEditor.java
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.descriptiveDataSet;
10
11 import java.util.ArrayList;
12 import java.util.Collection;
13 import java.util.Collections;
14 import java.util.HashSet;
15 import java.util.Iterator;
16 import java.util.List;
17 import java.util.Map;
18 import java.util.Set;
19 import java.util.UUID;
20 import java.util.stream.Collectors;
21
22 import javax.annotation.PostConstruct;
23 import javax.annotation.PreDestroy;
24 import javax.inject.Inject;
25 import javax.inject.Named;
26
27 import org.eclipse.core.runtime.IProgressMonitor;
28 import org.eclipse.e4.core.contexts.ContextInjectionFactory;
29 import org.eclipse.e4.core.contexts.IEclipseContext;
30 import org.eclipse.e4.ui.di.Focus;
31 import org.eclipse.e4.ui.di.Persist;
32 import org.eclipse.e4.ui.model.application.ui.MDirtyable;
33 import org.eclipse.e4.ui.services.EMenuService;
34 import org.eclipse.e4.ui.services.IServiceConstants;
35 import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
36 import org.eclipse.jface.util.LocalSelectionTransfer;
37 import org.eclipse.jface.viewers.IStructuredSelection;
38 import org.eclipse.jface.viewers.StructuredSelection;
39 import org.eclipse.jface.viewers.Viewer;
40 import org.eclipse.jface.window.Window;
41 import org.eclipse.swt.SWT;
42 import org.eclipse.swt.dnd.DND;
43 import org.eclipse.swt.dnd.Transfer;
44 import org.eclipse.swt.events.FocusAdapter;
45 import org.eclipse.swt.events.FocusEvent;
46 import org.eclipse.swt.events.KeyAdapter;
47 import org.eclipse.swt.events.KeyEvent;
48 import org.eclipse.swt.events.ModifyEvent;
49 import org.eclipse.swt.events.ModifyListener;
50 import org.eclipse.swt.events.SelectionAdapter;
51 import org.eclipse.swt.widgets.Composite;
52 import org.eclipse.swt.widgets.Display;
53 import org.eclipse.swt.widgets.Shell;
54
55 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
56 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
57 import eu.etaxonomy.cdm.api.service.IDescriptiveDataSetService;
58 import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
59 import eu.etaxonomy.cdm.api.service.ITermService;
60 import eu.etaxonomy.cdm.model.description.Character;
61 import eu.etaxonomy.cdm.model.description.DescriptiveDataSet;
62 import eu.etaxonomy.cdm.model.location.NamedArea;
63 import eu.etaxonomy.cdm.model.name.Rank;
64 import eu.etaxonomy.cdm.model.taxon.ITaxonTreeNode;
65 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
66 import eu.etaxonomy.cdm.model.term.DefinedTermBase;
67 import eu.etaxonomy.cdm.model.term.FeatureTree;
68 import eu.etaxonomy.cdm.model.term.TermType;
69 import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
70 import eu.etaxonomy.cdm.persistence.dto.TermDto;
71 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
72 import eu.etaxonomy.taxeditor.editor.l10n.Messages;
73 import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
74 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
75 import eu.etaxonomy.taxeditor.model.IPartContentHasSupplementalData;
76 import eu.etaxonomy.taxeditor.model.MessagingUtils;
77 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
78 import eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled;
79 import eu.etaxonomy.taxeditor.store.CdmStore;
80 import eu.etaxonomy.taxeditor.ui.element.CdmFormFactory;
81 import eu.etaxonomy.taxeditor.workbench.part.IE4SavablePart;
82
83 /**
84 * Editor for configuring DescriptiveDataSets
85 * @author pplitzner
86 * @since Nov 21, 2017
87 *
88 */
89 public class DescriptiveDataSetEditor implements IE4SavablePart, IConversationEnabled, ICdmEntitySessionEnabled,
90 IPartContentHasDetails, IPartContentHasSupplementalData, IDirtyMarkable {
91
92 private DescriptiveDataSetComposite composite;
93
94 private ConversationHolder conversation;
95
96 private ICdmEntitySession cdmEntitySession;
97
98 private DescriptiveDataSet descriptiveDataSet;
99
100 private final int dndOperations = DND.DROP_MOVE;
101
102 @Inject
103 private MDirtyable dirty;
104
105 @Inject
106 private ESelectionService selectionService;
107
108 private ModifyListener labelModifyListener;
109
110 @PostConstruct
111 public void create(Composite parent, IEclipseContext context, @Named(IServiceConstants.ACTIVE_SHELL)Shell shell,
112 EMenuService menuService){
113 if (CdmStore.isActive()){
114 if(conversation == null){
115 conversation = CdmStore.createConversation();
116 }
117 if(cdmEntitySession == null){
118 cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
119 }
120 }
121 else{
122 return;
123 }
124 CdmFormFactory cdmFormFactory = new CdmFormFactory(Display.getCurrent());
125 ContextInjectionFactory.inject(cdmFormFactory, context);
126
127 composite = new DescriptiveDataSetComposite(parent, SWT.NONE);
128
129 labelModifyListener = new ModifyListener() {
130 @Override
131 public void modifyText(ModifyEvent e) {
132 descriptiveDataSet.setLabel(composite.getTxt_label().getText());
133 dirty.setDirty(true);
134 }
135 };
136 composite.getRankMin().addSelectionChangedListener(event->dirty.setDirty(true));
137 composite.getRankMin().setText("Select min rank...");
138 composite.getRankMax().addSelectionChangedListener(event->dirty.setDirty(true));
139 composite.getRankMax().setText("Select max rank...");
140 composite.getBtnRemoveRankMin().addSelectionListener(new SelectionAdapter() {
141 @Override
142 public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
143 composite.getRankMin().setElement(null);
144 dirty.setDirty(true);
145 }
146 });
147 composite.getBtnRemoveRankMax().addSelectionListener(new SelectionAdapter() {
148 @Override
149 public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
150 composite.getRankMax().setElement(null);
151 dirty.setDirty(true);
152 }
153 });
154
155 composite.getBtnChooseArea().addSelectionListener(new SelectionAdapter() {
156
157 @Override
158 public void widgetSelected(org.eclipse.swt.events.SelectionEvent e) {
159 AreasSelectionDialog areasSelectionDialog = new AreasSelectionDialog(composite.getShell(), composite.getAreas());
160 if(areasSelectionDialog.open()==Window.OK){
161 List<TermDto> selectedAreas = areasSelectionDialog.getSelectedAreas();
162 if(selectedAreas!=null){
163 composite.setAreas(selectedAreas);
164 dirty.setDirty(true);
165 }
166 }
167 }
168 });
169
170 composite.getFeatureTreeEditorComposite().init("Character Tree",
171 TermType.Character,
172 null,
173 null,
174 e->selectionService.setSelection(e.getSelection()),
175 this,
176 null);
177
178 //add drag'n'drop support
179 Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer()};
180 composite.getTaxonNodeTree().addDropSupport(dndOperations, transfers, new TaxonNodeDropAdapter(this));
181
182 composite.getTaxonNodeTree().getTree().addKeyListener(new KeyAdapter() {
183 @Override
184 public void keyPressed(KeyEvent e) {
185 if(e.character==SWT.DEL){
186 removeSelectedTaxonNodes();
187 }
188 }
189 });
190
191
192 //create context menu
193 menuService.registerContextMenu(composite.getTaxonNodeTree().getControl(), "eu.etaxonomy.taxeditor.editor.descriptiveDataSet.DescriptiveDataSetEditor.popupmenu.specimeneditor");
194
195 }
196
197 public void init(UUID descriptiveDataSetUuid) {
198 this.descriptiveDataSet = cdmEntitySession.remoteLoad(CdmStore.getService(IDescriptiveDataSetService.class), descriptiveDataSetUuid);
199 if(descriptiveDataSet.getLabel()!=null){
200 composite.getTxt_label().setText(descriptiveDataSet.getLabel());
201 }
202 if(descriptiveDataSet.getDescriptiveSystem()!=null){
203 composite.setCharacters(descriptiveDataSet.getDescriptiveSystem());
204 }
205 Rank maxRank = descriptiveDataSet.getMaxRank();
206 if(maxRank!=null){
207 composite.setRankMax(maxRank);
208 }
209 Rank minRank = descriptiveDataSet.getMinRank();
210 if(minRank!=null){
211 composite.setRankMin(minRank);
212 }
213 Set<NamedArea> geoFilter = descriptiveDataSet.getGeoFilter();
214 if(geoFilter!=null && !geoFilter.isEmpty()){
215 Set<TermDto> terms = geoFilter.stream().map(filter->TermDto.fromTerm(filter, true)).collect(Collectors.toSet());
216 composite.setAreas(new ArrayList<>(terms));
217 }
218 Set<TaxonNode> taxonSubtreeFilter = descriptiveDataSet.getTaxonSubtreeFilter();
219 if(taxonSubtreeFilter!=null){
220 composite.getTaxonNodeTree().setInput(taxonSubtreeFilter);
221 }
222 composite.getTxt_label().addModifyListener(labelModifyListener);
223 composite.getTxt_label().addFocusListener(new FocusAdapter() {
224 @Override
225 public void focusGained(FocusEvent e) {
226 super.focusGained(e);
227 selectionService.setSelection(new StructuredSelection(descriptiveDataSet));
228 }
229 });
230 selectionService.setSelection(new StructuredSelection(descriptiveDataSet));
231 }
232
233 public void addTaxonNode(TaxonNodeDto taxonNodeDto){
234 TaxonNode taxonNode = cdmEntitySession.remoteLoad(CdmStore.getService(ITaxonNodeService.class), taxonNodeDto.getUuid());
235 //check if node belongs to same classification
236 Set<TaxonNode> taxonSubtreeFilter = descriptiveDataSet.getTaxonSubtreeFilter();
237 if(taxonSubtreeFilter!=null && !taxonSubtreeFilter.isEmpty()){
238 if(!taxonSubtreeFilter.iterator().next().getClassification().equals(taxonNode.getClassification())){
239 MessagingUtils.warningDialog(Messages.TaxonNodeDropAdapter_CLASSIFICATIONS_NO_MATCH, this.getClass(),
240 Messages.TaxonNodeDropAdapter_CLASSIFICATIONS_NO_MATCH_MESSAGE);
241 return;
242 }
243 }
244 Viewer taxonTreeViewer = getTaxonTreeViewer();
245 Object input = taxonTreeViewer.getInput();
246 Collection<TaxonNode> treeNodes;
247 if(input==null){
248 treeNodes = new ArrayList<>();
249 }
250 else{
251 treeNodes = (Collection<TaxonNode>) input;
252 }
253 treeNodes.add(taxonNode);
254 taxonTreeViewer.setInput(treeNodes);
255 dirty.setDirty(true);
256 }
257
258 public void removeSelectedTaxonNodes(){
259 IStructuredSelection selection = (IStructuredSelection) composite.getTaxonNodeTree().getSelection();
260 if(selection.toList().stream().anyMatch(object->!(object instanceof TaxonNode))){
261 MessagingUtils.warningDialog(Messages.DescriptiveDataSetEditor_DELETE_FAIL_TITLE, this.getClass(), Messages.DescriptiveDataSetEditor_DELETE_FAIL_MESSAGE);
262 return;
263 }
264 Iterator<Object> iterator = selection.iterator();
265 Viewer taxonTreeViewer = getTaxonTreeViewer();
266 Collection<TaxonNode> input = (Collection<TaxonNode>) taxonTreeViewer.getInput();
267 while(iterator.hasNext()){
268 Object next = iterator.next();
269 TaxonNode taxonNode = (TaxonNode) next;
270 input.remove(taxonNode);
271 descriptiveDataSet.removeTaxonSubtree(taxonNode);
272 }
273 taxonTreeViewer.setInput(input);
274 dirty.setDirty(true);
275 }
276
277 @Persist
278 @Override
279 public void save(IProgressMonitor monitor) {
280 Collection<TermDto> areas = composite.getAreas();
281 Object input = composite.getTaxonNodeTree().getInput();
282 if(input!=null){
283 descriptiveDataSet.setTaxonSubtreeFilter(new HashSet<>());//clear existing filter
284 Collection<ITaxonTreeNode> taxonNodes = (Collection<ITaxonTreeNode>) input;
285 for (ITaxonTreeNode taxonTreeNode : taxonNodes) {
286 if(taxonTreeNode instanceof TaxonNode){
287 TaxonNode taxonNode = (TaxonNode)taxonTreeNode;
288 descriptiveDataSet.addTaxonSubtree(taxonNode);
289 }
290 }
291 }
292 FeatureTree<Character> characters = composite.getCharacters();
293 if(characters!=null){
294 //save characters because they can be modified in this editor
295 characters.getDistinctFeatures().forEach(character->CdmStore.getService(ITermService.class).merge(character,true));
296 }
297
298
299 DefinedTermBase rankMaxSelection = composite.getRankMax().getSelection();
300 Rank rankMax = null;
301 if(rankMaxSelection instanceof Rank){
302 rankMax = (Rank) rankMaxSelection;
303 }
304 DefinedTermBase rankMinSelection = composite.getRankMin().getSelection();
305 Rank rankMin = null;
306 if(rankMinSelection instanceof Rank){
307 rankMin = (Rank) rankMinSelection;
308 }
309
310 descriptiveDataSet.setMaxRank(rankMax);
311 descriptiveDataSet.setMinRank(rankMin);
312 descriptiveDataSet.setDescriptiveSystem(characters);
313 List<DefinedTermBase> terms = CdmStore.getService(ITermService.class)
314 .load(areas.stream().map(area -> area.getUuid()).collect(Collectors.toList()), null);
315 Set<NamedArea> areaTerms = new HashSet<>();
316 terms.forEach(term->areaTerms.add((NamedArea) term));
317 descriptiveDataSet.setGeoFilter(areaTerms);
318
319 conversation.commit();
320 CdmStore.getService(IDescriptiveDataSetService.class).merge(descriptiveDataSet, true);
321
322 dirty.setDirty(false);
323 }
324
325 @Override
326 public boolean isDirty() {
327 return dirty.isDirty();
328 }
329
330 @PreDestroy
331 public void dispose() {
332 if (conversation != null) {
333 conversation.close();
334 conversation = null;
335 }
336 if(cdmEntitySession != null) {
337 cdmEntitySession.dispose();
338 cdmEntitySession = null;
339 }
340 dirty.setDirty(false);
341 }
342
343 @Focus
344 public void setFocus() {
345 if(composite!=null){
346 composite.setFocus();
347 }
348 if (getConversationHolder() != null) {
349 getConversationHolder().bind();
350 }
351 if(cdmEntitySession != null) {
352 cdmEntitySession.bind();
353 }
354 }
355
356 public Viewer getTaxonTreeViewer() {
357 return composite.getTaxonNodeTree();
358 }
359
360 public DescriptiveDataSet getDescriptiveDataSet() {
361 return descriptiveDataSet;
362 }
363
364 @Override
365 public void update(CdmDataChangeMap arg0) {
366 }
367
368 @Override
369 public ICdmEntitySession getCdmEntitySession() {
370 return cdmEntitySession;
371 }
372
373 @Override
374 public Collection<DescriptiveDataSet> getRootEntities() {
375 return Collections.singleton(descriptiveDataSet);
376 }
377
378 @Override
379 public Map<Object, List<String>> getPropertyPathsMap() {
380 return null;
381 }
382
383 @Override
384 public ConversationHolder getConversationHolder() {
385 return conversation;
386 }
387
388 @Override
389 public void changed(Object element) {
390 dirty.setDirty(true);
391 }
392
393 @Override
394 public void forceDirty() {
395 dirty.setDirty(true);
396 }
397
398 }