Project

General

Profile

Download (19.5 KB) Statistics
| Branch: | Tag: | Revision:
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
package eu.etaxonomy.taxeditor.navigation.navigator.e4;
10

    
11
import java.util.ArrayList;
12
import java.util.Comparator;
13
import java.util.HashSet;
14
import java.util.List;
15
import java.util.Observable;
16
import java.util.Observer;
17
import java.util.Set;
18
import java.util.UUID;
19

    
20
import javax.annotation.PostConstruct;
21
import javax.annotation.PreDestroy;
22
import javax.inject.Inject;
23

    
24
import org.eclipse.core.commands.operations.UndoContext;
25
import org.eclipse.core.runtime.IAdaptable;
26
import org.eclipse.core.runtime.IProgressMonitor;
27
import org.eclipse.e4.core.contexts.ContextInjectionFactory;
28
import org.eclipse.e4.core.contexts.IEclipseContext;
29
import org.eclipse.e4.core.di.annotations.Optional;
30
import org.eclipse.e4.ui.di.Focus;
31
import org.eclipse.e4.ui.di.UIEventTopic;
32
import org.eclipse.e4.ui.di.UISynchronize;
33
import org.eclipse.e4.ui.model.application.MApplication;
34
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
35
import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
36
import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement;
37
import org.eclipse.e4.ui.model.application.ui.menu.impl.HandledToolItemImpl;
38
import org.eclipse.e4.ui.services.EMenuService;
39
import org.eclipse.e4.ui.workbench.modeling.EModelService;
40
import org.eclipse.e4.ui.workbench.modeling.EPartService;
41
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
42
import org.eclipse.jface.util.LocalSelectionTransfer;
43
import org.eclipse.jface.viewers.IElementComparer;
44
import org.eclipse.jface.viewers.ISelection;
45
import org.eclipse.jface.viewers.ISelectionChangedListener;
46
import org.eclipse.jface.viewers.IStructuredSelection;
47
import org.eclipse.jface.viewers.StructuredSelection;
48
import org.eclipse.jface.viewers.TreePath;
49
import org.eclipse.jface.viewers.TreeViewer;
50
import org.eclipse.swt.SWT;
51
import org.eclipse.swt.dnd.DND;
52
import org.eclipse.swt.dnd.Transfer;
53
import org.eclipse.swt.layout.FillLayout;
54
import org.eclipse.swt.widgets.Composite;
55
import org.eclipse.swt.widgets.Tree;
56
import org.eclipse.ui.IMemento;
57

    
58
import eu.etaxonomy.cdm.api.application.CdmApplicationState;
59
import eu.etaxonomy.cdm.api.application.CdmChangeEvent;
60
import eu.etaxonomy.cdm.api.application.CdmChangeEvent.Action;
61
import eu.etaxonomy.cdm.api.application.ICdmChangeListener;
62
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
63
import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
64
import eu.etaxonomy.cdm.api.service.IClassificationService;
65
import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
66
import eu.etaxonomy.cdm.model.common.CdmBase;
67
import eu.etaxonomy.cdm.model.common.ICdmBase;
68
import eu.etaxonomy.cdm.model.taxon.Classification;
69
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
70
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDto;
71
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDtoByNameComparator;
72
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDtoByRankAndNameComparator;
73
import eu.etaxonomy.cdm.persistence.dto.TaxonNodeDtoNaturalComparator;
74
import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
75
import eu.etaxonomy.taxeditor.editor.ITaxonEditor;
76
import eu.etaxonomy.taxeditor.editor.name.e4.TaxonNameEditorE4;
77
import eu.etaxonomy.taxeditor.event.EventUtility;
78
import eu.etaxonomy.taxeditor.event.WorkbenchEventConstants;
79
import eu.etaxonomy.taxeditor.model.AbstractUtility;
80
import eu.etaxonomy.taxeditor.model.DataChangeBridge;
81
import eu.etaxonomy.taxeditor.model.IContextListener;
82
import eu.etaxonomy.taxeditor.model.IDataChangeBehavior;
83
import eu.etaxonomy.taxeditor.navigation.AppModelId;
84
import eu.etaxonomy.taxeditor.navigation.NavigationUtil;
85
import eu.etaxonomy.taxeditor.navigation.l10n.Messages;
86
import eu.etaxonomy.taxeditor.navigation.navigator.EmptyRoot;
87
import eu.etaxonomy.taxeditor.navigation.navigator.Root;
88
import eu.etaxonomy.taxeditor.navigation.navigator.TaxonNodeNavigatorComparator;
89
import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
90
import eu.etaxonomy.taxeditor.preference.NavigatorOrderEnum;
91
import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
92
import eu.etaxonomy.taxeditor.store.CdmStore;
93
import eu.etaxonomy.taxeditor.store.LoginManager;
94
import eu.etaxonomy.taxeditor.ui.element.LayoutConstants;
95
import eu.etaxonomy.taxeditor.workbench.part.ICollapsableExpandable;
96

    
97
/**
98
 * @author pplitzner
99
 * @since Sep 7, 2017
100
 */
