- sorting/ordering on all hierarchy levels (#3736)
authorPatric Plitzner <p.plitzner@bgbm.org>
Mon, 4 Nov 2013 11:06:01 +0000 (11:06 +0000)
committerPatric Plitzner <p.plitzner@bgbm.org>
Mon, 4 Nov 2013 11:06:01 +0000 (11:06 +0000)
.gitattributes
eu.etaxonomy.taxeditor.store/src/main/java/eu/etaxonomy/taxeditor/model/AbstractUtility.java
eu.etaxonomy.taxeditor.store/src/main/java/eu/etaxonomy/taxeditor/ui/combo/EnumComboElement.java
eu.etaxonomy.taxeditor.store/src/main/java/eu/etaxonomy/taxeditor/ui/element/CdmFormFactory.java
eu.etaxonomy.taxeditor.store/src/test/java/eu/etaxonomy/taxeditor/store/utility/AbstractUtilityTest.java [new file with mode: 0644]

index 73e4c2b94117e03443038c0aceeb5e64bc4fb373..4c063167a9eb014fb73f664e656a69bc2c15a8eb 100644 (file)
@@ -1526,6 +1526,7 @@ eu.etaxonomy.taxeditor.store/src/main/resources/eu/etaxonomy/cdm/editorApplicati
 eu.etaxonomy.taxeditor.store/src/main/resources/log4j.properties -text
 eu.etaxonomy.taxeditor.store/src/test/java/eu/etaxonomy/taxeditor/store/NameProtectTitleCacheTest.java -text
 eu.etaxonomy.taxeditor.store/src/test/java/eu/etaxonomy/taxeditor/store/operations/AbstractTaxeditorOperationTestBase.java -text
+eu.etaxonomy.taxeditor.store/src/test/java/eu/etaxonomy/taxeditor/store/utility/AbstractUtilityTest.java -text
 eu.etaxonomy.taxeditor.store/src/test/resources/eu/etaxonomy/cdm/applicationContext-test.xml -text
 eu.etaxonomy.taxeditor.store/src/test/resources/eu/etaxonomy/cdm/cdm.datasources.xml -text
 eu.etaxonomy.taxeditor.store/src/test/resources/eu/etaxonomy/cdm/cdmfs.xml -text
index ec74879c81d92101dabab082231e0fb74409ac49..ee7db7b294174c67dc825b4fefadfdb93c894121 100644 (file)
 package eu.etaxonomy.taxeditor.model;
 
 import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
 import java.util.Collection;
-import java.util.Comparator;
 import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.TreeMap;
+import java.util.List;
 import java.util.TreeSet;
 
 import org.apache.log4j.Logger;
@@ -951,44 +948,69 @@ public abstract class AbstractUtility {
      * @return a map which holds the terms as keys and their string
      *         representation via {@link IEnumTerm#getMessage()} as values
      */
-    public static <T extends IEnumTerm<?>> Map<T, String> orderTerms(Collection<T> terms) {
-        Comparator<T> comparator = new Comparator<T>() {
-            @Override
-            public int compare(T t1, T t2) {
-                return t1.getMessage().compareTo(t2.getMessage());
-            }
-        };
-        Map<T, String> result = new LinkedHashMap<T, String>();
-        Map<T, Set<T>> termHierarchy = new TreeMap<T, Set<T>>(comparator);
-
-        for(T term : terms) {
-            Set<T> childList = new TreeSet<T>(comparator);
-            // add root element as keys
-            if(term.getKindOf()==null){
-                termHierarchy.put(term, childList);
+    public static <T extends IEnumTerm<T>> LinkedHashMap<T, String> orderTerms(Collection<T> terms) {
+//        Comparator<TermNode<T>> comparator = new Comparator<TermNode<T>>() {
+//            @Override
+//            public int compare(TermNode<T> t1, TermNode<T> t2) {
+//                return t1.getTerm().getMessage().compareTo(t2.getTerm().getMessage());
+//            }
+//        };
+
+        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 += "  ";
             }
-            // add child element to topmost parent i.e. root
-            else{
-                T root = getRootFor(term);
-                if(termHierarchy.containsKey(root)){
-                    termHierarchy.get(root).add(term);
-                }
-                else{
-                    childList.add(term);
-                    termHierarchy.put(root, childList);
+            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);
+        }
+    }
 
-        // create list according to the type hierarchy (root elements alphabetically with recursive children also alphabetically)
-        for(Entry<T, Set<T>> entry:termHierarchy.entrySet()){
-            T root = entry.getKey();
-            result.put(root, root.getMessage());
-            for(T child:entry.getValue()){
-                result.put(child, "  " + child.getMessage());
+    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));
             }
         }
-        return result;
+        addToParents(parents, terms);
+        return parents;
     }
 
     @SuppressWarnings("unchecked")
@@ -997,137 +1019,92 @@ public abstract class AbstractUtility {
      * @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 getRootFor(T term){
+    private static<T extends IEnumTerm<T>> T getParentFor(T term){
         // PP: cast should be safe. Why is Eclipse complaining??
-        T parent = (T) term.getKindOf();
+        T parent = term.getKindOf();
         if(parent==null){
             return term;
         }
         else{
-            return getRootFor((T) term.getKindOf());
+            return getParentFor(term.getKindOf());
         }
     }
 
-    
-//    /**
-//     * Orders a Collection of {@link IEnumTerm}s according to the term
-//     * hierarchy. The hierarchy will be reduced to two layers: one layer being
-//     * the root elements (that have no parents) and the other being their
-//     * children and children's children recursively.<br>
-//     * The returned map will be be ordered primarily by root elements and
-//     * secondarily by the child elements, both ascending alphabetically. <br>
-//     * <br>
-//     * The reduced hierarchy could look like this:<br>
-//     * <ul>
-//     * <li>Root1
-//     *  <ul>
-//     *   <li>child1
-//     *   <li>child2
-//     *   <li>childOfChild2
-//     *  </ul>
-//     * <li>root2
-//     * <ul><li>child4</ul>
-//     * </ul>
-//     *
-//     * @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<?>> LinkedHashMap<T, String> orderTerms(Collection<T> terms) {
-//        Comparator<TermNode<T>> comparator = new Comparator<TermNode<T>>() {
-//            @Override
-//            public int compare(TermNode<T> t1, TermNode<T> t2) {
-//                return t1.getTerm().getMessage().compareTo(t2.getTerm().getMessage());
-//            }
-//        };
-//        TreeSet<TermNode<T>> parentElements = new TreeSet<TermNode<T>>(comparator);
-//        for(T term : terms) {
-//            Set<T> childList = new TreeSet<T>(comparator);
-//            // add root element as keys
-//            if(term.getKindOf()==null){
-//                parentElements.add(new TermNode<T>(term));
-//            }
-//            // add child element to parent
-//            else{
-//                T parent = getParentFor(term);
-//                if(termHierarchy.containsKey(parent)){
-//                    termHierarchy.get(parent).add(term);
-//                }
-//                else{
-//                    childList.add(term);
-//                    termHierarchy.put(parent, childList);
-//                }
-//            }
-//        }
-//
-//        // create list according to the type hierarchy (root elements alphabetically with recursive children also alphabetically)
-//        LinkedHashMap<T, String> result = new LinkedHashMap<T, String>();
-//        for(Entry<T, Set<T>> entry:termHierarchy.entrySet()){
-//            T root = entry.getKey();
-//            result.put(root, root.getMessage());
-//            for(T child:entry.getValue()){
-//                result.put(child, "  " + child.getMessage());
-//            }
-//        }
-//        return result;
-//    }
-//
-//    private static addToParents(TreeSet<TermNode<T>> parents, Collection<T> terms){
-//        for(T term:terms){
-//
-//        }
-//    }
-//
-//    @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 getParentFor(T term){
-//        // PP: cast should be safe. Why is Eclipse complaining??
-//        T parent = (T) term.getKindOf();
-//        if(parent==null){
-//            return term;
-//        }
-//        else{
-//            return getParentFor((T) term.getKindOf());
-//        }
-//    }
-//
-//    private class 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;
-//        }
-//    }
+    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());
+        }
+    }
 
 }
