Project

General

Profile

Download (18.8 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.di.annotations.Optional;
32
import org.eclipse.e4.ui.di.Focus;
33
import org.eclipse.e4.ui.di.UIEventTopic;
34
import org.eclipse.e4.ui.di.UISynchronize;
35
import org.eclipse.e4.ui.model.application.MApplication;
36
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
37
import org.eclipse.e4.ui.model.application.ui.menu.MToolBar;
38
import org.eclipse.e4.ui.model.application.ui.menu.MToolBarElement;
39
import org.eclipse.e4.ui.model.application.ui.menu.impl.HandledToolItemImpl;
40
import org.eclipse.e4.ui.services.EMenuService;
41
import org.eclipse.e4.ui.workbench.modeling.EModelService;
42
import org.eclipse.e4.ui.workbench.modeling.EPartService;
43
import org.eclipse.e4.ui.workbench.modeling.ESelectionService;
44
import org.eclipse.jface.util.LocalSelectionTransfer;
45
import org.eclipse.jface.viewers.IElementComparer;
46
import org.eclipse.jface.viewers.ISelection;
47
import org.eclipse.jface.viewers.ISelectionChangedListener;
48
import org.eclipse.jface.viewers.IStructuredSelection;
49
import org.eclipse.jface.viewers.StructuredSelection;
50
import org.eclipse.jface.viewers.TreePath;
51
import org.eclipse.jface.viewers.TreeViewer;
52
import org.eclipse.swt.SWT;
53
import org.eclipse.swt.dnd.DND;
54
import org.eclipse.swt.dnd.Transfer;
55
import org.eclipse.swt.layout.FillLayout;
56
import org.eclipse.swt.widgets.Composite;
57
import org.eclipse.swt.widgets.Tree;
58
import org.eclipse.ui.IMemento;
59

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

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

    
110
    private static final String RESTORING_TAXON_NAVIGATOR = Messages.TaxonNavigator_RESTORE;
111

    
112
	private static final String TREE_PATH = "treepath"; //$NON-NLS-1$
113

    
114
	private static final String TREE_PATHS = "treepaths"; //$NON-NLS-1$
115

    
116
	private final int dndOperations = DND.DROP_MOVE;
117

    
118
	private ConversationHolder conversation;
119

    
120
	private ICdmEntitySession cdmEntitySession;
121

    
122
	private IDataChangeBehavior dataChangeBehavior;
123

    
124
	private Root root;
125

    
126
	private TreeViewer viewer;
127

    
128
    @Inject
129
    private ESelectionService selService;
130

    
131
    @Inject
132
    private UISynchronize sync;
133

    
134
    private ISelectionChangedListener selectionChangedListener;
135

    
136
    private UndoContext undoContext;
137

    
138
    @Inject
139
    private MApplication application;
140

    
141
    @Inject
142
    private EModelService modelService;
143

    
144
    @Inject
145
    private EPartService partService;
146

    
147
    private boolean linkWithTaxon = false;
148

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

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

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

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

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

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

    
188
        //add drag'n'drop support
189
        Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer()};
190
        viewer.addDragSupport(dndOperations, transfers, new TreeNodeDragListenerE4(viewer));
191
        viewer.addDropSupport(dndOperations, transfers, new TreeNodeDropAdapterE4(this));
192

    
193
        updateSyncButton();
194

    
195

    
196

    
197
	    init();
198
	}
199

    
200
    protected void updateSyncButton() {
201
        MPart viewPart = partService.findPart(AppModelId.PARTDESCRIPTOR_EU_ETAXONOMY_TAXEDITOR_NAVIGATION_NAVIGATOR);
202
        if(viewPart!=null){
203
            MToolBar toolBar = viewPart.getToolbar();
204

    
205
            List<MToolBarElement> toolBarElements = toolBar.getChildren();
206

    
207
            MToolBarElement upperHandledMenuItem = toolBarElements.get(1);
208
            if (upperHandledMenuItem instanceof HandledToolItemImpl){
209
                ((HandledToolItemImpl)upperHandledMenuItem).setSelected(linkWithTaxon);
210
            }
211
        }
212
    }
213

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

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

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

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

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

    
257
		if (CdmStore.isActive()) {
258

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

    
265
			return root;
266
		}
267
		return new EmptyRoot();
268
	}
269

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

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

    
302
    public void setLinkWithTaxon(boolean linkWithTaxon) {
303
        this.linkWithTaxon = linkWithTaxon;
304
    }
305

    
306
    public boolean isLinkWithTaxon() {
307
        return linkWithTaxon;
308
    }
309

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

    
325
		updateSyncButton();
326
	}
327

    
328
	/**
329
     * Refresh this navigators viewer
330
     */
331
    public void refresh(Set<?> objects) {
332
        for(Object obj : objects) {
333
            viewer.refresh(obj);
334
        }
335
        updateSyncButton();
336
    }
337

    
338
    /**
339
     * Refresh this navigators viewer
340
     */
341
    public void refresh(Object object) {
342
        viewer.refresh(object);
343
        updateSyncButton();
344
    }
345

    
346
	/**
347
	 * Removes all content
348
	 */
349
	public void clear() {
350
	    viewer.setInput(new EmptyRoot());
351
	}
352

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

    
363
		IMemento treePathsMemento = memento.getChild(TREE_PATHS);
364

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

    
375
		subProgressMonitor.beginTask(RESTORING_TAXON_NAVIGATOR,
376
				1 + mementoWork + 5);
377
		subProgressMonitor.subTask(RESTORING_TAXON_NAVIGATOR);
378
		subProgressMonitor.worked(1);