101
public class TaxonNavigatorE4 implements
102
		IPostOperationEnabled, IConversationEnabled, Observer,
103
		ICdmChangeListener, IContextListener,
104
		ICollapsableExpandable {
105

    
106
    private static final String RESTORING_TAXON_NAVIGATOR = Messages.TaxonNavigator_RESTORE;
107

    
108
	private static final String TREE_PATH = "treepath"; //$NON-NLS-1$
109

    
110
	private static final String TREE_PATHS = "treepaths"; //$NON-NLS-1$
111

    
112
	private final int dndOperations = DND.DROP_MOVE;
113

    
114
	private ConversationHolder conversation;
115

    
116
//	private ICdmEntitySession cdmEntitySession;
117

    
118
	private IDataChangeBehavior dataChangeBehavior;
119

    
120
	private Root root;
121

    
122
	private TreeViewer viewer;
123

    
124
    @Inject
125
    private ESelectionService selService;
126

    
127
    @Inject
128
    private UISynchronize sync;
129

    
130
    private ISelectionChangedListener selectionChangedListener;
131

    
132
    private UndoContext undoContext;
133

    
134
    @Inject
135
    private MApplication application;
136

    
137
    @Inject
138
    private EModelService modelService;
139

    
140
    @Inject
141
    private EPartService partService;
142

    
143
    private boolean linkWithTaxon = false;
144

    
145
    @Inject
146
    public TaxonNavigatorE4() {
147
	    undoContext = new UndoContext();
148
	    CdmStore.getContextManager().addContextListener(this);
149
    }
150

    
151
	@PostConstruct
152
	private void create(Composite parent, EMenuService menuService, IEclipseContext context){
153
	    FillLayout layout = new FillLayout();
154
	    layout.marginHeight = 0;
155
	    layout.marginWidth = 0;
156
	    layout.type = SWT.VERTICAL;
157

    
158
	    parent.setLayout(layout);
159
	    viewer = new TreeViewer(new Tree(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI));
160
	    viewer.getControl().setLayoutData(LayoutConstants.FILL());
161

    
162
	    viewer.setContentProvider(new TaxonNavigatorContentProviderE4());
163
        viewer.setLabelProvider(new TaxonNavigatorLabelProviderE4());
164
        viewer.addDoubleClickListener(event->{
165
            ISelection selection = event.getSelection();
166
            if(selection instanceof IStructuredSelection){
167
                Object firstElement = ((IStructuredSelection) selection).getFirstElement();
168
                if(firstElement instanceof ICdmBase){
169
                    NavigationUtil.openEditor((ICdmBase) firstElement, viewer.getControl().getShell(), modelService, partService, application);
170
                }
171
                if(firstElement instanceof TaxonNodeDto){
172
                    NavigationUtil.openEditor((TaxonNodeDto) firstElement, viewer.getControl().getShell(), modelService, partService, application);
173
                }
174
            }
175
        });
176

    
177
        //propagate selection
178
        selectionChangedListener = (event -> selService.setSelection(event.getSelection()));
179
        viewer.addSelectionChangedListener(selectionChangedListener);
180

    
181
        //create context menu
182
        menuService.registerContextMenu(viewer.getControl(), AppModelId.POPUPMENU_EU_ETAXONOMY_TAXEDITOR_NAVIGATOR_POPUPMENU_TAXONNAVIGATOR );
183

    
184
        //add drag'n'drop support
185
        Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer()};
186
        viewer.addDragSupport(dndOperations, transfers, new TreeNodeDragListenerE4(viewer));
187
        TreeNodeDropAdapterE4 dropAdapter = new TreeNodeDropAdapterE4(this);
188
        ContextInjectionFactory.inject(dropAdapter, context);
189
        viewer.addDropSupport(dndOperations, transfers, dropAdapter);
190

    
191
        updateSyncButton();
192

    
193
	    init();
194
	}
