Project

General

Profile

Download (20.6 KB) Statistics
| Branch: | Tag: | Revision:
1
package eu.etaxonomy.taxeditor.editor.view.derivate;
2

    
3
import java.util.ArrayList;
4
import java.util.Arrays;
5
import java.util.Collection;
6
import java.util.HashMap;
7
import java.util.HashSet;
8
import java.util.List;
9
import java.util.Map;
10
import java.util.Set;
11
import java.util.UUID;
12

    
13
import org.eclipse.core.runtime.IProgressMonitor;
14
import org.eclipse.jface.action.MenuManager;
15
import org.eclipse.jface.util.LocalSelectionTransfer;
16
import org.eclipse.jface.viewers.AbstractTreeViewer;
17
import org.eclipse.jface.viewers.ISelection;
18
import org.eclipse.jface.viewers.IStructuredSelection;
19
import org.eclipse.jface.viewers.StructuredSelection;
20
import org.eclipse.jface.viewers.TreeNode;
21
import org.eclipse.jface.viewers.TreeSelection;
22
import org.eclipse.jface.viewers.TreeViewer;
23
import org.eclipse.swt.SWT;
24
import org.eclipse.swt.dnd.DND;
25
import org.eclipse.swt.dnd.Transfer;
26
import org.eclipse.swt.layout.GridData;
27
import org.eclipse.swt.layout.GridLayout;
28
import org.eclipse.swt.widgets.Composite;
29
import org.eclipse.swt.widgets.Control;
30
import org.eclipse.swt.widgets.Menu;
31
import org.eclipse.swt.widgets.Tree;
32
import org.eclipse.ui.IEditorInput;
33
import org.eclipse.ui.IEditorPart;
34
import org.eclipse.ui.IEditorSite;
35
import org.eclipse.ui.IMemento;
36
import org.eclipse.ui.ISelectionListener;
37
import org.eclipse.ui.ISelectionService;
38
import org.eclipse.ui.IWorkbenchPart;
39
import org.eclipse.ui.PartInitException;
40
import org.eclipse.ui.part.EditorPart;
41

    
42
import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
43
import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
44
import eu.etaxonomy.cdm.api.service.IOccurrenceService;
45
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
46
import eu.etaxonomy.cdm.model.common.CdmBase;
47
import eu.etaxonomy.cdm.model.molecular.SingleRead;
48
import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
49
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
50
import eu.etaxonomy.cdm.model.taxon.Taxon;
51
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
52
import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
53
import eu.etaxonomy.taxeditor.editor.EditorUtil;
54
import eu.etaxonomy.taxeditor.editor.Messages;
55
import eu.etaxonomy.taxeditor.editor.MultiPageTaxonEditor;
56
import eu.etaxonomy.taxeditor.editor.view.derivate.searchFilter.DerivateSearchCompositeController;
57
import eu.etaxonomy.taxeditor.model.IContextListener;
58
import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
59
import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
60
import eu.etaxonomy.taxeditor.model.IPartContentHasFactualData;
61
import eu.etaxonomy.taxeditor.model.IPartContentHasMedia;
62
import eu.etaxonomy.taxeditor.model.IPartContentHasSupplementalData;
63
import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
64
import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
65
import eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled;
66
import eu.etaxonomy.taxeditor.store.CdmStore;
67
import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateContentProvider;
68
import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateLabelProvider;
69

    
70
/**
71
 * Displays the derivate hierarchy of the specimen specified in the editor input.
72
 *
73
 */
74
public class DerivateView extends EditorPart implements IPartContentHasFactualData, IConversationEnabled,
75
        ICdmEntitySessionEnabled, IDirtyMarkable, IPostOperationEnabled, IPartContentHasDetails, IPartContentHasSupplementalData, IPartContentHasMedia,
