- added transparency to icons
[taxeditor.git] / eu.etaxonomy.taxeditor.store / src / main / java / eu / etaxonomy / taxeditor / model / AbstractUtility.java
index c9fb62deedeccc80bb4910a2ee4f55fff2674621..70d4ba028f39302bf9ebe680fbfb156836087b90 100644 (file)
@@ -1,16 +1,24 @@
 // $Id$
 /**
  * Copyright (C) 2007 EDIT
- * European Distributed Institute of Taxonomy 
+ * European Distributed Institute of Taxonomy
  * http://www.e-taxonomy.eu
- * 
+ *
  * The contents of this file are subject to the Mozilla Public License Version 1.1
  * See LICENSE.TXT at the top of this package for the full license terms.
  */
 
 package eu.etaxonomy.taxeditor.model;
 
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
 import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.TreeSet;
 
 import org.apache.log4j.Logger;
 import org.eclipse.core.commands.ExecutionException;
@@ -19,12 +27,14 @@ import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.ILog;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.MultiStatus;
 import org.eclipse.core.runtime.NullProgressMonitor;
 import org.eclipse.core.runtime.OperationCanceledException;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.core.runtime.SubProgressMonitor;
 import org.eclipse.core.runtime.jobs.ISchedulingRule;
 import org.eclipse.jface.action.IStatusLineManager;
+import org.eclipse.jface.dialogs.ErrorDialog;
 import org.eclipse.jface.dialogs.MessageDialog;
 import org.eclipse.jface.operation.IRunnableWithProgress;
 import org.eclipse.jface.resource.ColorRegistry;
@@ -49,6 +59,9 @@ import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
 import org.eclipse.ui.themes.ITheme;
 import org.eclipse.ui.themes.IThemeManager;
 
+import eu.etaxonomy.cdm.database.PermissionDeniedException;
+import eu.etaxonomy.cdm.model.common.IEnumTerm;
+import eu.etaxonomy.cdm.persistence.hibernate.permission.SecurityExceptionUtils;
 import eu.etaxonomy.taxeditor.operation.AbstractPostOperation;
 import eu.etaxonomy.taxeditor.operation.IPostOperationEnabled;
 import eu.etaxonomy.taxeditor.store.internal.TaxeditorStorePlugin;
@@ -60,7 +73,7 @@ import eu.etaxonomy.taxeditor.view.supplementaldata.SupplementalDataViewPart;
  * <p>
  * Abstract AbstractUtility class.
  * </p>
- * 
+ *
  * @author n.hoffmann
  * @created 11.05.2009
  * @version 1.0
