Project

General

Profile

Download (21.7 KB) Statistics
| Branch: | Tag: | Revision:
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
}
(2-2/35)