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