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