ref #10222: full featureTree support and first implementation for all fact categories...
authorAndreas Müller <a.mueller@bgbm.org>
Mon, 13 Feb 2023 17:05:44 +0000 (18:05 +0100)
committerAndreas Müller <a.mueller@bgbm.org>
Mon, 13 Feb 2023 17:06:22 +0000 (18:06 +0100)
14 files changed:
cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/CdmBaseDto.java
cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/CommonNameDto.java [new file with mode: 0644]
cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/ContainerDto.java
cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/DistributionInfoDto.java
cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/FactDto.java
cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/FeatureDto.java
cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/IFactDto.java
cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/IPortalDtoBase.java [new file with mode: 0644]
cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/IndividualsAssociationDto.java [new file with mode: 0644]
cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/TaxonInteractionDto.java [new file with mode: 0644]
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/TreeNode.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/geo/DistributionTree.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/portal/DistributionTreeDtoLoader.java
cdmlib-services/src/main/java/eu/etaxonomy/cdm/api/service/portal/PortalDtoLoader.java

index 22afc9e4cf063aec21dace2273678abb5391d253..dffcde59ca80c23f3395429c9945e9362215d108 100644 (file)
@@ -15,7 +15,7 @@ import java.util.UUID;
  * @author a.mueller
  * @date 07.01.2023
  */
-public class CdmBaseDto {
+public class CdmBaseDto implements IPortalDtoBase {
 
 
     //the taxon uuid
@@ -51,11 +51,12 @@ public class CdmBaseDto {
         this.id = id;
     }
 
+    @Override
     public LocalDateTime getLastUpdated() {
         return lastUpdated;
     }
+    @Override
     public void setLastUpdated(LocalDateTime lastUpdated) {
         this.lastUpdated = lastUpdated;
     }
-
-}
+}
\ No newline at end of file
diff --git a/cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/CommonNameDto.java b/cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/CommonNameDto.java
new file mode 100644 (file)
index 0000000..0150796
--- /dev/null
@@ -0,0 +1,68 @@
+/**
+* Copyright (C) 2023 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.cdm.api.dto.portal;
+
+import java.util.UUID;
+
+/**
+ * @author a.mueller
+ * @date 13.02.2023
+ */
+public class CommonNameDto extends SourcedDto implements IFactDto {
+
+    private String language;
+    private UUID languageUuid;
+
+    private String area;
+    private UUID areaUUID;
+
+    private String name;
+
+ // ****************** GETTER / SETTER *****************************/
+
+    public String getLanguage() {
+        return language;
+    }
+    public void setLanguage(String language) {
+        this.language = language;
+    }
+
+    public UUID getLanguageUuid() {
+        return languageUuid;
+    }
+    public void setLanguageUuid(UUID languageUuid) {
+        this.languageUuid = languageUuid;
+    }
+
+    public String getArea() {
+        return area;
+    }
+    public void setArea(String area) {
+        this.area = area;
+    }
+
+    public UUID getAreaUUID() {
+        return areaUUID;
+    }
+    public void setAreaUUID(UUID areaUUID) {
+        this.areaUUID = areaUUID;
+    }
+
+    public String getName() {
+        return name;
+    }
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    @Override
+    public String getClazz() {
+        return this.getClass().getSimpleName();
+    }
+}
\ No newline at end of file
index 389f99db86a60f8195ea6073a297b4f84c140a00..a85427e62ab2b807339e2cc41e5bd45f9ccb87d1 100644 (file)
@@ -20,7 +20,7 @@ import java.util.Collection;
  * @author a.mueller
  * @date 07.01.2023
  */