@@ -74,7 +87,7 @@ public abstract class AbstractUtility {
         * <p>
         * closeAll
         * </p>
-        * 
+        *
         * @return a boolean.
         */
        public static boolean closeAll() {
@@ -83,7 +96,7 @@ public abstract class AbstractUtility {
 
        /**
         * Close the given editor.
-        * 
+        *
         * @param editor
         *            The <tt>MultipageTaxonEditor</tt> to close.
         * @return <tt>true</tt> on success
@@ -96,7 +109,7 @@ public abstract class AbstractUtility {
         * <p>
         * getShell
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.swt.widgets.Shell} object.
         */
        public static Shell getShell() {
@@ -109,7 +122,7 @@ public abstract class AbstractUtility {
         * <p>
         * getActivePage
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.ui.IWorkbenchPage} object.
         */
        public static IWorkbenchPage getActivePage() {
@@ -122,7 +135,7 @@ public abstract class AbstractUtility {
         * <p>
         * getActivePart
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.ui.IWorkbenchPart} object.
         */
        public static IWorkbenchPart getActivePart() {
@@ -137,7 +150,7 @@ public abstract class AbstractUtility {
         * <p>
         * getWorkbenchWindow
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.jface.window.ApplicationWindow} object.
         */
        public static ApplicationWindow getWorkbenchWindow() {
@@ -151,7 +164,7 @@ public abstract class AbstractUtility {
         * <p>
         * showView
         * </p>
-        * 
+        *
         * @param id
         *            a {@link java.lang.String} object.
         * @return a {@link org.eclipse.ui.IViewPart} object.
@@ -171,7 +184,7 @@ public abstract class AbstractUtility {
         * <p>
         * hideView
         * </p>
-        * 
+        *
         * @param view
         *            a {@link org.eclipse.ui.IViewPart} object.
         */
@@ -184,7 +197,7 @@ public abstract class AbstractUtility {
         * <p>
         * getView
         * </p>
-        * 
+        *
         * @param id
         *            a {@link java.lang.String} object.
         * @param restore
@@ -206,7 +219,7 @@ public abstract class AbstractUtility {
         * <p>
         * getService
         * </p>
-        * 
+        *
         * @param api
         *            a {@link java.lang.Class} object.
         * @return a {@link java.lang.Object} object.
@@ -219,7 +232,7 @@ public abstract class AbstractUtility {
         * <p>
         * getCurrentTheme
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.ui.themes.ITheme} object.
         */
        public static ITheme getCurrentTheme() {
@@ -232,7 +245,7 @@ public abstract class AbstractUtility {
         * Fonts registered to the plugin may be obtained with the Eclipse themeing
         * functionality. Thus fonts are chooseable by the user via
         * Preferences->General->Appearance->Colors and Fonts
-        * 
+        *
         * @return the FontRegistry for the current theme
         */
        public static FontRegistry getFontRegistry() {
@@ -243,7 +256,7 @@ public abstract class AbstractUtility {
         * <p>
         * getFont
         * </p>
-        * 
+        *
         * @param symbolicName
         *            a {@link java.lang.String} object.
         * @return a {@link org.eclipse.swt.graphics.Font} object.
@@ -256,7 +269,7 @@ public abstract class AbstractUtility {
         * Color registered to the plugin may be obtained with the Eclipse themeing
         * functionality. Thus colors are editable by the user via
         * Preferences->General->Appearance->Colors and Fonts
-        * 
+        *
         * @return the ColorRegistry for the current theme
         */
        public static ColorRegistry getColorRegistry() {
@@ -267,7 +280,7 @@ public abstract class AbstractUtility {
         * <p>
         * getColor
         * </p>
-        * 
+        *
         * @param symbolicName
         *            a {@link java.lang.String} object.
         * @return a {@link org.eclipse.swt.graphics.Color} object.
@@ -279,7 +292,7 @@ public abstract class AbstractUtility {
        /**
         * Open a message box that informs the user about unimplemented
         * functionality. This method is for developer convenience.
-        * 
+        *
         * @param source
         *            a {@link java.lang.Object} object.
         */
@@ -292,7 +305,7 @@ public abstract class AbstractUtility {
         * <p>
         * informationDialog
         * </p>
-        * 
+        *
         * @param title
         *            a {@link java.lang.String} object.
         * @param message
@@ -302,17 +315,23 @@ public abstract class AbstractUtility {
                        final String message) {
                Display.getDefault().asyncExec(new Runnable() {
 
-                       public void run() {
+                       @Override
+            public void run() {
                                MessageDialog.openInformation(getShell(), title, message);
                        }
                });
        }
 
+       public static void informationDialog(final String title,
+                       final IStatus status) {
+               informationDialog(title, status.getMessage());
+       }
+
        /**
         * <p>
         * warningDialog
         * </p>
-        * 
+        *
         * @param title
         *            The dialogs title
         * @param source
@@ -324,7 +343,8 @@ public abstract class AbstractUtility {
                        final String message) {
                Display.getDefault().asyncExec(new Runnable() {
 
-                       public void run() {
+                       @Override
+            public void run() {
                                MessageDialog.openWarning(getShell(), title, message);
                                Class<? extends Object> clazz = source != null ? source
                                                .getClass() : AbstractUtility.class;
@@ -333,11 +353,21 @@ public abstract class AbstractUtility {
                });
        }
 
+       /**
+        * @param title
+        * @param termBase
+        * @param status
+        */
+       public static void warningDialog(String title, Object source,
+                       IStatus status) {
+               warningDialog(title, source, status.getMessage());
+       }
+
        /**
         * <p>
         * errorDialog
         * </p>
-        * 
+        *
         * @param title
         *            The dialogs title
         * @param source
@@ -353,7 +383,8 @@ public abstract class AbstractUtility {
                        final String message, final Throwable t) {
                Display.getDefault().asyncExec(new Runnable() {
 
-                       public void run() {
+                       @Override
+            public void run() {
                                MessageDialog.openError(getShell(), title, message + getCauseRecursively(t));
                                Class<? extends Object> clazz = source != null ? source
                                                .getClass() : this.getClass();
@@ -362,9 +393,9 @@ public abstract class AbstractUtility {
 
                        private String getCauseRecursively(Throwable t) {
                                if(t == null){
-                                       return null;
+                                       return "";
                                }
-                               
+
                                if(t.getCause() != null){
                                        return getCauseRecursively(t.getCause());
                                }else{
@@ -375,11 +406,16 @@ public abstract class AbstractUtility {
                });
        }
 
+       public static void errorDialog(final String title, final Object source,
+                       final String message){
+               errorDialog(title, source, message, null);
+       }
+
        /**
         * <p>
         * errorDialog
         * </p>
-        * 
+        *
         * @param title
         *            a {@link java.lang.String} object.
         * @param source
@@ -387,15 +423,16 @@ public abstract class AbstractUtility {
         * @param status
         *            a {@link org.eclipse.core.runtime.IStatus} object.
         */
-       public static void errorDialog(final String title, final Object source,
+       public static void errorDialog(final String title, final Object source, 
                        final IStatus status) {
                Display.getDefault().asyncExec(new Runnable() {
 
-                       public void run() {
-                               MessageDialog.openError(getShell(), title, status.getMessage());
+                       @Override
+            public void run() {
+                               ErrorDialog.openError(getShell(), title, null, status);
                                Class<? extends Object> clazz = source != null ? source
                                                .getClass() : this.getClass();
-                               error(clazz, status.getMessage(), status.getException());
+                               error(clazz, status);
                        }
                });
        }
@@ -404,7 +441,7 @@ public abstract class AbstractUtility {
         * <p>
         * confirmDialog
         * </p>
-        * 
+        *
         * @param title
         *            a {@link java.lang.String} object.
         * @param message
@@ -419,10 +456,10 @@ public abstract class AbstractUtility {
         * <p>
         * executeOperation
         * </p>
-        * 
+        *
         * @param operation
         *            a
-        *            {@link eu.etaxonomy.taxeditor.operation.AbstractPostOperation}
+        *            {@link eu.etaxonomy.taxeditor.operation.AbstractPostTaxonOperation}
         *            object.
         * @return a {@link org.eclipse.core.runtime.IStatus} object.
         */
@@ -437,7 +474,8 @@ public abstract class AbstractUtility {
 
                IRunnableWithProgress runnable = new IRunnableWithProgress() {
 
-                       public void run(IProgressMonitor monitor)
+                       @Override
+            public void run(IProgressMonitor monitor)
                                        throws InvocationTargetException, InterruptedException {
                                monitor.beginTask(operation.getLabel(), 100);
                                IStatus status = Status.CANCEL_STATUS;
@@ -446,11 +484,35 @@ public abstract class AbstractUtility {
                                        status = getOperationHistory().execute(operation, monitor,
                                                        uiInfoAdapter);
                                } catch (ExecutionException e) {
-                                       errorDialog("Error executing operation", getClass(), String.format("An error occured while executing %s.", operation.getLabel()), e);
+                                       MultiStatus info = null;
+                                       String title = null;
+
+                                       // FIXME cannot access TaxonomicEditorPlugin.PLUGIN_ID from here 
+                                       // String PID = TaxonomicEditorPlugin.PLUGIN_ID;
+                                       String PID = "eu.etaxonomy.taxeditor.application";
+                                       
+                                       RuntimeException securityRuntimeException = SecurityExceptionUtils.findSecurityRuntimeException(e);
+                                       
+                                       if(securityRuntimeException != null){
+                                               title = "Your changes could not be saved!";
+                                               info = new MultiStatus(PID, 1, String.format("You are missing sufficient permissions for the operation \"%s\".", operation.getLabel()), null);
+                                               info.add(new Status(IStatus.WARNING, PID, 1, securityRuntimeException.getMessage(), null));
+                                               
+                                       } else {
+                                               title = "Error executing operation";
+                                               info = new MultiStatus(PID, 1, String.format("An error occured while executing %s.", operation.getLabel()), null);
+                                               Writer writer = new StringWriter();
+                                           PrintWriter printWriter = new PrintWriter(writer);
+                                           e.printStackTrace(printWriter);
+                                               info.add(new Status(IStatus.ERROR, PID, 1, writer.toString(), null));
+                                       }
+                               
+                                       errorDialog(title, getClass(), info);
+                                       
                                } finally {
                                        monitor.done();
                                }
-                               
+
                                String statusString = status.equals(Status.OK_STATUS) ? "completed"
                                                : "cancelled";
                                setStatusLine(operation.getLabel() + " " + statusString + ".");
@@ -507,7 +569,7 @@ public abstract class AbstractUtility {
         * <p>
         * getOperationHistory
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.core.commands.operations.IOperationHistory}
         *         object.
         */
@@ -519,14 +581,15 @@ public abstract class AbstractUtility {
         * <p>
         * setStatusLine
         * </p>
-        * 
+        *
         * @param message
         *            a {@link java.lang.String} object.
         */
        public static void setStatusLine(final String message) {
                Display.getDefault().asyncExec(new Runnable() {
 
-                       public void run() {
+                       @Override
+            public void run() {
                                statusLineManager.setMessage(message);
                        }
 
@@ -538,7 +601,7 @@ public abstract class AbstractUtility {
         * <p>
         * getMonitor
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.core.runtime.IProgressMonitor} object.
         */
        public static IProgressMonitor getMonitor() {
@@ -549,7 +612,7 @@ public abstract class AbstractUtility {
        /**
         * Starts either the given {@link IProgressMonitor} if it's not
         * <code>null</code> or a new {@link NullProgressMonitor}.
-        * 
+        *
         * @param progressMonitor
         *            The {@link IProgressMonitor} or <code>null</code> if no
         *            progress should be reported.
@@ -574,7 +637,7 @@ public abstract class AbstractUtility {
         * Creates a {@link SubProgressMonitor} if the given
         * {@link IProgressMonitor} is not <code>null</code> and not a
         * {@link NullProgressMonitor}.
-        * 
+        *
         * @param progressMonitor
         *            The parent {@link IProgressMonitor} of the
         *            {@link SubProgressMonitor} to be created.
@@ -599,7 +662,7 @@ public abstract class AbstractUtility {
        /**
         * Checks whether the user canceled this operation. If not canceled, the
         * given number of steps are declared as done.
-        * 
+        *
         * @param newMonitor
         *            a {@link org.eclipse.core.runtime.IProgressMonitor} object.
         * @param steps
@@ -616,7 +679,7 @@ public abstract class AbstractUtility {
 
        /**
         * Present a progress dialog to the user. This dialog will block the UI
-        * 
+        *
         * @param runnable
         *            an implementation of {@link IRunnableWithProgress}
         * @throws java.lang.InterruptedException
@@ -633,7 +696,7 @@ public abstract class AbstractUtility {
         * <p>
         * runInUI
         * </p>
-        * 
+        *
         * @see {@link IProgressService#runInUI(org.eclipse.jface.operation.IRunnableContext, IRunnableWithProgress, ISchedulingRule)}
         * @param runnable
         *            a {@link org.eclipse.jface.operation.IRunnableWithProgress}
@@ -656,7 +719,7 @@ public abstract class AbstractUtility {
         * <p>
         * run
         * </p>
-        * 
+        *
         * @param fork
         *            a boolean.
         * @param cancelable
@@ -679,7 +742,7 @@ public abstract class AbstractUtility {
         * <p>
         * getProgressService
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.ui.progress.IProgressService} object.
         */
        public static IProgressService getProgressService() {
@@ -691,7 +754,7 @@ public abstract class AbstractUtility {
         * <p>
         * getProgressService2
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.ui.progress.IWorkbenchSiteProgressService}
         *         object.
         */
@@ -703,7 +766,7 @@ public abstract class AbstractUtility {
         * <p>
         * info
         * </p>
-        * 
+        *
         * @param message
         *            a {@link java.lang.String} object.
         */
@@ -716,7 +779,7 @@ public abstract class AbstractUtility {
         * <p>
         * info
         * </p>
-        * 
+        *
         * @param status
         *            a {@link org.eclipse.core.runtime.IStatus} object.
         */
@@ -728,7 +791,7 @@ public abstract class AbstractUtility {
         * <p>
         * warn
         * </p>
-        * 
+        *
         * @param source
         *            a {@link java.lang.Class} object.
         * @param message
@@ -739,7 +802,12 @@ public abstract class AbstractUtility {
                getLog4JLogger(source).warn(message);
                log(status);
        }
-       
+
+       public static void warn(Class source, IStatus status) {
+               getLog4JLogger(source).warn(status.getMessage(), status.getException());
+               log(status);
+       }
+
        public static void warn(Class source, Throwable t) {
                IStatus status = new Status(IStatus.WARNING, getPluginId(), t.getMessage(), t);
                getLog4JLogger(source).warn(t);
@@ -750,7 +818,7 @@ public abstract class AbstractUtility {
         * <p>
         * error
         * </p>
-        * 
+        *
         * @param source
         *            a {@link java.lang.Class} object.
         * @param t
@@ -764,7 +832,7 @@ public abstract class AbstractUtility {
         * <p>
         * error
         * </p>
-        * 
+        *
         * @param source
         *            a {@link java.lang.Class} object.
         * @param message
@@ -781,7 +849,7 @@ public abstract class AbstractUtility {
         * <p>
         * error
         * </p>
-        * 
+        *
         * @param source
         *            a {@link java.lang.Class} object.
         * @param status
@@ -797,7 +865,7 @@ public abstract class AbstractUtility {
         * <p>
         * getLog4JLogger
         * </p>
-        * 
+        *
         * @param clazz
         *            a {@link java.lang.Class} object.
         * @return a {@link org.apache.log4j.Logger} object.
@@ -808,7 +876,7 @@ public abstract class AbstractUtility {
 
        /**
         * @see {@link ILog#log(IStatus)}
-        * 
+        *
         * @param status
         */
        private static void log(IStatus status) {
@@ -819,10 +887,10 @@ public abstract class AbstractUtility {
         * <p>
         * getPluginId
         * </p>
-        * 
+        *
         * @return a {@link java.lang.String} object.
         */
-       protected static String getPluginId() {
+       public static String getPluginId() {
                return "eu.taxeditor";
        }
 
@@ -830,7 +898,7 @@ public abstract class AbstractUtility {
         * <p>
         * getActiveEditor
         * </p>
-        * 
+        *
         * @return a {@link org.eclipse.ui.IEditorPart} object.
         */
        public static IEditorPart getActiveEditor() {
@@ -842,7 +910,7 @@ public abstract class AbstractUtility {
         * <p>
         * getDetailsView
         * </p>
-        * 
+        *
         * @return a {@link eu.etaxonomy.taxeditor.view.detail.DetailsViewPart}
         *         object.
         */
@@ -883,4 +951,173 @@ public abstract class AbstractUtility {
                                        .reflow();
                }
        }
+
+
+    /**
+     * Orders a Collection of {@link IEnumTerm}s according to the term
+     * hierarchy. <br>
+     * <br>
+     * The returned map will be be ordered primarily by root elements,
+     * secondarily by the child elements and their children resp., both ascending alphabetically. <br>
+     * @param terms
+     *            A {@link Collection} of {@link IEnumTerm}s for which the term
+     *            hierarchy should be created
+     * @return a map which holds the terms as keys and their string
+     *         representation via {@link IEnumTerm#getMessage()} as values
+     */
+    public static <T extends IEnumTerm<T>> LinkedHashMap<T, String> orderTerms(Collection<T> terms) {
+        TreeSet<TermNode<T>> parentElements = new TreeSet<TermNode<T>>();
+        parentElements.addAll(getTermHierarchy(terms));
+
+        // create list according to the type hierarchy (root elements alphabetically with recursive children also alphabetically)
+        LinkedHashMap<T, String> result = new LinkedHashMap<T, String>();
+        parseTermTree(parentElements, result, -1);
+        return result;
+    }
+
+    private static<T extends IEnumTerm<T>> void parseTermTree(Collection<TermNode<T>> children, LinkedHashMap<T, String> result, int depth){
+        depth++;
+        for(TermNode<T> node:children){
+            String indentString = "";
+            for(int i=0;i<depth;i++){
+                indentString += "  ";
+            }
+            if(depth>0){
+                indentString += "- ";
+            }
+            result.put(node.term, indentString + node.term.getMessage());
+            parseTermTree(node.children, result, depth);
+        }
+    }
+
+    private static<T extends IEnumTerm<T>> void addToParents(List<TermNode<T>> parents, Collection<T> terms){
+        List<TermNode<T>> hasChildrenList = new ArrayList<TermNode<T>>();
+        for(T term:terms){
+            // only terms with parents
+            if(term.getKindOf()!=null){
+                TermNode<T> parentNode = new TermNode<T>(term.getKindOf());
+                TermNode<T> childNode = new TermNode<T>(term);
+                if(parents.contains(parentNode)){
+                    // parent found in parent list -> add this term to parent's child list
+                    parents.get(parents.indexOf(parentNode)).addChild(childNode);
+                    if(!term.getGeneralizationOf().isEmpty()){
+                        // has more children -> add to list which will be the parent for the next recursion
+                        hasChildrenList.add(childNode);
+                    }
+                }
+            }
+        }
+        if(!hasChildrenList.isEmpty()){
+            addToParents(hasChildrenList, terms);
+        }
+    }
+
+    private static<T extends IEnumTerm<T>> List<TermNode<T>> getTermHierarchy(Collection<T> terms){
+        List<TermNode<T>> parents = new ArrayList<TermNode<T>>();
+        // get root elements
+        for(T term:terms){
+            T parentTerm = term.getKindOf();
+            if(parentTerm==null){
+                // root element
+                parents.add(new TermNode<T>(term));
+            }
+        }
+        addToParents(parents, terms);
+        return parents;
+    }
+
+    @SuppressWarnings("unchecked")
+    /**
+     * Recursively iterates over all term parents until no more parent is found i.e. the root node
+     * @param term The term for which the parent should be found
+     * @return the root terms of the term hierarchy
+     */
+    private static<T extends IEnumTerm<T>> T getParentFor(T term){
+        // PP: cast should be safe. Why is Eclipse complaining??
+        T parent = term.getKindOf();
+        if(parent==null){
+            return term;
+        }
+        else{
+            return getParentFor(term.getKindOf());
+        }
+    }
+
+    private static class TermNode<T extends IEnumTerm<T>> implements Comparable<TermNode<T>>{
+        private final T term;
+        private final TreeSet<TermNode<T>> children;
+
+        /**
+         * @param term
+         * @param children
+         */
+        public TermNode(T term) {
+            super();
+            this.term = term;
+            this.children = new TreeSet<TermNode<T>>();
+        }
+
+        public void addChild(TermNode<T> child){
+            this.children.add(child);
+        }
+
+        /**
+         * @return the children
+         */
+        public TreeSet<TermNode<T>> getChildren() {
+            return children;
+        }
+
+        /**
+         * @return the term
+         */
+        public T getTerm() {
+            return term;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#hashCode()
+         */
+        @Override
+        public int hashCode() {
+            final int prime = 31;
+            int result = 1;
+            result = prime * result + ((term == null) ? 0 : term.hashCode());
+            return result;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Object#equals(java.lang.Object)
+         */
+        @Override
+        public boolean equals(Object obj) {
+            if (this == obj) {
+                return true;
+            }
+            if (obj == null) {
+                return false;
+            }
+            if (getClass() != obj.getClass()) {
+                return false;
+            }
+            TermNode other = (TermNode) obj;
+            if (term == null) {
+                if (other.term != null) {
+                    return false;
+                }
+            } else if (!term.equals(other.term)) {
+                return false;
+            }
+            return true;
+        }
+
+        /* (non-Javadoc)
+         * @see java.lang.Comparable#compareTo(java.lang.Object)
+         */
+        @Override
+        public int compareTo(TermNode<T> that) {
+            return this.term.getMessage().compareTo(that.term.getMessage());
+        }
+    }
+
 }