Fix potential "Widget is disposed" exception
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / view / derivate / DerivateView.java
index f63569495046fc8e457fc53a4ccc93993c11607e..904bee8c81f7ebb58fd2e525e17857c4f83594f6 100644 (file)
@@ -7,7 +7,6 @@ import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Set;
 import java.util.UUID;
 
@@ -28,36 +27,45 @@ import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.Tree;
+import org.eclipse.ui.IEditorInput;
 import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorSite;
 import org.eclipse.ui.IMemento;
-import org.eclipse.ui.ISaveablePart2;
+import org.eclipse.ui.ISelectionListener;
+import org.eclipse.ui.ISelectionService;
 import org.eclipse.ui.IWorkbenchPart;
+import org.eclipse.ui.PartInitException;
+import org.eclipse.ui.part.EditorPart;
 
 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
+import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
 import eu.etaxonomy.cdm.api.service.IOccurrenceService;
-import eu.etaxonomy.cdm.api.service.molecular.ISequenceService;
 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.model.common.CdmBase;
-import eu.etaxonomy.cdm.model.molecular.Sequence;
 import eu.etaxonomy.cdm.model.molecular.SingleRead;
 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
 import eu.etaxonomy.cdm.model.taxon.Taxon;
+import eu.etaxonomy.cdm.model.taxon.TaxonNode;
 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
 import eu.etaxonomy.taxeditor.editor.EditorUtil;
 import eu.etaxonomy.taxeditor.editor.Messages;
 import eu.etaxonomy.taxeditor.editor.MultiPageTaxonEditor;
 import eu.etaxonomy.taxeditor.editor.view.derivate.searchFilter.DerivateSearchCompositeController;
 import eu.etaxonomy.taxeditor.model.IContextListener;
+import eu.etaxonomy.taxeditor.model.IDirtyMarkable;
 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
 import eu.etaxonomy.taxeditor.model.IPartContentHasFactualData;
 import eu.etaxonomy.taxeditor.model.IPartContentHasMedia;
 import eu.etaxonomy.taxeditor.model.IPartContentHasSupplementalData;
+import eu.etaxonomy.taxeditor.model.MessagingUtils;
+import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
+import eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled;
 import eu.etaxonomy.taxeditor.store.CdmStore;
-import eu.etaxonomy.taxeditor.view.AbstractCdmViewPart;
 import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateContentProvider;
 import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateLabelProvider;
 
@@ -65,8 +73,9 @@ import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateLabelProvider;
  * Displays the derivate hierarchy of the specimen specified in the editor input.
  *
  */
-public class DerivateView extends AbstractCdmViewPart implements IPartContentHasFactualData, ISaveablePart2,
-        IPartContentHasDetails, IPartContentHasSupplementalData, IPartContentHasMedia, IContextListener {
+public class DerivateView extends EditorPart implements IPartContentHasFactualData, IConversationEnabled,
+        ICdmEntitySessionEnabled, IDirtyMarkable, IPostOperationEnabled, IPartContentHasDetails, IPartContentHasSupplementalData, IPartContentHasMedia,
+        IContextListener, ISelectionListener {
     public static final String ID = "eu.etaxonomy.taxeditor.editor.view.derivate.DerivateView"; //$NON-NLS-1$
 
     public static final String YOU_NEED_TO_SAVE_BEFORE_PERFORMING_THIS_ACTION = Messages.DerivateView_YOU_NEED_TO_SAVE;
@@ -88,6 +97,50 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
             "derivationEvents.derivatives.sources"
     });
 
+       private static final int WARN_THRESHOLD = 200;
+
+    private DelaySelection delaySelection = null;
+    /**
+     * This is the monitor for the DelaySelection runnable.
+     * If it is <code>true</code> then it is currently delaying a selection.
+     */
+    private boolean isInDelay;
+
+
+    /**
+     * This class invokes internal_selectionChanged() in a separate thread.
+     * This allows an asynchronous and/or delayed handling of selection changes
+     */
+    private class DelaySelection implements Runnable{
+        private IWorkbenchPart part;
+        private ISelection selection;
+
+        public DelaySelection(IWorkbenchPart part, ISelection selection) {
+            super();
+            this.part = part;
+            this.selection = selection;
+        }
+
+        @Override
+        public void run() {
+            try{
+                selectionChanged_internal(part, selection);
+            }
+            finally{
+                isInDelay = false;
+            }
+        }
+
+        public synchronized void setSelection(ISelection selection) {
+            this.selection = selection;
+        }
+
+        public synchronized void setPart(IWorkbenchPart part) {
+            this.part = part;
+        }
+
+    }
+
        private ConversationHolder conversation;
 
        private TreeViewer viewer;
