Project

General

Profile

Download (19.1 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

    
10
package eu.etaxonomy.taxeditor.navigation.navigator.e4;
11

    
12
import java.util.ArrayList;
13
import java.util.Arrays;
14
import java.util.Comparator;
15
import java.util.HashMap;
16
import java.util.HashSet;
17
import java.util.List;
18
import java.util.Map;
19
import java.util.Observable;
20
import java.util.Observer;
21
import java.util.Set;
22
import java.util.UUID;
23

    
24
import javax.annotation.PostConstruct;
25
import javax.annotation.PreDestroy;
26
import javax.inject.Inject;
27

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

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

    
101
/**
102
 *
103
 * @author pplitzner
104
 * @since Sep 7, 2017
105
 *
106
 */
107
public class TaxonNavigatorE4 implements
108
		IPostOperationEnabled, IConversationEnabled, Observer,
109
		ICdmEntitySessionEnabled, ICdmChangeListener, IContextListener,
110
		ICollapsableExpandable {
111

    
112
    private static final String RESTORING_TAXON_NAVIGATOR = Messages.TaxonNavigator_RESTORE;
113

    
114
	private static final String TREE_PATH = "treepath"; //$NON-NLS-1$
115

    
116
	private static final String TREE_PATHS = "treepaths"; //$NON-NLS-1$
117

    
118
	private final int dndOperations = DND.DROP_MOVE;
119

    
120
	private ConversationHolder conversation;
121

    
122
	private ICdmEntitySession cdmEntitySession;
123

    
124
	private IDataChangeBehavior dataChangeBehavior;
125

    
126
	private Root root;
127

    
128
	private TreeViewer viewer;
129

    
130
    @Inject
131
    private ESelectionService selService;
132

    
133
    @Inject
134
    private UISynchronize sync;
135

    
136
    private ISelectionChangedListener selectionChangedListener;
137

    
138
    private UndoContext undoContext;
139

    
140
    @Inject
141
    private MApplication application;
142

    
143
    @Inject
144
    private EModelService modelService;
145

    
146
    @Inject
147
    private EPartService partService;
148

    
149
    private boolean linkWithTaxon = false;
150

    
151
    @Inject
152
    public TaxonNavigatorE4() {
153
	    undoContext = new UndoContext();
154
	    CdmStore.getContextManager().addContextListener(this);
155
    }
156

    
157
	@PostConstruct
158
	private void create(Composite parent, EMenuService menuService, IEclipseContext context){
159
	    FillLayout layout = new FillLayout();
160
	    layout.marginHeight = 0;
161
	    layout.marginWidth = 0;
162
	    layout.type = SWT.VERTICAL;
163

    
164
	    parent.setLayout(layout);
165
	    viewer = new TreeViewer(new Tree(parent, SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION | SWT.MULTI));
166
	    viewer.getControl().setLayoutData(LayoutConstants.FILL());
167

    
168
	    viewer.setContentProvider(new TaxonNavigatorContentProviderE4());
169
        viewer.setLabelProvider(new TaxonNavigatorLabelProviderE4());
170
        viewer.addDoubleClickListener(event->{
171
            ISelection selection = event.getSelection();
172
            if(selection instanceof IStructuredSelection){
173
                Object firstElement = ((IStructuredSelection) selection).getFirstElement();
174
                if(firstElement instanceof ICdmBase){
175
                    NavigationUtil.openEditor((ICdmBase) firstElement, viewer.getControl().getShell(), modelService, partService, application);
176
                }
177
                if(firstElement instanceof TaxonNodeDto){
178
                    NavigationUtil.openEditor((TaxonNodeDto) firstElement, viewer.getControl().getShell(), modelService, partService, application);
179
                }
180
            }
181
        });
182

    
183
        //propagate selection
184
        selectionChangedListener = (event -> selService.setSelection(event.getSelection()));
185
        viewer.addSelectionChangedListener(selectionChangedListener);
186

    
187
        //create context menu
188
        menuService.registerContextMenu(viewer.getControl(), AppModelId.POPUPMENU_EU_ETAXONOMY_TAXEDITOR_NAVIGATOR_POPUPMENU_TAXONNAVIGATOR );
189

    
190
        //add drag'n'drop support
191
        Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer()};
192
        viewer.addDragSupport(dndOperations, transfers, new TreeNodeDragListenerE4(viewer));
193
        TreeNodeDropAdapterE4 dropAdapter = new TreeNodeDropAdapterE4(this);
194
        ContextInjectionFactory.inject(dropAdapter, context);
195
        viewer.addDropSupport(dndOperations, transfers, dropAdapter);
196

    
197
        updateSyncButton();
198

    
199

    
200

    
201
	    init();
202
	}
203

    
204
    protected void updateSyncButton() {
205
        MPart viewPart = partService.findPart(AppModelId.PARTDESCRIPTOR_EU_ETAXONOMY_TAXEDITOR_NAVIGATION_NAVIGATOR);
206
        if(viewPart!=null){
207
            MToolBar toolBar = viewPart.getToolbar();
208

    
209
            List<MToolBarElement> toolBarElements = toolBar.getChildren();
210

    
211
            MToolBarElement upperHandledMenuItem = toolBarElements.get(1);
212
            if (upperHandledMenuItem instanceof HandledToolItemImpl){
213
                ((HandledToolItemImpl)upperHandledMenuItem).setSelected(linkWithTaxon);
214
            }
215
        }
216
    }
217

    
218
	/** {@inheritDoc} */
219
	protected IAdaptable getInitialInput() {
220
		Comparator<TaxonNodeDto> comparator;
221
		if (PreferencesUtil.getSortNodesNaturally()){
222
			comparator = new TaxonNodeDtoNaturalComparator();
223
		} else if (PreferencesUtil.getSortNodesStrictlyAlphabetically()){
224
			comparator = new TaxonNodeDtoByNameComparator();
225
		}else {
226
			comparator = new TaxonNodeDtoByRankAndNameComparator();
227
		}
228

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

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

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

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

    
261
		if (CdmStore.isActive()) {
262

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

    
269
			return root;
270
		}
271
		return new EmptyRoot();
272
	}
273

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

    
286
	}
