33acf9faefae9cf401d5029470aa204fb2094155
[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.Map.Entry;
11 import java.util.Set;
12 import java.util.UUID;
13
14 import org.eclipse.core.runtime.IProgressMonitor;
15 import org.eclipse.jface.action.MenuManager;
16 import org.eclipse.jface.util.LocalSelectionTransfer;
17 import org.eclipse.jface.viewers.AbstractTreeViewer;
18 import org.eclipse.jface.viewers.ISelection;
19 import org.eclipse.jface.viewers.IStructuredSelection;
20 import org.eclipse.jface.viewers.StructuredSelection;
21 import org.eclipse.jface.viewers.TreeNode;
22 import org.eclipse.jface.viewers.TreeSelection;
23 import org.eclipse.jface.viewers.TreeViewer;
24 import org.eclipse.swt.SWT;
25 import org.eclipse.swt.dnd.DND;
26 import org.eclipse.swt.dnd.Transfer;
27 import org.eclipse.swt.layout.GridData;
28 import org.eclipse.swt.layout.GridLayout;
29 import org.eclipse.swt.widgets.Composite;
30 import org.eclipse.swt.widgets.Control;
31 import org.eclipse.swt.widgets.Menu;
32 import org.eclipse.swt.widgets.Tree;
33 import org.eclipse.ui.IEditorPart;
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.api.service.molecular.ISequenceService;
40 import eu.etaxonomy.cdm.model.common.CdmBase;
41 import eu.etaxonomy.cdm.model.molecular.Sequence;
42 import eu.etaxonomy.cdm.model.molecular.SingleRead;
43 import eu.etaxonomy.cdm.model.occurrence.FieldUnit;
44 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
45 import eu.etaxonomy.cdm.model.taxon.Taxon;
46 import eu.etaxonomy.cdm.persistence.hibernate.CdmDataChangeMap;
47 import eu.etaxonomy.taxeditor.editor.EditorUtil;
48 import eu.etaxonomy.taxeditor.editor.Messages;
49 import eu.etaxonomy.taxeditor.editor.view.derivate.searchFilter.DerivateSearchCompositeController;
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 {
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 Set<SingleRead> multiLinkSingleReads;
98
99 private ISelection selection = null;
100
101
102 private DerivateContentProvider contentProvider;
103
104 private DerivateSearchCompositeController derivateSearchCompositeController;
105
106 /**
107 * A map with keys being the derivative entities belonging to the {@link UUID}s passed to the constructor
108 * and values being the root elements of the hierarchy (may be the same objects as the derivative entities)
109 */
110 private Map<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>> derivateToRootEntityMap;
111
112 /**
113 * The set of root elements
114 */
115 private Set<SpecimenOrObservationBase<?>> rootElements;
116
117 private ICdmEntitySession cdmEntitySession;
118
119 /**
120 * <code>true</code> if this view is listening to selection changes
121 */
122 private boolean listenToSelectionChange;
123
124 /**
125 * Default constructor
126 */
127 public DerivateView() {
128 this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
129 this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
130
131 if (CdmStore.isActive() && conversation == null) {
132 conversation = CdmStore.createConversation();
133 }
134 if (CdmStore.isActive()) {
135 cdmEntitySession = CdmStore.getCurrentSessionManager().newSession(this, true);
136 }
137 }
138
139 @Override
140 public void createPartControl(Composite parent) {
141
142 parent.setLayout(new GridLayout());
143
144 //---search and filter---
145 derivateSearchCompositeController = new DerivateSearchCompositeController(parent, this);
146 derivateSearchCompositeController.setEnabled(CdmStore.isActive());
147 GridData gridDataSearchBar = new GridData();
148 gridDataSearchBar.horizontalAlignment = GridData.FILL;
149 gridDataSearchBar.grabExcessHorizontalSpace = true;
150 derivateSearchCompositeController.setLayoutData(gridDataSearchBar);
151
152 //---tree viewer---
153 viewer = new TreeViewer(new Tree(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.FULL_SELECTION));
154 GridData gridDataTree = new GridData();
155 gridDataTree.horizontalAlignment = GridData.FILL;
156 gridDataTree.verticalAlignment = GridData.FILL;
157 gridDataTree.grabExcessVerticalSpace = true;
158 gridDataTree.grabExcessHorizontalSpace = true;
159 viewer.getTree().setLayoutData(gridDataTree);
160 contentProvider = new DerivateContentProvider();
161 viewer.setContentProvider(contentProvider);
162 labelProvider = new DerivateLabelProvider();
163 labelProvider.setConversation(conversation);
164 viewer.setLabelProvider(labelProvider);
165 viewer.setAutoExpandLevel(AbstractTreeViewer.ALL_LEVELS);
166 // Propagate selection from viewer
167 getSite().setSelectionProvider(viewer);
168
169 //listen to selection changes
170 selectionService = getSite().getWorkbenchWindow().getSelectionService();
171 selectionService.addSelectionListener(this);
172
173 //create context menu
174 MenuManager menuManager = new MenuManager();
175 getSite().registerContextMenu(menuManager, viewer);
176 Control control = viewer.getControl();
177 Menu menu = menuManager.createContextMenu(control);
178 control.setMenu(menu);
179
180 //init tree
181 updateRootEntities();
182
183 //add drag'n'drop support
184 Transfer[] transfers = new Transfer[] {LocalSelectionTransfer.getTransfer(),};
185 viewer.addDragSupport(dndOperations, transfers, new DerivateDragListener(this));
186 viewer.addDropSupport(dndOperations, transfers, new DerivateDropListener(this));
187 }
188
189 public void updateRootEntities() {
190 updateRootEntities(null);
191 }
192
193 public void updateRootEntities(Collection<UUID> derivativeUuids) {
194 if(conversation!=null){
195 if (!conversation.isBound()) {
196 conversation.bind();
197 }
198 if(derivativeUuids!=null){
199 this.derivateToRootEntityMap = new HashMap<SpecimenOrObservationBase<?>, SpecimenOrObservationBase<?>>();
200 this.rootElements = new HashSet<SpecimenOrObservationBase<?>>();
201 for (UUID uuid : derivativeUuids) {
202 SpecimenOrObservationBase<?> derivate = CdmStore.getService(IOccurrenceService.class).load(uuid, SPECIMEN_INIT_STRATEGY);
203 if(derivate instanceof FieldUnit){
204 derivateToRootEntityMap.put(derivate, derivate);
205 }
206 else {
207 SpecimenOrObservationBase<?> topMostDerivate = EditorUtil.getTopMostDerivate(derivate);
208 if(topMostDerivate!=null){
209 derivateToRootEntityMap.put(derivate, topMostDerivate);
210 }
211 else{
212 derivateToRootEntityMap.put(derivate, derivate);
213 }
214 }
215 }
216 for (SpecimenOrObservationBase<?> specimen : derivateToRootEntityMap.values()) {
217 rootElements.add(specimen);
218 }
219 }
220 viewer.setInput(rootElements);
221 refreshTree();
222 }
223 }
224
225 @Override
226 public void doSave(IProgressMonitor monitor) {
227 String taskName = Messages.DerivateView_SAVING_HIERARCHY;
228 monitor.beginTask(taskName, 3);
229 if (!conversation.isBound()) {
230 conversation.bind();
231 }
232 monitor.worked(1);
233
234 // commit the conversation and start a new transaction immediately
235 conversation.commit(true);
236
237 if(CdmStore.getCurrentSessionManager().isRemoting()) {
238 CdmStore.getService(IOccurrenceService.class).merge(new ArrayList<SpecimenOrObservationBase>(rootElements), true);
239 }
240 monitor.worked(1);
241
242 this.setDirty(false);
243 monitor.worked(1);
244 monitor.done();
245 firePropertyChange(PROP_DIRTY);
246 refreshTree();
247 }
248
249 @Override
250 public void doSaveAs() {
251 }
252
253 public Set<SpecimenOrObservationBase<?>> getRootElements() {
254 return rootElements;
255 }
256
257 @Override
258 public String getTitleToolTip() {
259 return Messages.DerivateView_DERIVATIVE_EDITOR;
260 }
261
262 @Override
263 public boolean isDirty() {
264 return isDirty;
265 }
266
267 /**
268 * @param isDirty the isDirty to set
269 */
270 public void setDirty(boolean isDirty) {
271 this.isDirty = isDirty;
272 }
273
274 @Override
275 public boolean isSaveAsAllowed() {
276 return false;
277 }
278
279 @Override
280 public void setFocus() {
281 viewer.getControl().setFocus();
282 //make sure to bind again if maybe in another view the conversation was unbound
283 if(!conversation.isBound()){
284 conversation.bind();
285 }
286 if(cdmEntitySession != null) {
287 cdmEntitySession.bind();
288 }
289 }
290
291 @Override
292 public void update(CdmDataChangeMap changeEvents) {
293 }
294
295 @Override
296 public ConversationHolder getConversationHolder() {
297 return conversation;
298 }
299
300 /**
301 * @return the viewer
302 */
303 @Override
304 public TreeViewer getViewer() {
305 return viewer;
306 }
307
308 @Override
309 public void changed(Object element) {
310 setDirty(true);
311 firePropertyChange(IEditorPart.PROP_DIRTY);
312 viewer.refresh();
313 }
314
315 @Override
316 public void forceDirty() {
317 changed(null);
318 }
319
320 @Override
321 public Map<Object, List<String>> getPropertyPathsMap() {
322 List<String> specimenPropertyPaths = Arrays.asList(new String[] {
323 "descriptions",
324 "derivationEvents.derivates",
325 "annotations",
326 "markers",
327 "credits",
328 "extensions",
329 "rights",
330 "sources"
331 });
332 Map<Object, List<String>> specimenPropertyPathMap =
333 new HashMap<Object, List<String>>();
334 specimenPropertyPathMap.put(SpecimenOrObservationBase.class,specimenPropertyPaths);
335 return specimenPropertyPathMap;
336 }
337
338 /**
339 * Refreshes the derivate hierarchy tree and expands the tree
340 * to show and select the given object.
341 *
342 * @param expandTo the object to which the tree should be expanded
343 */
344 public void refreshTree(Object expandTo){
345 refreshTree();
346 TreeSelection selection = (TreeSelection) viewer.getSelection();
347 viewer.expandToLevel(selection.getFirstElement(), 1);
348 viewer.setSelection(new StructuredSelection(new TreeNode(expandTo)));
349 }
350
351 /**
352 * Refreshes the derivate hierarchy tree
353 */
354 public void refreshTree(){
355 generateMultiLinkSingleReads();
356 labelProvider.setMultiLinkSingleReads(multiLinkSingleReads);
357 viewer.refresh();
358 }
359
360 //FIXME:Remoting hack to make this work for remoting
361 //This should actually be resolved using remoting post operations
362 public void remove(Object obj) {
363 rootElements.remove(obj);
364 viewer.setInput(rootElements);
365 }
366
367 private void generateMultiLinkSingleReads() {
368 Set<SingleRead> multiLinkSingleReads = new HashSet<SingleRead>();
369 for(Entry<SingleRead, Collection<Sequence>> entry:CdmStore.getService(ISequenceService.class).getSingleReadSequencesMap().entrySet()){
370 if(entry.getValue().size()>1){
371 multiLinkSingleReads.add(entry.getKey());
372 }
373 }
374 this.multiLinkSingleReads = multiLinkSingleReads;
375 }
376
377 /**
378 * @return a set of {@link SingleRead}s that have multiple parents
379 */
380 public Set<SingleRead> getMultiLinkSingleReads() {
381 return this.multiLinkSingleReads;
382 }
383
384 public ISelection getSelection() {
385 return selection;
386 }
387
388 public DerivateLabelProvider getLabelProvider() {
389 return labelProvider;
390 }
391
392 @Override
393 public boolean postOperation(CdmBase objectAffectedByOperation) {
394 refreshTree();
395 if(objectAffectedByOperation!=null){
396 changed(objectAffectedByOperation);
397 }
398 return true;
399 }
400
401 @Override
402 public boolean onComplete() {
403 return true;
404 }
405
406
407 @Override
408 public boolean canAttachMedia() {
409 return true;
410 }
411
412 public void removeHierarchy(SpecimenOrObservationBase<?> specimenOrObservationBase) {
413 SpecimenOrObservationBase<?> rootElement = derivateToRootEntityMap.remove(specimenOrObservationBase);
414 rootElements.remove(rootElement);
415 }
416
417 public void addHierarchy(FieldUnit fieldUnit) {
418 rootElements.add(fieldUnit);
419 derivateToRootEntityMap.put(fieldUnit, fieldUnit);
420 }
421
422 @Override
423 public ICdmEntitySession getCdmEntitySession() {
424 return cdmEntitySession;
425 }
426
427 @Override
428 public void dispose() {
429 super.dispose();
430 if(conversation!=null){
431 conversation.close();
432 }
433 if(cdmEntitySession != null) {
434 cdmEntitySession.dispose();
435 }
436 }
437
438 @Override
439 public void selectionChanged(IWorkbenchPart part, ISelection selection) {
440 if(listenToSelectionChange && selection instanceof IStructuredSelection){
441 Object selectedElement = ((IStructuredSelection) selection).getFirstElement();
442 if(selectedElement instanceof Taxon){
443 Collection<SpecimenOrObservationBase> fieldUnits = CdmStore.getService(IOccurrenceService.class).listFieldUnitsByAssociatedTaxon((Taxon) selectedElement, null, null);
444 Collection<UUID> uuids = new HashSet<UUID>();
445 for (SpecimenOrObservationBase specimenOrObservationBase : fieldUnits) {
446 uuids.add(specimenOrObservationBase.getUuid());
447 }
448 updateRootEntities(uuids);
449 }
450 }
451 }
452
453 @Override
454 public void createViewer(Composite parent) {
455 // TODO Auto-generated method stub
456
457 }
458
459 @Override
460 public boolean isSaveOnCloseNeeded() {
461 return isDirty();
462 }
463
464 @Override
465 public int promptToSaveOnClose() {
466 return ISaveablePart2.DEFAULT;
467 }
468
469 public void toggleListenToSelectionChange() {
470 listenToSelectionChange = !listenToSelectionChange;
471 derivateSearchCompositeController.setEnabled(listenToSelectionChange);
472 }
473
474 }