Refresh only label of the element that has changed
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / view / derivate / DerivateView.java
index 1b82c9f8da85a803399a5175f860ee3953bfa9e5..630eef093a2ab5333fbadc7e6860ac6ba3207abf 100644 (file)
@@ -1,8 +1,12 @@
 package eu.etaxonomy.taxeditor.editor.view.derivate;
 
+import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Map.Entry;
+import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.UUID;
 
@@ -11,8 +15,7 @@ import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.util.LocalSelectionTransfer;
 import org.eclipse.jface.viewers.AbstractTreeViewer;
 import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.ISelectionChangedListener;
-import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.jface.viewers.TreeNode;
 import org.eclipse.jface.viewers.TreeSelection;
@@ -29,25 +32,37 @@ 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.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.molecular.ISequenceService;
+import eu.etaxonomy.cdm.api.service.IOccurrenceService;
+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.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.derivateSearch.DerivateContentProvider;
 import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateLabelProvider;
@@ -56,15 +71,30 @@ import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateLabelProvider;
  * Displays the derivate hierarchy of the specimen specified in the editor input.
  *
  */
-public class DerivateView extends EditorPart implements IPartContentHasFactualData, IDirtyMarkable,
-        IConversationEnabled, IPartContentHasDetails, IPartContentHasSupplementalData, IPartContentHasMedia,
-        ISelectionChangedListener, IPostOperationEnabled{
-
+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;
     public static final String VIEW_HAS_UNSAVED_CHANGES = Messages.DerivateView_UNSAVED_CHANGES;
 
+    private static final List<String> SPECIMEN_INIT_STRATEGY = Arrays.asList(new String[] {
+            "descriptions",
+            "annotations",
+            "markers",
+            "credits",
+            "extensions",
+            "rights",
+            "sources",
+            "derivationEvents.derivatives.annotations",
+            "derivationEvents.derivatives.markers",
+            "derivationEvents.derivatives.credits",
+            "derivationEvents.derivatives.extensions",
+            "derivationEvents.derivatives.rights",
+            "derivationEvents.derivatives.sources"
+    });
+
        private ConversationHolder conversation;
 
        private TreeViewer viewer;
@@ -75,14 +105,31 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
 
     private DerivateLabelProvider labelProvider;
 
-    private Set<SingleRead> multiLinkSingleReads;
+    private DerivateContentProvider contentProvider;
 
-    private ISelection selection = null;
+    private DerivateSearchCompositeController derivateSearchCompositeController;
 
+    /**
+     * A map with keys being the derivative entities belonging to the {@link UUID}s passed to the constructor
+     * and values being the root elements of the hierarchy (may be the same objects as the derivative entities)
+     */
+    private Map<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>> derivateToRootEntityMap;
 
-    private DerivateContentProvider contentProvider;
+    /**
+     * The set of root elements
+     */
+    private Set<SpecimenOrObservationBase<?>> rootElements;
 
-    private DerivateSearchCompositeController derivateSearchCompositeController;
+    private ICdmEntitySession cdmEntitySession;
+
+    /**
+     * <code>true</code> if this view is listening to selection  changes
+     */
+    private boolean listenToSelectionChange;
+
+    private Taxon selectedTaxon;
+
+    private ISelectionService selectionService;
 
     /**
      * Default constructor
@@ -90,6 +137,27 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
     public DerivateView() {
     }
 
+
+    /**
+     * {@inheritDoc}
+     */
+    @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<?>>();
+
+        if (CdmStore.isActive() && conversation == null) {
+            conversation = CdmStore.createConversation();
+        }
+        if (CdmStore.isActive()) {
+            cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
+        }
+        //listen to context changes
+        CdmStore.getContextManager().addContextListener(this);
+    }
+
     @Override
     public void createPartControl(Composite parent) {
 
@@ -97,11 +165,11 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
 
         //---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));
@@ -117,10 +185,13 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
         labelProvider.setConversation(conversation);
         viewer.setLabelProvider(labelProvider);
         viewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
+        viewer.getTree().setEnabled(CdmStore.isActive());
         // Propagate selection from viewer
         getSite().setSelectionProvider(viewer);
+
         //listen to selection changes
-        viewer.addSelectionChangedListener(this);
+        selectionService = getSite().getWorkbenchWindow().getSelectionService();
+        selectionService.addSelectionListener(this);
 
         //create context menu
         MenuManager menuManager = new MenuManager();
@@ -129,20 +200,8 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
         Menu menu = menuManager.createContextMenu(control);
         control.setMenu(menu);
 
-        generateMultiLinkSingleReads();
-        labelProvider.setMultiLinkSingleReads(multiLinkSingleReads);
-        IEditorInput editorInput = getEditorInput();
-        viewer.setInput(((DerivateViewEditorInput) editorInput).getRootEntities());
-        //set selection to selected derivate if only one was selected
-        if(editorInput instanceof DerivateViewEditorInput){
-            Set<SpecimenOrObservationBase<?>> derivateEntities = ((DerivateViewEditorInput) editorInput).getDerivateEntities();
-            if(derivateEntities.size()==1){
-                SpecimenOrObservationBase<?> specimen = derivateEntities.iterator().next();
-                if(specimen != null){
-                    viewer.setSelection(new StructuredSelection(new TreeNode(specimen)));
-                }
-            }
-        }
+        //init tree
+        updateRootEntities();
 
         //add drag'n'drop support
         Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer(),};
