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