@@ -98,8 +151,6 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
 
     private DerivateLabelProvider labelProvider;
 
-    private Set<SingleRead> multiLinkSingleReads;
-
     private DerivateContentProvider contentProvider;
 
     private DerivateSearchCompositeController derivateSearchCompositeController;
@@ -124,17 +175,22 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
 
     private Taxon selectedTaxon;
 
+    private ISelectionService selectionService;
+
     /**
      * Default constructor
      */
     public DerivateView() {
-        init();
     }
 
+
     /**
-     *
+     * {@inheritDoc}
      */
-    private void init() {
+    @Override
+    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
+        this.setSite(site);
+        this.setInput(input);
         this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
         this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
 
@@ -155,11 +211,11 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
 
         //---search and filter---
         derivateSearchCompositeController = new DerivateSearchCompositeController(parent, this);
-        derivateSearchCompositeController.setEnabled(CdmStore.isActive());
         GridData gridDataSearchBar = new GridData();
         gridDataSearchBar.horizontalAlignment = GridData.FILL;
         gridDataSearchBar.grabExcessHorizontalSpace = true;
         derivateSearchCompositeController.setLayoutData(gridDataSearchBar);
+        derivateSearchCompositeController.setEnabled(CdmStore.isActive());
 
         //---tree viewer---
         viewer = new TreeViewer(new Tree(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION));
@@ -175,6 +231,7 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
         labelProvider.setConversation(conversation);
         viewer.setLabelProvider(labelProvider);
         viewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
+        viewer.getTree().setEnabled(CdmStore.isActive());
         // Propagate selection from viewer
         getSite().setSelectionProvider(viewer);
 
@@ -184,13 +241,20 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
 
         //create context menu
         MenuManager menuManager = new MenuManager();
+        menuManager.setRemoveAllWhenShown(true);
         getSite().registerContextMenu(menuManager, viewer);
         Control control = viewer.getControl();
         Menu menu = menuManager.createContextMenu(control);
         control.setMenu(menu);
 
         //init tree
-        updateRootEntities();
+        Collection<UUID> derivativeUuids = ((DerivateViewEditorInput)getEditorInput()).getDerivativeUuids();
+        checkWarnThreshold(derivativeUuids);
+               updateRootEntities(derivativeUuids);
+        //set taxon filter
+        derivateSearchCompositeController.setTaxonFilter(((DerivateViewEditorInput) getEditorInput()).getTaxonUuid());
+        //reset status bar
+        getEditorSite().getActionBars().getStatusLineManager().setMessage("");
 
         //add drag'n'drop support
         Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer(),};
@@ -199,7 +263,7 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
     }
 
     public void updateRootEntities() {
-        updateRootEntities(null);
+        updateRootEntities((Collection)null);
     }
 
     public void updateRootEntities(Collection<UUID> derivativeUuids) {
@@ -208,23 +272,37 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
                 conversation.bind();
             }
             /*
-             * If the active session is not the session of the Derivate Editor then we will
-             * save it, bind temporarily to our session and rebind to the original session.
-             * This happens e.g. if a selection change happens in the taxon editor and
-             * "Link with editor" is enabled. The selection change event and thus the
-             * loading in updateRootEntities() happens in the session of the taxon
-             * editor.
+             * If the active session is not the session of the Derivative Editor
+             * then we will save the active session for later, bind temporarily
+             * to our session and rebind to the original session when we are
+             * done. This happens e.g. if a selection change happens in the
+             * taxon editor and "Link with editor" is enabled. The selection
+             * change event and thus the loading in updateRootEntities() happens
+             * in the session of the taxon editor.
              */
             ICdmEntitySession previousCdmEntitySession = CdmStore.getCurrentSessionManager().getActiveSession();
             if(cdmEntitySession != null) {
                 cdmEntitySession.bind();
             }
 