195

    
196
    protected void updateSyncButton() {
197
        MPart viewPart = partService.findPart(AppModelId.PARTDESCRIPTOR_EU_ETAXONOMY_TAXEDITOR_NAVIGATION_NAVIGATOR);
198
        if(viewPart!=null){
199
            MToolBar toolBar = viewPart.getToolbar();
200

    
201
            List<MToolBarElement> toolBarElements = toolBar.getChildren();
202

    
203
            MToolBarElement upperHandledMenuItem = toolBarElements.get(1);
204
            if (upperHandledMenuItem instanceof HandledToolItemImpl){
205
                ((HandledToolItemImpl)upperHandledMenuItem).setSelected(linkWithTaxon);
206
            }
207
        }
208
    }
209

    
210
	protected IAdaptable getInitialInput() {
211
		Comparator<TaxonNodeDto> comparator;
212
		NavigatorOrderEnum orderValue = NavigatorOrderEnum.RankAndNameOrder;
213
		try{
214
		    orderValue = PreferencesUtil.getSortNodes();
215
		}catch(IllegalArgumentException e){
216

    
217
		}
218

    
219
		if (orderValue.equals(NavigatorOrderEnum.NaturalOrder)){
220
			comparator = new TaxonNodeDtoNaturalComparator();
221
		} else if (orderValue.equals(NavigatorOrderEnum.AlphabeticalOrder)){
222
			comparator = new TaxonNodeDtoByNameComparator();
223
		}else {
224
			comparator = new TaxonNodeDtoByRankAndNameComparator();
225
		}
226

    
227
		TaxonNodeNavigatorComparator viewerComparator = new TaxonNodeNavigatorComparator(comparator);
228
		viewer.setComparator(viewerComparator);
229
		viewer.setComparer(new IElementComparer() {
230

    
231
	        @Override
232
            public int hashCode(Object element) {
233
	            if (element instanceof TaxonNodeDto){
234
    	            TaxonNodeDto nodeDto = (TaxonNodeDto)element;
235

    
236
    	            String s = nodeDto.getUuid().toString();
237
    	            if (s != null) {
238
    	                return s.hashCode();
239
    	            }
240
    	            return element.hashCode();
241
	            }else{
242
	                return element.toString().hashCode();
243
	            }
244
	        }
245

    
246
            @Override
247
            public boolean equals(Object element1, Object element2) {
248
                if (element1 instanceof TaxonNodeDto && element2 instanceof TaxonNodeDto){
249
                    TaxonNodeDto node1 = (TaxonNodeDto)element1;
250
                    TaxonNodeDto node2 = (TaxonNodeDto)element2;
251
                    return (node1.getUuid().equals(node2.getUuid()));
252
                }else {
253
                    return element1.equals(element2);
254
                }
255
            }
256
		}
257
		);
258

    
259
		if (CdmStore.isActive()) {
260

    
261
			// TODO when closing and reopening the taxon navigator
262
			// we do not preserve state. Closing the view, in contrary to
263
			// closing the whole application
264
			// should be handled by the state manager too
265
		    root = new Root(conversation);
266

    
267
			return root;
268
		}
269
		return new EmptyRoot();
270
	}
