Merge branch 'hotfix/5.18.2'
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / featuretree / e4 / AbstractTermTreeEditor.java
1 /**
2 * Copyright (C) 2007 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
10 package eu.etaxonomy.taxeditor.featuretree.e4;
11
12 import java.util.ArrayList;
13 import java.util.Arrays;
14 import java.util.HashMap;
15 import java.util.List;
16 import java.util.Map;
17 import java.util.UUID;
18
19 import javax.annotation.PostConstruct;
20 import javax.annotation.PreDestroy;
21 import javax.inject.Inject;
22
23 import org.eclipse.core.runtime.IProgressMonitor;
24 import org.eclipse.e4.core.di.annotations.Optional;
25 import org.eclipse.e4.ui.di.Focus;
26 import org.eclipse.e4.ui.di.Persist;
27 import org.eclipse.e4.ui.di.UIEventTopic;
28 import org.eclipse.e4.ui.di.UISynchronize;
29 import org.eclipse.e4.ui.model.application.ui.MDirtyable;
30 import org.eclipse.e4.ui.model.application.ui.basic.MPart;
31 import org.eclipse.e4.ui.services.EMenuService;
32 import org.eclipse.e4.ui.workbench.modeling.EPartService;
33 import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
34 import org.eclipse.jface.util.LocalSelectionTransfer;
35 import org.eclipse.jface.viewers.ISelection;
36 import org.eclipse.jface.viewers.ISelectionChangedListener;
37 import org.eclipse.jface.viewers.IStructuredSelection;
38 import org.eclipse.jface.viewers.SelectionChangedEvent;
39 import org.eclipse.jface.viewers.StructuredSelection;
40 import org.eclipse.jface.viewers.TreeViewer;
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.KeyAdapter;
45 import org.eclipse.swt.events.KeyEvent;
46 import org.eclipse.swt.layout.FillLayout;
47 import org.eclipse.swt.widgets.Composite;
48 import org.eclipse.ui.IMemento;
49
50 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
51 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
52 import eu.etaxonomy.cdm.api.service.ITermNodeService;
53 import eu.etaxonomy.cdm.api.service.ITermTreeService;
54 import eu.etaxonomy.cdm.model.term.DefinedTermBase;
55 import eu.etaxonomy.cdm.model.term.TermNode;
56 import eu.etaxonomy.cdm.persistence.dto.TermDto;
57 import eu.etaxonomy.cdm.persistence.dto.TermNodeDto;
58 import eu.etaxonomy.cdm.persistence.dto.TermTreeDto;
59 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
60 import eu.etaxonomy.taxeditor.editor.definedterm.FeatureTreeViewerComparator;
61 import eu.etaxonomy.taxeditor.editor.definedterm.TermTransfer;
62 import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
63 import eu.etaxonomy.taxeditor.featuretree.FeatureNodeDtoTransfer;
64 import eu.etaxonomy.taxeditor.featuretree.FeatureTreeContentProvider;
65 import eu.etaxonomy.taxeditor.featuretree.FeatureTreeLabelProvider;
66 import eu.etaxonomy.taxeditor.featuretree.e4.operation.AddFeatureOperation;
67 import eu.etaxonomy.taxeditor.model.AbstractUtility;
68 import eu.etaxonomy.taxeditor.model.IContextListener;
69 import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
70 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
71 import eu.etaxonomy.taxeditor.model.IPartContentHasSupplementalData;
72 import eu.etaxonomy.taxeditor.operation.AbstractPostOperation;
73 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
74 import eu.etaxonomy.taxeditor.store.AppModelId;
75 import eu.etaxonomy.taxeditor.store.CdmStore;
76 import eu.etaxonomy.taxeditor.store.StoreUtil;
77 import eu.etaxonomy.taxeditor.workbench.part.IE4ViewerPart;
78
79 /**
80 *
81 * @author pplitzner
82 * @date 06.06.2017
83 *
84 */
85 public abstract class AbstractTermTreeEditor<T extends DefinedTermBase> implements IFeatureTreeEditor, ISelectionChangedListener,
86 IE4ViewerPart, IPartContentHasDetails, IPartContentHasSupplementalData,
87 IContextListener, IConversationEnabled, IDirtyMarkable {
88
89 public static final List<String> TREE_PROPERTY_PATH = Arrays.asList(new String[] {
90 "root", //$NON-NLS-1$
91 "root.children", //$NON-NLS-1$
92 "root.children.inapplicableIf", //$NON-NLS-1$
93 "root.children.inapplicableIf.feature", //$NON-NLS-1$
94 "root.children.inapplicableIf.state", //$NON-NLS-1$
95 "root.children.onlyApplicableIf", //$NON-NLS-1$
96 "root.children.onlyApplicableIf.feature", //$NON-NLS-1$
97 "root.children.onlyApplicableIf.state", //$NON-NLS-1$
98 });
99
100 private ConversationHolder conversation;
101
102 private ICdmEntitySession cdmEntitySession;
103
104 @Inject
105 private ESelectionService selService;
106
107 @Inject
108 private MDirtyable dirty;
109
110 @Inject
111 private UISynchronize sync;
112
113 @Inject
114 private MPart thisPart;
115
116 private TreeViewer viewer;
117
118 Map<UUID,TermTreeDto> trees;
119
120 Map<UUID, TermNodeDto> uuidTermMap = new HashMap<>();
121 List<AbstractPostOperation<TermNode>> operationList = new ArrayList<>();
122 List<TermNodeDto> listToUpdate = new ArrayList<>();
123
124 @Inject
125 public AbstractTermTreeEditor() {
126 CdmStore.getContextManager().addContextListener(this);
127 }
128
129 @PostConstruct
130 public void createControl(Composite parent, EMenuService menuService){
131 if (CdmStore.isActive()){
132 initSession();
133 }
134 else{
135 return;
136 }
137 parent.setLayout(new FillLayout());
138 viewer = new TreeViewer(parent);
139 FeatureTreeContentProvider contentProvider = new FeatureTreeContentProvider();
140
141 viewer.setContentProvider(contentProvider);
142 viewer.setLabelProvider(new FeatureTreeLabelProvider());
143
144 int ops = DND.DROP_COPY | DND.DROP_MOVE;
145 Transfer[] transfers = new Transfer[] {
146 FeatureNodeDtoTransfer.getInstance(),
147 TermTransfer.getInstance(),
148 LocalSelectionTransfer.getTransfer()};
149 viewer.addDragSupport(ops, transfers, new FeatureNodeDtoDragListener(viewer));
150 viewer.addDropSupport(ops, transfers, new FeatureTreeDtoDropAdapter(this, viewer, sync));
151 viewer.addSelectionChangedListener(this);
152 viewer.getTree().addKeyListener(new KeyAdapter() {
153 @Override
154 public void keyPressed(KeyEvent e) {
155 if(e.stateMask == SWT.MOD1 && e.keyCode == 'c'){
156 copy(viewer.getStructuredSelection());
157 }
158 else if(e.stateMask == SWT.MOD1 && e.keyCode == 'v'){
159 paste(viewer.getStructuredSelection());
160 }
161 }
162 });
163
164 updateTrees();
165 viewer.setComparator(new FeatureTreeViewerComparator());
166 viewer.setInput(getTrees());
167 contentProvider.setUuidTermMap(uuidTermMap);
168 //create context menu
169 menuService.registerContextMenu(viewer.getControl(), AppModelId.POPUPMENU_EU_ETAXONOMY_TAXEDITOR_STORE_POPUPMENU_FEATURETREEEDITOR);
170 }
171
172 // protected abstract List<TermTreeDto> getTrees();
173
174 public void paste(IStructuredSelection selection) {
175 if (StoreUtil.promptCheckIsDirty(this)) {
176 return;
177 }
178
179 ISelection clipBoardSelection = LocalSelectionTransfer.getTransfer().getSelection();
180 Object firstElement = selection.getFirstElement();
181 TermNodeDto parentNode = null;
182 if(firstElement instanceof TermNodeDto){
183 parentNode = (TermNodeDto) firstElement;
184 }
185 else if(firstElement instanceof TermTreeDto){
186 parentNode = ((TermTreeDto)firstElement).getRoot();
187 }
188 if(parentNode!=null){
189 TermNodeDto copiedNode = (TermNodeDto) ((IStructuredSelection)clipBoardSelection).getFirstElement();
190
191 AddFeatureOperation operation = new AddFeatureOperation(copiedNode.getUuid(), parentNode, this, this);
192 // AbstractUtility.executeOperation(operation, sync);
193 this.addOperation(operation);
194 }
195
196 }
197
198 public void copy(IStructuredSelection selection) {
199 LocalSelectionTransfer.getTransfer().setSelection(selection);
200 }
201
202 private void initSession(){
203 if(conversation == null){
204 conversation = CdmStore.createConversation();
205 }
206 if(cdmEntitySession==null){
207 cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
208 }
209 }
210
211 private void clearSession() {
212 if(conversation!=null){
213 conversation.close();
214 conversation = null;
215 }
216 if(cdmEntitySession != null) {
217 cdmEntitySession.dispose();
218 cdmEntitySession = null;
219 }
220 dirty.setDirty(false);
221 }
222
223 public void setDirty(boolean isDirty){
224 this.dirty.setDirty(isDirty);
225 }
226 @Override
227 public void setDirty(){
228 this.dirty.setDirty(true);
229 }
230
231 @Override
232 public boolean isDirty(){
233 return dirty.isDirty();
234 }
235
236 @Override
237 public void selectionChanged(SelectionChangedEvent event) {
238 //propagate selection
239 selService.setSelection(event.getSelection());
240 }
241
242 @Focus
243 public void focus(){
244 if(viewer!=null){
245 viewer.getControl().setFocus();
246 }
247 if(conversation!=null && !conversation.isBound()){
248 conversation.bind();
249 }
250 if(cdmEntitySession != null) {
251 cdmEntitySession.bind();
252 }
253 }
254
255 @Override
256 public void refresh(){
257 viewer.refresh();
258 }
259
260 @Override
261 public TreeViewer getViewer(){
262 return viewer;
263 }
264
265 @Override
266 public IStructuredSelection getSelection() {
267 return (IStructuredSelection) viewer.getSelection();
268 }
269
270 @Override
271 public ConversationHolder getConversationHolder() {
272 return conversation;
273 }
274
275 @Override
276 @Persist
277 public void save(IProgressMonitor monitor){
278 if (!conversation.isBound()) {
279 conversation.bind();
280 }
281
282 // commit the conversation and start a new transaction immediately
283 Object[] expandedElements = this.viewer.getExpandedElements();
284 conversation.commit(true);
285 if (operationList != null && !operationList.isEmpty()){
286 for (AbstractPostOperation<TermNode> operation: operationList){
287 AbstractUtility.executeOperation(operation, sync);
288 }
289 operationList.clear();
290 }
291
292 CdmStore.getService(ITermNodeService.class).saveTermNodeDtoList(listToUpdate);
293 listToUpdate.clear();
294 List<TermTreeDto> rootEntities = getRootEntities();
295 CdmStore.getService(ITermTreeService.class).saveOrUpdateTermTreeDtoList(rootEntities);
296 initializeTrees();
297 conversation.commit(true);
298
299 this.setDirty(false);
300 // this.viewer.setExpandedElements(expandedElements);
301 }
302
303 private void initializeTrees() {
304 Object[] expandedElements = viewer.getExpandedElements();
305 viewer.getTree().removeAll();
306 updateTrees();
307 viewer.setInput(getTrees());
308 viewer.setExpandedElements(expandedElements);
309 }
310
311 @PreDestroy
312 public void dispose(){
313 selService.setSelection(null);
314 clearSession();
315 }
316
317 @Override
318 public ICdmEntitySession getCdmEntitySession() {
319 return cdmEntitySession;
320 }
321
322 @Override
323 public Map<Object, List<String>> getPropertyPathsMap() {
324 List<String> propertyPaths = Arrays.asList(new String[] {
325 "children", //$NON-NLS-1$
326 "feature", //$NON-NLS-1$
327 "featureTree", //$NON-NLS-1$
328 });
329 Map<Object, List<String>> propertyPathMap =
330 new HashMap<Object, List<String>>();
331 propertyPathMap.put(TermNode.class,propertyPaths);
332 return propertyPathMap;
333 }
334
335 @Override
336 public List<TermTreeDto> getRootEntities() {
337 return (List<TermTreeDto>) viewer.getInput();
338 }
339
340 @Override
341 public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
342 }
343
344 @Override
345 public void contextStop(IMemento memento, IProgressMonitor monitor) {
346 //close view when workbench closes
347 try{
348 thisPart.getContext().get(EPartService.class).hidePart(thisPart);
349 }
350 catch(Exception e){
351 //nothing
352 }
353 }
354
355 @Override
356 public void contextStart(IMemento memento, IProgressMonitor monitor) {
357 }
358
359 @Override
360 public void contextRefresh(IProgressMonitor monitor) {
361 }
362
363 @Override
364 public void workbenchShutdown(IMemento memento, IProgressMonitor monitor) {
365 }
366
367 @Override
368 public void update(CdmDataChangeMap arg0) {
369 }
370
371 @Override
372 public void changed(Object element) {
373 dirty.setDirty(true);
374 viewer.refresh();
375 }
376
377 @Override
378 public void forceDirty() {
379 dirty.setDirty(true);
380 }
381
382 @Override
383 public boolean postOperation(Object objectAffectedByOperation) {
384 initializeTrees();
385 viewer.refresh();
386 if(objectAffectedByOperation instanceof TermNodeDto){
387 TermNodeDto node = (TermNodeDto)objectAffectedByOperation;
388 // viewer.expandToLevel(((TermRelationBase) node).getGraph(), 1); find a solution for dto editor
389 }
390 if(objectAffectedByOperation!=null){
391 StructuredSelection selection = new StructuredSelection(objectAffectedByOperation);
392 viewer.setSelection(selection);
393 }
394 return true;
395 }
396
397 @Override
398 public boolean onComplete() {
399 return false;
400 }
401
402 @Override
403 public TermNodeDto getNodeDtoForUuid(UUID nodeUuid){
404 return uuidTermMap.get(nodeUuid);
405 }
406
407 protected void addAllNodesToMap(TermNodeDto root){
408 if (!uuidTermMap.containsKey(root.getUuid())){
409 uuidTermMap.put(root.getUuid(), root);
410 if (root.getTerm() != null){
411 getTreeDtoForUuid(root.getTree().getUuid()).addTerm(root.getTerm());
412 }
413 }
414 for (TermNodeDto child: root.getChildren()){
415 uuidTermMap.put(child.getUuid(), child);
416 if (child.getTerm() != null){
417 getTreeDtoForUuid(child.getTree().getUuid()).addTerm(child.getTerm());
418 }
419 if (child.getChildren() != null && !child.getChildren().isEmpty()){
420 addAllNodesToMap(child);
421 }
422 }
423 }
424
425 /**
426 * @param term
427 * @param treeUuid
428 */
429 @Override
430 public boolean checkDuplicates(UUID termUuid, UUID treeUuid) {
431 TermTreeDto tree = this.getTreeDtoForUuid(treeUuid);
432 for (TermDto dto: tree.getTerms()){
433 if (dto.getUuid().equals(termUuid)) {
434 return true;
435 }
436 }
437 return false;
438 }
439
440 protected void updateTrees(){
441 uuidTermMap.clear();
442 if (trees != null && !trees.isEmpty()){
443 trees.clear();
444 }
445 if (trees == null){
446 trees = new HashMap<>();
447 }
448 List<TermTreeDto> treeList = CdmStore.getService(ITermTreeService.class).listTermTreeDtosByTermType(getTermType());
449 treeList.stream().forEach(tree -> trees.put(tree.getUuid(), tree));
450 // trees.addAll(CdmStore.getService(ITermTreeService.class).listTermTreeDtosByTermType(getTermType()));
451 for (TermTreeDto tree: trees.values()){
452 addAllNodesToMap(tree.getRoot());
453 }
454 ((FeatureTreeContentProvider<T>)viewer.getContentProvider()).setUuidTermMap(uuidTermMap);
455 // viewer.refresh();
456
457 }
458
459 public List<TermTreeDto> getTrees(){
460 List<TermTreeDto> treeList = new ArrayList<>(trees.values());
461 return treeList;
462 }
463
464 public void putTree(TermTreeDto tree){
465 trees.put(tree.getUuid(), tree);
466
467 }
468
469 public void removeTree(TermTreeDto tree){
470 trees.remove(tree.getUuid());
471
472 }
473
474 @Override
475 public TermTreeDto getTreeDtoForUuid(UUID treeUuid){
476 return trees.get(treeUuid);
477 }
478
479 @Override
480 public void setTreeDtoForUuid(TermTreeDto tree){
481 trees.put(tree.getUuid(), tree);
482 }
483
484 @Override
485 public void setNodeDtoForUuid(TermNodeDto node){
486 this.uuidTermMap.put(node.getUuid(), node);
487 }
488
489 @Override
490 public void addOperation(AbstractPostOperation operation) {
491 operationList.add(operation);
492 }
493
494 @Inject
495 @Optional
496 private void addSaveCandidate(@UIEventTopic(WorkbenchEventConstants.ADD_SAVE_CANDIDATE) UUID cdmbaseUuid) {
497 for (UUID uuid: uuidTermMap.keySet()) {
498 if (uuid.equals(cdmbaseUuid)){
499 listToUpdate.add(uuidTermMap.get(uuid));
500 }
501
502 }
503 }
504
505 }