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