271

    
272
	public void init() {
273
		if (CdmStore.isActive() && conversation == null) {
274
			conversation = CdmStore.createConversation();
275
			conversation.registerForDataStoreChanges(TaxonNavigatorE4.this);
276
		}
277
		if (CdmStore.isActive()) {
278
//		    cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
279
		    CdmApplicationState.getCurrentDataChangeService().register(this);
280
		    viewer.setInput(getInitialInput());
281
		}
282
		CdmStore.getLoginManager().addObserver(this);
283

    
284
	}
285

    
286
	//Link with taxon selection
287
	@Inject
288
	@Optional
289
	public void updateCurrentTaxon(@UIEventTopic(WorkbenchEventConstants.CURRENT_ACTIVE_EDITOR)ITaxonEditor editor){
290
	    if(linkWithTaxon && editor!=null){
291
	        viewer.refresh();
292
	        TaxonNodeDto taxonNode = null;
293
	        if(editor.getTaxon()!=null && editor.getTaxon().getTaxonNodes()!=null){
294
	            if (editor instanceof TaxonNameEditorE4){
295
	                taxonNode = new TaxonNodeDto( ((TaxonNameEditorE4)editor).getEditorInput().getTaxonNode());
296
	            }else{
297
	                if (editor.getTaxon().getTaxonNodes() != null && !editor.getTaxon().getTaxonNodes().isEmpty()){
298
	                    taxonNode = new TaxonNodeDto(editor.getTaxon().getTaxonNodes().iterator().next());
299
	                }
300
	            }
301
                if (taxonNode != null){
302
                    viewer.reveal(taxonNode);
303
                    viewer.setSelection(new StructuredSelection(taxonNode));
304
                }else{
305
                    //TODO: show message in status bar
306
                }
307
	        }
308
	    }
309
	}
310

    
311
	@Inject
312
    @Optional
313
    public void updateLinkWithTaxon(@UIEventTopic(WorkbenchEventConstants.UPDATE_LINK_WITH_TAXON) boolean isDummy){
314
	    this.linkWithTaxon = !linkWithTaxon;
315

    
316
        if(this.isLinkWithTaxon()){
317
            this.updateCurrentTaxon(EventUtility.getTaxonEditor());
318
        }
319
	}
320

    
321
    public void setLinkWithTaxon(boolean linkWithTaxon) {
322
        this.linkWithTaxon = linkWithTaxon;
323
    }
324

    
325
    public boolean isLinkWithTaxon() {
326
        return linkWithTaxon;
327
    }
328

    
329
	/**
330
	 * Refresh this navigators viewer
331
	 */
332
	public void refresh() {
333
		if(getConversationHolder() != null){
334
			getConversationHolder().bind();
335
			//FIXME : Need to make sure this is a stable fix (ticket 3822)
336
			if(!getConversationHolder().isCompleted()){
337
			    getConversationHolder().commit();
338
			}
339
		}
340
		if(!viewer.getTree().isDisposed()){
341
		    if (CdmStore.isActive()){
342

    
343
		        viewer.setInput(getInitialInput());
344
		    }
345
		    viewer.refresh();
346
		}
347

    
348
		updateSyncButton();
349
	}
350

    
351
	/**
352
     * Refresh this navigators viewer
353
     */
354
    public void refresh(Set<?> objects) {
355
        for(Object obj : objects) {
356
            viewer.refresh(obj);
357
        }
358
        updateSyncButton();
359
    }
360

    
361
    /**
362
     * Refresh this navigators viewer
363
     */
364
    public void refresh(Object object) {
365
        viewer.refresh(object);
366
        updateSyncButton();
367
    }
368

    
369
	/**
370
	 * Removes all content
371
	 */
372
	public void clear() {
373
	    viewer.setInput(new EmptyRoot());
374
	}