@@ -150,13 +209,72 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
         viewer.addDropSupport(dndOperations, transfers, new DerivateDropListener(this));
     }
 
+    public void updateRootEntities() {
+        updateRootEntities((Collection)null);
+    }
+
+    public void updateRootEntities(Collection<UUID> derivativeUuids) {
+        if(conversation!=null){
+            if (!conversation.isBound()) {
+                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.
+             */
+            ICdmEntitySession previousCdmEntitySession = CdmStore.getCurrentSessionManager().getActiveSession();
+            if(cdmEntitySession != null) {
+                cdmEntitySession.bind();
+            }
+
+            if(derivativeUuids!=null){
+                this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
+                this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
+                List<SpecimenOrObservationBase> derivates =
+                        CdmStore.getService(IOccurrenceService.class).load(new ArrayList(derivativeUuids), SPECIMEN_INIT_STRATEGY);
+                updateRootEntities(derivates);
+            }
+            refreshTree(false);
+            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);
+                    }
+                    else {
+                        SpecimenOrObservationBase<?> topMostDerivate = EditorUtil.getTopMostDerivate(derivate);
+                        if(topMostDerivate!=null){
+                            derivateToRootEntityMap.put(derivate, topMostDerivate);
+                        }
+                        else{
+                            derivateToRootEntityMap.put(derivate, derivate);
+                        }
+                    }
+                }
+                for (SpecimenOrObservationBase<?> specimen : derivateToRootEntityMap.values()) {
+                    rootElements.add(specimen);
+                }
+            }
+            viewer.setInput(rootElements);
+
+    }
+
     @Override
     public void doSave(IProgressMonitor monitor) {
         String taskName = Messages.DerivateView_SAVING_HIERARCHY;
-        if(getEditorInput() instanceof DerivateViewEditorInput){
-            DerivateViewEditorInput derivateViewEditorInput = (DerivateViewEditorInput) getEditorInput();
-            taskName += " "+derivateViewEditorInput.getName(); //$NON-NLS-1$
-        }
         monitor.beginTask(taskName, 3);
         if (!conversation.isBound()) {
             conversation.bind();
@@ -165,7 +283,10 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
 
         // commit the conversation and start a new transaction immediately
         conversation.commit(true);
-        ((DerivateViewEditorInput) getEditorInput()).merge();
+
+        if(CdmStore.getCurrentSessionManager().isRemoting()) {
+            CdmStore.getService(IOccurrenceService.class).merge(new ArrayList<SpecimenOrObservationBase>(rootElements), true);
+        }
         monitor.worked(1);
 
         this.setDirty(false);
@@ -181,24 +302,9 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
 
     @Override
     public String getTitleToolTip() {
-        if(getEditorInput() instanceof DerivateViewEditorInput){
-            DerivateViewEditorInput derivateViewEditorInput = (DerivateViewEditorInput) getEditorInput();
-            return derivateViewEditorInput.getName();
-        }
         return Messages.DerivateView_DERIVATIVE_EDITOR;
     }
 
-    @Override
-    public void init(IEditorSite site, IEditorInput input) throws PartInitException {
-        setSite(site);
-        setInput(input);
-        if(input instanceof DerivateViewEditorInput){
-            DerivateViewEditorInput derivateViewEditorInput = (DerivateViewEditorInput) input;
-            conversation = derivateViewEditorInput.getConversationHolder();
-            setPartName(derivateViewEditorInput.getName());
-        }
-    }
-
     @Override
     public boolean isDirty() {
         return isDirty;
@@ -220,10 +326,12 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
     public void setFocus() {
         viewer.getControl().setFocus();
         //make sure to bind again if maybe in another view the conversation was unbound
-        if(!conversation.isBound()){
+        if(conversation!=null && !conversation.isBound()){
             conversation.bind();
         }
-        ((DerivateViewEditorInput) getEditorInput()).bind();
+        if(cdmEntitySession != null) {
+            cdmEntitySession.bind();
+        }
     }
 
     @Override
@@ -235,18 +343,11 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
         return conversation;
     }
 
-    /**
-     * @return the viewer
-     */
-    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
@@ -254,8 +355,22 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
         changed(null);
     }
 
-    protected void setRootEntities(Collection<UUID> rootEntityUuids){
-        ((DerivateViewEditorInput)getEditorInput()).updateRootEntities(rootEntityUuids);
+    @Override
+    public Map<Object, List<String>> getPropertyPathsMap() {
+        List<String> specimenPropertyPaths = Arrays.asList(new String[] {
+                "descriptions",
+                "derivationEvents.derivates",
+                "annotations",
+                "markers",
+                "credits",
+                "extensions",
+                "rights",
+                "sources"
+        });
+        Map<Object, List<String>> specimenPropertyPathMap =
+                new HashMap<Object, List<String>>();
+        specimenPropertyPathMap.put(SpecimenOrObservationBase.class,specimenPropertyPaths);
+        return specimenPropertyPathMap;
     }
 
     /**
@@ -275,43 +390,40 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
      * Refreshes the derivate hierarchy tree
      */
     public void refreshTree(){
-        generateMultiLinkSingleReads();
-        labelProvider.setMultiLinkSingleReads(multiLinkSingleReads);
-        viewer.refresh();
+        refreshTree(true);
+    }
+
+    /**
+     * Refreshes the derivate hierarchy tree
+     * @param refreshViewer if <code>true</code> then also the
+     * viewer will be refreshed. This was implemented due to
+     * performance reasons. If passing <code>false</code>
+     * does what was expected use <code>false</code> preferably.
+     */
+    public void refreshTree(boolean refreshViewer){
+        //refresh typedesignations
+        labelProvider.refresh();
+        if(refreshViewer){
+            viewer.refresh();
+        }
     }
 
     //FIXME:Remoting hack to make this work for remoting
     //This should actually be resolved using remoting post operations
     public void remove(Object obj) {
-        Set<SpecimenOrObservationBase<?>> rootEntities = ((DerivateViewEditorInput) getEditorInput()).getRootEntities();
-        rootEntities.remove(obj);
-        viewer.setInput(rootEntities);
-    }
-
-    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;
+        rootElements.remove(obj);
+        viewer.setInput(rootElements);
     }
 
     /**
      * @return a set of {@link SingleRead}s that have multiple parents
      */
     public Set<SingleRead> getMultiLinkSingleReads() {
-        return this.multiLinkSingleReads;
+        return DerivateLabelProvider.getMultiLinkSingleReads();
     }
 
-    @Override
-    public void selectionChanged(SelectionChangedEvent event) {
-        this.selection  = event.getSelection();
-    }
-
-    public ISelection getSelection() {
-        return selection;
+    public Object getSelectionInput() {
+        return selectedTaxon;
     }
 
     public DerivateLabelProvider getLabelProvider() {
@@ -332,15 +444,130 @@ public class DerivateView extends EditorPart implements IPartContentHasFactualDa
         return true;
     }
 
+
+    @Override
+    public boolean canAttachMedia() {
+        return true;
+    }
+
+    public void addFieldUnit(FieldUnit fieldUnit) {
+        rootElements.add(fieldUnit);
+        derivateToRootEntityMap.put(fieldUnit, fieldUnit);
+    }
+
+    @Override
+    public ICdmEntitySession getCdmEntitySession() {
+        return cdmEntitySession;
+    }
+
     @Override
     public void dispose() {
-        ((DerivateViewEditorInput) getEditorInput()).dispose();
         super.dispose();
+        if(conversation!=null){
+            conversation.close();
+        }
+        if(cdmEntitySession != null) {
+            cdmEntitySession.dispose();
+        }
     }
 
+    @Override
+    public void selectionChanged(IWorkbenchPart part, ISelection selection) {
+        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);
+                    }
+                }
+            }
+            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());
+            }
+            updateRootEntities(uuids);
+            setPartName("Derivative Editor: " + selectedTaxon.getName());
+        }
+    }
+
+    public TreeViewer getViewer() {
+        return viewer;
+    }
 
