Merge branch 'hotfix/3.12.4' into develop
[taxeditor.git] / eu.etaxonomy.taxeditor.navigation / src / main / java / eu / etaxonomy / taxeditor / navigation / navigator / TaxonNavigator.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.taxeditor.navigation.navigator;
12
13 import java.util.ArrayList;
14 import java.util.Arrays;
15 import java.util.Comparator;
16 import java.util.HashMap;
17 import java.util.HashSet;
18 import java.util.List;
19 import java.util.Map;
20 import java.util.Observable;
21 import java.util.Observer;
22 import java.util.Set;
23 import java.util.UUID;
24
25 import org.eclipse.core.runtime.IAdaptable;
26 import org.eclipse.core.runtime.IProgressMonitor;
27 import org.eclipse.jface.viewers.DoubleClickEvent;
28 import org.eclipse.jface.viewers.IStructuredSelection;
29 import org.eclipse.jface.viewers.TreePath;
30 import org.eclipse.ui.IMemento;
31 import org.eclipse.ui.IViewSite;
32 import org.eclipse.ui.PartInitException;
33 import org.eclipse.ui.navigator.CommonNavigator;
34
35 import eu.etaxonomy.cdm.api.application.CdmApplicationState;
36 import eu.etaxonomy.cdm.api.application.CdmChangeEvent;
37 import eu.etaxonomy.cdm.api.application.CdmChangeEvent.Action;
38 import eu.etaxonomy.cdm.api.application.ICdmChangeListener;
39 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
40 import eu.etaxonomy.cdm.api.conversation.IConversationEnabled;
41 import eu.etaxonomy.cdm.api.service.IClassificationService;
42 import eu.etaxonomy.cdm.model.common.CdmBase;
43 import eu.etaxonomy.cdm.model.taxon.Classification;
44 import eu.etaxonomy.cdm.model.taxon.TaxonNaturalComparator;
45 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
46 import eu.etaxonomy.cdm.model.taxon.TaxonNodeByRankAndNameComparator;
47 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
48 import eu.etaxonomy.taxeditor.model.DataChangeBridge;
49 import eu.etaxonomy.taxeditor.model.IDataChangeBehavior;
50 import eu.etaxonomy.taxeditor.model.MessagingUtils;
51 import eu.etaxonomy.taxeditor.navigation.NavigationUtil;
52 import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
53 import eu.etaxonomy.taxeditor.preference.PreferencesUtil;
54 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
55 import eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled;
56 import eu.etaxonomy.taxeditor.store.CdmStore;
57 import eu.etaxonomy.taxeditor.store.LoginManager;
58 import eu.etaxonomy.taxeditor.view.CdmViewerChooser;
59
60 /**
61 * Taxonomic tree implementation using Common Navigator Framework.
62 *
63 * @author p.ciardelli
64 * @author n.hoffmann
65 * @created 02.06.2009
66 * @version 1.0
67 */
68 public class TaxonNavigator extends CommonNavigator implements
69 IPostOperationEnabled, IConversationEnabled, Observer,
70 ICdmEntitySessionEnabled, ICdmChangeListener {
71
72 /**
73 * Constant
74 * <code>ID="eu.etaxonomy.taxeditor.navigation.navig"{trunked}</code>
75 */
76 public static final String ID = "eu.etaxonomy.taxeditor.navigation.navigator"; //$NON-NLS-1$
77
78 private static final String TREE_PATH = "treepath";
79
80 private static final String TREE_PATHS = "treepaths";
81
82 private ConversationHolder conversation;
83
84 private ICdmEntitySession cdmEntitySession;
85
86 private String partNameCache;
87
88 private IDataChangeBehavior dataChangeBehavior;
89
90 private Root root;
91
92 /*
93 * (non-Javadoc)
94 *
95 * @see org.eclipse.ui.navigator.CommonNavigator#getInitialInput()
96 */
97 /** {@inheritDoc} */
98 @Override
99 protected IAdaptable getInitialInput() {
100 Comparator<TaxonNode> comparator;
101 if (PreferencesUtil.getSortNodesNaturally()){
102 comparator = new TaxonNaturalComparator();
103 } else{
104 comparator = new TaxonNodeByRankAndNameComparator();
105 }
106 TaxonNodeNavigatorComparator viewerComparator = new TaxonNodeNavigatorComparator(comparator);
107 this.getCommonViewer().setComparator(viewerComparator);
108 setLinkingEnabled(true);
109 // this.getCommonViewer().addSelectionChangedListener(new ISelectionChangedListener() {
110 //
111 // @Override
112 // public void selectionChanged(SelectionChangedEvent arg0) {
113 // IStructuredSelection selection = (IStructuredSelection) getCommonViewer().getSelection();
114 //
115 // Object firstElement = selection.getFirstElement();
116 // //
117 // if (!(firstElement instanceof Classification)){
118 // //NavigationUtil.selectInNavigator(firstElement, null);
119 // NavigationUtil.openEditor(firstElement);
120 // }
121 //
122 // }
123 // } );
124
125 if (CdmStore.isActive()) {
126
127 // TODO when closing and reopening the taxon navigator
128 // we do not preserve state. Closing the view, in contrary to
129 // closing the whole application
130 // should be handled by the state manager too
131 root = new Root(conversation);
132 return root;
133 }
134 return new EmptyRoot();
135 }
136
137 /** {@inheritDoc} */
138 @Override
139 public void init(IViewSite site) throws PartInitException {
140 super.init(site);
141 init();
142 }
143
144 /**
145 * <p>
146 * init
147 * </p>
148 */
149 public void init() {
150
151 if (CdmStore.isActive() && conversation == null) {
152 conversation = CdmStore.createConversation();
153 conversation.registerForDataStoreChanges(TaxonNavigator.this);
154 }
155 if (CdmStore.isActive()) {
156 cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
157 CdmApplicationState.getCurrentDataChangeService().register(this);
158 }
159 CdmStore.getLoginManager().addObserver(this);
160 }
161
162 /**
163 * Refresh this navigators viewer
164 */
165 public void refresh() {
166 if(getConversationHolder() != null){
167 getConversationHolder().bind();
168 //FIXME : Need to make sure this is a stable fix (ticket 3822)
169 if(!getConversationHolder().isCompleted()){
170 getConversationHolder().commit();
171 }
172 }
173 getCommonViewer().refresh();
174 }
175
176 /**
177 * Refresh this navigators viewer
178 */
179 public void refresh(Set objects) {
180 for(Object obj : objects) {
181 getCommonViewer().refresh(obj);
182 }
183 }
184
185 /**
186 * Removes all content
187 */
188 public void clear() {
189 getCommonViewer().setInput(new EmptyRoot());
190 }
191
192 /**
193 * <p>
194 * restore
195 * </p>
196 *
197 * @param memento
198 * a {@link org.eclipse.ui.IMemento} object.
199 * @param monitor
200 * a {@link org.eclipse.core.runtime.IProgressMonitor} object.
201 */
202 public void restore(IMemento memento, IProgressMonitor monitor) {
203 root = new Root(conversation);
204 if (memento == null) {
205 getCommonViewer().setInput(root);
206 return;
207 }
208 int mementoWork = 0;
209 Set<TreePath> treePaths = new HashSet<TreePath>();
210 IMemento[] treePathMementos = null;
211
212 IMemento treePathsMemento = memento.getChild(TREE_PATHS);
213
214 if (treePathsMemento != null) {
215 treePathMementos = treePathsMemento.getChildren(TREE_PATH);
216 mementoWork = treePathMementos.length;
217 }
218 // begin the monitor with steps for all tree paths and steps for
219 // creating
220 // conversation s.o., refreshing the tree and setting the paths
221 IProgressMonitor subProgressMonitor = NavigationUtil
222 .getSubProgressMonitor(monitor, 1);
223
224 subProgressMonitor.beginTask("Restoring Taxon Navigator",
225 1 + mementoWork + 5);
226 subProgressMonitor.subTask("Restoring Taxon Navigator");
227 subProgressMonitor.worked(1);
228
229 conversation = CdmStore.createConversation();
230 subProgressMonitor.worked(1);
231 conversation.registerForDataStoreChanges(TaxonNavigator.this);
232 subProgressMonitor.worked(1);
233 getCommonViewer().setInput(root);
234 subProgressMonitor.worked(1);
235 getCommonViewer().refresh();
236 subProgressMonitor.worked(1);
237
238 if (treePathMementos != null && treePathMementos.length > 0) {
239 for (IMemento treePathMemento : treePathMementos) {
240 TreePath treePath = createTreePathFromString(treePathMemento
241 .getID());
242 if (!subProgressMonitor.isCanceled() && treePath != null) {
243 treePaths.add(treePath);
244 subProgressMonitor.worked(1);
245 }
246 }
247 }
248 if (treePaths.size() > 0) {
249 TaxonNavigator.this.getCommonViewer().setExpandedTreePaths(
250 treePaths.toArray(new TreePath[0]));
251 subProgressMonitor.worked(1);
252 }
253 subProgressMonitor.done();
254 }
255
256 /**
257 * @param string
258 * @return
259 */
260 private TreePath createTreePathFromString(String string) {
261
262 List<CdmBase> pathList = new ArrayList<CdmBase>();
263
264 if (string.length() == 0) {
265 return null;
266 }
267
268 for (String uuid : string.split(" ")) {
269 CdmBase cdmBaseObject = CdmStore.getService(
270 IClassificationService.class).getTaxonNodeByUuid(
271 UUID.fromString(uuid));
272 if (cdmBaseObject == null) {
273 // is this a tree uuid?
274 cdmBaseObject = CdmStore.getService(
275 IClassificationService.class).load(
276 UUID.fromString(uuid));
277
278 if (cdmBaseObject == null) {
279 return null;
280 }
281 }
282 pathList.add(cdmBaseObject);
283 }
284 return new TreePath(pathList.toArray());
285 }
286
287 /** {@inheritDoc} */
288 @Override
289 public void saveState(IMemento aMemento) {
290 //
291 }
292
293 /**
294 * <p>
295 * saveTreeState
296 * </p>
297 *
298 * @param memento
299 * a {@link org.eclipse.ui.IMemento} object.
300 * @param progressMonitor
301 * a {@link org.eclipse.core.runtime.IProgressMonitor} object.
302 */
303 public void saveTreeState(IMemento memento, IProgressMonitor progressMonitor) {
304 if (memento == null) {
305 return;
306 }
307 IProgressMonitor monitor = NavigationUtil.getSubProgressMonitor(
308 progressMonitor, 1);
309
310 super.saveState(memento);
311
312 memento = memento.createChild(TREE_PATHS);
313 TreePath[] treePaths = this.getCommonViewer().getExpandedTreePaths();
314
315 monitor.beginTask("Saving Taxon Navigator State", treePaths.length);
316
317 for (TreePath treePath : treePaths) {
318 int pathLength = treePath.getSegmentCount();
319 String path = "";
320 for (int i = 0; i < pathLength; i++) {
321 Object segment = treePath.getSegment(i);
322 if (segment instanceof CdmBase) {
323 path += ((CdmBase) segment).getUuid().toString() + " ";
324 monitor.worked(1);
325 } else {
326 MessagingUtils.warn(getClass(),
327 "Non-taxon tree path segment " + segment);
328 }
329 }
330 memento.createChild(TREE_PATH, path.trim());
331 }
332 monitor.done();
333 }
334
335 /*
336 * (non-Javadoc)
337 *
338 * @see
339 * eu.etaxonomy.cdm.api.conversation.IConversationEnabled#getConversationHolder
340 * ()
341 */
342 /**
343 * <p>
344 * getConversationHolder
345 * </p>
346 *
347 * @return a {@link eu.etaxonomy.cdm.api.conversation.ConversationHolder}
348 * object.
349 */
350 @Override
351 public ConversationHolder getConversationHolder() {
352 return conversation;
353 }
354
355 /*
356 * (non-Javadoc)
357 *
358 * @see
359 * eu.etaxonomy.cdm.persistence.hibernate.ICdmPostDataChangeObserver#update
360 * (eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap)
361 */
362 /** {@inheritDoc} */
363 @Override
364 public void update(CdmDataChangeMap changeEvents) {
365 if (dataChangeBehavior == null) {
366 dataChangeBehavior = new TaxonNavigatorDataChangeBehavior(this);
367 }
368
369 DataChangeBridge.handleDataChange(changeEvents, dataChangeBehavior);
370 }
371
372 /** {@inheritDoc} */
373 @Override
374 public String getFrameToolTipText(Object element) {
375 if (element instanceof Root) {
376 return "Taxonomic Tree";
377 }
378 return super.getFrameToolTipText(element);
379 }
380
381 /*
382 * (non-Javadoc)
383 *
384 * @see org.eclipse.ui.part.WorkbenchPart#dispose()
385 */
386 /** {@inheritDoc} */
387 @Override
388 public void dispose() {
389 super.dispose();
390 dataChangeBehavior = null;
391 if (conversation != null) {
392 conversation.unregisterForDataStoreChanges(this);
393 }
394 if(cdmEntitySession != null) {
395 cdmEntitySession.dispose();
396 }
397 if(CdmApplicationState.getCurrentDataChangeService() != null) {
398 CdmApplicationState.getCurrentDataChangeService().unregister(this);
399 }
400 }
401
402 /*
403 * (non-Javadoc)
404 *
405 * @see org.eclipse.ui.navigator.CommonNavigator#setFocus()
406 */
407 /** {@inheritDoc} */
408 @Override
409 public void setFocus() {
410 // logger.warn("Setting focus to navigator");
411 super.setFocus();
412 if (getConversationHolder() != null) {
413 getConversationHolder().bind();
414 }
415 if(cdmEntitySession != null) {
416 cdmEntitySession.bind();
417 }
418 }
419
420 /*
421 * (non-Javadoc)
422 *
423 * @see
424 * eu.etaxonomy.taxeditor.operations.IPostOperationEnabled#postOperation
425 * (eu.etaxonomy.cdm.model.common.CdmBase)
426 */
427 /** {@inheritDoc} */
428 @Override
429 public boolean postOperation(CdmBase objectAffectedByOperation) {
430 // nothing to do here
431 return true;
432 }
433
434 /**
435 * <p>
436 * save
437 * </p>
438 *
439 * @param memento
440 * a {@link org.eclipse.ui.IMemento} object.
441 * @param monitor
442 * a {@link org.eclipse.core.runtime.IProgressMonitor} object.
443 */
444 public void save(IMemento memento, IProgressMonitor monitor) {
445 saveTreeState(memento, monitor);
446 if (conversation != null) {
447 conversation.unregisterForDataStoreChanges(this);
448 conversation = null;
449 }
450 }
451
452 /** {@inheritDoc} */
453 @Override
454 protected void handleDoubleClick(DoubleClickEvent event) {
455 if(event.getSelection() instanceof IStructuredSelection){
456 Object selectedObject = ((IStructuredSelection) event.getSelection()).getFirstElement();
457 CdmViewerChooser chooser = new CdmViewerChooser(getSite().getShell());
458 chooser.chooseViewer(selectedObject);
459 }
460 // If the double click is passed up to the super-class it will
461 // expand/collapse trees.
462 // We do not want that
463 // super.handleDoubleClick(anEvent);
464 }
465
466 /**
467 * <p>
468 * onComplete
469 * </p>
470 *
471 * @return a boolean.
472 */
473 @Override
474 public boolean onComplete() {
475 return true;
476 }
477
478 /*
479 * (non-Javadoc)
480 *
481 * @see org.eclipse.ui.part.WorkbenchPart#showBusy(boolean)
482 */
483 /** {@inheritDoc} */
484 @Override
485 public void showBusy(boolean busy) {
486 super.showBusy(busy);
487 getCommonViewer().getControl().setEnabled(!busy);
488 if (busy) {
489 partNameCache = getPartName();
490 setPartName("Loading datasources");
491 } else {
492 if (partNameCache != null) {
493 setPartName(partNameCache);
494 }
495 }
496 }
497
498
499 /* (non-Javadoc)
500 * @see java.util.Observer#update(java.util.Observable, java.lang.Object)
501 */
502 @Override
503 public void update(Observable o, Object arg) {
504 if(o instanceof LoginManager){
505 refresh();
506 }
507
508 }
509
510 /* (non-Javadoc)
511 * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled#getCdmEntitySession()
512 */
513 @Override
514 public ICdmEntitySession getCdmEntitySession() {
515 return cdmEntitySession;
516 }
517
518 /* (non-Javadoc)
519 * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled#getRootEntities()
520 */
521 @Override
522 public List<Classification> getRootEntities() {
523 if(root != null) {
524 return root.getParentBeans();
525 }
526 return null;
527 }
528
529 /* (non-Javadoc)
530 * @see eu.etaxonomy.cdm.api.application.ICdmChangeListener#onChange(eu.etaxonomy.cdm.api.application.CdmChangeEvent)
531 */
532 @Override
533 public void onChange(CdmChangeEvent event) {
534 if(event.getAction() == Action.Delete && Classification.class.equals(event.getEntityType())) {
535 refresh();
536 return;
537 }
538 for(CdmBase cb : event.getChangedObjects()) {
539 if(cb instanceof TaxonNode) {
540 TaxonNode tn = (TaxonNode)cb;
541 if(tn.getTaxon() == null) {
542 getCommonViewer().refresh(tn.getClassification());
543 } else {
544 getCommonViewer().refresh(cb);
545 }
546 } else if (cb instanceof Classification) {
547 getCommonViewer().refresh();
548 }
549 }
550
551 }
552
553 /* (non-Javadoc)
554 * @see eu.etaxonomy.taxeditor.session.ICdmEntitySessionEnabled#getPropertyPathsMap()
555 */
556 @Override
557 public Map<Object, List<String>> getPropertyPathsMap() {
558 Map<Object, List<String>> propertyPathsMap = new HashMap<Object, List<String>>();
559 List<String> taxonNodePropertyPaths = Arrays.asList(new String[] {
560 "taxon.name"
561 });
562 propertyPathsMap.put("childNodes", taxonNodePropertyPaths);
563 return propertyPathsMap;
564 }
565 }