76
        IContextListener, ISelectionListener {
77
    public static final String ID = "eu.etaxonomy.taxeditor.editor.view.derivate.DerivateView"; //$NON-NLS-1$
78

    
79
    public static final String YOU_NEED_TO_SAVE_BEFORE_PERFORMING_THIS_ACTION = Messages.DerivateView_YOU_NEED_TO_SAVE;
80
    public static final String VIEW_HAS_UNSAVED_CHANGES = Messages.DerivateView_UNSAVED_CHANGES;
81

    
82
    private static final List<String> SPECIMEN_INIT_STRATEGY = Arrays.asList(new String[] {
83
            "descriptions",
84
            "annotations",
85
            "markers",
86
            "credits",
87
            "extensions",
88
            "rights",
89
            "sources",
90
            "derivationEvents.derivatives.annotations",
91
            "derivationEvents.derivatives.markers",
92
            "derivationEvents.derivatives.credits",
93
            "derivationEvents.derivatives.extensions",
94
            "derivationEvents.derivatives.rights",
95
            "derivationEvents.derivatives.sources"
96
    });
97

    
98
	private ConversationHolder conversation;
99

    
100
	private TreeViewer viewer;
101

    
102
    private boolean isDirty;
103

    
104
    private final int dndOperations = DND.DROP_MOVE;
105

    
106
    private DerivateLabelProvider labelProvider;
107

    
108
    private DerivateContentProvider contentProvider;
109

    
110
    private DerivateSearchCompositeController derivateSearchCompositeController;
111

    
112
    /**
113
     * A map with keys being the derivative entities belonging to the {@link UUID}s passed to the constructor
114
     * and values being the root elements of the hierarchy (may be the same objects as the derivative entities)
115
     */
116
    private Map<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>> derivateToRootEntityMap;
117

    
118
    /**
119
     * The set of root elements
120
     */
121
    private Set<SpecimenOrObservationBase<?>> rootElements;
122

    
123
    private ICdmEntitySession cdmEntitySession;
124

    
125
    /**
126
     * <code>true</code> if this view is listening to selection  changes
127
     */
128
    private boolean listenToSelectionChange;
129

    
130
    private Taxon selectedTaxon;
131

    
132
    private ISelectionService selectionService;
133

    
134
    /**
135
     * Default constructor
136
     */
137
    public DerivateView() {
138
    }
139

    
140

    
141
    /**
142
     * {@inheritDoc}
143
     */
144
    @Override
145
    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
146
        this.setSite(site);
147
        this.setInput(input);
148
        this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
149
        this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
150

    
151
        if (CdmStore.isActive() && conversation == null) {
152
            conversation = CdmStore.createConversation();
153
        }
154
        if (CdmStore.isActive()) {
155
            cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
156
        }
157
        //listen to context changes
158
        CdmStore.getContextManager().addContextListener(this);
159
    }
160

    
161
    @Override
162
    public void createPartControl(Composite parent) {
163

    
164
        parent.setLayout(new GridLayout());
165

    
166
        //---search and filter---
167
        derivateSearchCompositeController = new DerivateSearchCompositeController(parent, this);
168
        GridData gridDataSearchBar = new GridData();
169
        gridDataSearchBar.horizontalAlignment = GridData.FILL;
170
        gridDataSearchBar.grabExcessHorizontalSpace = true;
171
        derivateSearchCompositeController.setLayoutData(gridDataSearchBar);
172
        derivateSearchCompositeController.setEnabled(CdmStore.isActive());
173

    
174
        //---tree viewer---
175
        viewer = new TreeViewer(new Tree(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION));
176
        GridData gridDataTree = new GridData();
177
        gridDataTree.horizontalAlignment = GridData.FILL;
178
        gridDataTree.verticalAlignment = GridData.FILL;
179
        gridDataTree.grabExcessVerticalSpace = true;
180
        gridDataTree.grabExcessHorizontalSpace = true;
181
        viewer.getTree().setLayoutData(gridDataTree);
182
        contentProvider = new DerivateContentProvider();
183
        viewer.setContentProvider(contentProvider);
184
        labelProvider = new DerivateLabelProvider();
185
        labelProvider.setConversation(conversation);
186
        viewer.setLabelProvider(labelProvider);
187
        viewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
188
        viewer.getTree().setEnabled(CdmStore.isActive());
189
        // Propagate selection from viewer
190
        getSite().setSelectionProvider(viewer);
191

    
192
        //listen to selection changes
193
        selectionService = getSite().getWorkbenchWindow().getSelectionService();
194
        selectionService.addSelectionListener(this);
195

    
196
        //create context menu
197
        MenuManager menuManager = new MenuManager();
198
        menuManager.setRemoveAllWhenShown(true);
199
        getSite().registerContextMenu(menuManager, viewer);
200
        Control control = viewer.getControl();
201
        Menu menu = menuManager.createContextMenu(control);
202
        control.setMenu(menu);
203

    
204
        //init tree
205
        updateRootEntities(((DerivateViewEditorInput)getEditorInput()).getDerivativeUuids());
206
        //set taxon filter
207
        derivateSearchCompositeController.setTaxonFilter(((DerivateViewEditorInput) getEditorInput()).getTaxonUuid());
208
        //reset status bar
209
        getEditorSite().getActionBars().getStatusLineManager().setMessage("");
210

    
211
        //add drag'n'drop support
212
        Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer(),};
