--- /dev/null
+/**
+* Copyright (C) 2021 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.common;
+
+/**
+ * @author a.mueller
+ * @since 18.02.2021
+ */
+public class TripleResult<S extends Object, T extends Object, U extends Object>
+ extends DoubleResult<S,T> {
+
+ private U thirdResult;
+
+ public TripleResult(S firstResult, T secondResult, U thirdResult) {
+ super(firstResult, secondResult);
+ this.thirdResult = thirdResult;
+ }
+
+ public U getThirdResult() {
+ return thirdResult;
+ }
+
+ public void setThirdResult(U thirdResult) {
+ this.thirdResult = thirdResult;
+ }
+
+}
--- /dev/null
+/**
+* Copyright (C) 2016 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.ext.geo;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.codehaus.plexus.util.StringUtils;
+
+import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;
+import eu.etaxonomy.cdm.common.DoubleResult;
+import eu.etaxonomy.cdm.common.TripleResult;
+import eu.etaxonomy.cdm.common.UTF8;
+import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.model.description.Distribution;
+import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
+import eu.etaxonomy.cdm.model.location.NamedArea;
+import eu.etaxonomy.cdm.model.term.Representation;
+
+/**
+ * Base class for condensed distribution composers
+ *
+ * @author a.mueller
+ * @since 02.06.2016
+ */
+public class CondensedDistributionComposer {
+
+ //for old handling of status symbols
+ protected static Map<UUID, String> statusSymbols;
+
+ static {
+
+ // ==================================================
+ // Mapping as defined in ticket https://dev.e-taxonomy.eu/redmine/issues/3907
+ // ==================================================
+
+ statusSymbols = new HashMap<> ();
+ statusSymbols.put(PresenceAbsenceTerm.INTRODUCED_REPORTED_IN_ERROR().getUuid(), UTF8.EN_DASH.toString());
+ }
+
+
+ public enum StatusSymbolUsage{
+ Map,
+ Symbol1,
+ Symbol2,
+ IdInVoc,
+ AbbrelLabel;
+
+ public String getSymbol(PresenceAbsenceTerm status) {
+ if (this == Map){
+ return statusSymbols.get(status.getUuid());
+ }else if (this == Symbol1){
+ return status.getSymbol();
+ }else if (this == Symbol2){
+ return status.getSymbol2();
+ }else if (this == IdInVoc){
+ return status.getIdInVocabulary();
+ }else if (this == AbbrelLabel){
+ Representation r = status.getPreferredRepresentation((Language)null);
+ if (r != null){
+ String abbrevLabel = r.getAbbreviatedLabel();
+ if (abbrevLabel != null){
+ return abbrevLabel;
+ }
+ }
+ }
+ throw new RuntimeException("Unhandled enum value: " + this);
+ }
+ }
+
+ public CondensedDistribution createCondensedDistribution(Collection<Distribution> filteredDistributions,
+ List<Language> languages, CondensedDistributionConfiguration config) {
+
+ CondensedDistribution result = new CondensedDistribution();
+
+ Map<NamedArea, PresenceAbsenceTerm> areaToStatusMap = new HashMap<>();
+
+ DoubleResult<List<AreaNode>, List<AreaNode>> step1_3 = createAreaTreesAndStatusMap(filteredDistributions, areaToStatusMap, config);
+ List<AreaNode> topLevelNodes = step1_3.getFirstResult();
+ List<AreaNode> introducedTopLevelNodes = step1_3.getSecondResult();
+
+ //4. replace the area by the abbreviated representation and add symbols
+ AreaNodeComparator areaNodeComparator = new AreaNodeComparator();
+ Collections.sort(topLevelNodes, areaNodeComparator);
+
+ final boolean NOT_BOLED = false;
+ final boolean NOT_HANDLED_BY_PARENT = false;
+
+ List<AreaNode> outOfScopeNodes = new ArrayList<>();
+ if (!topLevelNodes.isEmpty()){
+ AreaNode areaOfScopeNode = topLevelNodes.remove(0);
+ outOfScopeNodes = topLevelNodes;
+
+ //handle areaOfScope (endemic area)
+ PresenceAbsenceTerm areaOfScopeStatus = areaToStatusMap.get(areaOfScopeNode.area);
+ DoubleResult<String, Boolean> areaOfScopeStatusSymbol = statusSymbol(areaOfScopeStatus, config, NOT_HANDLED_BY_PARENT);
+ String areaOfScopeLabel = config.showAreaOfScopeLabel? makeAreaLabel(languages, areaOfScopeNode.area, config, null):"";
+ result.addStatusAndAreaTaggedText(areaOfScopeStatusSymbol.getFirstResult(),
+ areaOfScopeLabel, areaOfScopeStatusSymbol.getSecondResult() || config.areasBold);
+
+ //subareas
+ handleSubAreas(result, areaOfScopeNode, config, areaNodeComparator, languages, areaToStatusMap,
+ areaOfScopeLabel, 0, NOT_BOLED, NOT_HANDLED_BY_PARENT, false);
+ }
+
+ //subareas with introduced status (if required by configuration)
+ if (config.splitNativeAndIntroduced && !introducedTopLevelNodes.isEmpty()){
+ String sep = (result.isEmpty()? "": " ") + config.introducedBracketStart;
+ result.addSeparatorTaggedText(sep, NOT_BOLED);
+ boolean isIntroduced = true;
+ handleSubAreasLoop(result, introducedTopLevelNodes.get(0), config, areaNodeComparator, languages,
+ areaToStatusMap, "", 0, NOT_BOLED, NOT_HANDLED_BY_PARENT, isIntroduced);
+ result.addSeparatorTaggedText(config.introducedBracketEnd, NOT_BOLED);
+ }
+
+ //outOfScope areas (areas outside the endemic area)
+ if (!outOfScopeNodes.isEmpty()){
+ result.addPostSeparatorTaggedText(config.outOfScopeAreasSeperator);
+ List<AreaNode> outOfScopeList = new ArrayList<>(outOfScopeNodes);
+ Collections.sort(outOfScopeList, areaNodeComparator);
+ boolean isFirst = true;
+ for (AreaNode outOfScopeNode: outOfScopeList){
+ String sep = isFirst ? "": " ";
+ result.addSeparatorTaggedText(sep);
+ handleSubAreaNode(languages, result, areaToStatusMap, areaNodeComparator,
+ outOfScopeNode, config, null, 0, NOT_BOLED, NOT_HANDLED_BY_PARENT, false);
+ isFirst = false;
+ }
+ }
+
+ return result;
+ }
+
+ protected Map<NamedArea, AreaNode>[] buildAreaHierarchie(Map<NamedArea, PresenceAbsenceTerm> areaToStatusMap,
+ CondensedDistributionConfiguration config) {
+
+ Map<NamedArea, AreaNode> areaNodeMap = new HashMap<>();
+ Map<NamedArea, AreaNode> introducedAreaNodeMap = new HashMap<>();
+
+ for(NamedArea area : areaToStatusMap.keySet()) {
+ Map<NamedArea, AreaNode> map = areaNodeMap;
+ if (config.splitNativeAndIntroduced && isIntroduced(areaToStatusMap.get(area))){
+ map = introducedAreaNodeMap;
+ }
+ mergeIntoHierarchy(areaToStatusMap.keySet(), map, area, config);
+ }
+ @SuppressWarnings("unchecked")
+ Map<NamedArea, AreaNode>[] result = new Map[]{areaNodeMap,introducedAreaNodeMap};
+ return result;
+ }
+
+ private boolean isIntroduced(PresenceAbsenceTerm status) {
+ return status.isAnyIntroduced();
+ }
+
+ private void mergeIntoHierarchy(Collection<NamedArea> areas, //areas not really needed anymore if we don't use findParentIn
+ Map<NamedArea, AreaNode> areaNodeMap, NamedArea area, CondensedDistributionConfiguration config) {
+
+ AreaNode node = areaNodeMap.get(area);
+ if(node == null) {
+ // putting area into list of areas as node
+ node = new AreaNode(area);
+ areaNodeMap.put(area, node);
+ }
+
+ NamedArea parent = getNonFallbackParent(area, config); // findParentIn(area, areas);
+
+ if(parent != null) {
+ AreaNode parentNode = areaNodeMap.get(parent);
+ if(parentNode == null) {
+ parentNode = new AreaNode(parent);
+ areaNodeMap.put(parent, parentNode);
+ }
+ parentNode.addSubArea(node);
+ mergeIntoHierarchy(areas, areaNodeMap, parentNode.area, config); //recursive to top
+ }
+ }
+
+ private NamedArea getNonFallbackParent(NamedArea area, CondensedDistributionConfiguration config) {
+ NamedArea parent = area.getPartOf();
+ while(parent != null && parent.hasMarker(config.fallbackAreaMarker, true)){
+ parent = parent.getPartOf();
+ }
+ return parent;
+ }
+
+ protected String makeAreaLabel(List<Language> langs, NamedArea area,
+ CondensedDistributionConfiguration config, String parentAreaLabel) {
+ //TODO config with symbols, not only idInVocabulary
+ String label = area.getIdInVocabulary() != null ? area.getIdInVocabulary() :area.getPreferredRepresentation(langs).getAbbreviatedLabel();
+ if (config.shortenSubAreaLabelsIfPossible && parentAreaLabel != null && !parentAreaLabel.isEmpty()){
+ //TODO make brackets not hardcoded, but also allow [],- etc., but how?
+ if (label.startsWith(parentAreaLabel+"(") && label.endsWith(")") ){
+ label = label.substring(parentAreaLabel.length()+1, label.length()-1);
+ }
+ }
+
+ return label;
+ }
+
+ protected TripleResult<String, Boolean, Boolean> statusSymbolForArea(AreaNode areaNode, Map<NamedArea, PresenceAbsenceTerm> areaToStatusMap,
+ CondensedDistributionConfiguration config, boolean onlyIntroduced) {
+
+ if (!config.showStatusOnParentAreaIfAllSame ){
+ return statusSymbol(areaToStatusMap.get(areaNode.area), config, false);
+ }else{
+ Set<PresenceAbsenceTerm> statusList = getStatusRecursive(areaNode, areaToStatusMap, new HashSet<>(), onlyIntroduced);
+ if (statusList.isEmpty()){
+ return statusSymbol(areaToStatusMap.get(areaNode.area), config, false);
+ }else if (statusList.size() == 1){
+ return statusSymbol(statusList.iterator().next(), config, true);
+ }else{
+ //subarea status is handled at subarea level, usually parent area status is empty as the parent area will not have a status
+ return statusSymbol(areaToStatusMap.get(areaNode.area), config, false);
+ }
+ }
+ }
+
+ private Set<PresenceAbsenceTerm> getStatusRecursive(AreaNode areaNode,
+ Map<NamedArea, PresenceAbsenceTerm> areaToStatusMap, Set<PresenceAbsenceTerm> statusList,
+ boolean onlyIntroduced) {
+
+ PresenceAbsenceTerm status = areaToStatusMap.get(areaNode.area);
+ if (status != null && (!onlyIntroduced || isIntroduced(status))){
+ statusList.add(status);
+ }
+
+ for (AreaNode subNode : areaNode.subAreas){
+ statusList.addAll(getStatusRecursive(subNode, areaToStatusMap, statusList, onlyIntroduced));
+ }
+ return statusList;
+ }
+
+ /**
+ * @param status
+ * @param config configuration
+ * @param statusHandledByParent indicates if the status is handled by the parent. Will be passed to the (third) result
+ * @return a triple result with the first result being the symbol string, the second result being the isBold flag
+ * and the third result indicates if this symbol includes information for all sub-areas (which passes the input parameter)
+ * to the output here
+ */
+ protected TripleResult<String, Boolean, Boolean> statusSymbol(PresenceAbsenceTerm status,
+ CondensedDistributionConfiguration config, boolean statusHandledByParent) {
+
+ List<StatusSymbolUsage> symbolPreferences = Arrays.asList(config.statusSymbolField);
+ if(status == null) {
+ return new TripleResult<>("", false, statusHandledByParent);
+ }
+
+ //usually the symbols should all come from the same field, but in case they don't ...
+ if (config.showAnyStatusSmbol){
+ for (StatusSymbolUsage usage : StatusSymbolUsage.values()){
+ if (!symbolPreferences.contains(usage)){
+ symbolPreferences.add(usage);
+ }
+ }
+ }
+
+ for (StatusSymbolUsage usage: symbolPreferences){
+ String symbol = usage.getSymbol(status);
+ if (symbol != null){
+ return new TripleResult<>(symbol, isBoldStatus(status, config), statusHandledByParent);
+ }
+ }
+
+ return new TripleResult<>("", isBoldStatus(status, config), statusHandledByParent);
+ }
+
+ private Boolean isBoldStatus(PresenceAbsenceTerm status, CondensedDistributionConfiguration config) {
+ return config.statusForBoldAreas.contains(status.getUuid());
+ }
+
+ /**
+ * Searches for the parent area of the area given as parameter in
+ * the collection of areas.
+ *
+ * @parent area
+ * The area which parent area is to be searched
+ * @param collection
+ * The areas to search in.
+ *
+ * @return
+ * Either the parent if it has been found or null.
+ */
+ protected NamedArea findParentIn(NamedArea area, Collection<NamedArea> areas) {
+ NamedArea parent = area.getPartOf();
+ if(parent != null && areas.contains(parent)){
+ return parent;
+ }
+ return null;
+ }
+
+ protected class AreaNode {
+
+ protected final NamedArea area;
+ protected AreaNode parent = null;
+ protected final Set<AreaNode> subAreas = new HashSet<>();
+
+ /**
+ * @param area
+ */
+ public AreaNode(NamedArea area) {
+ this.area = area;
+ }
+
+ public void addSubArea(AreaNode subArea) {
+ subAreas.add(subArea);
+ subArea.parent = this;
+ }
+
+ public AreaNode getParent() {
+ return parent;
+ }
+
+ public boolean hasParent() {
+ return getParent() != null;
+ }
+
+ public Collection<NamedArea> getSubareas() {
+ Collection<NamedArea> areas = new HashSet<>();
+ for(AreaNode node : subAreas) {
+ areas.add(node.area);
+ }
+ return areas;
+ }
+
+ @Override
+ public String toString() {
+ return "AreaNode [area=" + area + "]";
+ }
+ }
+
+ protected class AreaNodeComparator implements Comparator<AreaNode>{
+
+ @Override
+ public int compare(AreaNode areaNode1, AreaNode areaNode2) {
+ NamedArea area1 = areaNode1.area;
+ NamedArea area2 = areaNode2.area;
+
+ if (area1 == null && area2 == null){
+ return 0;
+ }else if (area1 == null){
+ return -1;
+ }else if (area2 == null){
+ return 1;
+ }else{
+ //- due to wrong ordering behavior in DefinedTerms
+ return - area1.compareTo(area2);
+ }
+ }
+ }
+
+ protected DoubleResult<List<AreaNode>, List<AreaNode>> createAreaTreesAndStatusMap(Collection<Distribution> filteredDistributions,
+ Map<NamedArea, PresenceAbsenceTerm> areaToStatusMap, CondensedDistributionConfiguration config){
+
+ //we expect every area only to have 1 status (multiple status should have been filtered beforehand)
+
+ //1. compute all areas and their status
+ for(Distribution distr : filteredDistributions) {
+ PresenceAbsenceTerm status = distr.getStatus();
+ NamedArea area = distr.getArea();
+
+ //TODO needed? Do we only want to have areas with status?
+ if(status == null || area == null) {
+ continue;
+ }
+
+ areaToStatusMap.put(area, status);
+ }
+
+ //2. build the area hierarchy
+ Map<NamedArea, AreaNode>[] areaNodeMaps = buildAreaHierarchie(areaToStatusMap, config);
+
+ //3. find root nodes
+ @SuppressWarnings("unchecked")
+ List<AreaNode>[] topLevelNodes = new ArrayList[]{new ArrayList<>(), new ArrayList<>()};
+
+ for (int i = 0; i < 2; i++){
+ for(AreaNode node : areaNodeMaps[i].values()) {
+ if(!node.hasParent() && ! topLevelNodes[i].contains(node)) {
+ topLevelNodes[i].add(node);
+ }
+ }
+ }
+
+ DoubleResult<List<AreaNode>, List<AreaNode>> result = new DoubleResult<>(topLevelNodes[0], topLevelNodes[1]);
+
+ return result;
+ }
+
+ private void handleSubAreas(CondensedDistribution result, AreaNode areaNode, CondensedDistributionConfiguration config,
+ AreaNodeComparator areaNodeComparator, List<Language> languages, Map<NamedArea, PresenceAbsenceTerm> areaToStatusMap,
+ String parentAreaLabel, int level, boolean isBold, boolean statusHandledByParent, boolean isIntroduced) {
+
+ if (!areaNode.subAreas.isEmpty()) {
+ String sepStart = getListValue(config.areaOfScopeSubAreaBracketStart, level, "(");
+ if (StringUtils.isNotBlank(sepStart)){
+ result.addSeparatorTaggedText(sepStart, isBold);
+ }
+ handleSubAreasLoop(result, areaNode, config, areaNodeComparator, languages, areaToStatusMap, parentAreaLabel,
+ level, isBold, statusHandledByParent, isIntroduced);
+ String sepEnd = getListValue(config.areaOfScopeSubAreaBracketEnd, level, ")");
+ if (StringUtils.isNotBlank(sepEnd)){
+ result.addSeparatorTaggedText(sepEnd, isBold);
+ }
+ if (isBold){
+ result.addSeparatorTaggedText("", true); //previous separator is otherwise not included in bold html tag by TaggedCacheHelper
+ }
+ }
+ }
+
+ private void handleSubAreasLoop(CondensedDistribution result, AreaNode areaNode,
+ CondensedDistributionConfiguration config, AreaNodeComparator areaNodeComparator, List<Language> languages,
+ Map<NamedArea, PresenceAbsenceTerm> areaToStatusMap, String parentLabel, int level, boolean isBold,
+ boolean statusHandledByParent, boolean isIntroduced) {
+
+ List<AreaNode> subAreas = new ArrayList<>(areaNode.subAreas);
+ Collections.sort(subAreas, areaNodeComparator);
+ boolean isFirst = true;
+ for (AreaNode subAreaNode: subAreas){
+ if (!isFirst){
+ result.addSeparatorTaggedText(" ", isBold);
+ }
+ handleSubAreaNode(languages, result, areaToStatusMap, areaNodeComparator,
+ subAreaNode, config, parentLabel, level, isBold, statusHandledByParent, isIntroduced);
+ isFirst = false;
+ }
+ }
+
+ private String getListValue(List<String> list, int i, String defaultStr) {
+ if (i < list.size()){
+ return list.get(i);
+ }
+ return defaultStr;
+ }
+
+ private void handleSubAreaNode(List<Language> languages, CondensedDistribution result,
+ Map<NamedArea, PresenceAbsenceTerm> areaToStatusMap, AreaNodeComparator areaNodeComparator,
+ AreaNode areaNode, CondensedDistributionConfiguration config, String parentAreaLabel, int level,
+ boolean parentIsBold, boolean statusHandledByParent, boolean isIntroduced) {
+
+ level++;
+ NamedArea area = areaNode.area;
+
+ TripleResult<String, Boolean, Boolean> statusSymbol = statusHandledByParent?
+ new TripleResult<>("", parentIsBold, statusHandledByParent):
+ statusSymbolForArea(areaNode, areaToStatusMap, config, isIntroduced);
+
+ String areaLabel = makeAreaLabel(languages, area, config, parentAreaLabel);
+ result.addStatusAndAreaTaggedText(statusSymbol.getFirstResult(), areaLabel, statusSymbol.getSecondResult() || config.areasBold);
+
+ boolean isBold = statusSymbol.getSecondResult();
+ boolean isHandledByParent = statusSymbol.getThirdResult();
+ handleSubAreas(result, areaNode, config, areaNodeComparator, languages, areaToStatusMap, areaLabel, level,
+ isBold, isHandledByParent, isIntroduced);
+ }
+}
+++ /dev/null
-/**
-* Copyright (C) 2016 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.ext.geo;
-
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-import eu.etaxonomy.cdm.model.common.Language;
-import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
-import eu.etaxonomy.cdm.model.location.NamedArea;
-import eu.etaxonomy.cdm.model.term.Representation;
-
-/**
- * Base class for condensed distribution composers
- *
- * @author a.mueller
- * @since 02.06.2016
- */
-public abstract class CondensedDistributionComposerBase implements ICondensedDistributionComposer{
-
- protected static Map<UUID, String> statusSymbols;
-
- protected String areaPreTag = "<b>";
- protected String areaPostTag = "</b>";
-
- protected boolean replaceCommonAreaLabelStart;
-
- //for future use in combined DistributionComposer
- private boolean sortByStatus;
-
- protected String makeAreaLabel(List<Language> langs, NamedArea area) {
- String result = area.getIdInVocabulary() != null ? area.getIdInVocabulary() :area.getPreferredRepresentation(langs).getAbbreviatedLabel();
- return areaPreTag + result + areaPostTag;
- }
-
- protected String statusSymbol(PresenceAbsenceTerm status) {
- if(status == null) {
- return "";
- }
- String symbol = statusSymbols.get(status.getUuid());
- if(symbol != null) {
- return symbol;
- }else if (status.getSymbol() != null){
- return status.getSymbol();
- }else if (status.getIdInVocabulary() != null){
- return status.getIdInVocabulary();
- }else {
- Representation r = status.getPreferredRepresentation((Language)null);
- if (r != null){
- String abbrevLabel = r.getAbbreviatedLabel();
- if (abbrevLabel != null){
- return abbrevLabel;
- }
- }
- }
-
- return "n.a.";
- }
-
- /**
- * Searches for the parent are of the area given as parameter in
- * the Collection of areas.
- *
- * @parent area
- * The area whose parent area is to be searched
- * @param collection
- * The areas to search in.
- *
- * @return
- * Either the parent if it has been found or null.
- */
- protected NamedArea findParentIn(NamedArea area, Collection<NamedArea> areas) {
- NamedArea parent = area.getPartOf();
- if(parent != null && areas.contains(parent)){
- return parent;
- }
- return null;
- }
-
- protected class AreaNode {
-
- protected final NamedArea area;
- protected AreaNode parent = null;
- protected final Set<AreaNode> subAreas = new HashSet<>();
-
- /**
- * @param area
- */
- public AreaNode(NamedArea area) {
- this.area = area;
- }
-
- public void addSubArea(AreaNode subArea) {
- subAreas.add(subArea);
- subArea.parent = this;
- }
-
- public AreaNode getParent() {
- return parent;
- }
-
- public boolean hasParent() {
- return getParent() != null;
- }
-
- public Collection<NamedArea> getSubareas() {
- Collection<NamedArea> areas = new HashSet<>();
- for(AreaNode node : subAreas) {
- areas.add(node.area);
- }
- return areas;
- }
-
- @Override
- public String toString() {
- return "AreaNode [area=" + area + "]";
- }
- }
-
-
- /**
- * @param status
- * @return
- */
- //Keep here only in case new version does creates problems in E+M
- //can be deleted if no problem occurs
- private String statusSymbolEuroMedOld(PresenceAbsenceTerm status) {
- if(status == null) {
- return "";
- }
- String symbol = statusSymbols.get(status.getUuid());
- if(symbol == null) {
- symbol = "";
- }
- return symbol;
- }
-
-
- public String getAreaPreTag() {
- return areaPreTag;
- }
-
- public void setAreaPreTag(String areaPreTag) {
- this.areaPreTag = areaPreTag;
- }
-
- public String getAreaPostTag() {
- return areaPostTag;
- }
-
- public void setAreaPostTag(String areaPostTag) {
- this.areaPostTag = areaPostTag;
- }
-}
--- /dev/null
+/**
+* Copyright (C) 2021 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.ext.geo;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.UUID;
+
+import eu.etaxonomy.cdm.common.UTF8;
+import eu.etaxonomy.cdm.ext.geo.CondensedDistributionComposer.StatusSymbolUsage;
+import eu.etaxonomy.cdm.model.common.MarkerType;
+import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
+
+/**
+ * @author a.mueller
+ * @since 17.02.2021
+ */
+public class CondensedDistributionConfiguration{
+
+ //if true, all areas are shown in bold, no matter which status they have
+ public boolean areasBold = false; //true for Cuba
+
+ //if true the area of scope (e.g. EM) area label is shown, otherwise only
+ //the status symbol (usually an endemism information is shown, if at all)
+ public boolean showAreaOfScopeLabel = false; //true for Cuba
+
+ //the separator before the list of outOfScope areas
+ public String outOfScopeAreasSeperator = " " + UTF8.EN_DASH.toString() + " "; //not needed for E+M
+
+ public boolean splitNativeAndIntroduced = true; //false for Cuba
+
+ public String introducedBracketStart = "[";
+
+ public String introducedBracketEnd = "]";
+
+ //if true a subarea Az(F) will be shortened to F if it is shown in brackets after the parent area
+ public boolean shortenSubAreaLabelsIfPossible = true; //not relevant for Cuba
+
+ public boolean showStatusOnParentAreaIfAllSame = true; //false for Cuba
+
+ public List<String> areaOfScopeSubAreaBracketStart = Arrays.asList(new String[]{" ","("}); // "(","(" for Cuba
+
+ public List<String> areaOfScopeSubAreaBracketEnd = Arrays.asList(new String[]{"",")"}); // ")",")" for Cuba
+
+ public List<UUID> statusForBoldAreas = Arrays.asList(new UUID[]{PresenceAbsenceTerm.uuidNative}); //empty for Cuba as for Cuba all areas are bold
+
+ public StatusSymbolUsage statusSymbolField = StatusSymbolUsage.Symbol2; //currently Symbol1, but may change in future
+
+ //if true, any non-empty symbol is taken from symbol2, symbol1, idInVoc and abbrevLabel according to the given order
+ public boolean showAnyStatusSmbol = false; //usually does not make sense to mix symbol fields
+
+ public UUID fallbackAreaMarker = MarkerType.uuidFallbackArea;
+
+//************************** FACTORY ***************************************/
+
+ public static CondensedDistributionConfiguration NewDefaultInstance() {
+ CondensedDistributionConfiguration result = new CondensedDistributionConfiguration();
+ return result;
+ }
+
+ public static CondensedDistributionConfiguration NewCubaInstance() {
+ CondensedDistributionConfiguration result = new CondensedDistributionConfiguration();
+ result.areasBold = true;
+ result.showAreaOfScopeLabel = true;
+ result.splitNativeAndIntroduced = false;
+ result.shortenSubAreaLabelsIfPossible = false;
+ result.showStatusOnParentAreaIfAllSame = false;
+ result.areaOfScopeSubAreaBracketStart.set(0, "(");
+ result.areaOfScopeSubAreaBracketEnd.set(0, ")");
+ result.statusForBoldAreas = new ArrayList<>();
+ result.statusSymbolField = StatusSymbolUsage.Symbol1;
+ return result;
+ }
+}
\ No newline at end of file
*/
package eu.etaxonomy.cdm.ext.geo;
-import eu.etaxonomy.cdm.ext.geo.EuroPlusMedCondensedDistributionComposer;
-import eu.etaxonomy.cdm.ext.geo.FloraCubaCondensedDistributionComposer;
-import eu.etaxonomy.cdm.ext.geo.ICondensedDistributionComposer;
import eu.etaxonomy.cdm.model.metadata.IKeyLabel;
/**
* @author a.kohlbecker
* @since Jun 24, 2015
*
+ * @deprecated the usage of this class is deprecated, please use {@link CondensedDistributionConfiguration} instead
*/
+ @Deprecated
public enum CondensedDistributionRecipe implements IKeyLabel{
/**
* <li>{@link http://dev.e-taxonomy.eu/trac/ticket/3907}</li>
* </ul>
*/
- EuroPlusMed("EuroPlusMed", "Euro + Med", EuroPlusMedCondensedDistributionComposer.class),
- FloraCuba("FloraCuba", "Flora Cuba", FloraCubaCondensedDistributionComposer.class);
+ EuroPlusMed("EuroPlusMed", "Euro + Med"),
+ FloraCuba("FloraCuba", "Flora Cuba");
- Class<? extends ICondensedDistributionComposer> implementation;
- String label;
- String key;
+ private String label;
+ private String key;
- CondensedDistributionRecipe(String key, String label, Class<? extends ICondensedDistributionComposer> implementation) {
+ private CondensedDistributionRecipe(String key, String label) {
this.key = key;
this.label = label;
- this.implementation = implementation;
- }
-
- public ICondensedDistributionComposer newCondensedDistributionComposerInstance() throws InstantiationException, IllegalAccessException {
- return implementation.newInstance();
}
@Override
public String getKey() {
return key;
}
+
+ public CondensedDistributionConfiguration toConfiguration(){
+ if (this == FloraCuba){
+ return CondensedDistributionConfiguration.NewCubaInstance();
+ } else {
+ return CondensedDistributionConfiguration.NewDefaultInstance();
+ }
+ }
}
}
if(parts.contains(InfoPart.condensedDistribution)) {
- dto.setCondensedDistribution(EditGeoServiceUtilities.getCondensedDistribution(
- filteredDistributions, recipe, languages));
+ CondensedDistribution condensedDistribution = EditGeoServiceUtilities.getCondensedDistribution(
+ filteredDistributions, recipe, languages);
+ dto.setCondensedDistribution(condensedDistribution);
}
if (parts.contains(InfoPart.mapUriParams)) {
public static CondensedDistribution getCondensedDistribution(Collection<Distribution> filteredDistributions,\r
CondensedDistributionRecipe recipe, List<Language> langs) {\r
\r
- ICondensedDistributionComposer composer;\r
- if(recipe == null) {\r
- throw new NullPointerException("parameter recipe must not be null");\r
- }\r
- try {\r
- composer = recipe.newCondensedDistributionComposerInstance();\r
- } catch (InstantiationException|IllegalAccessException e) {\r
- throw new RuntimeException(e);\r
- }\r
+ CondensedDistributionComposer composer = new CondensedDistributionComposer();\r
+\r
CondensedDistribution condensedDistribution = composer.createCondensedDistribution(\r
- filteredDistributions, langs);\r
+ filteredDistributions, langs, recipe.toConfiguration());\r
return condensedDistribution;\r
}\r
}\r
+++ /dev/null
-/**
-* Copyright (C) 2015 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.ext.geo;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
-
-import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
-
-import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;
-import eu.etaxonomy.cdm.common.SetMap;
-import eu.etaxonomy.cdm.common.UTF8;
-import eu.etaxonomy.cdm.model.common.Language;
-import eu.etaxonomy.cdm.model.description.Distribution;
-import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
-import eu.etaxonomy.cdm.model.location.NamedArea;
-
-/**
- * @author a.kohlbecker
- * @since Jun 24, 2015
- */
-public class EuroPlusMedCondensedDistributionComposer
- extends CondensedDistributionComposerBase {
-
- @SuppressWarnings("unused")
- private static final Logger logger = Logger.getLogger(EuroPlusMedCondensedDistributionComposer.class);
-
- private final CondensedDistribution condensedDistribution;
-
- private static Set<UUID> foreignStatusUuids;
-
- // these status uuids are special for EuroPlusMed and might also be used
- private final static UUID REPORTED_IN_ERROR_UUID = UUID.fromString("38604788-cf05-4607-b155-86db456f7680");
-
- static {
-
- // ==================================================
- // Mapping as defined in ticket https://dev.e-taxonomy.eu/redmine/issues/3907
- // ==================================================
-
- statusSymbols = new HashMap<> ();
- // â—� endemic (U+25CF BLACK CIRCLE)
- statusSymbols.put(PresenceAbsenceTerm.ENDEMIC_FOR_THE_RELEVANT_AREA().getUuid(), "\u25CF");
-
- // Lu native (incl. archaeophytes) TODO status archaeophytes?
- statusSymbols.put(PresenceAbsenceTerm.NATIVE().getUuid(), "");
- statusSymbols.put(PresenceAbsenceTerm.NATIVE_FORMERLY_NATIVE().getUuid(), ""); //wanted? differs from default "ne"
-
- // ?Lu doubtfully present (U+3F QUESTION MARK)
- statusSymbols.put(PresenceAbsenceTerm.INTRODUCED_PRESENCE_QUESTIONABLE().getUuid(), "?");
- statusSymbols.put(PresenceAbsenceTerm.NATIVE_PRESENCE_QUESTIONABLE().getUuid(), "?");
- statusSymbols.put(PresenceAbsenceTerm.PRESENT_DOUBTFULLY().getUuid(), "?");
-
- // dLu doubtfully native
- statusSymbols.put(PresenceAbsenceTerm.NATIVE_DOUBTFULLY_NATIVE().getUuid(), "d");
-
- // -Lu absent but reported in error (U+2D HYPHEN-MINUS)
- statusSymbols.put(PresenceAbsenceTerm.INTRODUCED_REPORTED_IN_ERROR().getUuid(), UTF8.EN_DASH.toString());
- statusSymbols.put(PresenceAbsenceTerm.NATIVE_REPORTED_IN_ERROR().getUuid(), UTF8.EN_DASH.toString());
- statusSymbols.put(REPORTED_IN_ERROR_UUID, "-");
-
- // †Lu (presumably) extinct (U+2020 DAGGER)
- // no such status in database!!!
-
- // [Lu] introduced (casual or naturalized) = introduced, introduced: naturalized
- statusSymbols.put(PresenceAbsenceTerm.INTRODUCED().getUuid(), ""); //wanted? differs from default "i"
-
- // [aLu] casual alien = introduced: adventitious (casual)
- statusSymbols.put(PresenceAbsenceTerm.CASUAL().getUuid(), "a");
-
- // [cLu] cultivated
- statusSymbols.put(PresenceAbsenceTerm.CULTIVATED() .getUuid(), "c");
- statusSymbols.put(PresenceAbsenceTerm.INTRODUCED_CULTIVATED().getUuid(), "c");
-
- // [nLu] naturalized
- statusSymbols.put(PresenceAbsenceTerm.NATURALISED().getUuid(), "n");
- statusSymbols.put(PresenceAbsenceTerm.NATURALISED().getUuid(), "n");
-
- foreignStatusUuids = new HashSet<>();
- foreignStatusUuids.add(PresenceAbsenceTerm.INTRODUCED().getUuid());
- foreignStatusUuids.add(PresenceAbsenceTerm.NATURALISED().getUuid());
- foreignStatusUuids.add(PresenceAbsenceTerm.CASUAL().getUuid());
- foreignStatusUuids.add(PresenceAbsenceTerm.INTRODUCED_CULTIVATED().getUuid());
- foreignStatusUuids.add(PresenceAbsenceTerm.NATURALISED().getUuid());
- foreignStatusUuids.add(PresenceAbsenceTerm.CULTIVATED().getUuid());
-
- }
-
- public EuroPlusMedCondensedDistributionComposer() {
- replaceCommonAreaLabelStart = true;
- condensedDistribution = new CondensedDistribution();
- }
-
- @Override
- public CondensedDistribution createCondensedDistribution(
- Collection<Distribution> filteredDistributions,
- List<Language> langs) {
-
- //1. group by PresenceAbsenceTerms
- SetMap<PresenceAbsenceTerm, NamedArea> areasByStatus = new SetMap<>();
- for(Distribution d : filteredDistributions) {
- PresenceAbsenceTerm status = d.getStatus();
- if(status == null) {
- continue;
- }
- areasByStatus.putItem(status, d.getArea());
- }
-
- //2. build the area hierarchy
- for(PresenceAbsenceTerm status : areasByStatus.keySet()) {
-
- Map<NamedArea, AreaNode> areaNodeMap = new HashMap<>();
-
- for(NamedArea area : areasByStatus.get(status)) {
- AreaNode node = areaNodeMap.get(area);
- if (node == null){
- node = new AreaNode(area);
- areaNodeMap.put(area, node);
- }
-
- NamedArea parent = findParentIn(area, areasByStatus.get(status));
- if(parent != null) {
- AreaNode parentNode = areaNodeMap.get(parent);;
- if (parentNode == null){
- parentNode = new AreaNode(parent);
- areaNodeMap.put(parent, parentNode);
- }
- parentNode.addSubArea(node);
- }
- }
-
- //3. find root nodes
- Set<AreaNode>hierarchy = new HashSet<>();
- for(AreaNode node : areaNodeMap.values()) {
- if(!node.hasParent()) {
- hierarchy.add(node);
- }
- }
-
- //4. replace the area by the abbreviated representation and add symbols
- for(AreaNode topLevelNode : hierarchy) {
-
- StringBuilder areaStatusString = new StringBuilder();
-
- String statusSymbol = statusSymbol(status);
- areaStatusString.append(statusSymbol);
-
- String areaLabel = topLevelNode.area.getPreferredRepresentation(langs).getAbbreviatedLabel();
- areaStatusString.append(areaLabel);
-
- if(!topLevelNode.subAreas.isEmpty()) {
- areaStatusString.append('(');
- subAreaLabels(langs, topLevelNode.subAreas, areaStatusString, statusSymbol, areaLabel);
- areaStatusString.append(')');
- }
-
- if(isForeignStatus(status)) {
- condensedDistribution.addForeignDistributionItem(status, areaStatusString.toString(), areaLabel);
- } else {
- condensedDistribution.addIndigenousDistributionItem(status, areaStatusString.toString(), areaLabel);
- }
- }
-
- }
- //5. order the condensedDistributions alphabetically
- condensedDistribution.sortForeign();
- condensedDistribution.sortIndigenous();
-
- return condensedDistribution;
- }
-
-
- private boolean isForeignStatus(PresenceAbsenceTerm status) {
- return foreignStatusUuids.contains(status.getUuid());
- }
-
- private void subAreaLabels(List<Language> langs, Collection<AreaNode> nodes, StringBuilder areaString,
- String statusSymbol, String parentLabel) {
-
- List<String> subAreaLabels = new ArrayList<>();
-
- for(AreaNode node : nodes) {
-
- StringBuilder subAreaString = new StringBuilder();
-
- subAreaString.append(statusSymbol);
-
- String areaLabel = node.area.getPreferredRepresentation(langs).getAbbreviatedLabel();
- if (replaceCommonAreaLabelStart){
- String cleanSubAreaLabel = StringUtils.replaceEach(areaLabel, new String[] {parentLabel, "(", ")"}, new String[] {"", "", ""});
- subAreaString.append(cleanSubAreaLabel);
- }else{
- subAreaString.append(areaLabel);
- }
-
- if(!node.subAreas.isEmpty()) {
- subAreaString.append('(');
- subAreaLabels(langs, node.subAreas, subAreaString, statusSymbol, areaLabel);
- subAreaString.append(')');
- }
-
- subAreaLabels.add(subAreaString.toString());
- }
- Collections.sort(subAreaLabels);
- areaString.append(StringUtils.join(subAreaLabels, " "));
- }
-}
\ No newline at end of file
+++ /dev/null
-/**
-* Copyright (C) 2015 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.ext.geo;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
-
-import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;
-import eu.etaxonomy.cdm.common.UTF8;
-import eu.etaxonomy.cdm.model.common.Language;
-import eu.etaxonomy.cdm.model.description.Distribution;
-import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
-import eu.etaxonomy.cdm.model.location.NamedArea;
-
-/**
- * @author a.mueller
- * @since Apr 05, 2016
- */
-public class FloraCubaCondensedDistributionComposer extends CondensedDistributionComposerBase {
-
- @SuppressWarnings("unused")
- private static final Logger logger = Logger.getLogger(FloraCubaCondensedDistributionComposer.class);
-
-// private static Set<UUID> foreignStatusUuids;
-
- //preliminary for Cuba, needs to be parameterized
- private UUID uuidInternalArea = UUID.fromString("d0144a6e-0e17-4a1d-bce5-d464a2aa7229"); //Cuba
-
- private String internalAreaSeparator = UTF8.EN_DASH.toString() + " ";
-
- static {
-
- // ==================================================
- // Mapping as defined in ticket https://dev.e-taxonomy.eu/redmine/issues/5682
- // ==================================================
-
- statusSymbols = new HashMap<> ();
- //no entries as we handle symbols now on model level
-
- }
-
-// ***************************** GETTER/SETTER ***********************************/
-
- public String getInternalAreaSeparator() {
- return internalAreaSeparator;
- }
- public void setInternalAreaSeparator(String internalAreaSeparator) {
- this.internalAreaSeparator = internalAreaSeparator;
- }
-
-// ***********************************************************************
-
- @Override
- public CondensedDistribution createCondensedDistribution(Collection<Distribution> filteredDistributions,
- List<Language> languages) {
-
- CondensedDistribution result = new CondensedDistribution();
-
- //we expect every area only to have 1 status (multiple status should have been filtered beforehand)
- Map<NamedArea, PresenceAbsenceTerm> areaToStatusMap = new HashMap<>();
-
-
- //1. compute all areas and their status
- for(Distribution distr : filteredDistributions) {
- PresenceAbsenceTerm status = distr.getStatus();
- NamedArea area = distr.getArea();
-
- //TODO needed? Do we only want to have areas with status?
- if(status == null || area == null) {
- continue;
- }
-
- areaToStatusMap.put(area, status);
- }
-
-
- //2. build the area hierarchy
- Map<NamedArea, AreaNode> areaNodeMap = new HashMap<>();
-
- for(NamedArea area : areaToStatusMap.keySet()) {
- AreaNode node;
- if(!areaNodeMap.containsKey(area)) {
- // putting area into hierarchy as node
- node = new AreaNode(area);
- areaNodeMap.put(area, node);
- } else {
- // is parent of another and thus already has a node
- node = areaNodeMap.get(area);
- }
-
- NamedArea parent = findParentIn(area, areaToStatusMap.keySet());
- if(parent != null) {
- AreaNode parentNode;
- if(!areaNodeMap.containsKey(parent)) {
- parentNode = new AreaNode(parent);
- areaNodeMap.put(parent, parentNode);
- } else {
- parentNode = areaNodeMap.get(parent);
- }
- parentNode.addSubArea(node);
- }
- }
-
- //3. find root nodes
- List<AreaNode>topLevelNodes = new ArrayList<>();
- for(AreaNode node : areaNodeMap.values()) {
- if(!node.hasParent() && ! topLevelNodes.contains(node)) {
- topLevelNodes.add(node);
- }
- }
-
- //4. replace the area by the abbreviated representation and add symbols
- boolean isFirstAfterAreaOfScope = false;
- AreaNodeComparator areaNodeComparator = new AreaNodeComparator();
-
- Collections.sort(topLevelNodes, areaNodeComparator);
-
- for(AreaNode topLevelNode : topLevelNodes) {
-
- StringBuilder areaStatusString = new StringBuilder();
-
- NamedArea area = topLevelNode.area;
- if (area.getUuid().equals(uuidInternalArea)){
- isFirstAfterAreaOfScope = true;
- }else if(isFirstAfterAreaOfScope && !area.getUuid().equals(uuidInternalArea)){
- areaStatusString.append(internalAreaSeparator);
- isFirstAfterAreaOfScope = false;
- }
-
-
- PresenceAbsenceTerm status = areaToStatusMap.get(area);
- String statusSymbol = statusSymbol(areaToStatusMap.get(area));
- areaStatusString.append(statusSymbol);
-
- String areaLabel = makeAreaLabel(languages, area);
- areaStatusString.append(areaLabel);
-
- if(!topLevelNode.subAreas.isEmpty()) {
- areaStatusString.append('(');
- subAreaLabels(languages, topLevelNode.subAreas, areaStatusString, statusSymbol,
- areaLabel, areaToStatusMap, areaNodeComparator);
- areaStatusString.append(')');
- }
-
-// if(isForeignStatus(status)) {
-// condensedDistribution.addForeignDistributionItem(status, areaStatusString.toString(), areaLabel);
-// } else {
- result.addIndigenousDistributionItem(status, areaStatusString.toString(), areaLabel);
-// }
- }
-
- return result;
- }
-
- /**
- * Recursive call to create sub area label strings
- * @param areaNodeComparator
- */
- private void subAreaLabels(List<Language> languages, Collection<AreaNode> nodes, StringBuilder totalStringBuilder,
- String parentStatusSymbol, String parentLabel,
- Map<NamedArea, PresenceAbsenceTerm> areaToStatusMap, AreaNodeComparator areaNodeComparator) {
-
- List<String> subAreaLabels = new ArrayList<>();
-
- List<AreaNode> areaNodes = new ArrayList<>(nodes);
- Collections.sort(areaNodes, areaNodeComparator);
-
- for(AreaNode node : areaNodes) {
-
- StringBuilder subAreaString = new StringBuilder();
-
- NamedArea area = node.area;
- PresenceAbsenceTerm status = areaToStatusMap.get(area);
- String subAreaStatusSymbol = statusSymbol(status);
- if (subAreaStatusSymbol != null && !subAreaStatusSymbol.equals(parentStatusSymbol)){
- subAreaString.append(subAreaStatusSymbol);
- }
-
- String areaLabel = makeAreaLabel(languages, area);
- if(replaceCommonAreaLabelStart){
- String cleanSubAreaLabel = StringUtils.replaceEach(areaLabel, new String[] {parentLabel, "(", ")"}, new String[] {"", "", ""});
- subAreaString.append(cleanSubAreaLabel);
- }else{
- subAreaString.append(areaLabel);
- }
-
- if(!node.subAreas.isEmpty()) {
- subAreaString.append('(');
- subAreaLabels(languages, node.subAreas, subAreaString, subAreaStatusSymbol, areaLabel,
- areaToStatusMap, areaNodeComparator);
- subAreaString.append(')');
- }
-
- subAreaLabels.add(subAreaString.toString());
- }
-// Collections.sort(subAreaLabels);
- totalStringBuilder.append(StringUtils.join(subAreaLabels, " "));
- }
-
- private class AreaNodeComparator implements Comparator<AreaNode>{
-
- @Override
- public int compare(AreaNode areaNode1, AreaNode areaNode2) {
- NamedArea area1 = areaNode1.area;
- NamedArea area2 = areaNode2.area;
-
- if (area1 == null && area2 == null){
- return 0;
- }else if (area1 == null){
- return -1;
- }else if (area2 == null){
- return 1;
- }else{
- //- due to wrong ordering behavior in DefinedTerms
- return - area1.compareTo(area2);
- }
- }
- }
-}
\ No newline at end of file
+++ /dev/null
-/**
-* Copyright (C) 2015 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.ext.geo;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.UUID;
-
-import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
-
-import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;
-import eu.etaxonomy.cdm.common.UTF8;
-import eu.etaxonomy.cdm.model.common.CdmBase;
-import eu.etaxonomy.cdm.model.common.Language;
-import eu.etaxonomy.cdm.model.description.Distribution;
-import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
-import eu.etaxonomy.cdm.model.location.NamedArea;
-import eu.etaxonomy.cdm.model.term.DefinedTermBase;
-import eu.etaxonomy.cdm.model.term.OrderedTermVocabulary;
-
-/**
- * @author a.mueller
- * @since Apr 05, 2016
- *
- */
-public class FloraCubaCondensedDistributionComposerOld extends CondensedDistributionComposerBase {
-
- @SuppressWarnings("unused")
- private static final Logger logger = Logger.getLogger(FloraCubaCondensedDistributionComposerOld.class);
-
-// private static Set<UUID> foreignStatusUuids;
-
- private UUID uuidInternalArea = UUID.fromString("d0144a6e-0e17-4a1d-bce5-d464a2aa7229"); //Cuba
-
- private String internalAreaSeparator = UTF8.EN_DASH.toString() + " ";
-
-
-// // these status uuids are special for EuroPlusMed and might also be used
-// private final static UUID REPORTED_IN_ERROR_UUID = UUID.fromString("38604788-cf05-4607-b155-86db456f7680");
-
- static {
-
- // ==================================================
- // Mapping as defined in ticket https://dev.e-taxonomy.eu/redmine/issues/5682
- // ==================================================
-
- statusSymbols = new HashMap<UUID, String> ();
- //no entries as we handle symbols now on model level
-
- }
-
- /**
- * {@inheritDoc}
- * @return
- */
- @Override
- public CondensedDistribution createCondensedDistribution(Collection<Distribution> filteredDistributions,
- List<Language> languages) {
-
- CondensedDistribution condensedDistribution = new CondensedDistribution();
-
- //empty
- if (filteredDistributions == null || filteredDistributions.isEmpty()){
- return condensedDistribution;
- }
-
- OrderedTermVocabulary<NamedArea> areaVocabulary = CdmBase.deproxy(filteredDistributions.iterator().next().getArea().getVocabulary(), OrderedTermVocabulary.class);
-
- //deproxy and reverse order
- List<NamedArea> areaList = new ArrayList<>();
- for (DefinedTermBase<NamedArea> dtb : areaVocabulary.getOrderedTerms()){
- areaList.add(0, (NamedArea)CdmBase.deproxy(dtb));
- }
-
-
- boolean isFirstAfterInternalArea = false;
- for (NamedArea area : areaList){
- if (area.getPartOf() != null){
- continue; //subarea are handled later
- }
- StringBuilder areaStatusString = new StringBuilder();
-
- Distribution distribution = getDistribution(area, filteredDistributions);
- if (distribution == null){
- continue;
- }
-
- if (area.getUuid().equals(uuidInternalArea)){
- isFirstAfterInternalArea = true;
- }else if(isFirstAfterInternalArea && !area.getUuid().equals(uuidInternalArea)){
- areaStatusString.append(internalAreaSeparator);
- isFirstAfterInternalArea = false;
- }
-
- PresenceAbsenceTerm status = distribution.getStatus();
-
- String statusSymbol = statusSymbol(status);
- areaStatusString.append(statusSymbol);
-
- String areaLabel = makeAreaLabel(languages, area);
- areaStatusString.append(areaLabel);
-
- if(!area.getIncludes().isEmpty()) {
-// areaStatusString.append('(');
- subAreaLabels(languages, area.getIncludes(), areaStatusString, statusSymbol, areaLabel, filteredDistributions);
-// areaStatusString.append(')');
- }
-
-
-// if(isForeignStatus(status)) {
-// condensedDistribution.addForeignDistributionItem(status, areaStatusString.toString(), areaLabel);
-// } else {
- condensedDistribution.addIndigenousDistributionItem(status, areaStatusString.toString(), areaLabel);
-// }
-
- }
-
-// }
-// //5. order the condensedDistributions alphabetically
-// // FIXME
-// condensedDistribution.sortForeign();
-// condensedDistribution.sortIndigenous();
-
- return condensedDistribution;
- }
-
- /**
- * @param area
- * @param filteredDistributions
- * @return
- */
- private Distribution getDistribution(NamedArea area, Collection<Distribution> filteredDistributions) {
- for (Distribution dist : filteredDistributions){
- if (dist.getArea() != null && dist.getArea().equals(area)){
- return dist;
- }
- }
- return null;
- }
-
-
-// private boolean isForeignStatus(PresenceAbsenceTerm status) {
-// return foreignStatusUuids.contains(status.getUuid());
-// }
-
- /**
- * @param langs
- * @param node
- * @param areaString
- * @param statusSymbol
- */
- private void subAreaLabels(List<Language> langs, Collection<NamedArea> subAreas, StringBuilder areaString,
- String statusSymbol, String parentLabel,
- Collection<Distribution> filteredDistributions) {
- //TODO very redundant with main method
- List<String> subAreaLabels = new ArrayList<String>();
-
- //deproxy and reverse order
- List<NamedArea> areaList = new ArrayList<NamedArea>();
- for (DefinedTermBase<NamedArea> dtb : subAreas){
- areaList.add(0, (NamedArea)CdmBase.deproxy(dtb));
- }
-// Collections.sort(areaList);
-
- for(NamedArea area : areaList) {
-
- StringBuilder subAreaString = new StringBuilder();
- Distribution distribution = getDistribution(area, filteredDistributions);
- if (distribution == null){
- continue;
- }
-
-
- PresenceAbsenceTerm status = distribution.getStatus();
- String subAreaStatusSymbol = statusSymbol(status);
- if (subAreaStatusSymbol != null && !subAreaStatusSymbol.equals(statusSymbol)){
- subAreaString.append(subAreaStatusSymbol);
- }
-
- String areaLabel = makeAreaLabel(langs, area);
-// String cleanSubAreaLabel = StringUtils.replaceEach(areaLabel, new String[] {parentLabel, "(", ")"}, new String[] {"", "", ""});
- String cleanSubAreaLabel = areaLabel;
- subAreaString.append(cleanSubAreaLabel);
-
- if(!area.getIncludes().isEmpty()) {
-// subAreaString.append('(');
- subAreaLabels(langs, area.getIncludes(), subAreaString, subAreaStatusSymbol, areaLabel, filteredDistributions);
-// subAreaString.append(')');
- }
-
- subAreaLabels.add(subAreaString.toString());
- }
-
-// Collections.sort(subAreaLabels);
- if (!subAreaLabels.isEmpty()){
- areaString.append("(" + StringUtils.join(subAreaLabels, " ") + ")");
- }
-
- }
-
-
- public String getInternalAreaSeparator() {
- return internalAreaSeparator;
- }
-
- public void setInternalAreaSeparator(String internalAreaSeparator) {
- this.internalAreaSeparator = internalAreaSeparator;
- }
-
-
-
-
-
-
-}
+++ /dev/null
-/**
-* Copyright (C) 2015 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.ext.geo;
-
-import java.util.Collection;
-import java.util.List;
-
-import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;
-import eu.etaxonomy.cdm.model.common.Language;
-import eu.etaxonomy.cdm.model.description.Distribution;
-
-/**
- * @author a.kohlbecker
- * @since Jun 24, 2015
- */
-public interface ICondensedDistributionComposer {
-
- public CondensedDistribution createCondensedDistribution(Collection<Distribution> filteredDistributions,
- List<Language> langs);
-
-}
--- /dev/null
+/**
+* Copyright (C) 2016 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.ext.geo;
+
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;
+import eu.etaxonomy.cdm.ext.geo.CondensedDistributionComposer.StatusSymbolUsage;
+import eu.etaxonomy.cdm.model.common.Language;
+import eu.etaxonomy.cdm.model.common.MarkerType;
+import eu.etaxonomy.cdm.model.description.Distribution;
+import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
+import eu.etaxonomy.cdm.model.location.NamedArea;
+import eu.etaxonomy.cdm.model.term.OrderedTermVocabulary;
+import eu.etaxonomy.cdm.model.term.TermType;
+import eu.etaxonomy.cdm.test.TermTestBase;
+
+/**
+ * @author a.mueller
+ * @since 15.06.2016
+ */
+public class CondensedDistributionComposerEuroMedTest extends TermTestBase {
+
+ private NamedArea europe;
+ private NamedArea westEurope;
+ private NamedArea germany;
+ private NamedArea berlin;
+ private NamedArea bawue;
+ private NamedArea france;
+ private NamedArea ileDeFrance;
+ private NamedArea italy;
+ private NamedArea spain;
+
+ private Set<Distribution> distributions;
+ private List<Language> languages;
+
+ private CondensedDistributionComposer composer;
+ private CondensedDistributionConfiguration config;
+
+ private Distribution bawueDistribution;
+
+ @Before
+ public void setUp(){
+
+ @SuppressWarnings("unchecked")
+ OrderedTermVocabulary<NamedArea> voc = OrderedTermVocabulary.NewInstance(TermType.NamedArea);
+ europe = NamedArea.NewInstance("", "Europe", "EU");
+ voc.addTerm(europe);
+
+ westEurope = NamedArea.NewInstance("", "West Europe", "WE");
+ westEurope.setPartOf(europe);
+ voc.addTerm(westEurope);
+ setAsFallback(westEurope);
+
+ //Germany
+ germany = NamedArea.NewInstance("", "Germany", "GER");
+ germany.setPartOf(europe);
+ berlin = NamedArea.NewInstance("", "Berlin", "GER(B)");
+ berlin.setPartOf(germany);
+ bawue = NamedArea.NewInstance("", "Baden Württemberg", "GER(BW)");
+ bawue.setPartOf(germany);
+ voc.addTerm(germany);
+ voc.addTerm(berlin);
+ voc.addTerm(bawue);
+
+ //France
+ france = NamedArea.NewInstance("", "France", "FR");
+ france.setPartOf(westEurope);
+ ileDeFrance = NamedArea.NewInstance("", "Ile-de-France", "FR(J)");
+ ileDeFrance.setPartOf(france);
+ voc.addTerm(france);
+ voc.addTerm(ileDeFrance);
+
+ //Italy
+ italy = NamedArea.NewInstance("", "Italy", "IT");
+ italy.setPartOf(europe);
+ voc.addTerm(italy);
+
+ //Spain
+ spain = NamedArea.NewInstance("", "Spain", "S");
+ spain.setPartOf(europe);
+ voc.addTerm(spain);
+
+ distributions = new HashSet<>();
+
+ languages = new ArrayList<>();
+
+ composer = new CondensedDistributionComposer();
+ config = CondensedDistributionConfiguration.NewDefaultInstance();
+ config.statusSymbolField = StatusSymbolUsage.Symbol1;
+ }
+
+ private void setAsFallback(NamedArea area) {
+ MarkerType fallbackMarkerType = MarkerType.NewInstance("Fallback area", "Fallback area", "fba");
+ fallbackMarkerType.setUuid(MarkerType.uuidFallbackArea); //as long as it is not an official CDM marker type yet
+ area.addMarker(fallbackMarkerType, true);
+ }
+
+ private void createDefaultDistributions() {
+ distributions.add(Distribution.NewInstance(europe, PresenceAbsenceTerm.ENDEMIC_FOR_THE_RELEVANT_AREA()));
+ distributions.add(Distribution.NewInstance(germany, PresenceAbsenceTerm.NATIVE()));
+ bawueDistribution = Distribution.NewInstance(bawue, PresenceAbsenceTerm.NATIVE());
+ distributions.add(bawueDistribution);
+
+ distributions.add(Distribution.NewInstance(berlin, PresenceAbsenceTerm.NATIVE()));
+
+ distributions.add(Distribution.NewInstance(italy, PresenceAbsenceTerm.PRESENT_DOUBTFULLY()));
+
+ distributions.add(Distribution.NewInstance(ileDeFrance, PresenceAbsenceTerm.CULTIVATED()));
+
+ distributions.add(Distribution.NewInstance(spain, PresenceAbsenceTerm.NATURALISED()));
+ }
+
+ @Test
+ public void testEuroMedCondensedDistributionDefault() {
+ createDefaultDistributions();
+
+ CondensedDistribution condensedDistribution = composer.createCondensedDistribution(
+ distributions, languages, config);
+
+ Assert.assertEquals("● <b>GER(B BW)</b> ?IT [cFR(J) nS]", condensedDistribution.toString());
+ }
+
+ @Test
+ public void testEuroMedCondensedDistributionWithDifferingSubareaStatus() {
+ createDefaultDistributions();
+
+ bawueDistribution.setStatus(PresenceAbsenceTerm.NATIVE_DOUBTFULLY_NATIVE());
+ Assert.assertEquals("● <b>GER(B</b> dBW<b>)</b> ?IT [cFR(J) nS]", composer.createCondensedDistribution(distributions, languages, config).toString());
+
+ bawueDistribution.setStatus(PresenceAbsenceTerm.CASUAL());
+ Assert.assertEquals("● <b>GER(B)</b> ?IT [aGER(BW) cFR(J) nS]", composer.createCondensedDistribution(distributions, languages, config).toString());
+
+ }
+
+ @Test
+ public void testEuroMedCondensedDistributionWithParentStatus() {
+ createDefaultDistributions();
+
+ distributions.add(Distribution.NewInstance(france, PresenceAbsenceTerm.CASUAL()));
+
+ CondensedDistribution condensedDistribution = composer.createCondensedDistribution(
+ distributions, languages, config);
+
+ Assert.assertEquals("● <b>GER(B BW)</b> ?IT [aFR(cJ) nS]", condensedDistribution.toString());
+ }
+
+ @Test
+ public void testEuroMedCondensedDistributionNotEndemicOrOnlyIntroduced() {
+
+ distributions.add(Distribution.NewInstance(germany, PresenceAbsenceTerm.NATIVE()));
+ Assert.assertEquals("<b>GER</b>", composer.createCondensedDistribution(
+ distributions, languages, config).toString());
+
+ distributions.add(Distribution.NewInstance(europe, PresenceAbsenceTerm.ENDEMISM_UNKNOWN()));
+ Assert.assertEquals("<b>GER</b>", composer.createCondensedDistribution(
+ distributions, languages, config).toString());
+
+ distributions.clear();
+ distributions.add(Distribution.NewInstance(germany, PresenceAbsenceTerm.CASUAL()));
+ Assert.assertEquals("[aGER]", composer.createCondensedDistribution(
+ distributions, languages, config).toString());
+ }
+
+ @Test
+ public void testCubaCondensedDistributionWithEMTestData() {
+ createDefaultDistributions();
+
+ distributions.add(Distribution.NewInstance(france, PresenceAbsenceTerm.CASUAL()));
+
+ CondensedDistribution condensedDistribution = composer.createCondensedDistribution(
+ distributions, languages, CondensedDistributionConfiguration.NewCubaInstance());
+
+ Assert.assertEquals("●<b>EU</b>(<b>GER</b>(<b>GER(B) GER(BW)</b>) a<b>FR</b>(c<b>FR(J)</b>) ?<b>IT</b> n<b>S</b>)", condensedDistribution.toString());
+ }
+}
\ No newline at end of file
import java.util.UUID;
import org.junit.Assert;
+import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
* @author a.mueller
* @since 07.04.2016
*/
-public class FloraCubaCondensedDistributionComposerTest extends TermTestBase {
+public class CondensedDistributionComposerFloraCubaTest extends TermTestBase {
+
+ private CondensedDistributionConfiguration config;
private static OrderedTermVocabulary<PresenceAbsenceTerm> statusVoc;
private static OrderedTermVocabulary<NamedArea> cubaAreasVocabualary;
// statusSymbols.put(UUID.fromString("71b72e24-c2b6-44a5-bdab-39f083bf0f06"), "-d");
}
+ @Before
+ public void setUp(){
+ config = CondensedDistributionConfiguration.NewCubaInstance();
+ }
+
// ********************* TESTS ******************************/
@Test
public void testCreateCondensedDistribution() {
- FloraCubaCondensedDistributionComposer composer = new FloraCubaCondensedDistributionComposer();
- composer.setAreaPreTag("<b>");
- composer.setAreaPostTag("</b>");
+ CondensedDistributionComposer composer = new CondensedDistributionComposer();
Set<Distribution> filteredDistributions = new HashSet<>();
filteredDistributions.add(Distribution.NewInstance(cuba, PresenceAbsenceTerm.NATURALISED()));
filteredDistributions.add(Distribution.NewInstance(bahamas, PresenceAbsenceTerm.NATIVE()));
filteredDistributions.add(Distribution.NewInstance(oldWorld, PresenceAbsenceTerm.NATIVE_PRESENCE_QUESTIONABLE()));
- CondensedDistribution condensedDistribution = composer.createCondensedDistribution(filteredDistributions, null);
+ CondensedDistribution condensedDistribution = composer.createCondensedDistribution(filteredDistributions, null, config);
String condensedString = condensedDistribution.toString();
- Assert.assertEquals("Condensed string for Cuba differs", "n<b>Cu</b>(-d<b>CuW</b>(-c<b>PR*</b>) (c)<b>CuE</b>(n<b>Ho</b>)) " + composer.getInternalAreaSeparator() + "<b>Bah</b> ?<b>VM</b> ", condensedString);
+ Assert.assertEquals("Condensed string for Cuba differs",
+ "n<b>Cu</b>(-d<b>CuW</b>(-c<b>PR*</b>) (c)<b>CuE</b>(n<b>Ho</b>))" + config.outOfScopeAreasSeperator + "<b>Bah</b> ?<b>VM</b>",
+ condensedString);
//TODO work in progress
}
@Test
public void testCreateCondensedDistributionOrderSubAreas() {
- FloraCubaCondensedDistributionComposer composer = new FloraCubaCondensedDistributionComposer();
- composer.setAreaPreTag("");
- composer.setAreaPostTag("");
+ CondensedDistributionComposer composer = new CondensedDistributionComposer();
Set<Distribution> filteredDistributions = new HashSet<>();
filteredDistributions.add(Distribution.NewInstance(cuba, PresenceAbsenceTerm.NATURALISED()));
filteredDistributions.add(Distribution.NewInstance(bahamas, PresenceAbsenceTerm.NATIVE()));
filteredDistributions.add(Distribution.NewInstance(oldWorld, PresenceAbsenceTerm.NATIVE_PRESENCE_QUESTIONABLE()));
- CondensedDistribution condensedDistribution = composer.createCondensedDistribution(filteredDistributions, null);
- String condensedString = condensedDistribution.toString();
+ config.areasBold = false;
+ CondensedDistribution condensedDistribution = composer.createCondensedDistribution(filteredDistributions, null, config);
Assert.assertEquals("Condensed string for Cuba differs",
- "nCu(-dCuW(PR* Art Hab* May Mat IJ) (c)CuE(nHo -cGu)) " + composer.getInternalAreaSeparator() + "Bah ?VM ",
- condensedString);
+ "nCu(-dCuW(PR* Art Hab* May Mat IJ) (c)CuE(nHo -cGu))" + config.outOfScopeAreasSeperator + "Bah ?VM",
+ condensedDistribution.toString());
//TODO work in progress
}
+++ /dev/null
-/**
-* Copyright (C) 2016 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.ext.geo;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.junit.Assert;
-import org.junit.Test;
-
-import eu.etaxonomy.cdm.api.service.dto.CondensedDistribution;
-import eu.etaxonomy.cdm.model.common.Language;
-import eu.etaxonomy.cdm.model.description.Distribution;
-import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
-import eu.etaxonomy.cdm.model.location.NamedArea;
-import eu.etaxonomy.cdm.test.TermTestBase;
-
-/**
- * @author a.mueller
- * @since 15.06.2016
- */
-public class EuroPlusMedCondensedDistributionComposerTest extends TermTestBase {
-
- @Test
- public void testGetCondensedDistribution() {
-
- //Germany
- NamedArea germany = NamedArea.NewInstance("Germany", "", "GER");
- NamedArea berlin = NamedArea.NewInstance("Berlin", "", "GER(B)");
- berlin.setPartOf(germany);
- NamedArea bawue = NamedArea.NewInstance("Baden Württemberg", "", "GER(BW)");
- bawue.setPartOf(germany);
- //France
- NamedArea france = NamedArea.NewInstance("France", "", "FR");
- NamedArea ileDeFrance = NamedArea.NewInstance("Ile-de-France", "", "FR(J)");
- ileDeFrance.setPartOf(france);
- //Italy
- NamedArea italy = NamedArea.NewInstance("Italy", "", "IT");
- //Spain
- NamedArea spain = NamedArea.NewInstance("Spain", "", "S");
-
- Set<Distribution> distributions = new HashSet<>();
- distributions.add(Distribution.NewInstance(germany, PresenceAbsenceTerm.NATIVE()));
- distributions.add(Distribution.NewInstance(bawue, PresenceAbsenceTerm.NATIVE()));
- distributions.add(Distribution.NewInstance(berlin, PresenceAbsenceTerm.NATIVE()));
-
- distributions.add(Distribution.NewInstance(italy, PresenceAbsenceTerm.PRESENT_DOUBTFULLY()));
-
- distributions.add(Distribution.NewInstance(france, PresenceAbsenceTerm.CASUAL()));
- distributions.add(Distribution.NewInstance(ileDeFrance, PresenceAbsenceTerm.CULTIVATED()));
-
- distributions.add(Distribution.NewInstance(spain, PresenceAbsenceTerm.NATURALISED()));
-
- List<Language> languages = new ArrayList<>();
-
- CondensedDistribution condensedDistribution = EditGeoServiceUtilities.getCondensedDistribution(
- distributions,
- CondensedDistributionRecipe.EuroPlusMed,
- languages);
-
- Assert.assertEquals("GER(B BW) ?IT [aFR cFR(J) nS]", condensedDistribution.toString());
- }
-}
\ No newline at end of file
//E+M, maybe general in future
public static final UUID uuidEpublished = UUID.fromString("212158af-c8cf-4b15-ab22-8d06667ea7e1");
+ public static final UUID uuidFallbackArea = UUID.fromString("e2b42891-aa85-4a09-981b-b7d8f5749c54");
+
protected static Map<UUID, MarkerType> termMap = null;
package eu.etaxonomy.cdm.model.description;
import java.text.ParseException;
+import java.util.Arrays;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.UUID;
import javax.persistence.Entity;
this.absenceTerm = isAbsenceTerm;
}
-
-}
+ private Set<UUID> isAnyIntroduced;
+ public boolean isAnyIntroduced() {
+ if (isAnyIntroduced == null){
+ isAnyIntroduced = new HashSet<>(Arrays.asList(new UUID[]{
+ uuidCasualPresenceQuestionable, uuidCasualReportedError,
+ uuidCultivated, uuidCultivatedPresenceQuestionable, uuidCultivatedReportedError,
+ uuidIntroduced, uuidIntroducedCultiated, uuidIntroducedDoubtfullyIntroduced,
+ uuidIntroducedFormerlyIntroduced, uuidIntroducedPresenceQuestionable,
+ uuidIntroducedReportedError, uuidIntroducedUncertainDegreeNaturalisation,
+ uuidIntroducedAdventitious,
+ uuidInvasive, uuidInvasivePresenceQuestionable,
+ uuidNaturalised, uuidNaturalisedPresenceQuestionable, uuidNaturalisedReportedError,
+ uuidIntroducedUncertainDegreeNaturalisation,
+ uuidInvasive, uuidInvasivePresenceQuestionable, uuidNonInvasive, uuidNonInvasivePresenceQuestionable
+ }));
+ }
+ return isAnyIntroduced.contains(uuid);
+ }
+}
\ No newline at end of file
public class HTMLTagRules {\r
\r
private List<TagRule> rules = new ArrayList<>();\r
+ private boolean includeSingleInstanceHtml = false;\r
\r
private class TagRule{\r
private TagRule(TagEnum type, String htmlTag){\r
return this;\r
}\r
\r
+ public SortedSet<String> getRule(TaggedText taggedText){\r
+ SortedSet<String> result = new TreeSet<>();\r
+ if (taggedText != null){\r
+ result = getRule(taggedText.getType());\r
+ if (this.includeSingleInstanceHtml && !taggedText.htmlTags().isEmpty()){\r
+ result.addAll(taggedText.htmlTags());\r
+ }\r
+ }\r
+ return result;\r
+ }\r
+\r
public SortedSet<String> getRule(TagEnum type){\r
SortedSet<String> result = new TreeSet<>();\r
for (TagRule rule : rules){\r
return false;\r
}\r
\r
+ public boolean isIncludeSingleInstanceHtml() {\r
+ return includeSingleInstanceHtml;\r
+ }\r
+\r
+ public void setIncludeSingleInstanceHtml(boolean includeSingleInstanceHtml) {\r
+ this.includeSingleInstanceHtml = includeSingleInstanceHtml;\r
+ }\r
+\r
@Override\r
public String toString(){\r
String result = "HTMLTagRules[";\r
for (TagRule rule : rules){\r
result += rule.toString() + ";";\r
}\r
- result = result.substring(0, result.length() -1) + "]";\r
+ result = result.substring(0, rules.isEmpty()? result.length(): result.length()-1) + "]";\r
return result;\r
}\r
\r
\r
+\r
}\r
*/
package eu.etaxonomy.cdm.strategy.cache;
+import java.util.ArrayList;
import java.util.List;
import java.util.SortedSet;
import java.util.Stack;
import java.util.TreeSet;
+import org.apache.commons.lang3.StringUtils;
+
/**
* Formatter class to create Strings from TaggedText Lists.
*
*/
public class TaggedCacheHelper {
+ private static final TaggedText WHITESPACE_TAG = TaggedText.NewWhitespaceInstance();
+
/**
* Creates a string from tagged text by concatenating all tags with a whitespace.
* @param tags
/**
* Creates a string from tagged text by concatenating all tags. If no separator tag is defined
* tags are separated by simple whitespace.
- * @param tags
- * @return
*/
- public static String createString(List<TaggedText> tags, HTMLTagRules htmlRules) {
+ public static String createString(List<TaggedText> tagsOrigin, HTMLTagRules htmlRules) {
if (htmlRules == null){
- return createString(tags);
+ return createString(tagsOrigin);
}
+ List<TaggedText> tags = new ArrayList<>(tagsOrigin);
+
//add whitespace separators
int index = 0;
boolean wasSeparator = true;
if (! tags.get(index).getType().isSeparator()){
if (wasSeparator == false){
- tags.add(index++, TaggedText.NewWhitespaceInstance());
+ tags.add(index++, WHITESPACE_TAG);
}else{
wasSeparator = false;
}
Stack<String> htmlStack = new Stack<>();
for (int i = 0; i < tags.size(); i++ ){
TaggedText tag = tags.get(i);
- TagEnum thisType = tag.getType();
- TagEnum lastType = (i == 0? null : tags.get(i - 1).getType());
- TagEnum nextType = (i + 1 >= tags.size() ? null : tags.get(i + 1).getType());
+ TaggedText lastTag = (i == 0? null : tags.get(i - 1));
+ TaggedText nextTag = (i + 1 >= tags.size() ? null : tags.get(i + 1));
+ TagEnum lastType = (lastTag == null ? null : lastTag.getType());
+ TagEnum nextType = (nextTag == null ? null : nextTag.getType());
boolean isSeparator = tag.getType().isSeparator();
+ boolean isBlankSeparator = isSeparator && StringUtils.isBlank(tag.getText());
//compute list of rules (tags)
SortedSet<String> separatorRules;
- if (isSeparator){
- separatorRules = getCommonRules(htmlRules.getRule(lastType), htmlRules.getRule(nextType));
+ if (isBlankSeparator){
+ separatorRules = getCommonRules(htmlRules.getRule(lastTag), htmlRules.getRule(nextTag));
}else{
- separatorRules = htmlRules.getRule(thisType);
+ separatorRules = htmlRules.getRule(tag);
}
//Close all tags not used anymore and remove all common tags from list of rules
}
//open all tags not yet existing
- if (! isSeparator){
+ if (! isBlankSeparator){
for (String rule : separatorRules){
htmlStack.add(rule);
result.append("<" + rule + ">");
}
//add whitespace
- if (lastType != null && ! lastType.isSeparator() && ! isSeparator && nextType != null){
+ if (!isSeparator && lastType != null && !lastType.isSeparator() && nextType != null){
result.append(" ");
}
result.append(tag.getText());
package eu.etaxonomy.cdm.strategy.cache;
import java.io.Serializable;
+import java.util.SortedSet;
+import java.util.TreeSet;
import org.apache.commons.lang3.StringUtils;
this.entityReference = entityReference;
}
+ /**
+ * To be overridden by subclasses if needed.
+ */
+ public SortedSet<String> htmlTags() {
+ return new TreeSet<>();
+ }
+
// **************************** TO STRING ***********************************/
@Override
return result;
}
}
+
}
\ No newline at end of file
--- /dev/null
+/**
+* Copyright (C) 2021 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.strategy.cache;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+
+/**
+ * @author a.mueller
+ * @since 23.02.2021
+ */
+public class TaggedCacheHelperTest {
+
+ private List<TaggedText> tags;
+
+ @Before
+ public void setUp() throws Exception {
+ tags = new ArrayList<>();
+ tags.add(TaggedText.NewInstance(TagEnum.label, "My"));
+ tags.add(TaggedText.NewInstance(TagEnum.label, "taxon"));
+ }
+
+ @Test
+ public void test() {
+ Assert.assertEquals("My taxon", TaggedCacheHelper.createString(tags));
+
+ tags.add(1, TaggedText.NewInstance(TagEnum.separator, ":"));
+ Assert.assertEquals("My:taxon", TaggedCacheHelper.createString(tags));
+ }
+
+ @Test
+ public void testHtmlRules() {
+ HTMLTagRules rules = new HTMLTagRules();
+ rules.addRule(TagEnum.label, "b");
+ Assert.assertEquals("<b>My taxon</b>", TaggedCacheHelper.createString(tags, rules));
+
+ tags.add(1, TaggedText.NewInstance(TagEnum.separator, ":"));
+ Assert.assertEquals("<b>My</b>:<b>taxon</b>", TaggedCacheHelper.createString(tags, rules));
+ }
+
+}
package eu.etaxonomy.cdm.api.service.dto;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
import java.util.List;
+import java.util.SortedSet;
-import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
+import javax.swing.text.html.HTML;
+
+import eu.etaxonomy.cdm.common.CdmUtils;
+import eu.etaxonomy.cdm.strategy.cache.HTMLTagRules;
+import eu.etaxonomy.cdm.strategy.cache.TagEnum;
+import eu.etaxonomy.cdm.strategy.cache.TaggedCacheHelper;
+import eu.etaxonomy.cdm.strategy.cache.TaggedText;
/**
+ * A class representing a condensed distribution stored as as list of {@link TaggedText}
+ * with TaggedText being extended by an isBold indicator.
+ *
+ * The class offers a method representing a string representation which uses {@link HTML}
+ * tag < b> to indicate bold text.
+ *
* @author a.kohlbecker
+ * @author a.mueller
* @since Jun 25, 2015
*/
public class CondensedDistribution {
- private List<DistributionItem> indigenous = new ArrayList<>();
- private List<DistributionItem> foreign = new ArrayList<>();
+ private List<TaggedText> taggedText = new ArrayList<>();
- public List<DistributionItem> getForeign() {
- return foreign;
- }
- public void setForeign(List<DistributionItem> foreign) {
- this.foreign = foreign;
- }
+ public static class DistributionTaggedText extends TaggedText{
+ private static final long serialVersionUID = 3904027767908153947L;
+ private boolean bold = false;
- public void setIndigenous(List<DistributionItem> indigenous) {
- this.indigenous = indigenous;
- }
- public List<DistributionItem> getIndigenous() {
- return indigenous;
- }
+ public static DistributionTaggedText NewInstance(TagEnum type, String text){
+ return new DistributionTaggedText(type, text, false);
+ }
- public void addForeignDistributionItem(PresenceAbsenceTerm status, String areaStatusLabel, String sortString) {
- foreign.add(new DistributionItem(status, areaStatusLabel, sortString));
- }
+ private DistributionTaggedText(TagEnum type, String text, boolean bold) {
+ super(type, text);
+ this.bold = bold;
+ }
+ public boolean isBold() {
+ return bold;
+ }
+ public void setBold(boolean bold) {
+ this.bold = bold;
+ }
- public void addIndigenousDistributionItem(PresenceAbsenceTerm status, String areaStatusLabel, String sortString) {
- indigenous.add(new DistributionItem(status, areaStatusLabel, sortString));
+ @Override
+ public SortedSet<String> htmlTags(){
+ SortedSet<String> result = super.htmlTags();
+ if (bold){
+ result.add("b");
+ }
+ return result;
+ }
}
-
- public void sortForeign() {
- Collections.sort(foreign, new CondensedDistributionComparator());
+ public void addStatusAndAreaTaggedText(String status, String area, boolean bold) {
+ addTaggedText(TagEnum.symbol, status, false);
+ if (CdmUtils.isNotBlank(status) && CdmUtils.isNotBlank(area)){
+ addTaggedText(TagEnum.separator,"",false);
+ }
+ addTaggedText(TagEnum.label, area, bold);
}
- public void sortIndigenous() {
- Collections.sort(indigenous, new CondensedDistributionComparator());
+ public void addSeparatorTaggedText(String sep){
+ addTaggedText(TagEnum.separator, sep, false);
+ }
+ public void addSeparatorTaggedText(String sep, boolean bold) {
+ addTaggedText(TagEnum.separator, sep, bold);
}
- @Override
- public String toString() {
-
- StringBuilder out = new StringBuilder();
-
- boolean isFirst = true;
- for(DistributionItem item : indigenous) {
- if(!isFirst) {
- out.append(" ");
- }
- out.append(item.areaStatusLabel);
- isFirst = false;
- }
+ /**
+ * @see TagEnum#postSeparator
+ */
+ public void addPostSeparatorTaggedText(String sep){
+ addTaggedText(TagEnum.postSeparator, sep, false);
+ }
- if(!isFirst) {
- out.append(" ");
+ public void addTaggedText(TagEnum type, String text, boolean bold) {
+ if (CdmUtils.isNotBlank(text)|| type.isSeparator() ){
+ DistributionTaggedText taggedText = DistributionTaggedText.NewInstance(type, text);
+ taggedText.bold = bold;
+ this.taggedText.add(taggedText);
}
- isFirst = true;
- if(!foreign.isEmpty()) {
- out.append("[");
- for(DistributionItem item : foreign) {
- if(!isFirst) {
- out.append(" ");
- }
- out.append(item.areaStatusLabel);
- isFirst = false;
- }
- out.append("]");
- }
- return out.toString();
}
- public class DistributionItem {
-
- private PresenceAbsenceTerm status;
- private String areaStatusLabel;
- private final String sortString;
-
- public DistributionItem(PresenceAbsenceTerm status, String areaStatusLabel, String sortString) {
- this.status = status;
- this.areaStatusLabel = areaStatusLabel;
- this.sortString = sortString;
- }
- public PresenceAbsenceTerm getStatus() {
- return status;
- }
- public void setStatus(PresenceAbsenceTerm status) {
- this.status = status;
- }
+ /**
+ * @return <code>true</code> if no tagged text was added yet (the inner tagged text list is still empty).
+ */
+ public boolean isEmpty() {
+ return this.taggedText.isEmpty();
+ }
- public String getAreaStatusLabel() {
- return areaStatusLabel;
- }
- public void setAreaStatusLabel(String areaStatusLabel) {
- this.areaStatusLabel = areaStatusLabel;
- }
+ public String getHtmlText(){
+ return toString();
}
- class CondensedDistributionComparator implements Comparator<DistributionItem>{
+//***************** STRING **************************************/
- @Override
- public int compare(DistributionItem o1, DistributionItem o2) {
- return o1.sortString.compareToIgnoreCase(o2.sortString);
- }
+ @Override
+ public String toString(){
+ HTMLTagRules htmlTagRules = new HTMLTagRules();
+ htmlTagRules.setIncludeSingleInstanceHtml(true);
+ return TaggedCacheHelper.createString(this.taggedText, htmlTagRules);
}
-}
+}
\ No newline at end of file