178c3f7f598010b1214797bbf3a6fbbe254c1991
[taxeditor.git] / eu.etaxonomy.taxeditor.editor / src / main / java / eu / etaxonomy / taxeditor / editor / view / derivate / DerivateView.java
1 package eu.etaxonomy.taxeditor.editor.view.derivate;
2
3 import java.util.ArrayList;
4 import java.util.Arrays;
5 import java.util.Collection;
6 import java.util.HashMap;
7 import java.util.HashSet;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11 import java.util.UUID;
12
13 import org.eclipse.core.runtime.IProgressMonitor;
14 import org.eclipse.jface.action.MenuManager;
15 import org.eclipse.jface.util.LocalSelectionTransfer;
16 import org.eclipse.jface.viewers.AbstractTreeViewer;
17 import org.eclipse.jface.viewers.ISelection;
18 import org.eclipse.jface.viewers.IStructuredSelection;
19 import org.eclipse.jface.viewers.StructuredSelection;
20 import org.eclipse.jface.viewers.TreeNode;
21 import org.eclipse.jface.viewers.TreeSelection;
22 import org.eclipse.jface.viewers.TreeViewer;
23 import org.eclipse.swt.SWT;
24 import org.eclipse.swt.dnd.DND;
25 import org.eclipse.swt.dnd.Transfer;
26 import org.eclipse.swt.layout.GridData;
27 import org.eclipse.swt.layout.GridLayout;
28 import org.eclipse.swt.widgets.Composite;
29 import org.eclipse.swt.widgets.Control;
30 import org.eclipse.swt.widgets.Menu;
31 import org.eclipse.swt.widgets.Tree;
32 import org.eclipse.ui.IEditorPart;
33 import org.eclipse.ui.IMemento;
34 import org.eclipse.ui.ISaveablePart2;
35 import org.eclipse.ui.IWorkbenchPart;
36
37 import eu.etaxonomy.cdm.api.conversation.ConversationHolder;
38 import eu.etaxonomy.cdm.api.service.IOccurrenceService;
39 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
40 import eu.etaxonomy.cdm.model.common.CdmBase;
41 import eu.etaxonomy.cdm.model.molecular.SingleRead;
42 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
43 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
44 import eu.etaxonomy.cdm.model.taxon.Taxon;
45 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
46 import eu.etaxonomy.taxeditor.editor.EditorUtil;
47 import eu.etaxonomy.taxeditor.editor.Messages;
48 import eu.etaxonomy.taxeditor.editor.view.derivate.searchFilter.DerivateSearchCompositeController;
49 import eu.etaxonomy.taxeditor.model.IContextListener;
50 import eu.etaxonomy.taxeditor.model.IPartContentHasDetails;
51 import eu.etaxonomy.taxeditor.model.IPartContentHasFactualData;
52 import eu.etaxonomy.taxeditor.model.IPartContentHasMedia;
53 import eu.etaxonomy.taxeditor.model.IPartContentHasSupplementalData;
54 import eu.etaxonomy.taxeditor.session.ICdmEntitySession;
55 import eu.etaxonomy.taxeditor.store.CdmStore;
56 import eu.etaxonomy.taxeditor.view.AbstractCdmViewPart;
57 import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateContentProvider;
58 import eu.etaxonomy.taxeditor.view.derivateSearch.DerivateLabelProvider;
59
60 /**
61 * Displays the derivate hierarchy of the specimen specified in the editor input.
62 *
63 */
64 public class DerivateView extends AbstractCdmViewPart implements IPartContentHasFactualData, ISaveablePart2,
65 IPartContentHasDetails, IPartContentHasSupplementalData, IPartContentHasMedia, IContextListener {
66 public static final String ID = "eu.etaxonomy.taxeditor.editor.view.derivate.DerivateView"; //$NON-NLS-1$
67
68 public static final String YOU_NEED_TO_SAVE_BEFORE_PERFORMING_THIS_ACTION = Messages.DerivateView_YOU_NEED_TO_SAVE;
69 public static final String VIEW_HAS_UNSAVED_CHANGES = Messages.DerivateView_UNSAVED_CHANGES;
70
71 private static final List<String> SPECIMEN_INIT_STRATEGY = Arrays.asList(new String[] {
72 "descriptions",
73 "annotations",
74 "markers",
75 "credits",
76 "extensions",
77 "rights",
78 "sources",
79 "derivationEvents.derivatives.annotations",
80 "derivationEvents.derivatives.markers",
81 "derivationEvents.derivatives.credits",
82 "derivationEvents.derivatives.extensions",
83 "derivationEvents.derivatives.rights",
84 "derivationEvents.derivatives.sources"
85 });
86
87 private ConversationHolder conversation;
88
89 private TreeViewer viewer;
90
91 private boolean isDirty;
92
93 private final int dndOperations = DND.DROP_MOVE;
94
95 private DerivateLabelProvider labelProvider;
96
97 private DerivateContentProvider contentProvider;
98
99 private DerivateSearchCompositeController derivateSearchCompositeController;
100
101 /**
102 * A map with keys being the derivative entities belonging to the {@link UUID}s passed to the constructor
103 * and values being the root elements of the hierarchy (may be the same objects as the derivative entities)
104 */
105 private Map<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>> derivateToRootEntityMap;
106
107 /**
108 * The set of root elements
109 */
110 private Set<SpecimenOrObservationBase<?>> rootElements;
111
112 private ICdmEntitySession cdmEntitySession;
113
114 /**
115 * <code>true</code> if this view is listening to selection changes
116 */
117 private boolean listenToSelectionChange;
118
119 private Taxon selectedTaxon;
120
121 /**
122 * Default constructor
123 */
124 public DerivateView() {
125 init();
126 }
127
128 /**
129 *
130 */
131 private void init() {
132 this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
133 this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
134
135 if (CdmStore.isActive() && conversation == null) {
136 conversation = CdmStore.createConversation();
137 }
138 if (CdmStore.isActive()) {
139 cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
140 }
141 //listen to context changes
142 CdmStore.getContextManager().addContextListener(this);
143 }
144
145 @Override
146 public void createPartControl(Composite parent) {
147
148 parent.setLayout(new GridLayout());
149
150 //---search and filter---
151 derivateSearchCompositeController = new DerivateSearchCompositeController(parent, this);
152 GridData gridDataSearchBar = new GridData();
153 gridDataSearchBar.horizontalAlignment = GridData.FILL;
154 gridDataSearchBar.grabExcessHorizontalSpace = true;
155 derivateSearchCompositeController.setLayoutData(gridDataSearchBar);
156 derivateSearchCompositeController.setEnabled(CdmStore.isActive());
157
158 //---tree viewer---
159 viewer = new TreeViewer(new Tree(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION));
160 GridData gridDataTree = new GridData();
161 gridDataTree.horizontalAlignment = GridData.FILL;
162 gridDataTree.verticalAlignment = GridData.FILL;
163 gridDataTree.grabExcessVerticalSpace = true;
164 gridDataTree.grabExcessHorizontalSpace = true;
165 viewer.getTree().setLayoutData(gridDataTree);
166 contentProvider = new DerivateContentProvider();
167 viewer.setContentProvider(contentProvider);
168 labelProvider = new DerivateLabelProvider();
169 labelProvider.setConversation(conversation);
170 viewer.setLabelProvider(labelProvider);
171 viewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
172 viewer.getTree().setEnabled(CdmStore.isActive());
173 // Propagate selection from viewer
174 getSite().setSelectionProvider(viewer);
175
176 //listen to selection changes
177 selectionService = getSite().getWorkbenchWindow().getSelectionService();
178 selectionService.addSelectionListener(this);
179
180 //create context menu
181 MenuManager menuManager = new MenuManager();
182 getSite().registerContextMenu(menuManager, viewer);
183 Control control = viewer.getControl();
184 Menu menu = menuManager.createContextMenu(control);
185 control.setMenu(menu);
186
187 //init tree
188 updateRootEntities();
189
190 //add drag'n'drop support
191 Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer(),};
192 viewer.addDragSupport(dndOperations, transfers, new DerivateDragListener(this));
193 viewer.addDropSupport(dndOperations, transfers, new DerivateDropListener(this));
194 }
195
196 public void updateRootEntities() {
197 updateRootEntities(null);
198 }
199
200 public void updateRootEntities(Collection<UUID> derivativeUuids) {
201 if(conversation!=null){
202 if (!conversation.isBound()) {
203 conversation.bind();
204 }
205 /*
206 * If the active session is not the session of the Derivate Editor then we will
207 * save it, bind temporarily to our session and rebind to the original session.
208 * This happens e.g. if a selection change happens in the taxon editor and
209 * "Link with editor" is enabled. The selection change event and thus the
210 * loading in updateRootEntities() happens in the session of the taxon
211 * editor.
212 */
213 ICdmEntitySession previousCdmEntitySession = CdmStore.getCurrentSessionManager().getActiveSession();
214 if(cdmEntitySession != null) {
215 cdmEntitySession.bind();
216 }
217
218 if(derivativeUuids!=null){
219 this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
220 this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
221 for (UUID uuid : derivativeUuids) {
222 SpecimenOrObservationBase<?> derivate = CdmStore.getService(IOccurrenceService.class).load(uuid, SPECIMEN_INIT_STRATEGY);
223 if(derivate instanceof FieldUnit){
224 derivateToRootEntityMap.put(derivate, derivate);
225 }
226 else {
227 SpecimenOrObservationBase<?> topMostDerivate = EditorUtil.getTopMostDerivate(derivate);
228 if(topMostDerivate!=null){
229 derivateToRootEntityMap.put(derivate, topMostDerivate);
230 }
231 else{
232 derivateToRootEntityMap.put(derivate, derivate);
233 }
234 }
235 }
236 for (SpecimenOrObservationBase<?> specimen : derivateToRootEntityMap.values()) {
237 rootElements.add(specimen);
238 }
239 }
240 viewer.setInput(rootElements);
241 refreshTree(false);
242 previousCdmEntitySession.bind();
243 }
244 }
245
246 @Override
247 public void doSave(IProgressMonitor monitor) {
248 String taskName = Messages.DerivateView_SAVING_HIERARCHY;
249 monitor.beginTask(taskName, 3);
250 if (!conversation.isBound()) {
251 conversation.bind();
252 }
253 monitor.worked(1);
254
255 // commit the conversation and start a new transaction immediately
256 conversation.commit(true);
257
258 if(CdmStore.getCurrentSessionManager().isRemoting()) {
259 CdmStore.getService(IOccurrenceService.class).merge(new ArrayList<SpecimenOrObservationBase>(rootElements), true);
260 }
261 monitor.worked(1);
262
263 this.setDirty(false);
264 monitor.worked(1);
265 monitor.done();
266 firePropertyChange(PROP_DIRTY);
267 refreshTree();
268 }
269
270 @Override
271 public void doSaveAs() {
272 }
273
274 @Override
275 public String getTitleToolTip() {
276 return Messages.DerivateView_DERIVATIVE_EDITOR;
277 }
278
279 @Override
280 public boolean isDirty() {
281 return isDirty;
282 }
283
284 /**
285 * @param isDirty the isDirty to set
286 */
287 public void setDirty(boolean isDirty) {
288 this.isDirty = isDirty;
289 }
290
291 @Override
292 public boolean isSaveAsAllowed() {
293 return false;
294 }
295
296 @Override
297 public void setFocus() {
298 viewer.getControl().setFocus();
299 //make sure to bind again if maybe in another view the conversation was unbound
300 if(conversation!=null && !conversation.isBound()){
301 conversation.bind();
302 }
303 if(cdmEntitySession != null) {
304 cdmEntitySession.bind();
305 }
306 }
307
308 @Override
309 public void update(CdmDataChangeMap changeEvents) {
310 }
311
312 @Override
313 public ConversationHolder getConversationHolder() {
314 return conversation;
315 }
316
317 /**
318 * @return the viewer
319 */
320 @Override
321 public TreeViewer getViewer() {
322 return viewer;
323 }
324
325 @Override
326 public void changed(Object element) {
327 setDirty(true);
328 firePropertyChange(IEditorPart.PROP_DIRTY);
329 viewer.refresh();
330 }
331
332 @Override
333 public void forceDirty() {
334 changed(null);
335 }
336
337 @Override
338 public Map<Object, List<String>> getPropertyPathsMap() {
339 List<String> specimenPropertyPaths = Arrays.asList(new String[] {
340 "descriptions",
341 "derivationEvents.derivates",
342 "annotations",
343 "markers",
344 "credits",
345 "extensions",
346 "rights",
347 "sources"
348 });
349 Map<Object, List<String>> specimenPropertyPathMap =
350 new HashMap<Object, List<String>>();
351 specimenPropertyPathMap.put(SpecimenOrObservationBase.class,specimenPropertyPaths);
352 return specimenPropertyPathMap;
353 }
354
355 /**
356 * Refreshes the derivate hierarchy tree and expands the tree
357 * to show and select the given object.
358 *
359 * @param expandTo the object to which the tree should be expanded
360 */
361 public void refreshTree(Object expandTo){
362 refreshTree();
363 TreeSelection selection = (TreeSelection) viewer.getSelection();
364 viewer.expandToLevel(selection.getFirstElement(), 1);
365 viewer.setSelection(new StructuredSelection(new TreeNode(expandTo)));
366 }
367
368 /**
369 * Refreshes the derivate hierarchy tree
370 */
371 public void refreshTree(){
372 refreshTree(true);
373 }
374
375 /**
376 * Refreshes the derivate hierarchy tree
377 * @param refreshViewer if <code>true</code> then also the
378 * viewer will be refreshed. This was implemented due to
379 * performance reasons. If passing <code>false</code>
380 * does what was expected use <code>false</code> preferably.
381 */
382 public void refreshTree(boolean refreshViewer){
383 //refresh typedesignations
384 labelProvider.refresh();
385 if(refreshViewer){
386 viewer.refresh();
387 }
388 }
389
390 //FIXME:Remoting hack to make this work for remoting
391 //This should actually be resolved using remoting post operations
392 public void remove(Object obj) {
393 rootElements.remove(obj);
394 viewer.setInput(rootElements);
395 }
396
397 /**
398 * @return a set of {@link SingleRead}s that have multiple parents
399 */
400 public Set<SingleRead> getMultiLinkSingleReads() {
401 return DerivateLabelProvider.getMultiLinkSingleReads();
402 }
403
404 public Object getSelectionInput() {
405 return selectedTaxon;
406 }
407
408 public DerivateLabelProvider getLabelProvider() {
409 return labelProvider;
410 }
411
412 @Override
413 public boolean postOperation(CdmBase objectAffectedByOperation) {
414 refreshTree();
415 if(objectAffectedByOperation!=null){
416 changed(objectAffectedByOperation);
417 }
418 return true;
419 }
420
421 @Override
422 public boolean onComplete() {
423 return true;
424 }
425
426
427 @Override
428 public boolean canAttachMedia() {
429 return true;
430 }
431
432 public void addFieldUnit(FieldUnit fieldUnit) {
433 rootElements.add(fieldUnit);
434 derivateToRootEntityMap.put(fieldUnit, fieldUnit);
435 }
436
437 @Override
438 public ICdmEntitySession getCdmEntitySession() {
439 return cdmEntitySession;
440 }
441
442 @Override
443 public void dispose() {
444 super.dispose();
445 if(conversation!=null){
446 conversation.close();
447 }
448 if(cdmEntitySession != null) {
449 cdmEntitySession.dispose();
450 }
451 }
452
453 @Override
454 public void selectionChanged(IWorkbenchPart part, ISelection selection) {
455 if(listenToSelectionChange && selection instanceof IStructuredSelection){
456 Object selectedElement = ((IStructuredSelection) selection).getFirstElement();
457 if(selectedElement instanceof CdmBase && ((CdmBase) selectedElement).isInstanceOf(Taxon.class)){
458 selectedTaxon = HibernateProxyHelper.deproxy(selectedElement, Taxon.class);
459 Collection<SpecimenOrObservationBase> fieldUnits = CdmStore.getService(IOccurrenceService.class).listFieldUnitsByAssociatedTaxon(selectedTaxon, null, null);
460 Collection<UUID> uuids = new HashSet<UUID>();
461 for (SpecimenOrObservationBase specimenOrObservationBase : fieldUnits) {
462 uuids.add(specimenOrObservationBase.getUuid());
463 }
464 updateRootEntities(uuids);
465 setPartName("Derivative Editor: " + selectedTaxon.getName());
466 }
467 }
468 }
469
470 /**
471 * {@inheritDoc}
472 */
473 @Override
474 public List<SpecimenOrObservationBase<?>> getRootEntities() {
475 return new ArrayList<SpecimenOrObservationBase<?>>(rootElements);
476 }
477
478 @Override
479 public void createViewer(Composite parent) {
480 // TODO Auto-generated method stub
481
482 }
483
484 @Override
485 public boolean isSaveOnCloseNeeded() {
486 return isDirty();
487 }
488
489 @Override
490 public int promptToSaveOnClose() {
491 return ISaveablePart2.DEFAULT;
492 }
493
494 public void toggleListenToSelectionChange() {
495 listenToSelectionChange = !listenToSelectionChange;
496 derivateSearchCompositeController.setEnabled(!listenToSelectionChange);
497 if(!listenToSelectionChange){
498 selectedTaxon = null;
499 setPartName("Derivative Editor");
500 }
501 else if(selectedTaxon==null){
502 setPartName("Derivative Editor [no taxon selected]");
503 }
504 }
505
506 public boolean isListenToSelectionChange(){
507 return listenToSelectionChange;
508 }
509
510 /**
511 * {@inheritDoc}
512 */
513 @Override
514 public void contextAboutToStop(IMemento memento, IProgressMonitor monitor) {
515 }
516
517 /**
518 * {@inheritDoc}
519 */
520 @Override
521 public void contextStop(IMemento memento, IProgressMonitor monitor) {
522 derivateSearchCompositeController.setEnabled(false);
523 viewer.getTree().setEnabled(false);
524 viewer.setInput(null);
525 }
526
527 /**
528 * {@inheritDoc}
529 */
530 @Override
531 public void contextStart(IMemento memento, IProgressMonitor monitor) {
532 init();
533 derivateSearchCompositeController.setEnabled(!listenToSelectionChange);
534 viewer.getTree().setEnabled(true);
535 refreshTree();
536 }
537
538 /**
539 * {@inheritDoc}
540 */
541 @Override
542 public void contextRefresh(IProgressMonitor monitor) {
543 }
544
545 /**
546 * {@inheritDoc}
547 */
548 @Override
549 public void workbenchShutdown(IMemento memento, IProgressMonitor monitor) {
550 }
551
552 }