213
        viewer.addDragSupport(dndOperations, transfers, new DerivateDragListener(this));
214
        viewer.addDropSupport(dndOperations, transfers, new DerivateDropListener(this));
215
    }
216

    
217
    public void updateRootEntities() {
218
        updateRootEntities((Collection)null);
219
    }
220

    
221
    public void updateRootEntities(Collection<UUID> derivativeUuids) {
222
        if(conversation!=null){
223
            if (!conversation.isBound()) {
224
                conversation.bind();
225
            }
226
            /*
227
             * If the active session is not the session of the Derivative Editor
228
             * then we will save the active session for later, bind temporarily
229
             * to our session and rebind to the original session when we are
230
             * done. This happens e.g. if a selection change happens in the
231
             * taxon editor and "Link with editor" is enabled. The selection
232
             * change event and thus the loading in updateRootEntities() happens
233
             * in the session of the taxon editor.
234
             */
235
            ICdmEntitySession previousCdmEntitySession = CdmStore.getCurrentSessionManager().getActiveSession();
236
            if(cdmEntitySession != null) {
237
                cdmEntitySession.bind();
238
            }
239

    
240
            List<SpecimenOrObservationBase> derivates = null;
241
            if(derivativeUuids!=null){
242
                this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
243
                this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
244
                derivates = CdmStore.getService(IOccurrenceService.class).load(new ArrayList(derivativeUuids), SPECIMEN_INIT_STRATEGY);
245
            }
246
            updateRootEntities(derivates);
247
            previousCdmEntitySession.bind();
248
        }
249
    }
250

    
251

    
252
    public void updateRootEntities(List<SpecimenOrObservationBase> derivates) {
253
            if(derivates!=null){
254
                this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
255
                this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
256
                for (SpecimenOrObservationBase derivate : derivates) {
257

    
258
                    if(derivate instanceof FieldUnit){
259
                        derivateToRootEntityMap.put(derivate, derivate);
260
                    }
261
                    else {
262
                        SpecimenOrObservationBase<?> topMostDerivate = EditorUtil.getTopMostDerivate(derivate);
263
                        if(topMostDerivate!=null){
264
                            derivateToRootEntityMap.put(derivate, topMostDerivate);
265
                        }
266
                        else{
267
                            derivateToRootEntityMap.put(derivate, derivate);
268
                        }
269
                    }
270
                }
271
                for (SpecimenOrObservationBase<?> specimen : derivateToRootEntityMap.values()) {
272
                    rootElements.add(specimen);
273
                }
274
            }
275
            labelProvider.updateLabelCache(rootElements);
276
            viewer.setInput(rootElements);
277

    
278
            getEditorSite().getActionBars().getStatusLineManager().setMessage(rootElements.size() +" derivative hierarchies found");
279

    
280
            //set selection to derivatives if the filter criteria
281
            //taxon assignment or derivative type are set
282
            if(derivates!=null && !derivateSearchCompositeController.isDefaultSearch()){
283
                List<TreeNode> nodesToSelect = new ArrayList<TreeNode>();
284
                for (SpecimenOrObservationBase specimenOrObservationBase : derivates) {
285
                    nodesToSelect.add(new TreeNode(specimenOrObservationBase));
286
                }
287
                viewer.setSelection(new StructuredSelection(nodesToSelect));
288
            }
289
            else{
290
                viewer.setSelection(null);
291
            }
292

    
293
    }
294

    
295
    public void updateLabelCache(){
296
        labelProvider.updateLabelCache(rootElements);
297
    }
298

    
299
    @Override
300
    public void doSave(IProgressMonitor monitor) {
301
        String taskName = Messages.DerivateView_SAVING_HIERARCHY;
302
        monitor.beginTask(taskName, 3);
303
        if (!conversation.isBound()) {
304
            conversation.bind();
305
        }
306
        monitor.worked(1);
307

    
308
        // commit the conversation and start a new transaction immediately
309
        conversation.commit(true);
310

    
311
        if(CdmStore.getCurrentSessionManager().isRemoting()) {
312
            CdmStore.getService(IOccurrenceService.class).merge(new ArrayList<SpecimenOrObservationBase>(rootElements), true);
313
        }
314
        monitor.worked(1);
315

    
316
        this.setDirty(false);
317
        monitor.worked(1);
318
        monitor.done();
319
        firePropertyChange(PROP_DIRTY);
320
        refreshTree();
321
    }