379

    
380
		conversation = CdmStore.createConversation();
381
		subProgressMonitor.worked(1);
382
		conversation.registerForDataStoreChanges(TaxonNavigatorE4.this);
383
		subProgressMonitor.worked(1);
384
		viewer.setInput(root);
385
		subProgressMonitor.worked(1);
386
		viewer.refresh();
387
		subProgressMonitor.worked(1);
388

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

    
407
	private TreePath createTreePathFromString(String string) {
408

    
409
		List<CdmBase> pathList = new ArrayList<CdmBase>();
410

    
411
		if (string.length() == 0) {
412
            return null;
413
        }
414

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

    
425
				if (cdmBaseObject == null) {
426
                    return null;
427
                }
428
			}
429
			pathList.add(cdmBaseObject);
430
		}
431
		return new TreePath(pathList.toArray());
432
	}
433

    
434
	/**
435
	 * {@inheritDoc}
436
	 */
437
	@Override
438
	public void collapse() {
439
	    viewer.collapseAll();
440
	}
441

    
442
	/**
443
	 * {@inheritDoc}
444
	 */
445
	@Override
446
	public void expand() {
447
	    viewer.expandAll();
448
	}
449

    
450
	@Override
451
	public ConversationHolder getConversationHolder() {
452
		return conversation;
453
	}
454

    
455
	/** {@inheritDoc} */
456
	@PreDestroy
457
	public void dispose() {
458
		dataChangeBehavior = null;
459
		if (conversation != null) {
460
			conversation.unregisterForDataStoreChanges(this);
461
			conversation.close();
462
		}
463
		if(cdmEntitySession != null) {
464
		    cdmEntitySession.dispose();
465
		    cdmEntitySession = null;
466
		}
467
		if(CdmApplicationState.getCurrentDataChangeService() != null) {
468
		    CdmApplicationState.getCurrentDataChangeService().unregister(this);
469
		}
470
	}
471

    
472
	/** {@inheritDoc} */
473
	@Focus
474
	public void setFocus() {
475
		if (getConversationHolder() != null) {
476
			getConversationHolder().bind();
477
		}
478
		if(cdmEntitySession != null) {
479
		    cdmEntitySession.bind();
480
		}
481
	}
482

    
483
    public UISynchronize getSync() {
484
        return sync;
485
    }
486

    
487
    public TreeViewer getViewer() {
488
        return viewer;
489
    }
490

    
491
    public UndoContext getUndoContext() {
492
        return undoContext;
493
    }
494

    
495
	/** {@inheritDoc} */
496
	@Override
497
	public boolean postOperation(CdmBase objectAffectedByOperation) {
498
	    viewer.refresh();
499
		return true;
500
	}
501

    
502
	@Override
503
	public boolean onComplete() {
504
		return true;
505
	}
506

    
507
	@Override
508
	public void update(Observable o, Object arg) {
509
		if(o instanceof LoginManager){
510
			refresh();
511
		}
512

    
513
	}
514
	   /** {@inheritDoc} */
515
    @Override
516
    public void update(CdmDataChangeMap changeEvents) {
517
        if (dataChangeBehavior == null) {
518
            dataChangeBehavior = new TaxonNavigatorDataChangeBehaviorE4(this);
519
        }
520

    
521
        DataChangeBridge.handleDataChange(changeEvents, dataChangeBehavior);
522
        updateSyncButton();
523

    
524
    }
525

    
526
    @Override
527
    public ICdmEntitySession getCdmEntitySession() {
528
       return cdmEntitySession;
529
    }
530

    
531
    @Override
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
        refresh();
542
        for(CdmBase cb : event.getChangedObjects()) {
543
        	if(cb instanceof TaxonNode) {
544
                TaxonNode tn = (TaxonNode)cb;
545
                if(tn.getTaxon() == null) {
546
                    viewer.refresh(tn.getClassification());
547
                } else {
548
                    viewer.refresh(cb);
549
                }
550
            } else if (cb instanceof Classification) {
551
                if ( event.getAction().equals(Action.Create)){
552
                    root.addRootNode((Classification)cb);
553
                } else if ( event.getAction().equals(Action.Delete)){
554
                    root.removeRootNode((Classification)cb);
555
                }
556
                viewer.refresh();
557
            }
558
        }
559
    }
560

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

    
571
    /**
572
     * {@inheritDoc}
573
     */
574
    @Override
575
    public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
576
        // TODO Auto-generated method stub
577

    
578
    }
579

    
580
    /**
581
     * {@inheritDoc}
582
     */
583
    @Override
584
    public void contextStop(IMemento memento, IProgressMonitor monitor) {
585
    }
586

    
587
    /**
588
     * {@inheritDoc}
589
     */
590
    @Override
591
    public void contextStart(IMemento memento, IProgressMonitor monitor) {
592
        if(viewer!=null && viewer.getControl()!=null && !viewer.getControl().isDisposed()){
593
            init();
594
        }
595
    }
596

    
597
    /**
598
     * {@inheritDoc}
599
     */
600
    @Override
601
    public void contextRefresh(IProgressMonitor monitor) {
602
    }
603

    
604
    /**
605
     * {@inheritDoc}
606
     */
607
    @Override
608
    public void workbenchShutdown(IMemento memento, IProgressMonitor monitor) {
609
    }
610

    
611
    @Inject
612
    @Optional
613
    private void updateView(@UIEventTopic(WorkbenchEventConstants.REFRESH_NAVIGATOR)boolean refresh){
614
        if(refresh){
615
            refresh();
616
        }
617
    }
618
}
(3-3/6)