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