3 * Copyright (C) 2015 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
10 package eu
.etaxonomy
.cdm
.ext
.geo
;
12 import java
.util
.ArrayList
;
13 import java
.util
.Collection
;
14 import java
.util
.Collections
;
15 import java
.util
.HashMap
;
16 import java
.util
.HashSet
;
17 import java
.util
.List
;
20 import java
.util
.UUID
;
22 import org
.apache
.commons
.lang
.StringUtils
;
23 import org
.apache
.log4j
.Logger
;
25 import eu
.etaxonomy
.cdm
.api
.service
.dto
.CondensedDistribution
;
26 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
27 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
28 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTerm
;
29 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
32 * @author a.kohlbecker
36 public class EuroPlusMedCondensedDistributionComposer
implements ICondensedDistributionComposer
{
38 private static final Logger logger
= Logger
.getLogger(EuroPlusMedCondensedDistributionComposer
.class);
40 private final CondensedDistribution condensedDistribution
;
42 private static Map
<UUID
, String
> statusSymbols
;
44 private static Set
<UUID
> foreignStatusUuids
;
46 // these status uuids are special for EuroPlusMed and might also be used
47 private final static UUID REPORTED_IN_ERROR_UUID
= UUID
.fromString("38604788-cf05-4607-b155-86db456f7680");
51 // ==================================================
52 // Mapping as defined in ticket http://dev.e-taxonomy.eu/trac/ticket/3907
53 // ==================================================
55 statusSymbols
= new HashMap
<UUID
, String
> ();
56 // ● endemic (U+25CF BLACK CIRCLE)
57 statusSymbols
.put(PresenceAbsenceTerm
.ENDEMIC_FOR_THE_RELEVANT_AREA().getUuid(), "\u25CF");
59 // Lu native (incl. archaeophytes) TODO status archaeophytes?
60 statusSymbols
.put(PresenceAbsenceTerm
.NATIVE().getUuid(), "");
61 statusSymbols
.put(PresenceAbsenceTerm
.NATIVE_FORMERLY_NATIVE().getUuid(), "");
63 // ?Lu doubtfully present (U+3F QUESTION MARK)
64 statusSymbols
.put(PresenceAbsenceTerm
.INTRODUCED_PRESENCE_QUESTIONABLE().getUuid(), "?");
65 statusSymbols
.put(PresenceAbsenceTerm
.NATIVE_PRESENCE_QUESTIONABLE().getUuid(), "?");
66 statusSymbols
.put(PresenceAbsenceTerm
.PRESENT_DOUBTFULLY().getUuid(), "?");
68 // dLu doubtfully native
69 statusSymbols
.put(PresenceAbsenceTerm
.NATIVE_DOUBTFULLY_NATIVE().getUuid(), "d");
71 // -Lu absent but reported in error (U+2D HYPHEN-MINUS)
72 statusSymbols
.put(PresenceAbsenceTerm
.INTRODUCED_REPORTED_IN_ERROR().getUuid(), "-");
73 statusSymbols
.put(PresenceAbsenceTerm
.NATIVE_REPORTED_IN_ERROR().getUuid(), "-");
74 statusSymbols
.put(REPORTED_IN_ERROR_UUID
, "-");
76 // †Lu (presumably) extinct (U+2020 DAGGER)
77 // no such status in database!!!
79 // [Lu] introduced (casual or naturalized) = introduced, introduced: naturalized
80 statusSymbols
.put(PresenceAbsenceTerm
.INTRODUCED().getUuid(), "");
81 statusSymbols
.put(PresenceAbsenceTerm
.INTRODUCED_NATURALIZED().getUuid(), "");
83 // [aLu] casual alien = introduced: adventitious (casual)
84 statusSymbols
.put(PresenceAbsenceTerm
.INTRODUCED_ADVENTITIOUS().getUuid(), "a");
87 statusSymbols
.put(PresenceAbsenceTerm
.CULTIVATED() .getUuid(), "c");
88 statusSymbols
.put(PresenceAbsenceTerm
.INTRODUCED_CULTIVATED().getUuid(), "c");
91 statusSymbols
.put(PresenceAbsenceTerm
.NATURALISED().getUuid(), "n");
93 foreignStatusUuids
= new HashSet
<UUID
>();
94 foreignStatusUuids
.add(PresenceAbsenceTerm
.INTRODUCED().getUuid());
95 foreignStatusUuids
.add(PresenceAbsenceTerm
.INTRODUCED_NATURALIZED().getUuid());
96 foreignStatusUuids
.add(PresenceAbsenceTerm
.INTRODUCED_ADVENTITIOUS().getUuid());
97 // foreignStatusUuids.add(PresenceAbsenceTerm.INTRODUCED_CULTIVATED().getUuid()); // how about this?
98 foreignStatusUuids
.add(PresenceAbsenceTerm
.NATURALISED().getUuid());
99 foreignStatusUuids
.add(PresenceAbsenceTerm
.CULTIVATED().getUuid());
103 public EuroPlusMedCondensedDistributionComposer() {
104 condensedDistribution
= new CondensedDistribution();
112 public CondensedDistribution
createCondensedDistribution(Collection
<Distribution
> filteredDistributions
,
113 List
<Language
> langs
) {
115 //1. order by PresenceAbsenceTerms
116 Map
<PresenceAbsenceTerm
, Collection
<NamedArea
>> byStatus
= new HashMap
<PresenceAbsenceTerm
, Collection
<NamedArea
>>();
117 for(Distribution d
: filteredDistributions
) {
118 if(!byStatus
.containsKey(d
.getStatus())) {
119 byStatus
.put(d
.getStatus(), new HashSet
<NamedArea
>());
121 byStatus
.get(d
.getStatus()).add(d
.getArea());
124 //2. build the area hierarchy
125 for(PresenceAbsenceTerm status
: byStatus
.keySet()) {
127 Map
<NamedArea
, AreaNode
> areaNodeMap
= new HashMap
<NamedArea
, AreaNode
>();
129 for(NamedArea area
: byStatus
.get(status
)) {
131 if(!areaNodeMap
.containsKey(area
)) {
132 // putting area into hierarchy as node
133 node
= new AreaNode(area
);
134 areaNodeMap
.put(area
, node
);
136 // is parent of another and thus already has a node
137 node
= areaNodeMap
.get(area
);
140 NamedArea parent
= findParentIn(area
, byStatus
.get(status
));
143 if(!areaNodeMap
.containsKey(parent
)) {
144 parentNode
= new AreaNode(parent
);
145 areaNodeMap
.put(parent
, parentNode
);
147 parentNode
= areaNodeMap
.get(parent
);
149 parentNode
.addSubArea(node
);
154 Set
<AreaNode
>hierarchy
= new HashSet
<AreaNode
>();
155 for(AreaNode node
: areaNodeMap
.values()) {
156 if(!node
.hasParent()) {
161 //4. replace the area by the abbreviated representation and add symbols
162 for(AreaNode node
: hierarchy
) {
164 StringBuilder areaStatusString
= new StringBuilder();
166 String statusSymbol
= statusSymbol(status
);
167 areaStatusString
.append(statusSymbol
);
169 String areaLabel
= node
.area
.getPreferredRepresentation(langs
).getAbbreviatedLabel();
170 areaStatusString
.append(areaLabel
);
172 if(!node
.subAreas
.isEmpty()) {
173 areaStatusString
.append('(');
174 subAreaLabels(langs
, node
.subAreas
, areaStatusString
, statusSymbol
, areaLabel
);
175 areaStatusString
.append(')');
178 if(isForeignStatus(status
)) {
179 condensedDistribution
.addForeignDistributionItem(status
, areaStatusString
.toString(), areaLabel
);
181 condensedDistribution
.addIndigenousDistributionItem(status
, areaStatusString
.toString(), areaLabel
);
187 //5. order the condensedDistributions alphabetically
188 condensedDistribution
.sortForeign();
189 condensedDistribution
.sortIndigenous();
191 return condensedDistribution
;
198 private String
statusSymbol(PresenceAbsenceTerm status
) {
199 String symbol
= statusSymbols
.get(status
.getUuid());
206 private boolean isForeignStatus(PresenceAbsenceTerm status
) {
207 return foreignStatusUuids
.contains(status
.getUuid());
214 * @param statusSymbol
216 private void subAreaLabels(List
<Language
> langs
, Collection
<AreaNode
> nodes
, StringBuilder areaString
, String statusSymbol
, String parentLabel
) {
218 List
<String
> subAreaLabels
= new ArrayList
<String
>();
220 for(AreaNode node
: nodes
) {
222 StringBuilder subAreaString
= new StringBuilder();
224 subAreaString
.append(statusSymbol
);
226 String areaLabel
= node
.area
.getPreferredRepresentation(langs
).getAbbreviatedLabel();
227 subAreaString
.append(StringUtils
.remove(areaLabel
, parentLabel
+ "-"));
229 if(!node
.subAreas
.isEmpty()) {
230 subAreaString
.append('(');
231 subAreaLabels(langs
, node
.subAreas
, subAreaString
, statusSymbol
, areaLabel
);
232 subAreaString
.append(')');
235 subAreaLabels
.add(subAreaString
.toString());
237 Collections
.sort(subAreaLabels
);
238 areaString
.append(StringUtils
.join(subAreaLabels
, " "));
243 * Searches for the parent are of the area given as parameter in
244 * the Collection of areas.
247 * The area whose parent area is to be searched
249 * The areas to search in.
252 * Either the parent if it has been found or null.
254 private NamedArea
findParentIn(NamedArea area
, Collection
<NamedArea
> areas
) {
255 NamedArea parent
= area
.getPartOf();
256 if(parent
!= null && areas
.contains(parent
)){
264 private final NamedArea area
;
265 private AreaNode parent
= null;
266 private final Set
<AreaNode
> subAreas
= new HashSet
<AreaNode
>();
271 public AreaNode(NamedArea area
) {
275 public void addSubArea(AreaNode subArea
) {
276 subAreas
.add(subArea
);
277 subArea
.parent
= this;
280 public AreaNode
getParent() {
284 public boolean hasParent() {
285 return getParent() != null;
288 public Collection
<NamedArea
> getSubareas() {
289 Collection
<NamedArea
> areas
= new HashSet
<NamedArea
>();
290 for(AreaNode node
: subAreas
) {
291 areas
.add(node
.area
);