322

    
323
    @Override
324
    public void doSaveAs() {
325
    }
326

    
327
    @Override
328
    public String getTitleToolTip() {
329
        return Messages.DerivateView_DERIVATIVE_EDITOR;
330
    }
331

    
332
    @Override
333
    public boolean isDirty() {
334
        return isDirty;
335
    }
336

    
337
    /**
338
     * @param isDirty the isDirty to set
339
     */
340
    public void setDirty(boolean isDirty) {
341
        this.isDirty = isDirty;
342
    }
343

    
344
    @Override
345
    public boolean isSaveAsAllowed() {
346
        return false;
347
    }
348

    
349
    @Override
350
    public void setFocus() {
351
        viewer.getControl().setFocus();
352
        //make sure to bind again if maybe in another view the conversation was unbound
353
        if(conversation!=null && !conversation.isBound()){
354
            conversation.bind();
355
        }
356
        if(cdmEntitySession != null) {
357
            cdmEntitySession.bind();
358
        }
359
    }
360

    
361
    @Override
362
    public void update(CdmDataChangeMap changeEvents) {
363
    }
364

    
365
    @Override
366
    public ConversationHolder getConversationHolder() {
367
        return conversation;
368
    }
369

    
370
    @Override
371
    public void changed(Object element) {
372
        setDirty(true);
373
        firePropertyChange(IEditorPart.PROP_DIRTY);
374
        viewer.update(new TreeNode(element), null);
375
    }
376

    
377
    @Override
378
    public void forceDirty() {
379
        changed(null);
380
    }
381

    
382
    @Override
383
    public Map<Object, List<String>> getPropertyPathsMap() {
384
        List<String> specimenPropertyPaths = Arrays.asList(new String[] {
385
                "descriptions",
386
                "derivationEvents.derivates",
387
                "annotations",
388
                "markers",
389
                "credits",
390
                "extensions",
391
                "rights",
392
                "sources"
393
        });
394
        Map<Object, List<String>> specimenPropertyPathMap =
395
                new HashMap<Object, List<String>>();
396
        specimenPropertyPathMap.put(SpecimenOrObservationBase.class,specimenPropertyPaths);
397
        return specimenPropertyPathMap;
398
    }
399

    
400
    /**
401
     * Refreshes the derivate hierarchy tree and expands the tree
402
     * to show and select the given object.
403
     *
404
     * @param expandTo the object to which the tree should be expanded
405
     */
406
    public void refreshTree(Object expandTo){
407
        refreshTree();
408
        TreeSelection selection = (TreeSelection) viewer.getSelection();
409
        viewer.expandToLevel(selection.getFirstElement(), 1);
410
        viewer.setSelection(new StructuredSelection(new TreeNode(expandTo)));
411
    }
412

    
413
    /**
414
     * Refreshes the derivate hierarchy tree
415
     */
416
    public void refreshTree(){
417
        viewer.refresh();
418
    }
419

    
420
    //FIXME:Remoting hack to make this work for remoting
421
    //This should actually be resolved using remoting post operations
422
    public void remove(Object obj) {
423
        rootElements.remove(obj);
424
        viewer.setInput(rootElements);
425
    }
426

    
427
    /**
428
     * @return a set of {@link SingleRead}s that have multiple parents
429
     */
430
    public Set<SingleRead> getMultiLinkSingleReads() {
431
        return DerivateLabelProvider.getMultiLinkSingleReads();
432
    }
433

    
434
    public Object getSelectionInput() {
435
        return selectedTaxon;
436
    }
437

    
438
    public DerivateLabelProvider getLabelProvider() {
439
        return labelProvider;
440
    }
441

    
442
    @Override
443
    public boolean postOperation(CdmBase objectAffectedByOperation) {
444
        refreshTree();
445
        if(objectAffectedByOperation!=null){
446
            changed(objectAffectedByOperation);
447
        }
448
        return true;
449
    }
450

    
451
    @Override
452
    public boolean onComplete() {
453
        return true;
454
    }
455

    
456

    
457
    @Override
