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