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