-public class ContainerDto<T extends CdmBaseDto> {
+public class ContainerDto<T extends IPortalDtoBase> {
 
     private int count;
 
index 1c28bb2a1fce105aad134bf1577fc6839756381b..6b34ce976d7b85f05d28ca4021036d4967e4c626 100644 (file)
@@ -8,6 +8,7 @@
 */
 package eu.etaxonomy.cdm.api.dto.portal;
 
+import java.time.LocalDateTime;
 import java.util.Set;
 
 import eu.etaxonomy.cdm.format.description.distribution.CondensedDistribution;
@@ -22,12 +23,18 @@ public class DistributionInfoDto implements IFactDto {
     private CondensedDistribution condensedDistribution = null;
     private IDistributionTree tree = null;
     private String mapUriParams = null;
+    private LocalDateTime lastUpdated;
 
     //TODO remove elements ??
     private Set<Distribution> elements = null;
 
 // ****************** GETTER / SETTER ******************************/
 
+    @Override
+    public String getClazz() {
+        return this.getClass().getSimpleName();
+    }
+
     public CondensedDistribution getCondensedDistribution() {
         return condensedDistribution;
     }
@@ -51,14 +58,26 @@ public class DistributionInfoDto implements IFactDto {
 
     public Set<Distribution> getElements() {
         return elements;
-    }    public void setElements(Set<Distribution> elements) {
+    }
+    public void setElements(Set<Distribution> elements) {
         this.elements = elements;
     }
 
+    @Override
+    public LocalDateTime getLastUpdated() {
+        return lastUpdated;
+    }
+    @Override
+    public void setLastUpdated(LocalDateTime lastUpdated) {
+        this.lastUpdated = lastUpdated;
+    }
+
     public enum InfoPart{
         condensedDistribution,
         tree,
         mapUriParams,
         elements,
     }
+
+
 }
\ No newline at end of file
index 9d06e61c5e4dc09ad7770ecb0c08ce482fcf14c8..f0ff21aeffff6645cc35ab0c2a50e0a9d9990b03 100644 (file)
@@ -8,6 +8,7 @@
 */
 package eu.etaxonomy.cdm.api.dto.portal;
 
+import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -17,16 +18,33 @@ import eu.etaxonomy.cdm.format.common.TypedLabel;
  * @author a.mueller
  * @date 11.01.2023
  */
-public class FactDto implements IFactDto {
+public class FactDto extends SourcedDto implements IFactDto {
 
     private List<TypedLabel> typedLabel = new ArrayList<>();
 
+    private LocalDateTime lastUpdated;
+
 // ****************** GETTER / SETTER ***********************/
 
+    @Override
+    public String getClazz() {
+        return this.getClass().getSimpleName();
+    }
+
     public List<TypedLabel> getTypedLabel() {
         return typedLabel;
     }
     public void setTypedLabel(List<TypedLabel> typedLabel) {
         this.typedLabel = typedLabel;
     }
+
+
+    @Override
+    public LocalDateTime getLastUpdated() {
+        return lastUpdated;
+    }
+    @Override
+    public void setLastUpdated(LocalDateTime lastUpdated) {
+        this.lastUpdated = lastUpdated;
+    }
 }
\ No newline at end of file
index a0e0451f9e978e671308245c5f12d6c1073eaf37..9b9307022b22d769925f3c6d5e645f6d8930948e 100644 (file)
@@ -8,8 +8,6 @@
 */
 package eu.etaxonomy.cdm.api.dto.portal;
 
-import java.util.ArrayList;
-import java.util.List;
 import java.util.UUID;
 
 /**
@@ -18,7 +16,9 @@ import java.util.UUID;
  */
 public class FeatureDto extends LabeledEntityDto {
 
-    private List<IFactDto> facts = new ArrayList<>();
+    private ContainerDto<IFactDto> facts;
+
+    private ContainerDto<FeatureDto> subFeatures;
 
     public FeatureDto(UUID uuid, int id, String label) {
         super(uuid, id, label);
@@ -26,10 +26,20 @@ public class FeatureDto extends LabeledEntityDto {
 
 // *********************** GETTER / ADDER ********************************/
 
-    public List<IFactDto> getFacts() {
+    public ContainerDto<IFactDto> getFacts() {
         return facts;
     }
     public void addFact(IFactDto factDto) {
-        facts.add(factDto);
+        if (facts == null) {
+            facts = new ContainerDto<>();
+        }
+        facts.addItem(factDto);
+    }
+
+    public ContainerDto<FeatureDto> getSubFeatures() {
+        return subFeatures;
+    }
+    public void setSubFeatures(ContainerDto<FeatureDto> subFeatures) {
+        this.subFeatures = subFeatures;
     }
 }
\ No newline at end of file
index 21ccebb2991546639736b9cbf691d4e175a406a0..0f2163fd6521cf59b7a42fe1d2b1b3f2f8159062 100644 (file)
@@ -14,6 +14,8 @@ package eu.etaxonomy.cdm.api.dto.portal;
  * @author a.mueller
  * @date 09.02.2023
  */
-public interface IFactDto {
+public interface IFactDto extends IPortalDtoBase {
+
+    public String getClazz();
 
 }
\ No newline at end of file
diff --git a/cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/IPortalDtoBase.java b/cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/IPortalDtoBase.java
new file mode 100644 (file)
index 0000000..aa22caa
--- /dev/null
@@ -0,0 +1,22 @@
+/**
+* Copyright (C) 2023 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.cdm.api.dto.portal;
+
+import java.time.LocalDateTime;
+
+/**
+ * @author a.mueller
+ * @date 13.02.2023
+ */
+public interface IPortalDtoBase {
+
+    public LocalDateTime getLastUpdated();
+    public void setLastUpdated(LocalDateTime lastUpdated);
+
+}
diff --git a/cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/IndividualsAssociationDto.java b/cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/IndividualsAssociationDto.java
new file mode 100644 (file)
index 0000000..86ef32f
--- /dev/null
@@ -0,0 +1,48 @@
+/**
+* Copyright (C) 2023 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.cdm.api.dto.portal;
+
+import java.util.UUID;
+
+/**
+ * @author a.mueller
+ * @date 13.02.2023
+ */
+public class IndividualsAssociationDto extends SourcedDto implements IFactDto {
+
+    private String descritpion;
+
+    private String occurrence;
+    private UUID occurrenceUuid;
+
+    // ****************** GETTER / SETTER *****************************/
+
+    public String getDescritpion() {
+        return descritpion;
+    }
+    public void setDescritpion(String descritpion) {
+        this.descritpion = descritpion;
+    }
+    public String getOccurrence() {
+        return occurrence;
+    }
+    public void setOccurrence(String occurrence) {
+        this.occurrence = occurrence;
+    }
+    public UUID getOccurrenceUuid() {
+        return occurrenceUuid;
+    }
+    public void setOccurrenceUuid(UUID occurrenceUuid) {
+        this.occurrenceUuid = occurrenceUuid;
+    }
+    @Override
+    public String getClazz() {
+        return this.getClass().getSimpleName();
+    }
+}
\ No newline at end of file
diff --git a/cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/TaxonInteractionDto.java b/cdmlib-api/src/main/java/eu/etaxonomy/cdm/api/dto/portal/TaxonInteractionDto.java
new file mode 100644 (file)
index 0000000..31bee67
--- /dev/null
@@ -0,0 +1,55 @@
+/**
+* Copyright (C) 2023 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.cdm.api.dto.portal;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+import eu.etaxonomy.cdm.strategy.cache.TaggedText;
+
+/**
+ * @author a.mueller
+ * @date 13.02.2023
+ */
+public class TaxonInteractionDto extends SourcedDto implements IFactDto{
+
+    private String descritpion;
+
+    private List<TaggedText> taxon = new ArrayList<>();
+    private UUID taxonUuid;
+
+    // ****************** GETTER / SETTER *****************************/
+
+    public String getDescritpion() {
+        return descritpion;
+    }
+    public void setDescritpion(String descritpion) {
+        this.descritpion = descritpion;
+    }
+
+    public List<TaggedText> getTaxon() {
+        return taxon;
+    }
+    public void setTaxon(List<TaggedText> taxon) {
+        this.taxon = taxon;
+    }
+
+    public UUID getTaxonUuid() {
+        return taxonUuid;
+    }
+    public void setTaxonUuid(UUID taxonUuid) {
+        this.taxonUuid = taxonUuid;
+    }
+
+    @Override
+    public String getClazz() {
+        return this.getClass().getSimpleName();
+    }
+}
\ No newline at end of file
index f8d0956d2d60a9706d9008a83c8828f5232f892e..9c2e603475248f4422cad3949b72493401d8d45e 100644 (file)
@@ -14,13 +14,14 @@ import java.util.List;
 
 /**
  * TODO move this class to another package.
+ *
  * Represents a node of the Tree<T> class. The TreeNode<T,S> is also a container, and
  * can be thought of as instrumentation to determine the location of the type T
  * in the Tree<T>.
  */
 public class TreeNode<T,S> {
 
-    public T data;
+    private T data;
     private S nodeId;
     public List<TreeNode<T,S>> children;
 
@@ -37,25 +38,25 @@ public class TreeNode<T,S> {
         return result;
     }
 
-        public TreeNode<T,S> getChild(TreeNode<T,S> TreeNode) {
-            boolean found = false;
-            TreeNode<T,S> result = null;
-            Iterator<TreeNode<T,S>> it = children.iterator();
-            while (!found && it.hasNext()) {
-                result = it.next();
-                if (result.data.equals(TreeNode.data)){
-                    found = true;
-                }
+    public TreeNode<T,S> getChild(TreeNode<T,S> TreeNode) {
+        boolean found = false;
+        TreeNode<T,S> result = null;
+        Iterator<TreeNode<T,S>> it = children.iterator();
+        while (!found && it.hasNext()) {
+            result = it.next();
+            if (result.data.equals(TreeNode.data)){
+                found = true;
             }
-            if (!found){
-                try {
-                    throw new Exception("The node was not found in among children and that is a precondition of getChild(node) method");
-                } catch (Exception e) {
-                    e.printStackTrace();
-                }
+        }
+        if (!found){
+            try {
+                throw new Exception("The node was not found in among children and that is a precondition of getChild(node) method");
+            } catch (Exception e) {
+                e.printStackTrace();
             }
-            return result;
         }
+        return result;
+    }
 
 //    /**
 //     * Convenience ctor to create a Node<T> with an instance of T.
@@ -66,10 +67,14 @@ public class TreeNode<T,S> {
 //    }
 
     public TreeNode(S nodeId) {
-        super();
         this.nodeId = nodeId;
     }
 
+    public TreeNode(S nodeId, T data) {
+        this.nodeId = nodeId;
+        this.data = data;
+    }
+
     public TreeNode() {
         super();
     }
@@ -112,12 +117,14 @@ public class TreeNode<T,S> {
      * Adds a child to the list of children for this TreeNode<T,S>. The addition of
      * the first child will create a new List<TreeNode<T,S>>.
      * @param child a TreeNode<T,S> object to set.
+     * @return
      */
-    public void addChild(TreeNode<T,S> child) {
+    public TreeNode<T, S> addChild(TreeNode<T,S> child) {
         if (children == null) {
             children = new ArrayList<>();
         }
         children.add(child);
+        return child;
     }
 
     /**
@@ -170,4 +177,21 @@ public class TreeNode<T,S> {
         this.nodeId = nodeId;
     }
 
+    /**
+     * Returns the (first) child node (of type TreeNode) with the given nodeID.
+     * @return the found node or null
+     */
+    public TreeNode<T,S> findChildNode(S  nodeID) {
+        if (this.getChildren() == null) {
+            return null;
+        }
+
+        for (TreeNode<T, S> node : this.getChildren()) {
+            if (node.getNodeId().equals(nodeID)) {
+                return node;
+            }
+        }
+        return null;
+    }
+
 }
index eab6a7f587ac1d9a8dcb781ee6cbfad0d2e235d4..ab5b359b17b9c5c54a161e22e74f930e69c93542 100644 (file)
@@ -55,22 +55,7 @@ public class DistributionTree
         this.termDao = termDao;
     }
 
-    /**
-     * Returns the (first) child node (of type TreeNode) with the given nodeID.
-     * @return the found node or null
-     */
-    public TreeNode<Set<Distribution>,NamedArea> findChildNode(TreeNode<Set<Distribution>, NamedArea> parentNode, NamedArea  nodeID) {
-        if (parentNode.getChildren() == null) {
-            return null;
-        }
 
-        for (TreeNode<Set<Distribution>, NamedArea> node : parentNode.getChildren()) {
-            if (node.getNodeId().equals(nodeID)) {
-                return node;
-            }
-        }
-        return null;
-    }
 
     /**
      * @param fallbackAreaMarkerTypes
@@ -145,8 +130,8 @@ public class DistributionTree
             //nothing => stop condition
             return;
         }else {
-            Collections.sort(treeNode.children, comparator);
-            for (TreeNode<Set<Distribution>, NamedArea> child : treeNode.children) {
+            Collections.sort(treeNode.getChildren(), comparator);
+            for (TreeNode<Set<Distribution>, NamedArea> child : treeNode.getChildren()) {
                 innerRecursiveSortChildren(child, comparator);
             }
         }
@@ -181,11 +166,11 @@ public class DistributionTree
         //getting the highest area and inserting it into the tree
         NamedArea highestArea = namedAreaPath.get(0);
 
-        TreeNode<Set<Distribution>, NamedArea> child = findChildNode(root, highestArea);
+        TreeNode<Set<Distribution>, NamedArea> child = root.findChildNode(highestArea);
         if (child == null) {
             // the highestDistNode is not yet in the set of children, so we add it
             child = new TreeNode<Set<Distribution>, NamedArea>(highestArea);
-            child.setData(new HashSet<Distribution>());
+            child.setData(new HashSet<>());
             root.addChild(child);
         }
 
index 31564ebf4b171d725ebb3a549d6d66d1d6dc6a44..3246ad134470b2da1d39feffea475bcba1e6b641 100644 (file)
@@ -156,8 +156,8 @@ public class DistributionTreeDtoLoader {
           //nothing => stop condition
           return;
       }else {
-          Collections.sort(treeNode.children, comparator);
-          for (TreeNode<Set<DistributionDto>, NamedAreaDto> child : treeNode.children) {
+          Collections.sort(treeNode.getChildren(), comparator);
+          for (TreeNode<Set<DistributionDto>, NamedAreaDto> child : treeNode.getChildren()) {
               innerRecursiveSortChildren(child, comparator);
           }
       }
index 1738f5348bd63bb5f26297481294e271e9584de4..cf29087adcf92d59e97fb57a396c04c011adc607 100644 (file)
@@ -12,6 +12,7 @@ import java.awt.Color;
 import java.time.LocalDateTime;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
@@ -31,10 +32,12 @@ import eu.etaxonomy.cdm.api.application.ICdmRepository;
 import eu.etaxonomy.cdm.api.dto.portal.AnnotatableDto;
 import eu.etaxonomy.cdm.api.dto.portal.AnnotationDto;
 import eu.etaxonomy.cdm.api.dto.portal.CdmBaseDto;
+import eu.etaxonomy.cdm.api.dto.portal.CommonNameDto;
 import eu.etaxonomy.cdm.api.dto.portal.ContainerDto;
 import eu.etaxonomy.cdm.api.dto.portal.DistributionInfoDto;
 import eu.etaxonomy.cdm.api.dto.portal.FactDto;
 import eu.etaxonomy.cdm.api.dto.portal.FeatureDto;
+import eu.etaxonomy.cdm.api.dto.portal.IndividualsAssociationDto;
 import eu.etaxonomy.cdm.api.dto.portal.MarkerDto;
 import eu.etaxonomy.cdm.api.dto.portal.MessagesDto;
 import eu.etaxonomy.cdm.api.dto.portal.MessagesDto.MessageType;
@@ -42,6 +45,7 @@ import eu.etaxonomy.cdm.api.dto.portal.SingleSourcedDto;
 import eu.etaxonomy.cdm.api.dto.portal.SourceDto;
 import eu.etaxonomy.cdm.api.dto.portal.SourcedDto;
 import eu.etaxonomy.cdm.api.dto.portal.TaxonBaseDto;
+import eu.etaxonomy.cdm.api.dto.portal.TaxonInteractionDto;
 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto;
 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.ConceptRelationDTO;
 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.HomotypicGroupDTO;
@@ -60,8 +64,11 @@ import eu.etaxonomy.cdm.api.service.l10n.LocaleContext;
 import eu.etaxonomy.cdm.api.service.name.TypeDesignationSetContainer;
 import eu.etaxonomy.cdm.api.service.name.TypeDesignationSetFormatter;
 import eu.etaxonomy.cdm.common.CdmUtils;
+import eu.etaxonomy.cdm.common.TreeNode;
 import eu.etaxonomy.cdm.compare.taxon.TaxonComparator;
 import eu.etaxonomy.cdm.format.common.TypedLabel;
+import eu.etaxonomy.cdm.format.description.CategoricalDataFormatter;
+import eu.etaxonomy.cdm.format.description.QuantitativeDataFormatter;
 import eu.etaxonomy.cdm.format.description.distribution.CondensedDistributionConfiguration;
 import eu.etaxonomy.cdm.format.taxon.TaxonRelationshipFormatter;
 import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
@@ -72,15 +79,22 @@ import eu.etaxonomy.cdm.model.common.Language;
 import eu.etaxonomy.cdm.model.common.LanguageString;
 import eu.etaxonomy.cdm.model.common.Marker;
 import eu.etaxonomy.cdm.model.common.MarkerType;
+import eu.etaxonomy.cdm.model.common.MultilanguageTextHelper;
 import eu.etaxonomy.cdm.model.common.SingleSourcedEntityBase;
 import eu.etaxonomy.cdm.model.common.VersionableEntity;
+import eu.etaxonomy.cdm.model.description.CategoricalData;
+import eu.etaxonomy.cdm.model.description.CommonTaxonName;
 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
 import eu.etaxonomy.cdm.model.description.Distribution;
 import eu.etaxonomy.cdm.model.description.Feature;
 import eu.etaxonomy.cdm.model.description.IndividualsAssociation;
 import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
+import eu.etaxonomy.cdm.model.description.QuantitativeData;
 import eu.etaxonomy.cdm.model.description.TaxonDescription;
+import eu.etaxonomy.cdm.model.description.TaxonInteraction;
+import eu.etaxonomy.cdm.model.description.TemporalData;
 import eu.etaxonomy.cdm.model.description.TextData;
+import eu.etaxonomy.cdm.model.location.NamedArea;
 import eu.etaxonomy.cdm.model.media.ImageFile;
 import eu.etaxonomy.cdm.model.media.Media;
 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
@@ -102,6 +116,10 @@ import eu.etaxonomy.cdm.model.taxon.TaxonNode;
 import eu.etaxonomy.cdm.model.taxon.TaxonNodeAgentRelation;
 import eu.etaxonomy.cdm.model.taxon.TaxonNodeStatus;
 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
+import eu.etaxonomy.cdm.model.term.Representation;
+import eu.etaxonomy.cdm.model.term.TermBase;
+import eu.etaxonomy.cdm.model.term.TermNode;
+import eu.etaxonomy.cdm.model.term.TermTree;
 import eu.etaxonomy.cdm.strategy.cache.TaggedCacheHelper;
 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
 import eu.etaxonomy.cdm.strategy.cache.taxon.TaxonBaseDefaultCacheStrategy;
@@ -338,6 +356,8 @@ public class PortalDtoLoader {
 
         //TODO depending on config add/remove accepted name
 
+        //TODO check publish flag
+
         //homotypic synonyms
         List<Synonym> homotypicSynonmys = taxon.getHomotypicSynonymsByHomotypicGroup(comparator);
         TaxonPageDto.HomotypicGroupDTO homotypicGroupDto = new TaxonPageDto.HomotypicGroupDTO();
@@ -465,12 +485,68 @@ public class PortalDtoLoader {
         hgDto.addSynonym(synDto);
     }
 
-    private void loadFacts(Taxon taxon, TaxonPageDto result, TaxonPageDtoConfiguration config) {
+    private void loadFacts(Taxon taxon, TaxonPageDto taxonPageDto, TaxonPageDtoConfiguration config) {
+
+        //compute the feature that do exist for this taxon
+        Map<UUID, Feature> existingFeatureUuids = getExistingFeatureUuids(taxon);
 
-        //TODO load feature tree
+        //evaluate feature tree if it exists
+        TreeNode<Feature, UUID> filteredRootNode;
+        if (config.getFeatureTree() != null) {
+
+            //TODO class cast
+            TermTree<Feature> featureTree = repository.getTermTreeService().find(config.getFeatureTree());
+            filteredRootNode = filterFeatureNode(featureTree.getRoot(), existingFeatureUuids.keySet());
+        } else {
+            filteredRootNode = createDefaultFeatureNode(taxon);
+        }
 
         //load facts per feature
-        Map<Feature,Set<DescriptionElementBase>> featureMap = new HashMap<>();
+        Map<UUID,Set<DescriptionElementBase>> featureMap = loadfeatureMap(taxon);
+
+        //load final result
+        if (!filteredRootNode.getChildren().isEmpty()) {
+            ContainerDto<FeatureDto> features = new ContainerDto<>();
+            for (TreeNode<Feature,UUID> node : filteredRootNode.getChildren()) {
+                handleFeatureNode(taxon, config, featureMap, features, node);
+            }
+            taxonPageDto.setFactualData(features);
+        }
+    }
+
+    private void handleFeatureNode(Taxon taxon, TaxonPageDtoConfiguration config,
+            Map<UUID, Set<DescriptionElementBase>> featureMap, ContainerDto<FeatureDto> features,
+            TreeNode<Feature, UUID> node) {
+
+        Feature feature = node.getData();
+        //TODO locale
+        FeatureDto featureDto = new FeatureDto(feature.getUuid(), feature.getId(), feature.getLabel());
+        features.addItem(featureDto);
+
+        List<Distribution> distributions = new ArrayList<>();
+        //
+        for (DescriptionElementBase fact : featureMap.get(feature.getUuid())){
+            if (fact.isInstanceOf(Distribution.class)) {
+                distributions.add(CdmBase.deproxy(fact, Distribution.class));
+            }else {
+                handleFact(featureDto, fact);
+            }
+        }
+
+        handleDistributions(config, featureDto, taxon, distributions);
+
+        //children
+        ContainerDto<FeatureDto> childFeatures = new ContainerDto<>();
+        for (TreeNode<Feature,UUID> child : node.getChildren()) {
+            handleFeatureNode(taxon, config, featureMap, childFeatures, child);
+        }
+        if (childFeatures.getCount() > 0) {
+            featureDto.setSubFeatures(childFeatures);
+        }
+    }
+
+    private Map<UUID, Set<DescriptionElementBase>> loadfeatureMap(Taxon taxon) {
+        Map<UUID, Set<DescriptionElementBase>> featureMap = new HashMap<>();
 
         //... load facts
         for (TaxonDescription taxonDescription : taxon.getDescriptions()) {
@@ -479,36 +555,90 @@ public class PortalDtoLoader {
             }
             for (DescriptionElementBase deb : taxonDescription.getElements()) {
                 Feature feature = deb.getFeature();
-                if (featureMap.get(feature) == null) {
-                    featureMap.put(feature, new HashSet<>());
+                if (featureMap.get(feature.getUuid()) == null) {
+                    featureMap.put(feature.getUuid(), new HashSet<>());
                 }
-                featureMap.get(feature).add(deb);
+                featureMap.get(feature.getUuid()).add(deb);
             }
         }
+        return featureMap;
+    }
 
-        //TODO sort
+    private TreeNode<Feature, UUID> createDefaultFeatureNode(Taxon taxon) {
+        TreeNode<Feature, UUID> root = new TreeNode<>();
+        Set<Feature> requiredFeatures = new HashSet<>();
 
-        if (!featureMap.isEmpty()) {
-            ContainerDto<FeatureDto> features = new ContainerDto<>();
-            result.setFactualData(features);
-            for (Feature feature : featureMap.keySet()) {
-                //TODO locale
-                FeatureDto featureDto = new FeatureDto(feature.getUuid(), feature.getId(), feature.getLabel());
-                features.addItem(featureDto);
-
-                List<Distribution> distributions = new ArrayList<>();
-                //
-                for (DescriptionElementBase fact : featureMap.get(feature)){
-                    if (fact.isInstanceOf(Distribution.class)) {
-                        distributions.add(CdmBase.deproxy(fact, Distribution.class));
-                    }else {
-                        handleFact(featureDto, fact);
-                    }
+        for (TaxonDescription taxonDescription : taxon.getDescriptions()) {
+            if (taxonDescription.isImageGallery()) {
+                continue;
+            }
+            for (DescriptionElementBase deb : taxonDescription.getElements()) {
+                Feature feature = deb.getFeature();
+                if (feature != null) {  //null should not happen
+                    requiredFeatures.add(feature);
                 }
+            }
+        }
+        List<Feature> sortedChildren = new ArrayList<>(requiredFeatures);
+        Collections.sort(sortedChildren, (f1,f2) -> f1.getTitleCache().compareTo(f2.getTitleCache()));
+        sortedChildren.stream().forEachOrdered(f->root.addChild(new TreeNode<>(f.getUuid(), f)));
+        return root;
+    }
+
+    /**
+     * Recursive call to a feature tree's feature node in order to creates a tree structure
+     * ordered in the same way as the according feature tree but only containing features
+     * that do really exist for the given taxon. If only a child node is required the parent
+     * node/feature is also considered to be required.<BR>
+     */
+    private TreeNode<Feature, UUID> filterFeatureNode(TermNode<Feature> featureNode,
+            Set<UUID> existingFeatureUuids) {
+
+        //first filter children
+        List<TreeNode<Feature, UUID>> requiredChildNodes = new ArrayList<>();
+        for (TermNode<Feature> childNode : featureNode.getChildNodes()) {
+            TreeNode<Feature, UUID> child = filterFeatureNode(childNode, existingFeatureUuids);
+            if (child != null) {
+                requiredChildNodes.add(child);
+            }
+        }
+
+        //if any child is required or this node is required ....
+        if (!requiredChildNodes.isEmpty() ||
+                featureNode.getTerm() != null && existingFeatureUuids.contains(featureNode.getTerm().getUuid())) {
+            TreeNode<Feature,UUID> result = new TreeNode<>();
+            //add this nodes data
+            Feature feature = featureNode.getTerm() == null ? null : featureNode.getTerm();
+            if (feature != null) {
+                result.setNodeId(feature.getUuid());
+                result.setData(feature);
+            }
+            //add child data
+            requiredChildNodes.stream().forEachOrdered(c->result.addChild(c));
+            return result;
+        }else {
+            return null;
+        }
+    }
 
-                handleDistributions(config, featureDto, taxon, distributions);
+    /**
+     * Computes the (unsorted) set of features for  which facts exist
+     * for the given taxon.
+     */
+    private Map<UUID, Feature> getExistingFeatureUuids(Taxon taxon) {
+        Map<UUID, Feature> result = new HashMap<>();
+        for (TaxonDescription taxonDescription : taxon.getDescriptions()) {
+            if (taxonDescription.isImageGallery()) {
+                continue;
+            }
+            for (DescriptionElementBase deb : taxonDescription.getElements()) {
+                Feature feature = deb.getFeature();
+                if (feature != null) {  //null should not happen
+                    result.put(feature.getUuid(), feature);
+                }
             }
         }
+        return result;
     }
 
     private void handleDistributions(TaxonPageDtoConfiguration config, FeatureDto featureDto,
@@ -560,25 +690,130 @@ public class PortalDtoLoader {
     }
 
     private void handleFact(FeatureDto featureDto, DescriptionElementBase fact) {
+        //TODO locale
+        Language localeLang = null;
         if (fact.isInstanceOf(TextData.class)) {
             TextData td = CdmBase.deproxy(fact, TextData.class);
-            //TODO locale
-            Language lang = null;
-            LanguageString ls = td.getPreferredLanguageString(lang);
+            LanguageString ls = td.getPreferredLanguageString(localeLang);
             String text = ls == null ? "" : CdmUtils.Nz(ls.getText());
 
             FactDto factDto = new FactDto();
-            featureDto.getFacts().add(factDto);
+            featureDto.addFact(factDto);
             //TODO do we really need type information for textdata here?
             TypedLabel typedLabel = new TypedLabel(text);
             typedLabel.setClassAndId(td);
             factDto.getTypedLabel().add(typedLabel);
+            loadBaseData(td, factDto);
+            //TODO
+        }else if (fact.isInstanceOf(CommonTaxonName.class)) {
+            CommonTaxonName ctn = CdmBase.deproxy(fact, CommonTaxonName.class);
+            CommonNameDto dto = new CommonNameDto();
+            featureDto.addFact(dto);
+
+            Language lang = ctn.getLanguage();
+            if (lang != null) {
+                String langLabel = getTermLabel(lang, localeLang);
+                dto.setLanguage(langLabel);
+                dto.setLanguageUuid(lang.getUuid());
+            }else {
+                //TODO
+                dto.setLanguage("-");
+            }
+            //area
+            NamedArea area = ctn.getArea();
+            if (area != null) {
+                String areaLabel = getTermLabel(area, localeLang);
+                dto.setArea(areaLabel);
+                dto.setAreaUUID(area.getUuid());
+            }
+            dto.setName(ctn.getName());
+            loadBaseData(ctn, dto);
+            //TODO sort all common names
+
+        } else if (fact.isInstanceOf(IndividualsAssociation.class)) {
+            IndividualsAssociation ia = CdmBase.deproxy(fact, IndividualsAssociation.class);
+            IndividualsAssociationDto dto = new IndividualsAssociationDto ();
+
+            LanguageString description = MultilanguageTextHelper.getPreferredLanguageString(ia.getDescription(), Arrays.asList(localeLang));
+            if (description != null) {
+                dto.setDescritpion(description.getText());
+            }
+            SpecimenOrObservationBase<?> specimen = ia.getAssociatedSpecimenOrObservation();
+            if (specimen != null) {
+                //TODO what to use here??
+                dto.setOccurrence(specimen.getTitleCache());
+                dto.setOccurrenceUuid(specimen.getUuid());
+            }
+
+            featureDto.addFact(dto);
+            loadBaseData(ia, dto);
+        } else if (fact.isInstanceOf(TaxonInteraction.class)) {
+            TaxonInteraction ti = CdmBase.deproxy(fact, TaxonInteraction.class);
+            TaxonInteractionDto dto = new TaxonInteractionDto ();
+
+            LanguageString description = MultilanguageTextHelper.getPreferredLanguageString(
+                    ti.getDescription(), Arrays.asList(localeLang));
+            if (description != null) {
+                dto.setDescritpion(description.getText());
+            }
+            Taxon taxon = ti.getTaxon2();
+            if (taxon != null) {
+                //TODO what to use here??
+                dto.setTaxon(taxon.cacheStrategy().getTaggedTitle(taxon));
+                dto.setTaxonUuid(taxon.getUuid());
+            }
+            featureDto.addFact(dto);
+            loadBaseData(ti, dto);
+        }else if (fact.isInstanceOf(CategoricalData.class)) {
+            CategoricalData cd = CdmBase.deproxy(fact, CategoricalData.class);
+            FactDto factDto = new FactDto();
+            featureDto.addFact(factDto);
+            //TODO do we really need type information for textdata here?
+            String label = CategoricalDataFormatter.NewInstance(null).format(cd, localeLang);
+            TypedLabel typedLabel = new TypedLabel(label);
+            typedLabel.setClassAndId(cd);
+            factDto.getTypedLabel().add(typedLabel);
+            //TODO
+            loadBaseData(cd, factDto);
+        }else if (fact.isInstanceOf(QuantitativeData.class)) {
+            QuantitativeData qd = CdmBase.deproxy(fact, QuantitativeData.class);
+            FactDto factDto = new FactDto();
+            featureDto.addFact(factDto);
+            //TODO do we really need type information for textdata here?
+            String label = QuantitativeDataFormatter.NewInstance(null).format(qd, localeLang);
+            TypedLabel typedLabel = new TypedLabel(label);
+            typedLabel.setClassAndId(qd);
+            factDto.getTypedLabel().add(typedLabel);
+            //TODO
+            loadBaseData(qd, factDto);
+        }else if (fact.isInstanceOf(TemporalData.class)) {
+            TemporalData td = CdmBase.deproxy(fact, TemporalData.class);
+            FactDto factDto = new FactDto();
+            featureDto.addFact(factDto);
+            //TODO do we really need type information for textdata here?
+            String label = td.toString();
+            TypedLabel typedLabel = new TypedLabel(label);
+            typedLabel.setClassAndId(td);
+            factDto.getTypedLabel().add(typedLabel);
+            //TODO
+            loadBaseData(td, factDto);
         }else {
 //            TODO
+            logger.warn("DescriptionElement type not yet handled: " + fact.getClass().getSimpleName());
         }
 
     }
 
+    private String getTermLabel(TermBase term, Language localeLang) {
+        if (term == null) {
+            return null;
+        }
+        Representation rep = term.getPreferredRepresentation(localeLang);
+        String label = rep == null ? null : rep.getLabel();
+        label = label == null ? term.getLabel() : label;
+        return label;
+    }
+
     /**
      * Compares an existing last date and the last date of an entity
      * and returns the resulting last date.