3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
11 package eu
.etaxonomy
.taxeditor
.model
;
13 import java
.lang
.reflect
.InvocationTargetException
;
14 import java
.util
.Collection
;
15 import java
.util
.Comparator
;
16 import java
.util
.LinkedHashMap
;
18 import java
.util
.Map
.Entry
;
20 import java
.util
.TreeMap
;
21 import java
.util
.TreeSet
;
23 import org
.apache
.log4j
.Logger
;
24 import org
.eclipse
.core
.commands
.ExecutionException
;
25 import org
.eclipse
.core
.commands
.operations
.IOperationHistory
;
26 import org
.eclipse
.core
.runtime
.IAdaptable
;
27 import org
.eclipse
.core
.runtime
.ILog
;
28 import org
.eclipse
.core
.runtime
.IProgressMonitor
;
29 import org
.eclipse
.core
.runtime
.IStatus
;
30 import org
.eclipse
.core
.runtime
.NullProgressMonitor
;
31 import org
.eclipse
.core
.runtime
.OperationCanceledException
;
32 import org
.eclipse
.core
.runtime
.Status
;
33 import org
.eclipse
.core
.runtime
.SubProgressMonitor
;
34 import org
.eclipse
.core
.runtime
.jobs
.ISchedulingRule
;
35 import org
.eclipse
.jface
.action
.IStatusLineManager
;
36 import org
.eclipse
.jface
.dialogs
.MessageDialog
;
37 import org
.eclipse
.jface
.operation
.IRunnableWithProgress
;
38 import org
.eclipse
.jface
.resource
.ColorRegistry
;
39 import org
.eclipse
.jface
.resource
.FontRegistry
;
40 import org
.eclipse
.jface
.window
.ApplicationWindow
;
41 import org
.eclipse
.swt
.graphics
.Color
;
42 import org
.eclipse
.swt
.graphics
.Font
;
43 import org
.eclipse
.swt
.widgets
.Display
;
44 import org
.eclipse
.swt
.widgets
.Shell
;
45 import org
.eclipse
.ui
.IEditorPart
;
46 import org
.eclipse
.ui
.IViewPart
;
47 import org
.eclipse
.ui
.IViewReference
;
48 import org
.eclipse
.ui
.IWorkbench
;
49 import org
.eclipse
.ui
.IWorkbenchPage
;
50 import org
.eclipse
.ui
.IWorkbenchPart
;
51 import org
.eclipse
.ui
.PartInitException
;
52 import org
.eclipse
.ui
.PlatformUI
;
53 import org
.eclipse
.ui
.ide
.undo
.WorkspaceUndoUtil
;
54 import org
.eclipse
.ui
.part
.EditorPart
;
55 import org
.eclipse
.ui
.progress
.IProgressService
;
56 import org
.eclipse
.ui
.progress
.IWorkbenchSiteProgressService
;
57 import org
.eclipse
.ui
.themes
.ITheme
;
58 import org
.eclipse
.ui
.themes
.IThemeManager
;
60 import eu
.etaxonomy
.cdm
.model
.common
.IEnumTerm
;
61 import eu
.etaxonomy
.taxeditor
.operation
.AbstractPostOperation
;
62 import eu
.etaxonomy
.taxeditor
.operation
.IPostOperationEnabled
;
63 import eu
.etaxonomy
.taxeditor
.store
.internal
.TaxeditorStorePlugin
;
64 import eu
.etaxonomy
.taxeditor
.view
.AbstractCdmDataViewer
;
65 import eu
.etaxonomy
.taxeditor
.view
.detail
.DetailsViewPart
;
66 import eu
.etaxonomy
.taxeditor
.view
.supplementaldata
.SupplementalDataViewPart
;
70 * Abstract AbstractUtility class.
77 public abstract class AbstractUtility
{
79 /** Constant <code>statusLineManager</code> */
80 protected static IStatusLineManager statusLineManager
;
89 public static boolean closeAll() {
90 return getActivePage().closeAllEditors(true);
94 * Close the given editor.
97 * The <tt>MultipageTaxonEditor</tt> to close.
98 * @return <tt>true</tt> on success
100 public static boolean close(EditorPart editor
) {
101 return getActivePage().closeEditor(editor
, true);
109 * @return a {@link org.eclipse.swt.widgets.Shell} object.
111 public static Shell
getShell() {
113 return TaxeditorStorePlugin
.getDefault().getWorkbench()
114 .getActiveWorkbenchWindow().getShell();
122 * @return a {@link org.eclipse.ui.IWorkbenchPage} object.
124 public static IWorkbenchPage
getActivePage() {
126 return TaxeditorStorePlugin
.getDefault().getWorkbench()
127 .getActiveWorkbenchWindow().getActivePage();
135 * @return a {@link org.eclipse.ui.IWorkbenchPart} object.
137 public static IWorkbenchPart
getActivePart() {
138 return getActivePage() != null ?
getActivePage().getActivePart() : null;
141 public static IWorkbench
getWorkbench() {
142 return TaxeditorStorePlugin
.getDefault().getWorkbench();
150 * @return a {@link org.eclipse.jface.window.ApplicationWindow} object.
152 public static ApplicationWindow
getWorkbenchWindow() {
153 if (getWorkbench().getWorkbenchWindowCount() > 1) {
154 throw new IllegalStateException("More than one workbench window");
156 return (ApplicationWindow
) getWorkbench().getWorkbenchWindows()[0];
165 * a {@link java.lang.String} object.
166 * @return a {@link org.eclipse.ui.IViewPart} object.
168 public static IViewPart
showView(String id
) {
170 return PlatformUI
.getWorkbench().getActiveWorkbenchWindow()
172 .showView(id
, null, IWorkbenchPage
.VIEW_VISIBLE
);
173 } catch (PartInitException e
) {
174 errorDialog("Error opening view", AbstractUtility
.class, "Could not open view: " + id
, e
);
185 * a {@link org.eclipse.ui.IViewPart} object.
187 public static void hideView(IViewPart view
) {
188 PlatformUI
.getWorkbench().getActiveWorkbenchWindow().getActivePage()
198 * a {@link java.lang.String} object.
201 * @return a {@link org.eclipse.ui.IViewPart} object.
203 public static IViewPart
getView(String id
, boolean restore
) {
204 IViewReference
[] references
= PlatformUI
.getWorkbench()
205 .getActiveWorkbenchWindow().getActivePage().getViewReferences();
206 for (IViewReference reference
: references
) {
207 if (reference
.getId().equals(id
)) {
208 return reference
.getView(restore
);
220 * a {@link java.lang.Class} object.
221 * @return a {@link java.lang.Object} object.
223 public static Object
getService(Class api
) {
224 return TaxeditorStorePlugin
.getDefault().getWorkbench().getService(api
);
232 * @return a {@link org.eclipse.ui.themes.ITheme} object.
234 public static ITheme
getCurrentTheme() {
235 IThemeManager themeManager
= TaxeditorStorePlugin
.getDefault()
236 .getWorkbench().getThemeManager();
237 return themeManager
.getCurrentTheme();
241 * Fonts registered to the plugin may be obtained with the Eclipse themeing
242 * functionality. Thus fonts are chooseable by the user via
243 * Preferences->General->Appearance->Colors and Fonts
245 * @return the FontRegistry for the current theme
247 public static FontRegistry
getFontRegistry() {
248 return getCurrentTheme().getFontRegistry();
256 * @param symbolicName
257 * a {@link java.lang.String} object.
258 * @return a {@link org.eclipse.swt.graphics.Font} object.
260 public static Font
getFont(String symbolicName
) {
261 return getFontRegistry().get(symbolicName
);
265 * Color registered to the plugin may be obtained with the Eclipse themeing
266 * functionality. Thus colors are editable by the user via
267 * Preferences->General->Appearance->Colors and Fonts
269 * @return the ColorRegistry for the current theme
271 public static ColorRegistry
getColorRegistry() {
272 return getCurrentTheme().getColorRegistry();
280 * @param symbolicName
281 * a {@link java.lang.String} object.
282 * @return a {@link org.eclipse.swt.graphics.Color} object.
284 public static Color
getColor(String symbolicName
) {
285 return getColorRegistry().get(symbolicName
);
289 * Open a message box that informs the user about unimplemented
290 * functionality. This method is for developer convenience.
293 * a {@link java.lang.Object} object.
295 public static void notImplementedMessage(Object source
) {
296 warningDialog("Not yet implemented", source
,
297 "This functionality is not yet implemented.");
306 * a {@link java.lang.String} object.
308 * a {@link java.lang.String} object.
310 public static void informationDialog(final String title
,
311 final String message
) {
312 Display
.getDefault().asyncExec(new Runnable() {
316 MessageDialog
.openInformation(getShell(), title
, message
);
321 public static void informationDialog(final String title
,
322 final IStatus status
) {
323 informationDialog(title
, status
.getMessage());
334 * The object where the warning was generated (used by log4j)
336 * An informative String to be presented to the user
338 public static void warningDialog(final String title
, final Object source
,
339 final String message
) {
340 Display
.getDefault().asyncExec(new Runnable() {
344 MessageDialog
.openWarning(getShell(), title
, message
);
345 Class
<?
extends Object
> clazz
= source
!= null ? source
346 .getClass() : AbstractUtility
.class;
347 warn(clazz
, message
);
357 public static void warningDialog(String title
, Object source
,
359 warningDialog(title
, source
, status
.getMessage());
370 * The object where the warning was generated (used by log4j)
372 * An informative String to be presented to the user
376 * a Throwable if one exists or null
378 public static void errorDialog(final String title
, final Object source
,
379 final String message
, final Throwable t
) {
380 Display
.getDefault().asyncExec(new Runnable() {
384 MessageDialog
.openError(getShell(), title
, message
+ getCauseRecursively(t
));
385 Class
<?
extends Object
> clazz
= source
!= null ? source
386 .getClass() : this.getClass();
387 error(clazz
, message
, t
);
390 private String
getCauseRecursively(Throwable t
) {
395 if(t
.getCause() != null){
396 return getCauseRecursively(t
.getCause());
398 return String
.format("\n\nException: %s\nMessage: %s", t
.getClass().getSimpleName(), t
.getMessage());
405 public static void errorDialog(final String title
, final Object source
,
406 final String message
){
407 errorDialog(title
, source
, message
, null);
416 * a {@link java.lang.String} object.
418 * a {@link java.lang.Object} object.
420 * a {@link org.eclipse.core.runtime.IStatus} object.
422 public static void errorDialog(final String title
, final Object source
,
423 final IStatus status
) {
424 Display
.getDefault().asyncExec(new Runnable() {
428 MessageDialog
.openError(getShell(), title
, status
.getMessage());
429 Class
<?
extends Object
> clazz
= source
!= null ? source
430 .getClass() : this.getClass();
431 error(clazz
, status
.getMessage(), status
.getException());
442 * a {@link java.lang.String} object.
444 * a {@link java.lang.String} object.
447 public static boolean confirmDialog(String title
, String message
) {
448 return MessageDialog
.openQuestion(getShell(), title
, message
);
458 * {@link eu.etaxonomy.taxeditor.operation.AbstractPostOperation}
460 * @return a {@link org.eclipse.core.runtime.IStatus} object.
462 public static IStatus
executeOperation(final AbstractPostOperation operation
) {
463 if (getOperationHistory() == null) {
464 throw new IllegalArgumentException(
465 "There is no operation history for this context");
468 final IAdaptable uiInfoAdapter
= WorkspaceUndoUtil
469 .getUIInfoAdapter(getShell());
471 IRunnableWithProgress runnable
= new IRunnableWithProgress() {
474 public void run(IProgressMonitor monitor
)
475 throws InvocationTargetException
, InterruptedException
{
476 monitor
.beginTask(operation
.getLabel(), 100);
477 IStatus status
= Status
.CANCEL_STATUS
;
479 operation
.addContext(IOperationHistory
.GLOBAL_UNDO_CONTEXT
);
480 status
= getOperationHistory().execute(operation
, monitor
,
482 } catch (ExecutionException e
) {
483 errorDialog("Error executing operation", getClass(), String
.format("An error occured while executing %s.", operation
.getLabel()), e
);
488 String statusString
= status
.equals(Status
.OK_STATUS
) ?
"completed"
490 setStatusLine(operation
.getLabel() + " " + statusString
+ ".");
496 runInUI(runnable
, null);
497 } catch (Exception e
) {
498 errorDialog("Error executing operation", AbstractUtility
.class, "An error occured while executing " + operation
.getLabel(), e
);
501 // // Start the main progress monitor.
502 // IProgressMonitor newMonitor =
503 // startMainMonitor(getMonitor(),operation.getLabel(), 100);
505 // // Check whether operation was canceled and do some steps.
506 // workedChecked(newMonitor, 10);
509 // IStatus status = getOperationHistory().execute(operation, newMonitor,
510 // WorkspaceUndoUtil.getUIInfoAdapter(getShell()));
512 // // Check whether operation was canceled and do some steps.
513 // workedChecked(newMonitor, 30);
515 // String statusString = status.equals(Status.OK_STATUS) ? "completed" :
517 // setStatusLine(operation.getLabel() + " " + statusString + ".");
520 // } catch (ExecutionException e) {
521 // logger.error("Error executing operation: " + operation.getLabel(),
523 // errorDialog("Error executing operation: " + operation.getLabel(),
524 // "Please refer to the error log.");
528 // // Stop the progress monitor.
529 // newMonitor.done();
532 IPostOperationEnabled postOperationEnabled
= operation
533 .getPostOperationEnabled();
534 if (postOperationEnabled
!= null) {
535 postOperationEnabled
.onComplete();
537 return Status
.OK_STATUS
;
542 * getOperationHistory
545 * @return a {@link org.eclipse.core.commands.operations.IOperationHistory}
548 public static IOperationHistory
getOperationHistory() {
549 return getWorkbench().getOperationSupport().getOperationHistory();
558 * a {@link java.lang.String} object.
560 public static void setStatusLine(final String message
) {
561 Display
.getDefault().asyncExec(new Runnable() {
565 statusLineManager
.setMessage(message
);
577 * @return a {@link org.eclipse.core.runtime.IProgressMonitor} object.
579 public static IProgressMonitor
getMonitor() {
580 statusLineManager
.setCancelEnabled(false);
581 return statusLineManager
.getProgressMonitor();
585 * Starts either the given {@link IProgressMonitor} if it's not
586 * <code>null</code> or a new {@link NullProgressMonitor}.
588 * @param progressMonitor
589 * The {@link IProgressMonitor} or <code>null</code> if no
590 * progress should be reported.
592 * The name of the main task.
594 * The number of steps this task is subdivided into.
595 * @return The {@link IProgressMonitor}.
597 public static IProgressMonitor
startMainMonitor(
598 IProgressMonitor progressMonitor
, String taskName
, int steps
) {
599 IProgressMonitor newMonitor
= progressMonitor
;
600 if (newMonitor
== null) {
601 newMonitor
= new NullProgressMonitor();
603 newMonitor
.beginTask(taskName
== null ?
"" : taskName
, steps
);
604 newMonitor
.subTask(" ");
609 * Creates a {@link SubProgressMonitor} if the given
610 * {@link IProgressMonitor} is not <code>null</code> and not a
611 * {@link NullProgressMonitor}.
613 * @param progressMonitor
614 * The parent {@link IProgressMonitor} of the
615 * {@link SubProgressMonitor} to be created.
617 * The number of steps this subtask is subdivided into. Must be a
618 * positive number and must not be
619 * {@link IProgressMonitor#UNKNOWN}.
620 * @return The {@link IProgressMonitor}.
622 public static IProgressMonitor
getSubProgressMonitor(
623 IProgressMonitor progressMonitor
, int ticks
) {
624 if (progressMonitor
== null) {
625 return new NullProgressMonitor();
627 if (progressMonitor
instanceof NullProgressMonitor
) {
628 return progressMonitor
;
631 return new SubProgressMonitor(progressMonitor
, ticks
);
635 * Checks whether the user canceled this operation. If not canceled, the
636 * given number of steps are declared as done.
639 * a {@link org.eclipse.core.runtime.IProgressMonitor} object.
643 public static void workedChecked(IProgressMonitor newMonitor
, int steps
) {
644 // In case the progress monitor was canceled throw an exception.
645 if (newMonitor
.isCanceled()) {
646 throw new OperationCanceledException();
648 // Otherwise declare this step as done.
649 newMonitor
.worked(steps
);
653 * Present a progress dialog to the user. This dialog will block the UI
656 * an implementation of {@link IRunnableWithProgress}
657 * @throws java.lang.InterruptedException
659 * @throws java.lang.reflect.InvocationTargetException
662 public static void busyCursorWhile(IRunnableWithProgress runnable
)
663 throws InvocationTargetException
, InterruptedException
{
664 getProgressService().busyCursorWhile(runnable
);
672 * @see {@link IProgressService#runInUI(org.eclipse.jface.operation.IRunnableContext, IRunnableWithProgress, ISchedulingRule)}
674 * a {@link org.eclipse.jface.operation.IRunnableWithProgress}
677 * a {@link org.eclipse.core.runtime.jobs.ISchedulingRule}
679 * @throws java.lang.reflect.InvocationTargetException
681 * @throws java.lang.InterruptedException
684 public static void runInUI(IRunnableWithProgress runnable
,
685 ISchedulingRule rule
) throws InvocationTargetException
,
686 InterruptedException
{
687 getProgressService().runInUI(getWorkbenchWindow(), runnable
, rule
);
700 * a {@link org.eclipse.jface.operation.IRunnableWithProgress}
702 * @throws java.lang.reflect.InvocationTargetException
704 * @throws java.lang.InterruptedException
707 public static void run(boolean fork
, boolean cancelable
,
708 IRunnableWithProgress runnable
) throws InvocationTargetException
,
709 InterruptedException
{
710 getProgressService().run(fork
, cancelable
, runnable
);
718 * @return a {@link org.eclipse.ui.progress.IProgressService} object.
720 public static IProgressService
getProgressService() {
721 IWorkbench workbench
= PlatformUI
.getWorkbench();
722 return workbench
.getProgressService();
727 * getProgressService2
730 * @return a {@link org.eclipse.ui.progress.IWorkbenchSiteProgressService}
733 public static IWorkbenchSiteProgressService
getProgressService2() {
734 return (IWorkbenchSiteProgressService
) getService(IWorkbenchSiteProgressService
.class);
743 * a {@link java.lang.String} object.
745 public static void info(String message
) {
746 IStatus status
= new Status(IStatus
.INFO
, getPluginId(), message
);
756 * a {@link org.eclipse.core.runtime.IStatus} object.
758 public static void info(IStatus status
) {
768 * a {@link java.lang.Class} object.
770 * a {@link java.lang.String} object.
772 public static void warn(Class source
, String message
) {
773 IStatus status
= new Status(IStatus
.WARNING
, getPluginId(), message
);
774 getLog4JLogger(source
).warn(message
);
778 public static void warn(Class source
, IStatus status
) {
779 getLog4JLogger(source
).warn(status
.getMessage(), status
.getException());
783 public static void warn(Class source
, Throwable t
) {
784 IStatus status
= new Status(IStatus
.WARNING
, getPluginId(), t
.getMessage(), t
);
785 getLog4JLogger(source
).warn(t
);
795 * a {@link java.lang.Class} object.
797 * a {@link java.lang.Throwable} object.
799 public static void error(Class source
, Throwable t
) {
800 error(source
.getClass(), t
.getMessage(), t
);
809 * a {@link java.lang.Class} object.
811 * a {@link java.lang.String} object.
813 * a {@link java.lang.Throwable} object.
815 public static void error(Class source
, String message
, Throwable t
) {
816 IStatus status
= new Status(IStatus
.ERROR
, getPluginId(), message
, t
);
817 error(source
, status
);
826 * a {@link java.lang.Class} object.
828 * a {@link org.eclipse.core.runtime.IStatus} object.
830 public static void error(Class source
, IStatus status
) {
831 getLog4JLogger(source
)
832 .error(status
.getMessage(), status
.getException());
842 * a {@link java.lang.Class} object.
843 * @return a {@link org.apache.log4j.Logger} object.
845 public static Logger
getLog4JLogger(Class clazz
) {
846 return Logger
.getLogger(clazz
);
850 * @see {@link ILog#log(IStatus)}
854 private static void log(IStatus status
) {
855 TaxeditorStorePlugin
.getDefault().getLog().log(status
);
863 * @return a {@link java.lang.String} object.
865 public static String
getPluginId() {
866 return "eu.taxeditor";
874 * @return a {@link org.eclipse.ui.IEditorPart} object.
876 public static IEditorPart
getActiveEditor() {
877 return getActivePage() != null ?
getActivePage().getActiveEditor()
886 * @return a {@link eu.etaxonomy.taxeditor.view.detail.DetailsViewPart}
889 public static DetailsViewPart
getDetailsView() {
890 return (DetailsViewPart
) getView(DetailsViewPart
.ID
, false);
895 * refreshDetailsViewer
898 public static void refreshDetailsViewer() {
899 if (getDetailsView() != null) {
900 ((AbstractCdmDataViewer
) getDetailsView().getViewer()).refresh();
906 * reflowDetailsViewer
909 public static void reflowDetailsViewer() {
910 if (getDetailsView() != null) {
911 ((AbstractCdmDataViewer
) getDetailsView().getViewer()).reflow();
915 public static SupplementalDataViewPart
getSupplementalDataView() {
916 return (SupplementalDataViewPart
) getView(SupplementalDataViewPart
.ID
,
920 public static void reflowSupplementalViewer() {
921 if (getSupplementalDataView() != null) {
922 ((AbstractCdmDataViewer
) getSupplementalDataView().getViewer())
928 * Orders a Collection of {@link IEnumTerm}s according to the term
929 * hierarchy. The hierarchy will be reduced to two layers: one layer being
930 * the root elements (that have no parents) and the other being their
931 * children and children's children recursively.<br>
932 * The returned map will be be ordered primarily by root elements and
933 * secondarily by the child elements, both ascending alphabetically. <br>
935 * The reduced hierarchy could look like this:<br>
944 * <ul><li>child4</ul>
948 * A {@link Collection} of {@link IEnumTerm}s for which the term
949 * hierarchy should be created
950 * @return a map which holds the terms as keys and their string
951 * representation via {@link IEnumTerm#getMessage()} as values
953 public static <T
extends IEnumTerm
<?
>> Map
<T
, String
> orderTerms(Collection
<T
> terms
) {
954 Comparator
<T
> comparator
= new Comparator
<T
>() {
956 public int compare(T t1
, T t2
) {
957 return t1
.getMessage().compareTo(t2
.getMessage());
960 Map
<T
, String
> result
= new LinkedHashMap
<T
, String
>();
961 Map
<T
, Set
<T
>> termHierarchy
= new TreeMap
<T
, Set
<T
>>(comparator
);
963 for(T term
: terms
) {
964 Set
<T
> childList
= new TreeSet
<T
>(comparator
);
965 // add root element as keys
966 if(term
.getKindOf()==null){
967 termHierarchy
.put(term
, childList
);
969 // add child element to topmost parent i.e. root
971 T root
= getRootFor(term
);
972 if(termHierarchy
.containsKey(root
)){
973 termHierarchy
.get(root
).add(term
);
977 termHierarchy
.put(root
, childList
);
982 // create list according to the type hierarchy (root elements alphabetically with recursive children also alphabetically)
983 for(Entry
<T
, Set
<T
>> entry
:termHierarchy
.entrySet()){
984 T root
= entry
.getKey();
985 result
.put(root
, root
.getMessage());
986 for(T child
:entry
.getValue()){
987 result
.put(child
, " " + child
.getMessage());
993 @SuppressWarnings("unchecked")
995 * Recursively iterates over all term parents until no more parent is found i.e. the root node
996 * @param term The term for which the parent should be found
997 * @return the root terms of the term hierarchy
999 private static<T
extends IEnumTerm
<?
>> T
getRootFor(T term
){
1000 // PP: cast should be safe. Why is Eclipse complaining??
1001 T parent
= (T
) term
.getKindOf();
1006 return getRootFor((T
) term
.getKindOf());