375

    
376
	private void restore(IMemento memento, IProgressMonitor monitor) {
377
	    root = new Root(conversation);
378
		if (memento == null) {
379
		    viewer.setInput(root);
380
			return;
381
		}
382
		int mementoWork = 0;
383
		Set<TreePath> treePaths = new HashSet<>();
384
		IMemento[] treePathMementos = null;
385

    
386
		IMemento treePathsMemento = memento.getChild(TREE_PATHS);
387

    
388
		if (treePathsMemento != null) {
389
			treePathMementos = treePathsMemento.getChildren(TREE_PATH);
390
			mementoWork = treePathMementos.length;
391
		}
392
		// begin the monitor with steps for all tree paths and steps for
393
		// creating
394
		// conversation s.o., refreshing the tree and setting the paths
395
		IProgressMonitor subProgressMonitor = AbstractUtility
396
				.getSubProgressMonitor(monitor, 1);
397

    
398
		subProgressMonitor.beginTask(RESTORING_TAXON_NAVIGATOR,
399
				1 + mementoWork + 5);
400
		subProgressMonitor.subTask(RESTORING_TAXON_NAVIGATOR);
401
		subProgressMonitor.worked(1);
402

    
403
		conversation = CdmStore.createConversation();
404
		subProgressMonitor.worked(1);
405
		conversation.registerForDataStoreChanges(TaxonNavigatorE4.this);
406
		subProgressMonitor.worked(1);
407
		viewer.setInput(root);
408
		subProgressMonitor.worked(1);
409
		viewer.refresh();
410
		subProgressMonitor.worked(1);
411

    
412
		if (treePathMementos != null && treePathMementos.length > 0) {
413
			for (IMemento treePathMemento : treePathMementos) {
414
				TreePath treePath = createTreePathFromString(treePathMemento
415
						.getID());
416
				if (!subProgressMonitor.isCanceled() && treePath != null) {
417
					treePaths.add(treePath);
418
					subProgressMonitor.worked(1);
419
				}
420
			}
421
		}
422
		if (treePaths.size() > 0) {
423
			TaxonNavigatorE4.this.viewer.setExpandedTreePaths(
424
					treePaths.toArray(new TreePath[0]));
425
			subProgressMonitor.worked(1);
426
		}
427
		subProgressMonitor.done();
428
	}
429

    
430
	private TreePath createTreePathFromString(String string) {
431

    
432
		List<CdmBase> pathList = new ArrayList<>();
433

    
434
		if (string.length() == 0) {
435
            return null;
436
        }
437

    
438
		for (String uuid : string.split(" ")) { //$NON-NLS-1$
439
			CdmBase cdmBaseObject = CdmStore.getService(
440
					ITaxonNodeService.class).find(
441
					UUID.fromString(uuid));
442
			if (cdmBaseObject == null) {
443
				// is this a tree uuid?
444
				cdmBaseObject = CdmStore.getService(
445
						IClassificationService.class).load(
446
						UUID.fromString(uuid));
447

    
448
				if (cdmBaseObject == null) {
449
                    return null;
450
                }
451
			}
452
			pathList.add(cdmBaseObject);
453
		}
454
		return new TreePath(pathList.toArray());
455
	}
456

    
457
	@Override
458
	public void collapse() {
459
	    viewer.collapseAll();
460
	}
461

    
462
	@Override
463
	public void expand() {
464
	    viewer.expandAll();
465
	}
466

    
467
	@Override
468
	public ConversationHolder getConversationHolder() {
469
		return conversation;
470
	}
471

    
472
	@PreDestroy
473
	public void dispose() {
474
		dataChangeBehavior = null;
475
		if (conversation != null) {
476
			conversation.unregisterForDataStoreChanges(this);
477
			conversation.close();
478
		}
479
		if(CdmApplicationState.getCurrentDataChangeService() != null) {
480
		    CdmApplicationState.getCurrentDataChangeService().unregister(this);
481
		}
482
	}
483

    
484
	@Focus
