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