287

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

    
307
    public void setLinkWithTaxon(boolean linkWithTaxon) {
308
        this.linkWithTaxon = linkWithTaxon;
309
    }
310

    
311
    public boolean isLinkWithTaxon() {
312
        return linkWithTaxon;
313
    }
314

    
315
	/**
316
	 * Refresh this navigators viewer
317
	 */
318
	public void refresh() {
319
		if(getConversationHolder() != null){
320
			getConversationHolder().bind();
321
			//FIXME : Need to make sure this is a stable fix (ticket 3822)
322
			if(!getConversationHolder().isCompleted()){
323
			    getConversationHolder().commit();
324
			}
325
		}
326
		if(!viewer.getTree().isDisposed()){
327
		    viewer.refresh();
328
		}
329

    
330
		updateSyncButton();
331
	}
332

    
333
	/**
334
     * Refresh this navigators viewer
335
     */
336
    public void refresh(Set<?> objects) {
337
        for(Object obj : objects) {
338
            viewer.refresh(obj);
339
        }
340
        updateSyncButton();
341
    }
342

    
343
    /**
344
     * Refresh this navigators viewer
345
     */
346
    public void refresh(Object object) {
347
        viewer.refresh(object);
348
        updateSyncButton();
349
    }
350

    
351
	/**
352
	 * Removes all content
353
	 */
354
	public void clear() {
355
	    viewer.setInput(new EmptyRoot());
356
	}
357

    
358
	private void restore(IMemento memento, IProgressMonitor monitor) {
359
	    root = new Root(conversation);
360
		if (memento == null) {
361
		    viewer.setInput(root);
362
			return;
363
		}
364
		int mementoWork = 0;
365
		Set<TreePath> treePaths = new HashSet<TreePath>();
366
		IMemento[] treePathMementos = null;
367

    
368
		IMemento treePathsMemento = memento.getChild(TREE_PATHS);
369

    
370
		if (treePathsMemento != null) {
371
			treePathMementos = treePathsMemento.getChildren(TREE_PATH);
372
			mementoWork = treePathMementos.length;
373
		}
374
		// begin the monitor with steps for all tree paths and steps for
375
		// creating
376
		// conversation s.o., refreshing the tree and setting the paths
377
		IProgressMonitor subProgressMonitor = AbstractUtility
378
				.getSubProgressMonitor(monitor, 1);
379

    
380
		subProgressMonitor.beginTask(RESTORING_TAXON_NAVIGATOR,
381
				1 + mementoWork + 5);
382
		subProgressMonitor.subTask(RESTORING_TAXON_NAVIGATOR);
383
		subProgressMonitor.worked(1);
384

    
385
		conversation = CdmStore.createConversation();
386
		subProgressMonitor.worked(1);
387
		conversation.registerForDataStoreChanges(TaxonNavigatorE4.this);
388
		subProgressMonitor.worked(1);
389
		viewer.setInput(root);
390
		subProgressMonitor.worked(1);
391
		viewer.refresh();
392
		subProgressMonitor.worked(1);
393

    
394
		if (treePathMementos != null && treePathMementos.length > 0) {
395
			for (IMemento treePathMemento : treePathMementos) {
396
				TreePath treePath = createTreePathFromString(treePathMemento
397
						.getID());
398
				if (!subProgressMonitor.isCanceled() && treePath != null) {
399
					treePaths.add(treePath);
400
					subProgressMonitor.worked(1);
401
				}
402
			}
403
		}
404
		if (treePaths.size() > 0) {
405
			TaxonNavigatorE4.this.viewer.setExpandedTreePaths(
406
					treePaths.toArray(new TreePath[0]));
407
			subProgressMonitor.worked(1);
408
		}
409
		subProgressMonitor.done();
410
	}
