Project

General

Profile

Download (21.9 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.io.PrintWriter;
14
import java.io.StringWriter;
15
import java.io.Writer;
16
import java.lang.reflect.InvocationTargetException;
17
import java.util.ArrayList;
18
import java.util.Collection;
19
import java.util.LinkedHashMap;
20
import java.util.List;
21
import java.util.TreeSet;
22

    
23
import org.eclipse.core.commands.ExecutionException;
24
import org.eclipse.core.commands.operations.IOperationHistory;
25
import org.eclipse.core.runtime.IAdaptable;
26
import org.eclipse.core.runtime.IProgressMonitor;
27
import org.eclipse.core.runtime.IStatus;
28
import org.eclipse.core.runtime.MultiStatus;
29
import org.eclipse.core.runtime.NullProgressMonitor;
30
import org.eclipse.core.runtime.OperationCanceledException;
31
import org.eclipse.core.runtime.Status;
32
import org.eclipse.core.runtime.SubProgressMonitor;
33
import org.eclipse.core.runtime.jobs.ISchedulingRule;
34
import org.eclipse.jface.action.IStatusLineManager;
35
import org.eclipse.jface.operation.IRunnableWithProgress;
36
import org.eclipse.jface.resource.ColorRegistry;
37
import org.eclipse.jface.resource.FontRegistry;
38
import org.eclipse.jface.window.ApplicationWindow;
39
import org.eclipse.swt.graphics.Color;
40
import org.eclipse.swt.graphics.Font;
41
import org.eclipse.swt.widgets.Display;
42
import org.eclipse.swt.widgets.Shell;
43
import org.eclipse.ui.IEditorPart;
44
import org.eclipse.ui.IViewPart;
45
import org.eclipse.ui.IViewReference;
46
import org.eclipse.ui.IWorkbench;
47
import org.eclipse.ui.IWorkbenchPage;
48
import org.eclipse.ui.IWorkbenchPart;
49
import org.eclipse.ui.PartInitException;
50
import org.eclipse.ui.PlatformUI;
51
import org.eclipse.ui.ide.undo.WorkspaceUndoUtil;
52
import org.eclipse.ui.part.EditorPart;
53
import org.eclipse.ui.progress.IProgressService;
54
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
55
import org.eclipse.ui.themes.ITheme;
56
import org.eclipse.ui.themes.IThemeManager;
57

    
58
import eu.etaxonomy.cdm.model.common.IEnumTerm;
59
import eu.etaxonomy.cdm.persistence.hibernate.permission.SecurityExceptionUtils;
60
import eu.etaxonomy.taxeditor.operation.AbstractPostOperation;
61
import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
62
import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
63
import eu.etaxonomy.taxeditor.view.AbstractCdmDataViewer;
64
import eu.etaxonomy.taxeditor.view.detail.DetailsViewPart;
65
import eu.etaxonomy.taxeditor.view.supplementaldata.SupplementalDataViewPart;
66

    
67
/**
68
 * <p>
69
 * Abstract AbstractUtility class.
70
 * </p>
71
 *
72
 * @author n.hoffmann
73
 * @created 11.05.2009
74
 * @version 1.0
75
 */
76
public abstract class AbstractUtility {
77

    
78
	/** Constant <code>statusLineManager</code> */
79
	protected static IStatusLineManager statusLineManager;
80

    
81
	/**
82
	 * <p>
83
	 * closeAll
84
	 * </p>
85
	 *
86
	 * @return a boolean.
87
	 */
88
	public static boolean closeAll() {
89
		return getActivePage().closeAllEditors(true);
90
	}
91

    
92
	/**
93
	 * Close the given editor.
94
	 *
95
	 * @param editor
96
	 *            The <tt>MultipageTaxonEditor</tt> to close.
97
	 * @return <tt>true</tt> on success
98
	 */
99
	public static boolean close(EditorPart editor) {
100
		return getActivePage().closeEditor(editor, true);
101
	}
102

    
103
	/**
104
	 * <p>
105
	 * getShell
106
	 * </p>
107
	 *
108
	 * @return a {@link org.eclipse.swt.widgets.Shell} object.
109
	 */
110
	public static Shell getShell() {
111

    
112
		return TaxeditorStorePlugin.getDefault().getWorkbench()
113
				.getActiveWorkbenchWindow().getShell();
114
	}
115

    
116
	/**
117
	 * <p>
118
	 * getActivePage
119
	 * </p>
120
	 *
121
	 * @return a {@link org.eclipse.ui.IWorkbenchPage} object.
122
	 */
123
	public static IWorkbenchPage getActivePage() {
124

    
125
		return TaxeditorStorePlugin.getDefault().getWorkbench()
126
				.getActiveWorkbenchWindow().getActivePage();
127
	}
128

    
129
	/**
130
	 * <p>
131
	 * getActivePart
132
	 * </p>
133
	 *
134
	 * @return a {@link org.eclipse.ui.IWorkbenchPart} object.
135
	 */
136
	public static IWorkbenchPart getActivePart() {
137
		return getActivePage() != null ? getActivePage().getActivePart() : null;
138
	}
139

    
140
	public static IWorkbench getWorkbench() {
141
		return TaxeditorStorePlugin.getDefault().getWorkbench();
142
	}
143

    
144
	/**
145
	 * <p>
146
	 * getWorkbenchWindow
147
	 * </p>
148
	 *
149
	 * @return a {@link org.eclipse.jface.window.ApplicationWindow} object.
150
	 */
151
	public static ApplicationWindow getWorkbenchWindow() {
152
		if (getWorkbench().getWorkbenchWindowCount() > 1) {
153
			throw new IllegalStateException("More than one workbench window");
154
		}
155
		return (ApplicationWindow) getWorkbench().getWorkbenchWindows()[0];
156
	}
157

    
158
	/**
159
	 * <p>
160
	 * showView
161
	 * </p>
162
	 *
163
	 * @param id
164
	 *            a {@link java.lang.String} object.
165
	 * @return a {@link org.eclipse.ui.IViewPart} object.
166
	 */
167
	public static IViewPart showView(String id) {
168
		try {
169
			return PlatformUI.getWorkbench().getActiveWorkbenchWindow()
170
					.getActivePage()
171
					.showView(id, null, IWorkbenchPage.VIEW_VISIBLE);
172
		} catch (PartInitException e) {
173
			MessagingUtils.messageDialog("Error opening view", AbstractUtility.class, "Could not open view: " + id, e);
174
			return null;
175
		}
176
	}
177

    
178
	/**
179
	 * <p>
180
	 * hideView
181
	 * </p>
182
	 *
183
	 * @param view
184
	 *            a {@link org.eclipse.ui.IViewPart} object.
185
	 */
186
	public static void hideView(IViewPart view) {
187
		PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage()
188
				.hideView(view);
189
	}
190

    
191
	/**
192
	 * <p>
193
	 * getView
194
	 * </p>
195
	 *
196
	 * @param id
197
	 *            a {@link java.lang.String} object.
198
	 * @param restore
199
	 *            a boolean.
200
	 * @return a {@link org.eclipse.ui.IViewPart} object.
201
	 */
202
	public static IViewPart getView(String id, boolean restore) {
203
		IViewReference[] references = PlatformUI.getWorkbench()
204
				.getActiveWorkbenchWindow().getActivePage().getViewReferences();
205
		for (IViewReference reference : references) {
206
			if (reference.getId().equals(id)) {
207
				return reference.getView(restore);
208
			}
209
		}
210
		return null;
211
	}
212

    
213
	/**
214
	 * <p>
215
	 * getService
216
	 * </p>
217
	 *
218
	 * @param api
219
	 *            a {@link java.lang.Class} object.
220
	 * @return a {@link java.lang.Object} object.
221
	 */
222
	public static Object getService(Class api) {
223
		return TaxeditorStorePlugin.getDefault().getWorkbench().getService(api);
224
	}
225

    
226
	/**
227
	 * <p>
228
	 * getCurrentTheme
229
	 * </p>
230
	 *
231
	 * @return a {@link org.eclipse.ui.themes.ITheme} object.
232
	 */
233
	public static ITheme getCurrentTheme() {
234
		IThemeManager themeManager = TaxeditorStorePlugin.getDefault()
235
				.getWorkbench().getThemeManager();
236
		return themeManager.getCurrentTheme();
237
	}
238

    
239
	/**
240
	 * Fonts registered to the plugin may be obtained with the Eclipse themeing
241
	 * functionality. Thus fonts are chooseable by the user via
242
	 * Preferences->General->Appearance->Colors and Fonts
243
	 *
244
	 * @return the FontRegistry for the current theme
245
	 */
246
	public static FontRegistry getFontRegistry() {
247
		return getCurrentTheme().getFontRegistry();
248
	}
249

    
250
	/**
251
	 * <p>
252
	 * getFont
253
	 * </p>
254
	 *
255
	 * @param symbolicName
256
	 *            a {@link java.lang.String} object.
257
	 * @return a {@link org.eclipse.swt.graphics.Font} object.
258
	 */
259
	public static Font getFont(String symbolicName) {
260
		return getFontRegistry().get(symbolicName);
261
	}
262

    
263
	/**
264
	 * Color registered to the plugin may be obtained with the Eclipse themeing
265
	 * functionality. Thus colors are editable by the user via
266
	 * Preferences->General->Appearance->Colors and Fonts
267
	 *
268
	 * @return the ColorRegistry for the current theme
269
	 */
270
	public static ColorRegistry getColorRegistry() {
271
		return getCurrentTheme().getColorRegistry();
272
	}
273

    
274
	/**
275
	 * <p>
276
	 * getColor
277
	 * </p>
278
	 *
279
	 * @param symbolicName
280
	 *            a {@link java.lang.String} object.
281
	 * @return a {@link org.eclipse.swt.graphics.Color} object.
282
	 */
283
	public static Color getColor(String symbolicName) {
284
		return getColorRegistry().get(symbolicName);
285
	}
286

    
287
	/**
288
	 * <p>
289
	 * executeOperation
290
	 * </p>
291
	 *
292
	 * @param operation
293
	 *            a
294
	 *            {@link eu.etaxonomy.taxeditor.operation.AbstractPostTaxonOperation}
295
	 *            object.
296
	 * @return a {@link org.eclipse.core.runtime.IStatus} object.
297
	 */
298
	public static IStatus executeOperation(final AbstractPostOperation operation) {
299
		if (getOperationHistory() == null) {
300
			throw new IllegalArgumentException(
301
					"There is no operation history for this context");
302
		}
303

    
304
		final IAdaptable uiInfoAdapter = WorkspaceUndoUtil
305
				.getUIInfoAdapter(getShell());
306

    
307
		IRunnableWithProgress runnable = new IRunnableWithProgress() {
308

    
309
			@Override
310
            public void run(IProgressMonitor monitor)
311
					throws InvocationTargetException, InterruptedException {
312
				String operationlabel = operation.getLabel();
313
                monitor.beginTask(operationlabel, 100);
314
				IStatus status = Status.CANCEL_STATUS;
315
				try {
316
					operation.addContext(IOperationHistory.GLOBAL_UNDO_CONTEXT);
317
					status = getOperationHistory().execute(operation, monitor,
318
							uiInfoAdapter);
319
				} catch (ExecutionException e) {
320

    
321
					MessagingUtils.operationDialog(this, e, operationlabel, null);
322

    
323
				} finally {
324
					monitor.done();
325
				}
326

    
327
				String statusString = status.equals(Status.OK_STATUS) ? "completed"
328
						: "cancelled";
329
				setStatusLine(operationlabel + " " + statusString + ".");
330

    
331
			}
332
		};
333

    
334
		try {
335
			runInUI(runnable, null);
336
		} catch (Exception e) {
337
			MessagingUtils.messageDialog("Error executing operation", AbstractUtility.class, "An error occured while executing " + operation.getLabel(), e);
338
		}
339

    
340
		IPostOperationEnabled postOperationEnabled = operation
341
				.getPostOperationEnabled();
342
		if (postOperationEnabled != null) {
343
			postOperationEnabled.onComplete();
344
		}
345
		return Status.OK_STATUS;
346
	}
347

    
348
	/**
349
	 * <p>
350
	 * getOperationHistory
351
	 * </p>
352
	 *
353
	 * @return a {@link org.eclipse.core.commands.operations.IOperationHistory}
354
	 *         object.
355
	 */
356
	public static IOperationHistory getOperationHistory() {
357
		return getWorkbench().getOperationSupport().getOperationHistory();
358
	}
359

    
360
	/**
361
	 * <p>
362
	 * setStatusLine
363
	 * </p>
364
	 *
365
	 * @param message
366
	 *            a {@link java.lang.String} object.
367
	 */
368
	public static void setStatusLine(final String message) {
369
		Display.getDefault().asyncExec(new Runnable() {
370

    
371
			@Override
372
            public void run() {
373
				statusLineManager.setMessage(message);
374
			}
375

    
376
		});
377

    
378
	}
379

    
380
	/**
381
	 * <p>
382
	 * getMonitor
383
	 * </p>
384
	 *
385
	 * @return a {@link org.eclipse.core.runtime.IProgressMonitor} object.
386
	 */
387
	public static IProgressMonitor getMonitor() {
388
		statusLineManager.setCancelEnabled(false);
389
		return statusLineManager.getProgressMonitor();
390
	}
391

    
392
	/**
393
	 * Starts either the given {@link IProgressMonitor} if it's not
394
	 * <code>null</code> or a new {@link NullProgressMonitor}.
395
	 *
396
	 * @param progressMonitor
397
	 *            The {@link IProgressMonitor} or <code>null</code> if no
398
	 *            progress should be reported.
399
	 * @param taskName
400
	 *            The name of the main task.
401
	 * @param steps
402
	 *            The number of steps this task is subdivided into.
403
	 * @return The {@link IProgressMonitor}.
404
	 */
405
	public static IProgressMonitor startMainMonitor(
406
			IProgressMonitor progressMonitor, String taskName, int steps) {
407
		IProgressMonitor newMonitor = progressMonitor;
408
		if (newMonitor == null) {
409
			newMonitor = new NullProgressMonitor();
410
		}
411
		newMonitor.beginTask(taskName == null ? "" : taskName, steps);
412
		newMonitor.subTask(" ");
413
		return newMonitor;
414
	}
415

    
416
	/**
417
	 * Creates a {@link SubProgressMonitor} if the given
418
	 * {@link IProgressMonitor} is not <code>null</code> and not a
419
	 * {@link NullProgressMonitor}.
420
	 *
421
	 * @param progressMonitor
422
	 *            The parent {@link IProgressMonitor} of the
423
	 *            {@link SubProgressMonitor} to be created.
424
	 * @param ticks
425
	 *            The number of steps this subtask is subdivided into. Must be a
426
	 *            positive number and must not be
427
	 *            {@link IProgressMonitor#UNKNOWN}.
428
	 * @return The {@link IProgressMonitor}.
429
	 */
430
	public static IProgressMonitor getSubProgressMonitor(
431
			IProgressMonitor progressMonitor, int ticks) {
432
		if (progressMonitor == null) {
433
			return new NullProgressMonitor();
434
		}
435
		if (progressMonitor instanceof NullProgressMonitor) {
436
			return progressMonitor;
437
		}
438

    
439
		return new SubProgressMonitor(progressMonitor, ticks);
440
	}
441

    
442
	/**
443
	 * Checks whether the user canceled this operation. If not canceled, the
444
	 * given number of steps are declared as done.
445
	 *
446
	 * @param newMonitor
447
	 *            a {@link org.eclipse.core.runtime.IProgressMonitor} object.
448
	 * @param steps
449
	 *            a int.
450
	 */
451
	public static void workedChecked(IProgressMonitor newMonitor, int steps) {
452
		// In case the progress monitor was canceled throw an exception.
453
		if (newMonitor.isCanceled()) {
454
			throw new OperationCanceledException();
455
		}
456
		// Otherwise declare this step as done.
457
		newMonitor.worked(steps);
458
	}
459

    
460
	/**
461
	 * Present a progress dialog to the user. This dialog will block the UI
462
	 *
463
	 * @param runnable
464
	 *            an implementation of {@link IRunnableWithProgress}
465
	 * @throws java.lang.InterruptedException
466
	 *             if any.
467
	 * @throws java.lang.reflect.InvocationTargetException
468
	 *             if any.
469
	 */
470
	public static void busyCursorWhile(IRunnableWithProgress runnable)
471
			throws InvocationTargetException, InterruptedException {
472
		getProgressService().busyCursorWhile(runnable);
473
	}
474

    
475
	/**
476
	 * <p>
477
	 * runInUI
478
	 * </p>
479
	 *
480
	 * @see {@link IProgressService#runInUI(org.eclipse.jface.operation.IRunnableContext, IRunnableWithProgress, ISchedulingRule)}
481
	 * @param runnable
482
	 *            a {@link org.eclipse.jface.operation.IRunnableWithProgress}
483
	 *            object.
484
	 * @param rule
485
	 *            a {@link org.eclipse.core.runtime.jobs.ISchedulingRule}
486
	 *            object.
487
	 * @throws java.lang.reflect.InvocationTargetException
488
	 *             if any.
489
	 * @throws java.lang.InterruptedException
490
	 *             if any.
491
	 */
492
	public static void runInUI(IRunnableWithProgress runnable,
493
			ISchedulingRule rule) throws InvocationTargetException,
494
			InterruptedException {
495
		getProgressService().runInUI(getWorkbenchWindow(), runnable, rule);
496
	}
497

    
498
	/**
499
	 * <p>
500
	 * run
501
	 * </p>
502
	 *
503
	 * @param fork
504
	 *            a boolean.
505
	 * @param cancelable
506
	 *            a boolean.
507
	 * @param runnable
508
	 *            a {@link org.eclipse.jface.operation.IRunnableWithProgress}
509
	 *            object.
510
	 * @throws java.lang.reflect.InvocationTargetException
511
	 *             if any.
512
	 * @throws java.lang.InterruptedException
513
	 *             if any.
514
	 */
515
	public static void run(boolean fork, boolean cancelable,
516
			IRunnableWithProgress runnable) throws InvocationTargetException,
517
			InterruptedException {
518
		getProgressService().run(fork, cancelable, runnable);
519
	}
520

    
521
	/**
522
	 * <p>
523
	 * getProgressService
524
	 * </p>
525
	 *
526
	 * @return a {@link org.eclipse.ui.progress.IProgressService} object.
527
	 */
528
	public static IProgressService getProgressService() {
529
		IWorkbench workbench = PlatformUI.getWorkbench();
530
		return workbench.getProgressService();
531
	}
532

    
533
	/**
534
	 * <p>
535
	 * getProgressService2
536
	 * </p>
537
	 *
538
	 * @return a {@link org.eclipse.ui.progress.IWorkbenchSiteProgressService}
539
	 *         object.
540
	 */
541
	public static IWorkbenchSiteProgressService getProgressService2() {
542
		return (IWorkbenchSiteProgressService) getService(IWorkbenchSiteProgressService.class);
543
	}
544

    
545
	/**
546
	 * <p>
547
	 * getPluginId
548
	 * </p>
549
	 *
550
	 * @return a {@link java.lang.String} object.
551
	 */
552
	public static String getPluginId() {
553
		return "eu.taxeditor";
554
	}
555

    
556
	/**
557
	 * <p>
558
	 * getActiveEditor
559
	 * </p>
560
	 *
561
	 * @return a {@link org.eclipse.ui.IEditorPart} object.
562
	 */
563
	public static IEditorPart getActiveEditor() {
564
		return getActivePage() != null ? getActivePage().getActiveEditor()
565
				: null;
566
	}
567

    
568
	/**
569
	 * <p>
570
	 * getDetailsView
571
	 * </p>
572
	 *
573
	 * @return a {@link eu.etaxonomy.taxeditor.view.detail.DetailsViewPart}
574
	 *         object.
575
	 */
576
	public static DetailsViewPart getDetailsView() {
577
		return (DetailsViewPart) getView(DetailsViewPart.ID, false);
578
	}
579

    
580
	/**
581
	 * <p>
582
	 * refreshDetailsViewer
583
	 * </p>
584
	 */
585
	public static void refreshDetailsViewer() {
586
		if (getDetailsView() != null) {
587
			((AbstractCdmDataViewer) getDetailsView().getViewer()).refresh();
588
		}
589
	}
590

    
591
	/**
592
	 * <p>
593
	 * reflowDetailsViewer
594
	 * </p>
595
	 */
596
	public static void reflowDetailsViewer() {
597
		if (getDetailsView() != null) {
598
			((AbstractCdmDataViewer) getDetailsView().getViewer()).reflow();
599
		}
600
	}
601

    
602
	public static SupplementalDataViewPart getSupplementalDataView() {
603
		return (SupplementalDataViewPart) getView(SupplementalDataViewPart.ID,
604
				false);
605
	}
606

    
607
	public static void reflowSupplementalViewer() {
608
		if (getSupplementalDataView() != null) {
609
			((AbstractCdmDataViewer) getSupplementalDataView().getViewer())
610
					.reflow();
611
		}
612
	}
613

    
614

    
615
    /**
616
     * Orders a Collection of {@link IEnumTerm}s according to the term
617
     * hierarchy. <br>
618
     * <br>
619
     * The returned map will be be ordered primarily by root elements,
620
     * secondarily by the child elements and their children resp., both ascending alphabetically. <br>
621
     * @param terms
622
     *            A {@link Collection} of {@link IEnumTerm}s for which the term
623
     *            hierarchy should be created
624
     * @return a map which holds the terms as keys and their string
625
     *         representation via {@link IEnumTerm#getMessage()} as values
626
     */
627
    public static <T extends IEnumTerm<T>> LinkedHashMap<T, String> orderTerms(Collection<T> terms) {
628
        TreeSet<TermNode<T>> parentElements = new TreeSet<TermNode<T>>();
629
        parentElements.addAll(getTermHierarchy(terms));
630

    
631
        // create list according to the type hierarchy (root elements alphabetically with recursive children also alphabetically)
632
        LinkedHashMap<T, String> result = new LinkedHashMap<T, String>();
633
        parseTermTree(parentElements, result, -1);
634
        return result;
635
    }
636

    
637
    private static<T extends IEnumTerm<T>> void parseTermTree(Collection<TermNode<T>> children, LinkedHashMap<T, String> result, int depth){
638
        depth++;
639
        for(TermNode<T> node:children){
640
            String indentString = "";
641
            for(int i=0;i<depth;i++){
642
                indentString += "  ";
643
            }
644
            if(depth>0){
645
                indentString += "- ";
646
            }
647
            result.put(node.term, indentString + node.term.getMessage());
648
            parseTermTree(node.children, result, depth);
649
        }
650
    }
651

    
652
    private static<T extends IEnumTerm<T>> void addToParents(List<TermNode<T>> parents, Collection<T> terms){
653
        List<TermNode<T>> hasChildrenList = new ArrayList<TermNode<T>>();
654
        for(T term:terms){
655
            // only terms with parents
656
            if(term.getKindOf()!=null){
657
                TermNode<T> parentNode = new TermNode<T>(term.getKindOf());
658
                TermNode<T> childNode = new TermNode<T>(term);
659
                if(parents.contains(parentNode)){
660
                    // parent found in parent list -> add this term to parent's child list
661
                    parents.get(parents.indexOf(parentNode)).addChild(childNode);
662
                    if(!term.getGeneralizationOf().isEmpty()){
663
                        // has more children -> add to list which will be the parent for the next recursion
664
                        hasChildrenList.add(childNode);
665
                    }
666
                }
667
            }
668
        }
669
        if(!hasChildrenList.isEmpty()){
670
            addToParents(hasChildrenList, terms);
671
        }
672
    }
673

    
674
    private static<T extends IEnumTerm<T>> List<TermNode<T>> getTermHierarchy(Collection<T> terms){
675
        List<TermNode<T>> parents = new ArrayList<TermNode<T>>();
676
        // get root elements
677
        for(T term:terms){
678
            T parentTerm = term.getKindOf();
679
            if(parentTerm==null){
680
                // root element
681
                parents.add(new TermNode<T>(term));
682
            }
683
        }
684
        addToParents(parents, terms);
685
        return parents;
686
    }
687

    
688
    @SuppressWarnings("unchecked")
689
    /**
690
     * Recursively iterates over all term parents until no more parent is found i.e. the root node
691
     * @param term The term for which the parent should be found
692
     * @return the root terms of the term hierarchy
693
     */
694
    private static<T extends IEnumTerm<T>> T getParentFor(T term){
695
        // PP: cast should be safe. Why is Eclipse complaining??
696
        T parent = term.getKindOf();
697
        if(parent==null){
698
            return term;
699
        }
700
        else{
701
            return getParentFor(term.getKindOf());
702
        }
703
    }
704

    
705
    private static class TermNode<T extends IEnumTerm<T>> implements Comparable<TermNode<T>>{
706
        private final T term;
707
        private final TreeSet<TermNode<T>> children;
708

    
709
        /**
710
         * @param term
711
         * @param children
712
         */
713
        public TermNode(T term) {
714
            super();
715
            this.term = term;
716
            this.children = new TreeSet<TermNode<T>>();
717
        }
718

    
719
        public void addChild(TermNode<T> child){
720
            this.children.add(child);
721
        }
722

    
723
        /**
724
         * @return the children
725
         */
726
        public TreeSet<TermNode<T>> getChildren() {
727
            return children;
728
        }
729

    
730
        /**
731
         * @return the term
732
         */
733
        public T getTerm() {
734
            return term;
735
        }
736

    
737
        /* (non-Javadoc)
738
         * @see java.lang.Object#hashCode()
739
         */
740
        @Override
741
        public int hashCode() {
742
            final int prime = 31;
743
            int result = 1;
744
            result = prime * result + ((term == null) ? 0 : term.hashCode());
745
            return result;
746
        }
747

    
748
        /* (non-Javadoc)
749
         * @see java.lang.Object#equals(java.lang.Object)
750
         */
751
        @Override
752
        public boolean equals(Object obj) {
753
            if (this == obj) {
754
                return true;
755
            }
756
            if (obj == null) {
757
                return false;
758
            }
759
            if (getClass() != obj.getClass()) {
760
                return false;
761
            }
762
            TermNode other = (TermNode) obj;
763
            if (term == null) {
764
                if (other.term != null) {
765
                    return false;
766
                }
767
            } else if (!term.equals(other.term)) {
768
                return false;
769
            }
770
            return true;
771
        }
772

    
773
        /* (non-Javadoc)
774
         * @see java.lang.Comparable#compareTo(java.lang.Object)
775
         */
776
        @Override
777
        public int compareTo(TermNode<T> that) {
778
            return this.term.getMessage().compareTo(that.term.getMessage());
779
        }
780
    }
781

    
782

    
783
}
(2-2/34)