+            List<SpecimenOrObservationBase> derivates = null;
             if(derivativeUuids!=null){
                 this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
                 this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
-                for (UUID uuid : derivativeUuids) {
-                    SpecimenOrObservationBase<?> derivate = CdmStore.getService(IOccurrenceService.class).load(uuid, SPECIMEN_INIT_STRATEGY);
+                derivates = CdmStore.getService(IOccurrenceService.class).load(new ArrayList(derivativeUuids), SPECIMEN_INIT_STRATEGY);
+            }
+            updateRootEntities(derivates);
+            previousCdmEntitySession.bind();
+        }
+    }
+
+
+    public void updateRootEntities(List<SpecimenOrObservationBase> derivates) {
+            if(derivates!=null){
+                this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
+                this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
+                for (SpecimenOrObservationBase derivate : derivates) {
+
                     if(derivate instanceof FieldUnit){
                         derivateToRootEntityMap.put(derivate, derivate);
                     }
@@ -242,10 +320,28 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
                     rootElements.add(specimen);
                 }
             }
+            labelProvider.updateLabelCache(rootElements);
             viewer.setInput(rootElements);
-            refreshTree();
-            previousCdmEntitySession.bind();
-        }
+
+            getEditorSite().getActionBars().getStatusLineManager().setMessage(rootElements.size() +" derivative hierarchies found");
+
+            //set selection to derivatives if the filter criteria
+            //taxon assignment or derivative type are set
+            if(derivates!=null && !derivateSearchCompositeController.isDefaultSearch()){
+                List<TreeNode> nodesToSelect = new ArrayList<TreeNode>();
+                for (SpecimenOrObservationBase specimenOrObservationBase : derivates) {
+                    nodesToSelect.add(new TreeNode(specimenOrObservationBase));
+                }
+                viewer.setSelection(new StructuredSelection(nodesToSelect));
+            }
+            else{
+                viewer.setSelection(null);
+            }
+
+    }
+
+    public void updateLabelCache(){
+        labelProvider.updateLabelCache(rootElements);
     }
 
     @Override
@@ -319,19 +415,11 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
         return conversation;
     }
 
-    /**
-     * @return the viewer
-     */
-    @Override
-    public TreeViewer getViewer() {
-        return viewer;
-    }
-
     @Override
     public void changed(Object element) {
         setDirty(true);
         firePropertyChange(IEditorPart.PROP_DIRTY);
-        viewer.refresh();
+        viewer.update(new TreeNode(element), null);
     }
 
     @Override
@@ -374,9 +462,9 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
      * Refreshes the derivate hierarchy tree
      */
     public void refreshTree(){
-        generateMultiLinkSingleReads();
-        labelProvider.setMultiLinkSingleReads(multiLinkSingleReads);
-        viewer.refresh();
+       if(!viewer.getTree().isDisposed()){
+               viewer.refresh();
+       }
     }
 
     //FIXME:Remoting hack to make this work for remoting
@@ -386,21 +474,11 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
         viewer.setInput(rootElements);
     }
 
-    private void generateMultiLinkSingleReads() {
-        Set<SingleRead> multiLinkSingleReads = new HashSet<SingleRead>();
-        for(Entry<SingleRead, Collection<Sequence>> entry:CdmStore.getService(ISequenceService.class).getSingleReadSequencesMap().entrySet()){
-            if(entry.getValue().size()>1){
-                multiLinkSingleReads.add(entry.getKey());
-            }
-        }
-        this.multiLinkSingleReads = multiLinkSingleReads;
-    }
-
     /**
      * @return a set of {@link SingleRead}s that have multiple parents
      */
     public Set<SingleRead> getMultiLinkSingleReads() {
-        return this.multiLinkSingleReads;
+        return DerivateLabelProvider.getMultiLinkSingleReads();
     }
 
     public Object getSelectionInput() {
@@ -451,57 +529,86 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
             cdmEntitySession.dispose();
         }
     }
