added new error dialog for operations
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / model / AbstractUtility.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.model;
12
13 import java.lang.reflect.InvocationTargetException;
14 import java.util.ArrayList;
15 import java.util.Collection;
16 import java.util.LinkedHashMap;
17 import java.util.List;
18 import java.util.TreeSet;
19
20 import org.eclipse.core.commands.ExecutionException;
21 import org.eclipse.core.commands.operations.IOperationHistory;
22 import org.eclipse.core.runtime.IAdaptable;
23 import org.eclipse.core.runtime.IProgressMonitor;
24 import org.eclipse.core.runtime.IStatus;
25 import org.eclipse.core.runtime.NullProgressMonitor;
26 import org.eclipse.core.runtime.OperationCanceledException;
27 import org.eclipse.core.runtime.Status;
28 import org.eclipse.core.runtime.SubProgressMonitor;
29 import org.eclipse.core.runtime.jobs.ISchedulingRule;
30 import org.eclipse.jface.action.IStatusLineManager;
31 import org.eclipse.jface.operation.IRunnableWithProgress;
32 import org.eclipse.jface.resource.ColorRegistry;
33 import org.eclipse.jface.resource.FontRegistry;
34 import org.eclipse.jface.window.ApplicationWindow;
35 import org.eclipse.swt.graphics.Color;
36 import org.eclipse.swt.graphics.Font;
37 import org.eclipse.swt.widgets.Display;
38 import org.eclipse.swt.widgets.Shell;
39 import org.eclipse.ui.IEditorPart;
40 import org.eclipse.ui.IViewPart;
41 import org.eclipse.ui.IViewReference;
42 import org.eclipse.ui.IWorkbench;
43 import org.eclipse.ui.IWorkbenchPage;
44 import org.eclipse.ui.IWorkbenchPart;
45 import org.eclipse.ui.PartInitException;
46 import org.eclipse.ui.PlatformUI;
47 import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
48 import org.eclipse.ui.part.EditorPart;
49 import org.eclipse.ui.progress.IProgressService;
50 import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
51 import org.eclipse.ui.themes.ITheme;
52 import org.eclipse.ui.themes.IThemeManager;
53
54 import eu.etaxonomy.cdm.model.common.IEnumTerm;
55 import eu.etaxonomy.taxeditor.operation.AbstractPostOperation;
56 import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
57 import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
58 import eu.etaxonomy.taxeditor.view.AbstractCdmDataViewer;
59 import eu.etaxonomy.taxeditor.view.detail.DetailsViewPart;
60 import eu.etaxonomy.taxeditor.view.supplementaldata.SupplementalDataViewPart;
61
62 /**
63 * <p>
64 * Abstract AbstractUtility class.
65 * </p>
66 *
67 * @author n.hoffmann
68 * @created 11.05.2009
69 * @version 1.0
70 */
71 public abstract class AbstractUtility {
72
73 /** Constant <code>statusLineManager</code> */
74 protected static IStatusLineManager statusLineManager;
75
76
77 /**
78 * <p>
79 * closeAll
80 * </p>
81 *
82 * @return a boolean.
83 */
84 public static boolean closeAll() {
85 return getActivePage().closeAllEditors(true);
86 }
87
88 /**
89 * Close the given editor.
90 *
91 * @param editor
92 * The <tt>MultipageTaxonEditor</tt> to close.
93 * @return <tt>true</tt> on success
94 */
95 public static boolean close(EditorPart editor) {
96 return getActivePage().closeEditor(editor, true);
97 }
98
99 /**
100 * <p>
101 * getShell
102 * </p>
103 *
104 * @return a {@link org.eclipse.swt.widgets.Shell} object.
105 */
106 public static Shell getShell() {
107
108 return TaxeditorStorePlugin.getDefault().getWorkbench()
109 .getActiveWorkbenchWindow().getShell();
110 }
111
112 /**
113 * <p>
114 * getActivePage
115 * </p>
116 *
117 * @return a {@link org.eclipse.ui.IWorkbenchPage} object.
118 */
119 public static IWorkbenchPage getActivePage() {
120
121 return TaxeditorStorePlugin.getDefault().getWorkbench()
122 .getActiveWorkbenchWindow().getActivePage();
123 }
124
125 /**
126 * <p>
127 * getActivePart
128 * </p>
129 *
130 * @return a {@link org.eclipse.ui.IWorkbenchPart} object.
131 */
132 public static IWorkbenchPart getActivePart() {
133 return getActivePage() != null ? getActivePage().getActivePart() : null;
134 }
135
136 public static IWorkbench getWorkbench() {
137 return TaxeditorStorePlugin.getDefault().getWorkbench();
138 }
139
140 /**
141 * <p>
142 * getWorkbenchWindow
143 * </p>
144 *
145 * @return a {@link org.eclipse.jface.window.ApplicationWindow} object.
146 */
147 public static ApplicationWindow getWorkbenchWindow() {
148 if (getWorkbench().getWorkbenchWindowCount() > 1) {
149 throw new IllegalStateException("More than one workbench window");
150 }
151 return (ApplicationWindow) getWorkbench().getWorkbenchWindows()[0];
152 }
153
154 /**
155 * <p>
156 * showView
157 * </p>
158 *
159 * @param id
160 * a {@link java.lang.String} object.
161 * @return a {@link org.eclipse.ui.IViewPart} object.
162 */
163 public static IViewPart showView(String id) {
164 try {
165 return PlatformUI.getWorkbench().getActiveWorkbenchWindow()
166 .getActivePage()
167 .showView(id, null, IWorkbenchPage.VIEW_VISIBLE);
168 } catch (PartInitException e) {
169 MessagingUtils.messageDialog("Error opening view", AbstractUtility.class, "Could not open view: " + id, e);
170 return null;
171 }
172 }
173
174 /**
175 * <p>
176 * hideView
177 * </p>
178 *
179 * @param view
180 * a {@link org.eclipse.ui.IViewPart} object.
181 */
182 public static void hideView(IViewPart view) {
183 PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
184 .hideView(view);
185 }
186
187 /**
188 * <p>
189 * getView
190 * </p>
191 *
192 * @param id
193 * a {@link java.lang.String} object.
194 * @param restore
195 * a boolean.
196 * @return a {@link org.eclipse.ui.IViewPart} object.
197 */
198 public static IViewPart getView(String id, boolean restore) {
199 IViewReference[] references = PlatformUI.getWorkbench()
200 .getActiveWorkbenchWindow().getActivePage().getViewReferences();
201 for (IViewReference reference : references) {
202 if (reference.getId().equals(id)) {
203 return reference.getView(restore);
204 }
205 }
206 return null;
207 }
208
209 /**
210 * <p>
211 * getService
212 * </p>
213 *
214 * @param api
215 * a {@link java.lang.Class} object.
216 * @return a {@link java.lang.Object} object.
217 */
218 public static Object getService(Class api) {
219 return TaxeditorStorePlugin.getDefault().getWorkbench().getService(api);
220 }
221
222 /**
223 * <p>
224 * getCurrentTheme
225 * </p>
226 *
227 * @return a {@link org.eclipse.ui.themes.ITheme} object.
228 */
229 public static ITheme getCurrentTheme() {
230 IThemeManager themeManager = TaxeditorStorePlugin.getDefault()
231 .getWorkbench().getThemeManager();
232 return themeManager.getCurrentTheme();
233 }
234
235 /**
236 * Fonts registered to the plugin may be obtained with the Eclipse themeing
237 * functionality. Thus fonts are chooseable by the user via
238 * Preferences->General->Appearance->Colors and Fonts
239 *
240 * @return the FontRegistry for the current theme
241 */
242 public static FontRegistry getFontRegistry() {
243 return getCurrentTheme().getFontRegistry();
244 }
245
246 /**
247 * <p>
248 * getFont
249 * </p>
250 *
251 * @param symbolicName
252 * a {@link java.lang.String} object.
253 * @return a {@link org.eclipse.swt.graphics.Font} object.
254 */
255 public static Font getFont(String symbolicName) {
256 return getFontRegistry().get(symbolicName);
257 }
258
259 /**
260 * Color registered to the plugin may be obtained with the Eclipse themeing
261 * functionality. Thus colors are editable by the user via
262 * Preferences->General->Appearance->Colors and Fonts
263 *
264 * @return the ColorRegistry for the current theme
265 */
266 public static ColorRegistry getColorRegistry() {
267 return getCurrentTheme().getColorRegistry();
268 }
269
270 /**
271 * <p>
272 * getColor
273 * </p>
274 *
275 * @param symbolicName
276 * a {@link java.lang.String} object.
277 * @return a {@link org.eclipse.swt.graphics.Color} object.
278 */
279 public static Color getColor(String symbolicName) {
280 return getColorRegistry().get(symbolicName);
281 }
282
283 /**
284 * <p>
285 * executeOperation
286 * </p>
287 *
288 * @param operation
289 * a
290 * {@link eu.etaxonomy.taxeditor.operation.AbstractPostTaxonOperation}
291 * object.
292 * @return a {@link org.eclipse.core.runtime.IStatus} object.
293 */
294 public static IStatus executeOperation(final AbstractPostOperation operation) {
295 if (getOperationHistory() == null) {
296 throw new IllegalArgumentException(
297 "There is no operation history for this context");
298 }
299
300 final IAdaptable uiInfoAdapter = WorkspaceUndoUtil
301 .getUIInfoAdapter(getShell());
302
303 IRunnableWithProgress runnable = new IRunnableWithProgress() {
304
305 @Override
306 public void run(IProgressMonitor monitor)
307 throws InvocationTargetException, InterruptedException {
308 String operationlabel = operation.getLabel();
309 monitor.beginTask(operationlabel, 100);
310 IStatus status = Status.CANCEL_STATUS;
311 try {
312 operation.addContext(IOperationHistory.GLOBAL_UNDO_CONTEXT);
313 status = getOperationHistory().execute(operation, monitor,
314 uiInfoAdapter);
315 } catch (ExecutionException e) {
316
317 MessagingUtils.operationDialog(this, e, TaxeditorStorePlugin.PLUGIN_ID, operationlabel, null);
318
319 } finally {
320 monitor.done();
321 }
322
323 String statusString = status.equals(Status.OK_STATUS) ? "completed"
324 : "cancelled";
325 setStatusLine(operationlabel + " " + statusString + ".");
326
327 }
328 };
329
330 try {
331 runInUI(runnable, null);
332 } catch (Exception e) {
333 MessagingUtils.messageDialog("Error executing operation", AbstractUtility.class, "An error occured while executing " + operation.getLabel(), e);
334 }
335
336 IPostOperationEnabled postOperationEnabled = operation
337 .getPostOperationEnabled();
338 if (postOperationEnabled != null) {
339 postOperationEnabled.onComplete();
340 }
341 return Status.OK_STATUS;
342 }
343
344 /**
345 * <p>
346 * getOperationHistory
347 * </p>
348 *
349 * @return a {@link org.eclipse.core.commands.operations.IOperationHistory}
350 * object.
351 */
352 public static IOperationHistory getOperationHistory() {
353 return getWorkbench().getOperationSupport().getOperationHistory();
354 }
355
356 /**
357 * <p>
358 * setStatusLine
359 * </p>
360 *
361 * @param message
362 * a {@link java.lang.String} object.
363 */
364 public static void setStatusLine(final String message) {
365 Display.getDefault().asyncExec(new Runnable() {
366
367 @Override
368 public void run() {
369 statusLineManager.setMessage(message);
370 }
371
372 });
373
374 }
375
376 /**
377 * <p>
378 * getMonitor
379 * </p>
380 *
381 * @return a {@link org.eclipse.core.runtime.IProgressMonitor} object.
382 */
383 public static IProgressMonitor getMonitor() {
384 statusLineManager.setCancelEnabled(false);
385 return statusLineManager.getProgressMonitor();
386 }
387
388 /**
389 * Starts either the given {@link IProgressMonitor} if it's not
390 * <code>null</code> or a new {@link NullProgressMonitor}.
391 *
392 * @param progressMonitor
393 * The {@link IProgressMonitor} or <code>null</code> if no
394 * progress should be reported.
395 * @param taskName
396 * The name of the main task.
397 * @param steps
398 * The number of steps this task is subdivided into.
399 * @return The {@link IProgressMonitor}.
400 */
401 public static IProgressMonitor startMainMonitor(
402 IProgressMonitor progressMonitor, String taskName, int steps) {
403 IProgressMonitor newMonitor = progressMonitor;
404 if (newMonitor == null) {
405 newMonitor = new NullProgressMonitor();
406 }
407 newMonitor.beginTask(taskName == null ? "" : taskName, steps);
408 newMonitor.subTask(" ");
409 return newMonitor;
410 }
411
412 /**
413 * Creates a {@link SubProgressMonitor} if the given
414 * {@link IProgressMonitor} is not <code>null</code> and not a
415 * {@link NullProgressMonitor}.
416 *
417 * @param progressMonitor
418 * The parent {@link IProgressMonitor} of the
419 * {@link SubProgressMonitor} to be created.
420 * @param ticks
421 * The number of steps this subtask is subdivided into. Must be a
422 * positive number and must not be
423 * {@link IProgressMonitor#UNKNOWN}.
424 * @return The {@link IProgressMonitor}.
425 */
426 public static IProgressMonitor getSubProgressMonitor(
427 IProgressMonitor progressMonitor, int ticks) {
428 if (progressMonitor == null) {
429 return new NullProgressMonitor();
430 }
431 if (progressMonitor instanceof NullProgressMonitor) {
432 return progressMonitor;
433 }
434
435 return new SubProgressMonitor(progressMonitor, ticks);
436 }
437
438 /**
439 * Checks whether the user canceled this operation. If not canceled, the
440 * given number of steps are declared as done.
441 *
442 * @param newMonitor
443 * a {@link org.eclipse.core.runtime.IProgressMonitor} object.
444 * @param steps
445 * a int.
446 */
447 public static void workedChecked(IProgressMonitor newMonitor, int steps) {
448 // In case the progress monitor was canceled throw an exception.
449 if (newMonitor.isCanceled()) {
450 throw new OperationCanceledException();
451 }
452 // Otherwise declare this step as done.
453 newMonitor.worked(steps);
454 }
455
456 /**
457 * Present a progress dialog to the user. This dialog will block the UI
458 *
459 * @param runnable
460 * an implementation of {@link IRunnableWithProgress}
461 * @throws java.lang.InterruptedException
462 * if any.
463 * @throws java.lang.reflect.InvocationTargetException
464 * if any.
465 */
466 public static void busyCursorWhile(IRunnableWithProgress runnable)
467 throws InvocationTargetException, InterruptedException {
468 getProgressService().busyCursorWhile(runnable);
469 }
470
471 /**
472 * <p>
473 * runInUI
474 * </p>
475 *
476 * @see {@link IProgressService#runInUI(org.eclipse.jface.operation.IRunnableContext, IRunnableWithProgress, ISchedulingRule)}
477 * @param runnable
478 * a {@link org.eclipse.jface.operation.IRunnableWithProgress}
479 * object.
480 * @param rule
481 * a {@link org.eclipse.core.runtime.jobs.ISchedulingRule}
482 * object.
483 * @throws java.lang.reflect.InvocationTargetException
484 * if any.
485 * @throws java.lang.InterruptedException
486 * if any.
487 */
488 public static void runInUI(IRunnableWithProgress runnable,
489 ISchedulingRule rule) throws InvocationTargetException,
490 InterruptedException {
491 getProgressService().runInUI(getWorkbenchWindow(), runnable, rule);
492 }
493
494 /**
495 * <p>
496 * run
497 * </p>
498 *
499 * @param fork
500 * a boolean.
501 * @param cancelable
502 * a boolean.
503 * @param runnable
504 * a {@link org.eclipse.jface.operation.IRunnableWithProgress}
505 * object.
506 * @throws java.lang.reflect.InvocationTargetException
507 * if any.
508 * @throws java.lang.InterruptedException
509 * if any.
510 */
511 public static void run(boolean fork, boolean cancelable,
512 IRunnableWithProgress runnable) throws InvocationTargetException,
513 InterruptedException {
514 getProgressService().run(fork, cancelable, runnable);
515 }
516
517 /**
518 * <p>
519 * getProgressService
520 * </p>
521 *
522 * @return a {@link org.eclipse.ui.progress.IProgressService} object.
523 */
524 public static IProgressService getProgressService() {
525 IWorkbench workbench = PlatformUI.getWorkbench();
526 return workbench.getProgressService();
527 }
528
529 /**
530 * <p>
531 * getProgressService2
532 * </p>
533 *
534 * @return a {@link org.eclipse.ui.progress.IWorkbenchSiteProgressService}
535 * object.
536 */
537 public static IWorkbenchSiteProgressService getProgressService2() {
538 return (IWorkbenchSiteProgressService) getService(IWorkbenchSiteProgressService.class);
539 }
540
541 /**
542 * <p>
543 * getPluginId
544 * </p>
545 *
546 * @return a {@link java.lang.String} object.
547 */
548 public static String getPluginId() {
549 return "eu.taxeditor";
550 }
551
552 /**
553 * <p>
554 * getActiveEditor
555 * </p>
556 *
557 * @return a {@link org.eclipse.ui.IEditorPart} object.
558 */
559 public static IEditorPart getActiveEditor() {
560 return getActivePage() != null ? getActivePage().getActiveEditor()
561 : null;
562 }
563
564 /**
565 * <p>
566 * getDetailsView
567 * </p>
568 *
569 * @return a {@link eu.etaxonomy.taxeditor.view.detail.DetailsViewPart}
570 * object.
571 */
572 public static DetailsViewPart getDetailsView() {
573 return (DetailsViewPart) getView(DetailsViewPart.ID, false);
574 }
575
576 /**
577 * <p>
578 * refreshDetailsViewer
579 * </p>
580 */
581 public static void refreshDetailsViewer() {
582 if (getDetailsView() != null) {
583 ((AbstractCdmDataViewer) getDetailsView().getViewer()).refresh();
584 }
585 }
586
587 /**
588 * <p>
589 * reflowDetailsViewer
590 * </p>
591 */
592 public static void reflowDetailsViewer() {
593 if (getDetailsView() != null) {
594 ((AbstractCdmDataViewer) getDetailsView().getViewer()).reflow();
595 }
596 }
597
598 public static SupplementalDataViewPart getSupplementalDataView() {
599 return (SupplementalDataViewPart) getView(SupplementalDataViewPart.ID,
600 false);
601 }
602
603 public static void reflowSupplementalViewer() {
604 if (getSupplementalDataView() != null) {
605 ((AbstractCdmDataViewer) getSupplementalDataView().getViewer())
606 .reflow();
607 }
608 }
609
610
611 /**
612 * Orders a Collection of {@link IEnumTerm}s according to the term
613 * hierarchy. <br>
614 * <br>
615 * The returned map will be be ordered primarily by root elements,
616 * secondarily by the child elements and their children resp., both ascending alphabetically. <br>
617 * @param terms
618 * A {@link Collection} of {@link IEnumTerm}s for which the term
619 * hierarchy should be created
620 * @return a map which holds the terms as keys and their string
621 * representation via {@link IEnumTerm#getMessage()} as values
622 */
623 public static <T extends IEnumTerm<T>> LinkedHashMap<T, String> orderTerms(Collection<T> terms) {
624 TreeSet<TermNode<T>> parentElements = new TreeSet<TermNode<T>>();
625 parentElements.addAll(getTermHierarchy(terms));
626
627 // create list according to the type hierarchy (root elements alphabetically with recursive children also alphabetically)
628 LinkedHashMap<T, String> result = new LinkedHashMap<T, String>();
629 parseTermTree(parentElements, result, -1);
630 return result;
631 }
632
633 private static<T extends IEnumTerm<T>> void parseTermTree(Collection<TermNode<T>> children, LinkedHashMap<T, String> result, int depth){
634 depth++;
635 for(TermNode<T> node:children){
636 String indentString = "";
637 for(int i=0;i<depth;i++){
638 indentString += " ";
639 }
640 if(depth>0){
641 indentString += "- ";
642 }
643 result.put(node.term, indentString + node.term.getMessage());
644 parseTermTree(node.children, result, depth);
645 }
646 }
647
648 private static<T extends IEnumTerm<T>> void addToParents(List<TermNode<T>> parents, Collection<T> terms){
649 List<TermNode<T>> hasChildrenList = new ArrayList<TermNode<T>>();
650 for(T term:terms){
651 // only terms with parents
652 if(term.getKindOf()!=null){
653 TermNode<T> parentNode = new TermNode<T>(term.getKindOf());
654 TermNode<T> childNode = new TermNode<T>(term);
655 if(parents.contains(parentNode)){
656 // parent found in parent list -> add this term to parent's child list
657 parents.get(parents.indexOf(parentNode)).addChild(childNode);
658 if(!term.getGeneralizationOf().isEmpty()){
659 // has more children -> add to list which will be the parent for the next recursion
660 hasChildrenList.add(childNode);
661 }
662 }
663 }
664 }
665 if(!hasChildrenList.isEmpty()){
666 addToParents(hasChildrenList, terms);
667 }
668 }
669
670 private static<T extends IEnumTerm<T>> List<TermNode<T>> getTermHierarchy(Collection<T> terms){
671 List<TermNode<T>> parents = new ArrayList<TermNode<T>>();
672 // get root elements
673 for(T term:terms){
674 T parentTerm = term.getKindOf();
675 if(parentTerm==null){
676 // root element
677 parents.add(new TermNode<T>(term));
678 }
679 }
680 addToParents(parents, terms);
681 return parents;
682 }
683
684 @SuppressWarnings("unchecked")
685 /**
686 * Recursively iterates over all term parents until no more parent is found i.e. the root node
687 * @param term The term for which the parent should be found
688 * @return the root terms of the term hierarchy
689 */
690 private static<T extends IEnumTerm<T>> T getParentFor(T term){
691 // PP: cast should be safe. Why is Eclipse complaining??
692 T parent = term.getKindOf();
693 if(parent==null){
694 return term;
695 }
696 else{
697 return getParentFor(term.getKindOf());
698 }
699 }
700
701 private static class TermNode<T extends IEnumTerm<T>> implements Comparable<TermNode<T>>{
702 private final T term;
703 private final TreeSet<TermNode<T>> children;
704
705 /**
706 * @param term
707 * @param children
708 */
709 public TermNode(T term) {
710 super();
711 this.term = term;
712 this.children = new TreeSet<TermNode<T>>();
713 }
714
715 public void addChild(TermNode<T> child){
716 this.children.add(child);
717 }
718
719 /**
720 * @return the children
721 */
722 public TreeSet<TermNode<T>> getChildren() {
723 return children;
724 }
725
726 /**
727 * @return the term
728 */
729 public T getTerm() {
730 return term;
731 }
732
733 /* (non-Javadoc)
734 * @see java.lang.Object#hashCode()
735 */
736 @Override
737 public int hashCode() {
738 final int prime = 31;
739 int result = 1;
740 result = prime * result + ((term == null) ? 0 : term.hashCode());
741 return result;
742 }
743
744 /* (non-Javadoc)
745 * @see java.lang.Object#equals(java.lang.Object)
746 */
747 @Override
748 public boolean equals(Object obj) {
749 if (this == obj) {
750 return true;
751 }
752 if (obj == null) {
753 return false;
754 }
755 if (getClass() != obj.getClass()) {
756 return false;
757 }
758 TermNode other = (TermNode) obj;
759 if (term == null) {
760 if (other.term != null) {
761 return false;
762 }
763 } else if (!term.equals(other.term)) {
764 return false;
765 }
766 return true;
767 }
768
769 /* (non-Javadoc)
770 * @see java.lang.Comparable#compareTo(java.lang.Object)
771 */
772 @Override
773 public int compareTo(TermNode<T> that) {
774 return this.term.getMessage().compareTo(that.term.getMessage());
775 }
776 }
777
778
779 }