cleanup
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / geo / DistributionServiceUtilities.java
index 4722596353dbdd6d3e4372e2cd28a781445f8718..5bb0a8a87f19d2b4960f63a89af2cada4c5137cc 100644 (file)
@@ -142,12 +142,12 @@ public class DistributionServiceUtilities {
      *            and a {@link TaggedText} representation of the condensed distribution string.
      */
     public static CondensedDistribution getCondensedDistribution(Collection<Distribution> filteredDistributions,
-            SetMap<NamedArea, NamedArea> parentAreaMap, CondensedDistributionConfiguration config, List<Language> languages) {
+            SetMap<NamedArea,TermNode<NamedArea>> area2TermNodesMap, CondensedDistributionConfiguration config, List<Language> languages) {
 
         CondensedDistributionComposer composer = new CondensedDistributionComposer();
 
         CondensedDistribution condensedDistribution = composer.createCondensedDistribution(
-                filteredDistributions, parentAreaMap, languages, config);
+                filteredDistributions, area2TermNodesMap, languages, config);
         return condensedDistribution;
     }
 
@@ -378,8 +378,8 @@ public class DistributionServiceUtilities {
      * @param distribution
      * @param area
      */
-    private static void addAreaToLayerMap(Map<String, Map<Integer,
-            Set<Distribution>>> layerMap,
+    private static void addAreaToLayerMap(Map<String,
+            Map<Integer,Set<Distribution>>> layerMap,
             List<PresenceAbsenceTerm> statusList,
             Distribution distribution,
             NamedArea area,
@@ -541,12 +541,13 @@ public class DistributionServiceUtilities {
 
     private static void addDistributionToStyleMap(Distribution distribution, Map<Integer, Set<Distribution>> styleMap,
             List<PresenceAbsenceTerm> statusList) {
+
         PresenceAbsenceTerm status = distribution.getStatus();
         if (status != null) {
             int style = statusList.indexOf(status);
             Set<Distribution> distributionSet = styleMap.get(style);
             if (distributionSet == null) {
-                distributionSet = new HashSet<Distribution>();
+                distributionSet = new HashSet<>();
                 styleMap.put(style, distributionSet);
             }
             distributionSet.add(distribution);
@@ -661,24 +662,25 @@ public class DistributionServiceUtilities {
      *
      * <li><b>Prefer aggregated rule</b>: if this flag is set to <code>true</code> aggregated
      * distributions are preferred over non-aggregated elements.
-     * (Aggregated description elements are identified by the description having type
-     * {@link DescriptionType.AGGREGATED_DISTRIBUTION}). This means if an non-aggregated status
+     * (Aggregated descriptions are identified by their description having type
+     * {@link DescriptionType.AGGREGATED_DISTRIBUTION}). This means if a non-aggregated status
      * information exists for the same area for which aggregated data is available,
      * the aggregated data has to be given preference over other data.
-     * see parameter <code>preferAggregated</code></li>
+     * See parameter <code>preferAggregated</code></li>
      *
      * <li><b>Status order preference rule</b>: In case of multiple distribution
      * status ({@link PresenceAbsenceTermBase}) for the same area the status
      * with the highest order is preferred, see
      * {@link DefinedTermBase#compareTo(DefinedTermBase)}. This rule is
      * optional, see parameter <code>statusOrderPreference</code></li>
+     *
      * <li><b>Sub area preference rule</b>: If there is an area with a <i>direct
      * sub area</i> and both areas have the same status only the
      * information on the sub area should be reported, whereas the super area
      * should be ignored. This rule is optional, see parameter
      * <code>subAreaPreference</code>. Can be run separately from the other filters.
      * This rule affects any distribution,
-     * that is to computed and edited equally. For more details see
+     * that is to be computed and edited equally. For more details see
      * {@link https://dev.e-taxonomy.eu/redmine/issues/5050})</li>
      * </ol>
      *
@@ -700,23 +702,32 @@ public class DistributionServiceUtilities {
      * @return the filtered collection of distribution elements.
      */
     public static Set<Distribution> filterDistributions(Collection<Distribution> distributions,
-            TermTree<NamedArea> areaTree,
-            Set<MarkerType> fallbackAreaMarkerTypes, boolean preferAggregated, boolean statusOrderPreference,
-            boolean subAreaPreference, boolean keepFallBackOnlyIfNoSubareaDataExists, boolean ignoreDistributionStatusUndefined) {
-
-        SetMap<NamedArea, Distribution> filteredDistributions = new SetMap<>(distributions.size());
+            TermTree<NamedArea> areaTree, TermTree<PresenceAbsenceTerm> statusTree,
+            Set<MarkerType> fallbackAreaMarkerTypes,
+            boolean preferAggregated, boolean statusOrderPreference,
+            boolean subAreaPreference, boolean keepFallBackOnlyIfNoSubareaDataExists) {
+
+        SetMap<NamedArea, Distribution> filteredDistributionsPerArea = new SetMap<>(distributions.size());
+        Set<UUID> statusPositiveSet = null;
+        if (statusTree != null) {
+            statusPositiveSet = new HashSet<>();
+            for (PresenceAbsenceTerm status : statusTree.asTermList()) {
+                statusPositiveSet.add(status.getUuid());
+            }
+        }
 
-        // sort Distributions by the area and filter undefinedStatus
+        // assign distributions to the area and filter undefined status
         for(Distribution distribution : distributions){
             NamedArea area = distribution.getArea();
             if(area == null) {
                 logger.debug("skipping distribution with NULL area");
                 continue;
             }
-            boolean filterUndefined = ignoreDistributionStatusUndefined && distribution.getStatus() != null
-                    && distribution.getStatus().getUuid().equals(PresenceAbsenceTerm.uuidUndefined);
-            if (!filterUndefined){
-                filteredDistributions.putItem(area, distribution);
+            boolean filterOutStatus = statusPositiveSet != null &&
+                    (distribution.getStatus() == null
+                      || !statusPositiveSet.contains(distribution.getStatus().getUuid()));
+            if (!filterOutStatus){
+                filteredDistributionsPerArea.putItem(area, distribution);
             }
         }
 
@@ -725,46 +736,46 @@ public class DistributionServiceUtilities {
         }
 
         // -------------------------------------------------------------------
-        // 1) skip distributions having an area with markers matching hiddenAreaMarkerTypes
+        // 1) skip distributions having an area with markers matching fallbackAreaMarkerTypes
         //    but keep distributions for fallback areas (areas with hidden marker, but with visible sub-areas)
         //TODO since using area tree this is only relevant if keepFallBackOnlyIfNoSubareaDataExists = true
         //     as the area tree should also exclude real hidden areas
 //        if(!CdmUtils.isNullSafeEmpty(fallbackAreaMarkerTypes)) {
-            removeHiddenAndKeepFallbackAreas(areaTree, fallbackAreaMarkerTypes, filteredDistributions, keepFallBackOnlyIfNoSubareaDataExists);
+            removeHiddenAndKeepFallbackAreas(areaTree, fallbackAreaMarkerTypes, filteredDistributionsPerArea, keepFallBackOnlyIfNoSubareaDataExists);
 //        }
 
         // -------------------------------------------------------------------
         // 2) remove not computed distributions for areas for which computed
         //    distributions exists
         if(preferAggregated) {
-            handlePreferAggregated(filteredDistributions);
+            handlePreferAggregated(filteredDistributionsPerArea);
         }
 
         // -------------------------------------------------------------------
         // 3) status order preference rule
         if (statusOrderPreference) {
-            SetMap<NamedArea, Distribution> tmpMap = new SetMap<>(filteredDistributions.size());
-            for(NamedArea key : filteredDistributions.keySet()){
-                tmpMap.put(key, filterByHighestDistributionStatusForArea(filteredDistributions.get(key)));
+            SetMap<NamedArea, Distribution> tmpMap = new SetMap<>(filteredDistributionsPerArea.size());
+            for(NamedArea key : filteredDistributionsPerArea.keySet()){
+                tmpMap.put(key, filterByHighestDistributionStatusForArea(filteredDistributionsPerArea.get(key)));
             }
-            filteredDistributions = tmpMap;
+            filteredDistributionsPerArea = tmpMap;
         }
 
         // -------------------------------------------------------------------
         // 4) Sub area preference rule
         if(subAreaPreference){
-            handleSubAreaPreferenceRule(filteredDistributions, areaTree);
+            handleSubAreaPreferenceRule(filteredDistributionsPerArea, areaTree);
         }
 
-        return valuesOfAllInnerSets(filteredDistributions.values());
+        return valuesOfAllInnerSets(filteredDistributionsPerArea.values());
     }
 
     static TermTree<NamedArea> getAreaTree(Collection<Distribution> distributions,
-            Set<MarkerType> hiddenAreaMarkerTypes) {
+            Set<MarkerType> fallbackAreaMarkerTypes) {
 
         //TODO see comment in below method
 //        List<TermVocabulary<NamedArea>> vocs = getVocabualries(distributions);
-        TermTree<NamedArea> areaTree = createAreaTreeByDistributions(distributions, hiddenAreaMarkerTypes);
+        TermTree<NamedArea> areaTree = createAreaTreeByDistributions(distributions, fallbackAreaMarkerTypes);
         return areaTree;
     }
 
@@ -788,20 +799,20 @@ public class DistributionServiceUtilities {
      * Removes hidden areas marked as such if they have no children.
      */
     private static TermTree<NamedArea> createAreaTreeByDistributions(Collection<Distribution> distributions,
-            Set<MarkerType> hiddenAreaMarkerTypes) {
+            Set<MarkerType> fallbackAreaMarkerTypes) {
 
-        hiddenAreaMarkerTypes = hiddenAreaMarkerTypes == null ? new HashSet<>() : hiddenAreaMarkerTypes;
+        fallbackAreaMarkerTypes = fallbackAreaMarkerTypes == null ? new HashSet<>() : fallbackAreaMarkerTypes;
         TermTree<NamedArea> result = TermTree.NewInstance(TermType.NamedArea, NamedArea.class);
 
         //create area list
         Set<NamedArea> relevantAreas = new HashSet<>();
         for (Distribution distribution : distributions) {
-            NamedArea area = distribution.getArea();
+            NamedArea area = HibernateProxyHelper.deproxy(distribution.getArea(), NamedArea.class) ;
             if (area != null && !relevantAreas.contains(area)) {
-                boolean isHidden = isMarkedHidden(area, hiddenAreaMarkerTypes);
+                boolean isHidden = isMarkedAs(area, fallbackAreaMarkerTypes);
                 if (isHidden) {
                     //if is hidden area either ignore (hidden area) or add unhidden children (fallback area)
-                    Set<NamedArea> unhiddenChildren = getUnhiddenChildren(area, hiddenAreaMarkerTypes);
+                    Set<NamedArea> unhiddenChildren = getUnhiddenChildren(area, fallbackAreaMarkerTypes);
                     if (!unhiddenChildren.isEmpty()) {
                         relevantAreas.addAll(unhiddenChildren);
                     }
@@ -857,32 +868,37 @@ public class DistributionServiceUtilities {
             return areaNode;
     }
 
-    private static Set<NamedArea> getUnhiddenChildren(NamedArea area, Set<MarkerType> hiddenAreaMarkerTypes) {
+    private static Set<NamedArea> getUnhiddenChildren(NamedArea area, Set<MarkerType> fallbackAreaMarkerTypes) {
         Set<NamedArea> result = new HashSet<>();
         for (NamedArea child : area.getIncludes()) {
-            if (!isMarkedHidden(child, hiddenAreaMarkerTypes)) {
+            if (!isMarkedAs(child, fallbackAreaMarkerTypes)) {
                 result.add(child);
             }
-            result.addAll(getUnhiddenChildren(child, hiddenAreaMarkerTypes));
+            result.addAll(getUnhiddenChildren(child, fallbackAreaMarkerTypes));
         }
         return result;
     }
 
-
+    /**
+     * Removes all distributions that have an area being a parent of
+     * another distribution area. E.g. removes distribution for "Europe"
+     * if a distribution for "France" exists in the list, where Europe
+     * is a direct parent for France.
+     */
     private static void handleSubAreaPreferenceRule(SetMap<NamedArea, Distribution> filteredDistributions,
             TermTree<NamedArea> areaTree) {
 
-        SetMap<NamedArea, NamedArea> parentMap = areaTree.getParentMap();
+        SetMap<NamedArea, NamedArea> childToParentsMap = areaTree.getParentMap();
         Set<NamedArea> removeCandidatesArea = new HashSet<>();
         for(NamedArea area : filteredDistributions.keySet()){
             if(removeCandidatesArea.contains(area)){
                 continue;
             }
-            //xx;
-            NamedArea parent = parentMap.getFirstValue(area);
-            if(parent != null && filteredDistributions.containsKey(parent)){
-                removeCandidatesArea.add(parent);
-            }
+            childToParentsMap.get(area).forEach(parent->{
+                if(parent != null && filteredDistributions.containsKey(parent)){
+                    removeCandidatesArea.add(parent);
+                }
+            });
         }
         for(NamedArea removeKey : removeCandidatesArea){
             filteredDistributions.remove(removeKey);
@@ -892,7 +908,7 @@ public class DistributionServiceUtilities {
     /**
      * Remove areas not in area tree but keep fallback areas.
      */
-    private static void removeHiddenAndKeepFallbackAreas(TermTree<NamedArea> areaTree, Set<MarkerType> hiddenAreaMarkerTypes,
+    private static void removeHiddenAndKeepFallbackAreas(TermTree<NamedArea> areaTree, Set<MarkerType> fallbackAreaMarkerTypes,
             SetMap<NamedArea,Distribution> filteredDistributionsPerArea, boolean keepFallBackOnlyIfNoSubareaDataExists) {
 
         Set<NamedArea> areasHiddenByMarker = new HashSet<>();
@@ -901,13 +917,13 @@ public class DistributionServiceUtilities {
         for(NamedArea area : filteredDistributionsPerArea.keySet()) {
             if (! availableAreas.contains(area)) {
                 areasHiddenByMarker.add(area);
-            }else if(isMarkedHidden(area, hiddenAreaMarkerTypes)) {
+            }else if(isMarkedAs(area, fallbackAreaMarkerTypes)) {
                 Set<TermNode<NamedArea>> nodes = areaTree.getNodesForTerm(area);
 
                 // if at least one sub area is not hidden by a marker
                 // the given area is a fall-back area for this sub area
                 SetMap<NamedArea, Distribution> distributionsForSubareaCheck = keepFallBackOnlyIfNoSubareaDataExists ? filteredDistributionsPerArea : null;
-                boolean isFallBackArea = isRemainingFallBackArea(nodes, hiddenAreaMarkerTypes, distributionsForSubareaCheck);
+                boolean isFallBackArea = isRemainingFallBackArea(nodes, fallbackAreaMarkerTypes, distributionsForSubareaCheck);
                 if (!isFallBackArea) {
                     // this area does not need to be shown as
                     // fall-back for another area so it will be hidden.
@@ -921,7 +937,7 @@ public class DistributionServiceUtilities {
     }
 
     //if filteredDistributions == null it can be ignored if data exists or not
-    private static boolean isRemainingFallBackArea(Set<TermNode<NamedArea>> areaNode, Set<MarkerType> hiddenAreaMarkerTypes,
+    private static boolean isRemainingFallBackArea(Set<TermNode<NamedArea>> areaNode, Set<MarkerType> fallbackAreaMarkerTypes,
             SetMap<NamedArea, Distribution> filteredDistributions) {
 
         Set<TermNode<NamedArea>> childNodes = new HashSet<>();
@@ -933,8 +949,8 @@ public class DistributionServiceUtilities {
             Set<TermNode<NamedArea>> childNodeAsSet = new HashSet<>();
             childNodeAsSet.add(included);
             //if subarea is not hidden and data exists return true
-            if (isMarkedHidden(subArea, hiddenAreaMarkerTypes)){
-                boolean subAreaIsFallback = isRemainingFallBackArea(childNodeAsSet, hiddenAreaMarkerTypes, filteredDistributions);
+            if (isMarkedAs(subArea, fallbackAreaMarkerTypes)){
+                boolean subAreaIsFallback = isRemainingFallBackArea(childNodeAsSet, fallbackAreaMarkerTypes, filteredDistributions);
                 if (subAreaIsFallback && noOrIgnoreData){
                     return true;
                 }else{
@@ -952,12 +968,12 @@ public class DistributionServiceUtilities {
 //            if (isNotHidden_AndHasNoData_OrDataCanBeIgnored) {
 //                return true;
 //            }
-//            if (!isMarkedHidden(subArea, hiddenAreaMarkerTypes) ){
+//            if (!isMarkedHidden(subArea, fallbackAreaMarkerTypes) ){
 //
 //            }
 //
 //            //do the same recursively
-//            boolean hasVisibleSubSubarea = isRemainingFallBackArea(subArea, hiddenAreaMarkerTypes, filteredDistributions, areasHiddenByMarker);
+//            boolean hasVisibleSubSubarea = isRemainingFallBackArea(subArea, fallbackAreaMarkerTypes, filteredDistributions, areasHiddenByMarker);
 //            if (hasVisibleSubSubarea){
 //                return true;
 //            }
@@ -1001,9 +1017,9 @@ public class DistributionServiceUtilities {
         return false;
     }
 
-    public static boolean isMarkedHidden(NamedArea area, Set<MarkerType> hiddenAreaMarkerTypes) {
-        if(hiddenAreaMarkerTypes != null) {
-            for(MarkerType markerType : hiddenAreaMarkerTypes){
+    public static boolean isMarkedAs(NamedArea area, Set<MarkerType> markerTypes) {
+        if(markerTypes != null) {
+            for(MarkerType markerType : markerTypes){
                 if(area.hasMarker(markerType, true)){
                     return true;
                 }
@@ -1035,6 +1051,7 @@ public class DistributionServiceUtilities {
             Collection<Distribution> distributions,
             SetMap<NamedArea, NamedArea> parentAreaMap,
             Set<MarkerType> fallbackAreaMarkerTypes,
+            Set<MarkerType> alternativeRootAreaMarkerTypes,
             boolean neverUseFallbackAreaAsParent,
             DistributionOrder distributionOrder,
             IDefinedTermDao termDao) {
@@ -1043,7 +1060,9 @@ public class DistributionServiceUtilities {
 
         if (logger.isDebugEnabled()){logger.debug("order tree ...");}
         //order by areas
-        tree.orderAsTree(distributions, parentAreaMap, omitLevels, fallbackAreaMarkerTypes, neverUseFallbackAreaAsParent);
+        tree.orderAsTree(distributions, parentAreaMap, omitLevels, fallbackAreaMarkerTypes,
+                neverUseFallbackAreaAsParent);
+        tree.handleAlternativeRootArea(alternativeRootAreaMarkerTypes);
         tree.recursiveSortChildren(distributionOrder); // TODO respect current locale for sorting
         if (logger.isDebugEnabled()){logger.debug("create tree - DONE");}
         return tree;
@@ -1052,21 +1071,32 @@ public class DistributionServiceUtilities {
     public static DistributionTreeDto buildOrderedTreeDto(Set<NamedAreaLevel> omitLevels,
             Collection<DistributionDto> distributions,
             SetMap<NamedArea, NamedArea> parentAreaMap,
-            Set<MarkerType> fallbackAreaMarkerTypes,
+            TermTree<NamedArea> areaTree, Set<MarkerType> fallbackAreaMarkerTypes,
+            Set<MarkerType> alternativeRootAreaMarkerType,
             boolean neverUseFallbackAreaAsParent,
             DistributionOrder distributionOrder,
-            IDefinedTermDao termDao) {
+            IDefinedTermDao termDao,
+            boolean useSecondMethod) {
 
         //TODO loader needed?
         DistributionTreeDtoLoader loader = new DistributionTreeDtoLoader(termDao);
-        DistributionTreeDto dto = loader.load();
+        DistributionTreeDto distributionTreeDto = loader.load();
 
         if (logger.isDebugEnabled()){logger.debug("order tree ...");}
+
         //order by areas
-        loader.orderAsTree(dto, distributions, parentAreaMap, omitLevels, fallbackAreaMarkerTypes, neverUseFallbackAreaAsParent);
-        loader.recursiveSortChildren(dto, distributionOrder); // TODO respect current locale for sorting
+        if (!useSecondMethod) {
+            loader.orderAsTree(distributionTreeDto, distributions, parentAreaMap, omitLevels,
+                    fallbackAreaMarkerTypes, neverUseFallbackAreaAsParent);
+        }else {
+            loader.orderAsTree2(distributionTreeDto, distributions, areaTree, omitLevels,
+                    fallbackAreaMarkerTypes, neverUseFallbackAreaAsParent);
+        }
+        loader.handleAlternativeRootArea(distributionTreeDto, alternativeRootAreaMarkerType);
+        loader.recursiveSortChildren(distributionTreeDto, distributionOrder); // TODO respect current locale for sorting
+
         if (logger.isDebugEnabled()){logger.debug("create tree - DONE");}
-        return dto;
+        return distributionTreeDto;
     }
 
     /**