+    /**
+     * {@inheritDoc}
+     */
     @Override
-    public boolean canAttachMedia() {
-        return true;
+    public List<SpecimenOrObservationBase<?>> getRootEntities() {
+        return new ArrayList<SpecimenOrObservationBase<?>>(rootElements);
+    }
+
+    public void toggleListenToSelectionChange() {
+        listenToSelectionChange = !listenToSelectionChange;
+        derivateSearchCompositeController.setEnabled(!listenToSelectionChange);
+        if(!listenToSelectionChange){
+            selectedTaxon = null;
+            setPartName("Derivative Editor");
+        }
+        else if(selectedTaxon==null){
+            setPartName("Derivative Editor [no taxon selected]");
+        }
+    }
+
+    public boolean isListenToSelectionChange(){
+        return listenToSelectionChange;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void contextStop(IMemento memento, IProgressMonitor monitor) {
+        derivateSearchCompositeController.setEnabled(false);
+        viewer.getTree().setEnabled(false);
+        viewer.setInput(null);
     }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void contextStart(IMemento memento, IProgressMonitor monitor) {
+        derivateSearchCompositeController.setEnabled(!listenToSelectionChange);
+        viewer.getTree().setEnabled(true);
+        refreshTree();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void contextRefresh(IProgressMonitor monitor) {
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void workbenchShutdown(IMemento memento, IProgressMonitor monitor) {
+    }
+
 }