458
    public boolean canAttachMedia() {
459
        return true;
460
    }
461

    
462
    public void addFieldUnit(FieldUnit fieldUnit) {
463
        rootElements.add(fieldUnit);
464
        derivateToRootEntityMap.put(fieldUnit, fieldUnit);
465
    }
466

    
467
    @Override
468
    public ICdmEntitySession getCdmEntitySession() {
469
        return cdmEntitySession;
470
    }
471

    
472
    @Override
473
    public void dispose() {
474
        super.dispose();
475
        if(conversation!=null){
476
            conversation.close();
477
        }
478
        if(cdmEntitySession != null) {
479
            cdmEntitySession.dispose();
480
        }
481
    }
482

    
483
    @Override
484
    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
485
        if(part == this){
486
            return;
487
        }
488
        if(viewer.getTree().isDisposed()){
489
            return;
490
        }
491
        if(listenToSelectionChange){
492
            if(part instanceof MultiPageTaxonEditor){
493
                selectedTaxon = ((MultiPageTaxonEditor) part).getTaxon();
494
            }
495
            else if(selection instanceof IStructuredSelection){
496
                Object selectedElement = ((IStructuredSelection) selection).getFirstElement();
497
                if(selectedElement instanceof CdmBase){
498
                    if(((CdmBase) selectedElement).isInstanceOf(TaxonNode.class)){
499
                        selectedTaxon = HibernateProxyHelper.deproxy(selectedElement, TaxonNode.class).getTaxon();
500
                    }
501
                    else if(((CdmBase) selectedElement).isInstanceOf(Taxon.class)){
502
                        selectedTaxon = HibernateProxyHelper.deproxy(selectedElement, Taxon.class);
503
                    }
504
                }
505
            }
506
            if(selectedTaxon!=null){
507
                Collection<SpecimenOrObservationBase> fieldUnits = CdmStore.getService(IOccurrenceService.class).listFieldUnitsByAssociatedTaxon(selectedTaxon, null, null);
508
                Collection<UUID> uuids = new HashSet<UUID>();
509
                for (SpecimenOrObservationBase specimenOrObservationBase : fieldUnits) {
510
                    uuids.add(specimenOrObservationBase.getUuid());
511
                }
512
                updateRootEntities(uuids);
513
                setPartName("Derivative Editor: " + selectedTaxon.getName());
514
            }
515
        }
516
    }
517

    
518
    public TreeViewer getViewer() {
519
        return viewer;
520
    }
521

    
522
    /**
523
     * {@inheritDoc}
524
     */
525
    @Override
526
    public List<SpecimenOrObservationBase<?>> getRootEntities() {
527
        return new ArrayList<SpecimenOrObservationBase<?>>(rootElements);
528
    }
529

    
530
    public void toggleListenToSelectionChange() {
531
        listenToSelectionChange = !listenToSelectionChange;
532
        derivateSearchCompositeController.setEnabled(!listenToSelectionChange);
533
        if(!listenToSelectionChange){
534
            selectedTaxon = null;
535
            setPartName("Derivative Editor");
536
        }
537
        else if(selectedTaxon==null){
538
            setPartName("Derivative Editor [no taxon selected]");
539
        }
540
    }
541

    
542
    public boolean isListenToSelectionChange(){
543
        return listenToSelectionChange;
544
    }
545

    
546
    /**
547
     * {@inheritDoc}
548
     */
549
    @Override
550
    public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
551
    }
552

    
553
    /**
554
     * {@inheritDoc}
555
     */
556
    @Override
557
    public void contextStop(IMemento memento, IProgressMonitor monitor) {
558
        derivateSearchCompositeController.setEnabled(false);
559
        if(!viewer.getTree().isDisposed()) {
560
            viewer.getTree().setEnabled(false);
561
            viewer.setInput(null);
562
        }
563
    }
564

    
565
    /**
566
     * {@inheritDoc}
567
     */
568
    @Override
569
    public void contextStart(IMemento memento, IProgressMonitor monitor) {
570
        derivateSearchCompositeController.setEnabled(!listenToSelectionChange);
571
        if(!viewer.getTree().isDisposed()){
572
            viewer.getTree().setEnabled(true);
573
        }
574
        refreshTree();
575
    }
576

    
577
    /**
578
     * {@inheritDoc}
579
     */
580
    @Override
581
    public void contextRefresh(IProgressMonitor monitor) {
582
    }
583

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

    
591
}
(3-3/6)