-
-    @Override
-    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
-        if(listenToSelectionChange && selection instanceof IStructuredSelection){
-            Object selectedElement = ((IStructuredSelection) selection).getFirstElement();
-            if(selectedElement instanceof CdmBase && ((CdmBase) selectedElement).isInstanceOf(Taxon.class)){
-                selectedTaxon = HibernateProxyHelper.deproxy(selectedElement, Taxon.class);
+    public void selectionChanged_internal(IWorkbenchPart part, ISelection selection) {
+       if(part == this){
+            return;
+        }
+        if(viewer.getTree().isDisposed()){
+            return;
+        }
+        if(listenToSelectionChange){
+            if(part instanceof MultiPageTaxonEditor){
+                selectedTaxon = ((MultiPageTaxonEditor) part).getTaxon();
+            }
+            else if(selection instanceof IStructuredSelection){
+                Object selectedElement = ((IStructuredSelection) selection).getFirstElement();
+                if(selectedElement instanceof CdmBase){
+                    if(((CdmBase) selectedElement).isInstanceOf(TaxonNode.class)){
+                        selectedTaxon = HibernateProxyHelper.deproxy(selectedElement, TaxonNode.class).getTaxon();
+                    }
+                    else if(((CdmBase) selectedElement).isInstanceOf(Taxon.class)){
+                        selectedTaxon = HibernateProxyHelper.deproxy(selectedElement, Taxon.class);
+                    }
+                }
+            }
+            if(selectedTaxon!=null){
                 Collection<SpecimenOrObservationBase> fieldUnits = CdmStore.getService(IOccurrenceService.class).listFieldUnitsByAssociatedTaxon(selectedTaxon, null, null);
                 Collection<UUID> uuids = new HashSet<UUID>();
                 for (SpecimenOrObservationBase specimenOrObservationBase : fieldUnits) {
                     uuids.add(specimenOrObservationBase.getUuid());
                 }
+                checkWarnThreshold(uuids);
                 updateRootEntities(uuids);
-            }
-            if(part instanceof MultiPageTaxonEditor){
-                Taxon taxon = ((MultiPageTaxonEditor) part).getTaxon();
-                derivateSearchCompositeController.setTaxon(taxon);
+                setPartName("Derivative Editor: " + selectedTaxon.getName());
             }
         }
     }
 
-    /**
-     * {@inheritDoc}
-     */
     @Override
-    public List<SpecimenOrObservationBase<?>> getRootEntities() {
-        return new ArrayList<SpecimenOrObservationBase<?>>(rootElements);
+    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+       if(delaySelection==null){
+            delaySelection = new DelaySelection(part, selection);
+        }
+        delaySelection.setPart(part);
+        delaySelection.setSelection(selection);
+        if(!isInDelay){
+            isInDelay = true;
+            Display.getCurrent().asyncExec(delaySelection);
+        }
     }
 
-    @Override
-    public void createViewer(Composite parent) {
-        // TODO Auto-generated method stub
 
-    }
+       private void checkWarnThreshold(Collection<UUID> uuids) {
+               if(uuids!=null && uuids.size()>WARN_THRESHOLD){
+                       MessagingUtils.warningDialog("Performance warning", this.getClass(), String.format("Specimens will not be loaded!\n"
+                                       + "There are %d specimens associated with the current "
+                                       + "selection. If you really want to show all of them in the specimen editor please "
+                                       + "use the taxon filter in the search bar.", uuids.size()));
+                       uuids.clear();
+               }
+       }
 
-    @Override
-    public boolean isSaveOnCloseNeeded() {
-        return isDirty();
+    public TreeViewer getViewer() {
+        return viewer;
     }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public int promptToSaveOnClose() {
-        return ISaveablePart2.DEFAULT;
+    public List<SpecimenOrObservationBase<?>> getRootEntities() {
+        return new ArrayList<SpecimenOrObservationBase<?>>(rootElements);
     }
 
     public void toggleListenToSelectionChange() {
         listenToSelectionChange = !listenToSelectionChange;
         derivateSearchCompositeController.setEnabled(!listenToSelectionChange);
-        derivateSearchCompositeController.setTaxon(null);
         if(!listenToSelectionChange){
             selectedTaxon = null;
+            setPartName("Derivative Editor");
+        }
+        else if(selectedTaxon==null){
+            setPartName("Derivative Editor [no taxon selected]");
         }
     }
 
@@ -522,7 +629,10 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
     @Override
     public void contextStop(IMemento memento, IProgressMonitor monitor) {
         derivateSearchCompositeController.setEnabled(false);
-        viewer.setInput(null);
+        if(!viewer.getTree().isDisposed()) {
+            viewer.getTree().setEnabled(false);
+            viewer.setInput(null);
+        }
     }
 
     /**
@@ -530,8 +640,10 @@ public class DerivateView extends AbstractCdmViewPart implements IPartContentHas
      */
     @Override
     public void contextStart(IMemento memento, IProgressMonitor monitor) {
-        init();
         derivateSearchCompositeController.setEnabled(!listenToSelectionChange);
+        if(!viewer.getTree().isDisposed()){
+            viewer.getTree().setEnabled(true);
+        }
         refreshTree();
     }