485
	public void setFocus() {
486
		if (getConversationHolder() != null) {
487
			getConversationHolder().bind();
488
		}
489
	}
490

    
491
    public UISynchronize getSync() {
492
        return sync;
493
    }
494

    
495
    public TreeViewer getViewer() {
496
        return viewer;
497
    }
498

    
499
    public UndoContext getUndoContext() {
500
        return undoContext;
501
    }
502

    
503
	@Override
504
	public boolean postOperation(Object objectAffectedByOperation) {
505
	    viewer.refresh();
506
		return true;
507
	}
508

    
509
	@Override
510
	public boolean onComplete() {
511
		return true;
512
	}
513

    
514
	@Override
515
	public void update(Observable o, Object arg) {
516
		if(o instanceof LoginManager){
517
			refresh();
518
		}
519
	}
520

    
521
    @Override
522
    public void update(CdmDataChangeMap changeEvents) {
523
        if (dataChangeBehavior == null) {
524
            dataChangeBehavior = new TaxonNavigatorDataChangeBehaviorE4(this);
525
        }
526

    
527
        DataChangeBridge.handleDataChange(changeEvents, dataChangeBehavior);
528
        updateSyncButton();
529

    
530
    }
531

    
532
    public List<TaxonNodeDto> getRootEntities() {
533
        if(root != null) {
534
            return root.getParentBeans();
535
        }
536
        return null;
537
    }
538

    
539
    @Override
540
    public void onChange(CdmChangeEvent event) {
541
        Object[] expandedElements = viewer.getExpandedElements();
542
        refresh();
543
        for(CdmBase cb : event.getChangedObjects()) {
544
        	if(cb instanceof TaxonNode) {
545
                TaxonNode tn = (TaxonNode)cb;
546
                if(tn.getTaxon() == null) {
547
                    viewer.refresh(tn.getClassification());
548
                } else {
549
                    viewer.refresh(cb);
550
                }
551
            } else if (cb instanceof Classification) {
552
                if ( event.getAction().equals(Action.Create)){
553
                    root.addRootNode((Classification)cb);
554
                } else if ( event.getAction().equals(Action.Delete)){
555
                    root.removeRootNode((Classification)cb);
556
                }
557
                viewer.refresh();
558
            }
559
        }
560
        viewer.setExpandedElements(expandedElements);
561
    }
562

    
563
    @Override
564
    public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
565
        // TODO Auto-generated method stub
566
    }
567

    
568
    @Override
569
    public void contextStop(IMemento memento, IProgressMonitor monitor) {
570
    }
571

    
572
    @Override
573
    public void contextStart(IMemento memento, IProgressMonitor monitor) {
574
        if(viewer!=null && viewer.getControl()!=null && !viewer.getControl().isDisposed()){
575
            init();
576
        }
577
    }
578

    
579
    @Override
580
    public void contextRefresh(IProgressMonitor monitor) {
581
//        viewer.refresh();
582
    }
583

    
584
    @Override
585
    public void workbenchShutdown(IMemento memento, IProgressMonitor monitor) {
586
    }
587

    
588
    @Inject
589
    @Optional
590
    private void updateView(@UIEventTopic(WorkbenchEventConstants.REFRESH_NAVIGATOR)TaxonNodeDto dto){
591
        if(dto!= null){
592
            Object[] expandedElements = viewer.getExpandedElements();
593
            ISelection sel = viewer.getSelection();
594
            refresh(dto);
595
            viewer.setExpandedElements(expandedElements);
596
            if (sel != null && !sel.isEmpty()){
597
                viewer.reveal(dto);
598
                viewer.setSelection(sel);
599
            }
600
        }
601
    }
602

    
603
    @Inject
604
    @Optional
605
    private void updateView(@UIEventTopic(WorkbenchEventConstants.REFRESH_NAVIGATOR)boolean refresh){
606
        if(refresh){
607
            Object[] expandedElements = viewer.getExpandedElements();
608
            refresh();
609
            viewer.setExpandedElements(expandedElements);
610
        }
611
    }
612
}
(3-3/6)