Set taxon filter when opening derivativwe editor from taxon navigator
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / view / derivate / DerivateView.java
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 }