index 16047827455312dafafd1c47f1220d2db85c680b..c73a79271dd28a31c3c9bf4ff0432fae903857f6 100644 (file)
@@ -13,6 +13,7 @@ package eu.etaxonomy.taxeditor.ui.combo;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map.Entry;
 
@@ -48,7 +49,7 @@ import eu.etaxonomy.taxeditor.ui.element.LayoutConstants;
  * @created Mar 16, 2010
  * @version 1.0
  */
-public class EnumComboElement<T extends IEnumTerm> extends
+public class EnumComboElement<T extends IEnumTerm<T>> extends
                AbstractCdmFormElement implements SelectionListener,
                IEnableableFormElement, ISelectable {
 
@@ -192,7 +193,9 @@ public class EnumComboElement<T extends IEnumTerm> extends
         * </p>
         */
        private void populateTypes(){
-               for(Entry<T, String> keyLabelPair : AbstractUtility.orderTerms(getElementsForClass(enumType)).entrySet()){
+           Collection<T> elementsForClass = getElementsForClass(enumType);
+        LinkedHashMap<T, String> orderedTerms = AbstractUtility.orderTerms(elementsForClass);
+               for(Entry<T, String> keyLabelPair : orderedTerms.entrySet()){
                        elementTypeList.add(keyLabelPair.getKey());
                        combo.add(keyLabelPair.getValue());
                }
index adaf493bce497aeba791497636da69a95a0815a1..00818c1455b98f60c566345fbb4f73cf30b40c25 100644 (file)
@@ -830,7 +830,7 @@ public class CdmFormFactory extends FormToolkit {
         * @return a {@link eu.etaxonomy.taxeditor.ui.term.AbstractEnumComboElement}
         *         object.
         */
-       public <T extends IEnumTerm> EnumComboElement<T> createEnumComboElement(
+       public <T extends IEnumTerm<T>> EnumComboElement<T> createEnumComboElement(
                        Class<T> enumComboType, ICdmFormElement parentElement,
                        int style) {
                EnumComboElement<T> element = new EnumComboElement<T>(this, parentElement, enumComboType, style);
diff --git a/eu.etaxonomy.taxeditor.store/src/test/java/eu/etaxonomy/taxeditor/store/utility/AbstractUtilityTest.java b/eu.etaxonomy.taxeditor.store/src/test/java/eu/etaxonomy/taxeditor/store/utility/AbstractUtilityTest.java
new file mode 100644 (file)
index 0000000..2391f91
--- /dev/null
@@ -0,0 +1,65 @@
+// $Id$
+/**
+* Copyright (C) 2013 EDIT
+* 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.store.utility;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.LinkedHashMap;
+
+import org.junit.Test;
+
+import eu.etaxonomy.cdm.model.common.IEnumTerm;
+import eu.etaxonomy.cdm.model.common.OriginalSourceType;
+import eu.etaxonomy.cdm.model.common.TermType;
+import eu.etaxonomy.cdm.model.metadata.PreferencePredicate;
+import eu.etaxonomy.cdm.model.name.NomenclaturalCode;
+import eu.etaxonomy.cdm.model.name.RankClass;
+import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationType;
+import eu.etaxonomy.cdm.model.reference.ReferenceType;
+import eu.etaxonomy.taxeditor.model.AbstractUtility;
+
+/**
+ * @author pplitzner
+ * @date 04.11.2013
+ *
+ */
+public class AbstractUtilityTest {
+
+    @Test
+    public void testOrderTerms(){
+        LinkedHashMap<? extends IEnumTerm<?>, String> orderedTerms;
+
+        orderedTerms = AbstractUtility.orderTerms(Arrays.asList(SpecimenOrObservationType.values()));
+        checkSize(orderedTerms, Arrays.asList(SpecimenOrObservationType.values()));
+        orderedTerms = AbstractUtility.orderTerms(Arrays.asList(NomenclaturalCode.values()));
+        checkSize(orderedTerms, Arrays.asList(NomenclaturalCode.values()));
+        orderedTerms = AbstractUtility.orderTerms(Arrays.asList(OriginalSourceType.values()));
+        checkSize(orderedTerms, Arrays.asList(OriginalSourceType.values()));
+        orderedTerms = AbstractUtility.orderTerms(Arrays.asList(PreferencePredicate.values()));
+        checkSize(orderedTerms, Arrays.asList(PreferencePredicate.values()));
+        orderedTerms = AbstractUtility.orderTerms(Arrays.asList(RankClass.values()));
+        checkSize(orderedTerms, Arrays.asList(RankClass.values()));
+        orderedTerms = AbstractUtility.orderTerms(Arrays.asList(ReferenceType.values()));
+        checkSize(orderedTerms, Arrays.asList(ReferenceType.values()));
+        orderedTerms = AbstractUtility.orderTerms(Arrays.asList(TermType.values()));
+        checkSize(orderedTerms, Arrays.asList(TermType.values()));
+    }
+
+    /**
+     * @param orderedTerms
+     * @param expectedTerms
+     */
+    private void checkSize(LinkedHashMap<? extends IEnumTerm<?>, String> orderedTerms, Collection<? extends IEnumTerm<?>> expectedTerms) {
+        assertEquals("Some terms got lost while ordering.", expectedTerms.size(), orderedTerms.size());
+    }
+
+}