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