411

    
412
	private TreePath createTreePathFromString(String string) {
413

    
414
		List<CdmBase> pathList = new ArrayList<CdmBase>();
415

    
416
		if (string.length() == 0) {
417
            return null;
418
        }
419

    
420
		for (String uuid : string.split(" ")) { //$NON-NLS-1$
421
			CdmBase cdmBaseObject = CdmStore.getService(
422
					ITaxonNodeService.class).find(
423
					UUID.fromString(uuid));
424
			if (cdmBaseObject == null) {
425
				// is this a tree uuid?
426
				cdmBaseObject = CdmStore.getService(
427
						IClassificationService.class).load(
428
						UUID.fromString(uuid));
429

    
430
				if (cdmBaseObject == null) {
431
                    return null;
432
                }
433
			}
434
			pathList.add(cdmBaseObject);
435
		}
436
		return new TreePath(pathList.toArray());
437
	}
438

    
439
	/**
440
	 * {@inheritDoc}
441
	 */
442
	@Override
443
	public void collapse() {
444
	    viewer.collapseAll();
445
	}
446

    
447
	/**
448
	 * {@inheritDoc}
449
	 */
450
	@Override
451
	public void expand() {
452
	    viewer.expandAll();
453
	}
454

    
455
	@Override
456
	public ConversationHolder getConversationHolder() {
457
		return conversation;
458
	}
459

    
460
	/** {@inheritDoc} */
461
	@PreDestroy
462
	public void dispose() {
463
		dataChangeBehavior = null;
464
		if (conversation != null) {
465
			conversation.unregisterForDataStoreChanges(this);
466
			conversation.close();
467
		}
468
		if(cdmEntitySession != null) {
469
		    cdmEntitySession.dispose();
470
		    cdmEntitySession = null;
471
		}
472
		if(CdmApplicationState.getCurrentDataChangeService() != null) {
473
		    CdmApplicationState.getCurrentDataChangeService().unregister(this);
474
		}
475
	}
476

    
477
	/** {@inheritDoc} */
478
	@Focus
479
	public void setFocus() {
480
		if (getConversationHolder() != null) {
481
			getConversationHolder().bind();
482
		}
483
		if(cdmEntitySession != null) {
484
		    cdmEntitySession.bind();
485
		}
486
	}
487

    
488
    public UISynchronize getSync() {
489
        return sync;
490
    }
491

    
492
    public TreeViewer getViewer() {
493
        return viewer;
494
    }
495

    
496
    public UndoContext getUndoContext() {
497
        return undoContext;
498
    }
499

    
500
	/** {@inheritDoc} */
501
	@Override
502
	public boolean postOperation(Object objectAffectedByOperation) {
503
	    viewer.refresh();
504
		return true;
505
	}
506

    
507
	@Override
508
	public boolean onComplete() {
509
		return true;
510
	}
511

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

    
518
	}
519
	   /** {@inheritDoc} */
520
    @Override
521
    public void update(CdmDataChangeMap changeEvents) {
522
        if (dataChangeBehavior == null) {
523
            dataChangeBehavior = new TaxonNavigatorDataChangeBehaviorE4(this);
524
        }
525

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

    
529
    }
530

    
531
    @Override
532
    public ICdmEntitySession getCdmEntitySession() {
533
       return cdmEntitySession;
534
    }
535

    
536
    @Override
537
    public List<TaxonNodeDto> getRootEntities() {
538
        if(root != null) {
539
            return root.getParentBeans();
540
        }
541
        return null;
542
    }
543

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

    
566
    @Override
567
    public Map<Object, List<String>> getPropertyPathsMap() {
568
        Map<Object, List<String>> propertyPathsMap = new HashMap<Object, List<String>>();
569
        List<String> taxonNodePropertyPaths = Arrays.asList(new String[] {
570
                "taxon.name" //$NON-NLS-1$
571
        });
572
         propertyPathsMap.put("childNodes", taxonNodePropertyPaths); //$NON-NLS-1$
573
        return propertyPathsMap;
574
    }
575

    
576
    /**
577
     * {@inheritDoc}
578
     */
579
    @Override
580
    public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
581
        // TODO Auto-generated method stub
582

    
583
    }
584

    
585
    /**
586
     * {@inheritDoc}
587
     */
588
    @Override
589
    public void contextStop(IMemento memento, IProgressMonitor monitor) {
590
    }
591

    
592
    /**
593
     * {@inheritDoc}
594
     */
595
    @Override
596
    public void contextStart(IMemento memento, IProgressMonitor monitor) {
597
        if(viewer!=null && viewer.getControl()!=null && !viewer.getControl().isDisposed()){
598
            init();
599
        }
600
    }
601

    
602
    /**
603
     * {@inheritDoc}
604
     */
605
    @Override
606
    public void contextRefresh(IProgressMonitor monitor) {
607
    }
608

    
609
    /**
610
     * {@inheritDoc}
611
     */
612
    @Override
613
    public void workbenchShutdown(IMemento memento, IProgressMonitor monitor) {
614
    }
615

    
616
    @Inject
617
    @Optional
618
    private void updateView(@UIEventTopic(WorkbenchEventConstants.REFRESH_NAVIGATOR)boolean refresh){
619
        if(refresh){
620
            refresh();
621
        }
622
    }
623
}
(3-3/6)