2 * Copyright (C) 2023 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.cdm
.api
.service
.portal
;
11 import java
.awt
.Color
;
12 import java
.time
.LocalDateTime
;
13 import java
.util
.ArrayList
;
14 import java
.util
.Arrays
;
15 import java
.util
.Collections
;
16 import java
.util
.HashMap
;
17 import java
.util
.HashSet
;
18 import java
.util
.List
;
21 import java
.util
.UUID
;
22 import java
.util
.stream
.Collectors
;
24 import org
.apache
.commons
.lang3
.StringUtils
;
25 import org
.apache
.logging
.log4j
.LogManager
;
26 import org
.apache
.logging
.log4j
.Logger
;
27 import org
.joda
.time
.DateTime
;
29 import com
.fasterxml
.jackson
.core
.JsonProcessingException
;
31 import eu
.etaxonomy
.cdm
.api
.application
.ICdmRepository
;
32 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.AnnotatableDto
;
33 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.AnnotationDto
;
34 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.CdmBaseDto
;
35 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.CommonNameDto
;
36 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.ContainerDto
;
37 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.DistributionInfoDto
;
38 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.FactDto
;
39 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.FactDtoBase
;
40 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.FeatureDto
;
41 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.IFactDto
;
42 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.IndividualsAssociationDto
;
43 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.MarkerDto
;
44 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.MessagesDto
;
45 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.SingleSourcedDto
;
46 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.SourceDto
;
47 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.SourcedDto
;
48 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonBaseDto
;
49 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonBaseDto
.TaxonNameDto
;
50 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonInteractionDto
;
51 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
;
52 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.ConceptRelationDTO
;
53 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.HomotypicGroupDTO
;
54 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.KeyDTO
;
55 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.MediaDTO
;
56 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.MediaRepresentationDTO
;
57 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.NameRelationDTO
;
58 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.SpecimenDTO
;
59 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.TaxonNodeAgentsRelDTO
;
60 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.TaxonNodeDTO
;
61 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.config
.DistributionInfoConfiguration
;
62 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.config
.TaxonPageDtoConfiguration
;
63 import eu
.etaxonomy
.cdm
.api
.service
.geo
.DistributionServiceUtilities
;
64 import eu
.etaxonomy
.cdm
.api
.service
.geo
.IDistributionService
;
65 import eu
.etaxonomy
.cdm
.api
.service
.l10n
.LocaleContext
;
66 import eu
.etaxonomy
.cdm
.api
.service
.name
.TypeDesignationSetContainer
;
67 import eu
.etaxonomy
.cdm
.api
.service
.name
.TypeDesignationSetFormatter
;
68 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
69 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
70 import eu
.etaxonomy
.cdm
.common
.TreeNode
;
71 import eu
.etaxonomy
.cdm
.compare
.taxon
.TaxonComparator
;
72 import eu
.etaxonomy
.cdm
.format
.common
.TypedLabel
;
73 import eu
.etaxonomy
.cdm
.format
.description
.CategoricalDataFormatter
;
74 import eu
.etaxonomy
.cdm
.format
.description
.QuantitativeDataFormatter
;
75 import eu
.etaxonomy
.cdm
.format
.description
.distribution
.CondensedDistributionConfiguration
;
76 import eu
.etaxonomy
.cdm
.format
.reference
.OriginalSourceFormatter
;
77 import eu
.etaxonomy
.cdm
.format
.taxon
.TaxonRelationshipFormatter
;
78 import eu
.etaxonomy
.cdm
.model
.common
.AnnotatableEntity
;
79 import eu
.etaxonomy
.cdm
.model
.common
.Annotation
;
80 import eu
.etaxonomy
.cdm
.model
.common
.AnnotationType
;
81 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
82 import eu
.etaxonomy
.cdm
.model
.common
.ICdmBase
;
83 import eu
.etaxonomy
.cdm
.model
.common
.IPublishable
;
84 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
85 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
86 import eu
.etaxonomy
.cdm
.model
.common
.Marker
;
87 import eu
.etaxonomy
.cdm
.model
.common
.MarkerType
;
88 import eu
.etaxonomy
.cdm
.model
.common
.MultilanguageTextHelper
;
89 import eu
.etaxonomy
.cdm
.model
.common
.SingleSourcedEntityBase
;
90 import eu
.etaxonomy
.cdm
.model
.common
.VersionableEntity
;
91 import eu
.etaxonomy
.cdm
.model
.description
.CategoricalData
;
92 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
93 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionBase
;
94 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
95 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementSource
;
96 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
97 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
98 import eu
.etaxonomy
.cdm
.model
.description
.IDescribable
;
99 import eu
.etaxonomy
.cdm
.model
.description
.IndividualsAssociation
;
100 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKey
;
101 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTerm
;
102 import eu
.etaxonomy
.cdm
.model
.description
.QuantitativeData
;
103 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
104 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
105 import eu
.etaxonomy
.cdm
.model
.description
.TemporalData
;
106 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
107 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
108 import eu
.etaxonomy
.cdm
.model
.media
.ExternalLink
;
109 import eu
.etaxonomy
.cdm
.model
.media
.ImageFile
;
110 import eu
.etaxonomy
.cdm
.model
.media
.Media
;
111 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentation
;
112 import eu
.etaxonomy
.cdm
.model
.media
.MediaRepresentationPart
;
113 import eu
.etaxonomy
.cdm
.model
.name
.HomotypicalGroup
;
114 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationship
;
115 import eu
.etaxonomy
.cdm
.model
.name
.NameRelationshipType
;
116 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalSource
;
117 import eu
.etaxonomy
.cdm
.model
.name
.SpecimenTypeDesignation
;
118 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
119 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationBase
;
120 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
121 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
122 import eu
.etaxonomy
.cdm
.model
.reference
.ISourceable
;
123 import eu
.etaxonomy
.cdm
.model
.reference
.NamedSource
;
124 import eu
.etaxonomy
.cdm
.model
.reference
.NamedSourceBase
;
125 import eu
.etaxonomy
.cdm
.model
.reference
.OriginalSourceBase
;
126 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
127 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
128 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
129 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
130 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
131 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
132 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeAgentRelation
;
133 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeStatus
;
134 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
135 import eu
.etaxonomy
.cdm
.model
.term
.Representation
;
136 import eu
.etaxonomy
.cdm
.model
.term
.TermBase
;
137 import eu
.etaxonomy
.cdm
.model
.term
.TermNode
;
138 import eu
.etaxonomy
.cdm
.model
.term
.TermTree
;
139 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedCacheHelper
;
140 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
141 import eu
.etaxonomy
.cdm
.strategy
.cache
.name
.INameCacheStrategy
;
142 import eu
.etaxonomy
.cdm
.strategy
.cache
.taxon
.TaxonBaseDefaultCacheStrategy
;
145 * Loads the portal dto from a taxon instance.
146 * Maybe later also supports loading from persistence.
151 public class PortalDtoLoader
{
153 @SuppressWarnings("unused")
154 private static final Logger logger
= LogManager
.getLogger();
156 private ICdmRepository repository
;
158 public PortalDtoLoader(ICdmRepository repository
) {
159 this.repository
= repository
;
162 public TaxonPageDto
load(Taxon taxon
, TaxonPageDtoConfiguration config
) {
163 TaxonPageDto result
= new TaxonPageDto();
165 loadAcceptedTaxon(taxon
, config
, result
);
167 loadTaxonNodes(taxon
, result
, config
);
168 loadSynonyms(taxon
, result
, config
);
169 loadConceptRelations(taxon
, result
, config
);
170 loadFacts(taxon
, result
, config
);
171 loadMedia(taxon
, result
, config
);
172 loadSpecimens(taxon
, result
, config
);
173 loadKeys(taxon
, result
, config
);
178 private void loadAcceptedTaxon(Taxon taxon
, TaxonPageDtoConfiguration config
, TaxonPageDto result
) {
180 TaxonName name
= taxon
.getName();
183 //TODO supplementalData for name
184 loadBaseData(taxon
, result
);
185 result
.setLastUpdated(getLastUpdated(null, taxon
));
186 result
.setLabel(CdmUtils
.Nz(taxon
.getTitleCache()));
187 // result.setTypedTaxonLabel(getTypedTaxonLabel(taxon, config));
188 result
.setTaggedLabel(getTaggedTaxon(taxon
, config
));
190 handleName(config
, result
, name
, result
);
192 if (taxon
.getSec() != null) {
193 result
.setSecTitleCache(taxon
.getSec().getTitleCache());
195 } catch (Exception e
) {
196 //e.printStackTrace();
197 result
.addMessage(MessagesDto
.NewErrorInstance("Error when loading accepted name data.", e
));
201 private void handleName(TaxonPageDtoConfiguration config
, TaxonBaseDto taxonDto
, TaxonName name
, TaxonPageDto pageDto
) {
202 TaxonNameDto nameDto
= taxonDto
.new TaxonNameDto();
203 loadBaseData(name
, nameDto
);
205 INameCacheStrategy formatter
= name
.cacheStrategy();
206 formatter
.setEtAlPosition(config
.getEtAlPosition());
208 taxonDto
.setName(nameDto
);
209 taxonDto
.setNameLabel(formatter
.getTitleCache(name
));
210 handleRelatedNames(name
, taxonDto
, config
);
211 loadProtologues(name
, taxonDto
);
212 taxonDto
.setNameUuid(name
.getUuid());
213 taxonDto
.setNameType(name
.getNameType().toString());
214 loadNameFacts(name
, taxonDto
, config
, pageDto
);
215 nameDto
.setTaggedName(formatter
.getTaggedFullTitle(name
));
218 private List
<TaggedText
> getTaggedTaxon(TaxonBase
<?
> taxon
, TaxonPageDtoConfiguration config
) {
219 // List<TypedLabel> result = new ArrayList<>();
220 TaxonBaseDefaultCacheStrategy
<TaxonBase
<?
>> formatter
= new TaxonBaseDefaultCacheStrategy
<>();
221 List
<TaggedText
> tags
= formatter
.getTaggedTitle(taxon
);
225 private void loadKeys(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
227 ContainerDto
<KeyDTO
> container
= new ContainerDto
<>();
229 //TODO other key types, but type must not be null, otherwise NPE
230 Pager
<PolytomousKey
> keys
= repository
.getIdentificationKeyService().findKeysConvering(taxon
, PolytomousKey
.class, null, null, null);
231 for (PolytomousKey key
: keys
.getRecords()) {
232 KeyDTO dto
= new KeyDTO();
233 loadBaseData(key
, dto
);
234 dto
.setLabel(key
.getTitleCache());
235 dto
.setKeyClass(key
.getClass().getSimpleName());
236 container
.addItem(dto
);
238 if (container
.getCount() > 0) {
239 result
.setKeys(container
);
241 } catch (Exception e
) {
242 //e.printStackTrace();
243 result
.addMessage(MessagesDto
.NewErrorInstance("Error when loading identification key data.", e
));
247 private void loadSpecimens(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
248 //TODO load specimen from multiple places
251 ContainerDto
<SpecimenDTO
> container
= new ContainerDto
<>();
253 List
<SpecimenOrObservationBase
<?
>> specimens
= new ArrayList
<>();
254 for (TaxonDescription taxonDescription
: taxon
.getDescriptions()) {
255 if (taxonDescription
.isImageGallery()) {
258 for (DescriptionElementBase el
: taxonDescription
.getElements()) {
259 if (el
.isInstanceOf(IndividualsAssociation
.class)) {
260 IndividualsAssociation indAss
= CdmBase
.deproxy(el
, IndividualsAssociation
.class);
261 SpecimenOrObservationBase
<?
> specimen
= indAss
.getAssociatedSpecimenOrObservation();
262 specimens
.add(specimen
);
266 List
<SpecimenOrObservationBase
<?
>> typeSpecimens
= loadTypeSpecimen(taxon
.getName(), config
);
267 specimens
.addAll(typeSpecimens
);
268 for (TaxonName syn
: taxon
.getSynonymNames()) {
269 typeSpecimens
= loadTypeSpecimen(syn
, config
);
270 specimens
.addAll(typeSpecimens
);
273 //TODO maybe still need to check full derivate path #4484, #8424, #9559
274 for (SpecimenOrObservationBase
<?
> specimen
: filterPublished(specimens
)) {
275 SpecimenDTO dto
= new SpecimenDTO();
276 loadBaseData(specimen
, dto
);
277 dto
.setLabel(specimen
.getTitleCache());
278 container
.addItem(dto
);
280 if (container
.getCount() > 0 ) {
281 result
.setSpecimens(container
);
283 } catch (Exception e
) {
284 //e.printStackTrace();
285 result
.addMessage(MessagesDto
.NewErrorInstance("Error when loading specimen data.", e
));
289 private List
<SpecimenOrObservationBase
<?
>> loadTypeSpecimen(TaxonName name
, TaxonPageDtoConfiguration config
) {
290 List
<SpecimenOrObservationBase
<?
>> result
= new ArrayList
<>();
291 for (SpecimenTypeDesignation desig
: name
.getSpecimenTypeDesignations()){
292 DerivedUnit specimen
= desig
.getTypeSpecimen();
293 if (specimen
!= null) {
294 result
.add(specimen
);
300 private void loadMedia(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
303 ContainerDto
<MediaDTO
> container
= new ContainerDto
<TaxonPageDto
.MediaDTO
>();
305 List
<Media
> medias
= new ArrayList
<>();
306 for (TaxonDescription taxonDescription
: taxon
.getDescriptions()) {
307 if (!taxonDescription
.isImageGallery()) {
311 List
<Media
> newMedia
= taxonDescription
.getElements().stream()
312 .filter(el
->el
.isInstanceOf(TextData
.class))
313 .map(el
->CdmBase
.deproxy(el
, TextData
.class))
315 .flatMap(td
->td
.getMedia().stream())
316 .collect(Collectors
.toList())
318 medias
.addAll(newMedia
);
320 //TODO collect media from elsewhere
321 for (Media media
: medias
) {
322 MediaDTO dto
= new TaxonPageDto
.MediaDTO();
323 loadBaseData(media
, dto
);
324 dto
.setLabel(media
.getTitleCache());
325 ContainerDto
<MediaRepresentationDTO
> representations
= new ContainerDto
<>();
326 for (MediaRepresentation rep
: media
.getRepresentations()) {
327 MediaRepresentationDTO repDto
= new MediaRepresentationDTO();
328 loadBaseData(rep
, dto
);
329 repDto
.setMimeType(rep
.getMimeType());
330 repDto
.setSuffix(rep
.getSuffix());
331 if (!rep
.getParts().isEmpty()) {
332 //TODO handle message if n(parts) > 1
333 MediaRepresentationPart part
= rep
.getParts().get(0);
334 repDto
.setUri(part
.getUri());
335 repDto
.setClazz(part
.getClass());
336 repDto
.setSize(part
.getSize());
337 if (part
.isInstanceOf(ImageFile
.class)) {
338 ImageFile image
= CdmBase
.deproxy(part
, ImageFile
.class);
339 repDto
.setHeight(image
.getHeight());
340 repDto
.setWidth(image
.getWidth());
342 //TODO AudioFile etc.
344 representations
.addItem(repDto
);
346 if (representations
.getCount() > 0) {
347 dto
.setRepresentations(representations
);
349 //TODO load representation data
350 container
.addItem(dto
);
353 if (container
.getCount() > 0) {
354 result
.setMedia(container
);
356 } catch (Exception e
) {
357 //e.printStackTrace();
358 result
.addMessage(MessagesDto
.NewErrorInstance("Error when loading media data.", e
));
362 private void loadTaxonNodes(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
364 ContainerDto
<TaxonNodeDTO
> container
= new ContainerDto
<TaxonPageDto
.TaxonNodeDTO
>();
365 for (TaxonNode node
: taxon
.getTaxonNodes()) {
366 TaxonNodeDTO dto
= new TaxonNodeDTO();
367 loadBaseData(node
, dto
);
369 Classification classification
= node
.getClassification();
370 if (classification
!= null) {
371 dto
.setClassificationUuid(node
.getClassification().getUuid());
372 dto
.setClassificationLabel(classification
.getName().getText());
375 Language language
= Language
.DEFAULT();
378 TaxonNodeStatus status
= node
.getStatus();
379 if (status
!= null) {
380 dto
.setStatus(status
.getLabel(language
));
383 Map
<Language
, LanguageString
> statusNote
= node
.getStatusNote();
384 if (statusNote
!= null) {
385 //TODO handle fallback lang
386 LanguageString statusNoteStr
= statusNote
.get(language
);
387 if (statusNoteStr
== null && statusNote
.size() > 0) {
388 statusNoteStr
= statusNote
.entrySet().iterator().next().getValue();
390 if (statusNoteStr
!= null) {
391 dto
.setStatusNote(statusNoteStr
.getText());
395 Set
<TaxonNodeAgentRelation
> agents
= node
.getAgentRelations();
396 if (!agents
.isEmpty()) {
397 for (TaxonNodeAgentRelation rel
: agents
) {
398 TaxonNodeAgentsRelDTO agentDto
= new TaxonNodeAgentsRelDTO();
399 loadBaseData(rel
, agentDto
);
402 if (rel
.getAgent() != null) {
403 agentDto
.setAgent(rel
.getAgent().getFullTitle());
404 agentDto
.setAgentUuid(rel
.getAgent().getUuid());
405 //TODO compute preferred external link
406 agentDto
.setAgentLink(null);
408 if (rel
.getType() != null) {
409 agentDto
.setType(rel
.getType().getTitleCache());
410 agentDto
.setTypeUuid(rel
.getType().getUuid());
412 dto
.addAgent(agentDto
);
415 container
.addItem(dto
);
417 if (container
.getCount() > 0) {
418 result
.setTaxonNodes(container
);
420 } catch (Exception e
) {
421 // e.printStackTrace();
422 result
.addMessage(MessagesDto
.NewErrorInstance("Error when loading taxon node data.", e
));
426 private void loadSynonyms(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
429 // List<HomotypicalGroup> homotypicGroups = taxon.getHomotypicSynonymyGroups();
431 TaxonComparator comparator
= new TaxonComparator();
433 TaxonName name
= taxon
.getName();
435 //TODO depending on config add/remove accepted name
438 List
<Synonym
> homotypicSynonmys
= filterPublished(taxon
.getHomotypicSynonymsByHomotypicGroup(comparator
));
440 TaxonPageDto
.HomotypicGroupDTO homotypicGroupDto
= new TaxonPageDto
.HomotypicGroupDTO();
441 if (homotypicSynonmys
!= null && !homotypicSynonmys
.isEmpty()) {
442 loadBaseData(name
.getHomotypicalGroup(), homotypicGroupDto
);
444 for (Synonym syn
: homotypicSynonmys
) {
445 loadSynonymsInGroup(homotypicGroupDto
, syn
, config
, result
);
449 handleTypification(name
.getHomotypicalGroup(), homotypicGroupDto
, result
, config
);
451 result
.setHomotypicSynonyms(homotypicGroupDto
);
453 //heterotypic synonyms
454 List
<HomotypicalGroup
> heteroGroups
= taxon
.getHeterotypicSynonymyGroups();
455 if (heteroGroups
.isEmpty()) {
458 ContainerDto
<HomotypicGroupDTO
> heteroContainer
= new ContainerDto
<>();
459 result
.setHeterotypicSynonymGroups(heteroContainer
);
461 for (HomotypicalGroup hg
: heteroGroups
) {
462 TaxonPageDto
.HomotypicGroupDTO hgDto
= new TaxonPageDto
.HomotypicGroupDTO();
463 loadBaseData(taxon
.getName().getHomotypicalGroup(), hgDto
);
464 heteroContainer
.addItem(hgDto
);
466 List
<Synonym
> heteroSyns
= filterPublished(taxon
.getSynonymsInGroup(hg
, comparator
));
467 for (Synonym syn
: heteroSyns
) {
468 loadSynonymsInGroup(hgDto
, syn
, config
, result
);
470 handleTypification(hg
, hgDto
, result
, config
);
472 } catch (Exception e
) {
473 //e.printStackTrace();
474 result
.addMessage(MessagesDto
.NewErrorInstance("Error when loading synonym data.", e
));
478 private <P
extends IPublishable
> List
<P
> filterPublished(List
<P
> listToPublish
) {
479 if (listToPublish
== null) {
482 return listToPublish
.stream().filter(s
->s
.isPublish()).collect(Collectors
.toList());
485 private <P
extends IPublishable
> Set
<P
> filterPublished(Set
<P
> setToPublish
) {
486 if (setToPublish
== null) {
489 return setToPublish
.stream().filter(s
->s
.isPublish()).collect(Collectors
.toSet());
493 private void handleTypification(HomotypicalGroup homotypicalGroup
, HomotypicGroupDTO hgDto
,
494 TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
496 boolean withCitation
= true;
497 boolean withStartingTypeLabel
= true;
498 boolean withNameIfAvailable
= false;
499 TypeDesignationSetFormatter formatter
= new TypeDesignationSetFormatter(
500 withCitation
, withStartingTypeLabel
, withNameIfAvailable
);
501 Set
<TypeDesignationBase
<?
>> desigs
= homotypicalGroup
.getTypeDesignations();
503 TypeDesignationSetContainer manager
= TypeDesignationSetContainer
.NewDefaultInstance((Set
)desigs
);
504 List
<TaggedText
> tags
= formatter
.toTaggedText(manager
);
505 String label
= TaggedCacheHelper
.createString(tags
);
506 hgDto
.setTypes(label
);
507 hgDto
.setTaggedTypes(tags
);
508 // hgDto.setTypedTypes(null);
510 } catch (Exception e
) {
511 // e.printStackTrace();
512 result
.addMessage(MessagesDto
.NewErrorInstance("Error when creating type designation information", e
));
516 private void loadConceptRelations(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
520 ContainerDto
<ConceptRelationDTO
> conceptRelContainer
= new ContainerDto
<>();
521 TaxonRelationshipFormatter taxRelFormatter
= TaxonRelationshipFormatter
.INSTANCE();
524 Set
<TaxonRelationship
> misappliedRels
= taxon
.getMisappliedNameRelations();
525 for (TaxonRelationship rel
: misappliedRels
) {
526 boolean inverse
= true;
527 boolean withoutName
= false;
528 loadConceptRelation(taxRelFormatter
, rel
, conceptRelContainer
, inverse
, withoutName
);
531 //... pro parte Synonyms
532 Set
<TaxonRelationship
> proParteRels
= taxon
.getProParteAndPartialSynonymRelations();
533 for (TaxonRelationship rel
: proParteRels
) {
534 boolean inverse
= true;
535 boolean withoutName
= false;
536 loadConceptRelation(taxRelFormatter
, rel
, conceptRelContainer
, inverse
, withoutName
);
539 //TODO MAN and pp from this taxon
542 Set
<TaxonRelationship
> toRels
= taxon
.getRelationsToThisTaxon();
543 toRels
.removeAll(misappliedRels
);
544 toRels
.removeAll(proParteRels
);
545 for (TaxonRelationship rel
: toRels
) {
546 boolean inverse
= true;
547 boolean withoutName
= false;
548 loadConceptRelation(taxRelFormatter
, rel
, conceptRelContainer
, inverse
, withoutName
);
552 Set
<TaxonRelationship
> fromRels
= taxon
.getRelationsFromThisTaxon();
553 for (TaxonRelationship rel
: fromRels
) {
554 boolean inverse
= false;
555 boolean withoutName
= false;
556 loadConceptRelation(taxRelFormatter
, rel
, conceptRelContainer
, inverse
, withoutName
);
559 if (conceptRelContainer
.getCount() > 0) {
560 result
.setConceptRelations(conceptRelContainer
);
562 } catch (Exception e
) {
563 //e.printStackTrace();
564 result
.addMessage(MessagesDto
.NewErrorInstance("Error when loading concept relation data.", e
));
568 private void loadConceptRelation(TaxonRelationshipFormatter taxRelFormatter
, TaxonRelationship rel
, ContainerDto
<ConceptRelationDTO
> conceptRelContainer
, boolean inverse
,
569 boolean withoutName
) {
571 List
<Language
> languages
= Arrays
.asList(new Language
[] {Language
.DEFAULT()}); // TODO config.locales;
572 List
<TaggedText
> tags
= taxRelFormatter
.getTaggedText(rel
, inverse
, languages
, withoutName
);
573 String relLabel
= TaggedCacheHelper
.createString(tags
);
574 ConceptRelationDTO dto
= new TaxonPageDto
.ConceptRelationDTO();
575 loadBaseData(rel
, dto
);
576 dto
.setRelSource(makeSource(rel
.getSource()));
577 Taxon relTaxon
= inverse ? rel
.getFromTaxon() : rel
.getToTaxon();
578 dto
.setRelTaxonId(relTaxon
.getId());
579 dto
.setRelTaxonUuid(relTaxon
.getUuid());
580 dto
.setRelTaxonLabel(relTaxon
.getTitleCache());
581 dto
.setSecSource(makeSource(relTaxon
.getSecSource()));
582 dto
.setLabel(relLabel
);
583 dto
.setTaggedLabel(tags
);
584 dto
.setNameUuid(relTaxon
.getName() != null ? relTaxon
.getName().getUuid() : null);
586 if (rel
.getType() != null) {
587 dto
.setRelTypeUuid(rel
.getType().getUuid());
589 for (TaxonNode node
: relTaxon
.getTaxonNodes()) {
590 Classification classification
= node
.getClassification();
591 if (classification
!= null) {
592 dto
.addClassificationUuids(classification
.getUuid());
595 conceptRelContainer
.addItem(dto
);
598 private void loadSynonymsInGroup(TaxonPageDto
.HomotypicGroupDTO hgDto
, Synonym syn
,
599 TaxonPageDtoConfiguration config
, TaxonPageDto pageDto
) {
601 TaxonBaseDto synDto
= new TaxonBaseDto();
602 loadBaseData(syn
, synDto
);
603 synDto
.setLabel(syn
.getTitleCache());
604 synDto
.setTaggedLabel(getTaggedTaxon(syn
, config
));
606 if (syn
.getName() != null) {
607 handleName(config
, synDto
, syn
.getName(), pageDto
);
611 hgDto
.addSynonym(synDto
);
614 private void loadProtologues(TaxonName name
, TaxonBaseDto taxonBaseDto
) {
615 NomenclaturalSource nomSource
= name
.getNomenclaturalSource();
616 if (nomSource
!= null) {
617 Set
<ExternalLink
> links
= nomSource
.getLinks();
618 for (ExternalLink link
: links
) {
619 if (link
.getUri() != null) {
620 taxonBaseDto
.addProtologue(link
.getUri());
626 private void handleRelatedNames(TaxonName name
, TaxonBaseDto taxonDto
, TaxonPageDtoConfiguration config
) {
627 //exclusions TODO handle via config
628 Set
<UUID
> excludedTypes
= new HashSet
<>(); //both directions
629 excludedTypes
.add(NameRelationshipType
.uuidBasionym
);
630 excludedTypes
.add(NameRelationshipType
.uuidReplacedSynonym
);
631 Set
<UUID
> excludedFromTypes
= new HashSet
<>(excludedTypes
);
632 Set
<UUID
> excludedToTypes
= new HashSet
<>(excludedTypes
);
635 //TODO config.getLocales();
636 Language locale
= Language
.DEFAULT();
638 for (NameRelationship rel
: name
.getRelationsFromThisName()) {
639 TaxonName relatedName
= rel
.getToName();
640 if (relatedName
== null || rel
.getType() == null || excludedFromTypes
.contains(rel
.getType().getUuid())) {
643 NameRelationDTO dto
= new NameRelationDTO();
644 loadBaseData(rel
, dto
);
646 dto
.setNameUuid(relatedName
.getUuid());
647 dto
.setNameLabel(relatedName
.getTaggedName());
649 dto
.setRelTypeUuid(rel
.getType().getUuid());
650 Representation rep
= rel
.getType().getPreferredRepresentation(locale
);
651 dto
.setRelType(rep
== null ? rel
.getType().toString() : rep
.getLabel());
653 dto
.setInverse(false);
655 dto
.setRuleConsidered(rel
.getRuleConsidered());
656 taxonDto
.addRelatedName(dto
);
660 for (NameRelationship rel
: name
.getRelationsToThisName()) {
661 TaxonName relatedName
= rel
.getFromName();
662 if (relatedName
== null || rel
.getType() == null || excludedFromTypes
.contains(rel
.getType().getUuid())) {
665 NameRelationDTO dto
= new NameRelationDTO();
666 loadBaseData(rel
, dto
);
668 dto
.setNameUuid(relatedName
.getUuid());
669 dto
.setNameLabel(relatedName
.getTaggedName());
671 dto
.setRelTypeUuid(rel
.getType().getUuid());
672 Representation rep
= rel
.getType().getPreferredInverseRepresentation(Arrays
.asList(new Language
[] {locale
}));
673 dto
.setRelType(rep
== null ? rel
.getType().toString() : rep
.getLabel());
675 dto
.setInverse(true);
676 taxonDto
.addRelatedName(dto
);
680 private void loadFacts(Taxon taxon
, TaxonPageDto taxonPageDto
, TaxonPageDtoConfiguration config
) {
683 //compute the features that do exist for this taxon
684 Map
<UUID
, Feature
> existingFeatureUuids
= getExistingFeatureUuids(taxon
);
686 //filter, sort and structure according to feature tree
687 TreeNode
<Feature
, UUID
> filteredRootNode
;
688 if (config
.getFeatureTree() != null) {
690 @SuppressWarnings({ "unchecked"})
691 TermTree
<Feature
> featureTree
= repository
.getTermTreeService().find(config
.getFeatureTree());
692 filteredRootNode
= filterFeatureNode(featureTree
.getRoot(), existingFeatureUuids
.keySet());
694 filteredRootNode
= createDefaultFeatureNode(taxon
);
697 //load facts per feature
698 Map
<UUID
,Set
<DescriptionElementBase
>> featureMap
= loadFeatureMap(taxon
);
701 if (filteredRootNode
!= null && !filteredRootNode
.getChildren().isEmpty()) {
702 ContainerDto
<FeatureDto
> features
= new ContainerDto
<>();
703 for (TreeNode
<Feature
,UUID
> node
: filteredRootNode
.getChildren()) {
704 handleFeatureNode(config
, featureMap
, features
, node
, taxonPageDto
);
706 taxonPageDto
.setTaxonFacts(features
);
708 } catch (Exception e
) {
709 //e.printStackTrace();
710 taxonPageDto
.addMessage(MessagesDto
.NewErrorInstance("Error when loading factual data.", e
));
714 //TODO merge with loadFacts, it is almost the same, see //DIFFERENT
715 private void loadNameFacts(TaxonName name
, TaxonBaseDto nameDto
, TaxonPageDtoConfiguration config
, TaxonPageDto pageDto
) {
718 //compute the features that do exist for this taxon
719 Map
<UUID
, Feature
> existingFeatureUuids
= getExistingFeatureUuids(name
);
721 //filter, sort and structure according to feature tree
722 TreeNode
<Feature
, UUID
> filteredRootNode
;
724 // if (config.getFeatureTree() != null) {
726 // @SuppressWarnings({ "unchecked"})
727 // TermTree<Feature> featureTree = repository.getTermTreeService().find(config.getFeatureTree());
728 // filteredRootNode = filterFeatureNode(featureTree.getRoot(), existingFeatureUuids.keySet());
730 filteredRootNode
= createDefaultFeatureNode(name
);
733 //load facts per feature
734 Map
<UUID
,Set
<DescriptionElementBase
>> featureMap
= loadFeatureMap(name
);
737 if (!filteredRootNode
.getChildren().isEmpty()) {
738 ContainerDto
<FeatureDto
> features
= new ContainerDto
<>();
739 for (TreeNode
<Feature
,UUID
> node
: filteredRootNode
.getChildren()) {
740 handleFeatureNode(config
, featureMap
, features
, node
, pageDto
);
743 nameDto
.setNameFacts(features
);
745 } catch (Exception e
) {
746 //e.printStackTrace();
748 pageDto
.addMessage(MessagesDto
.NewErrorInstance("Error when loading factual data.", e
));
752 private void handleFeatureNode(TaxonPageDtoConfiguration config
,
753 Map
<UUID
, Set
<DescriptionElementBase
>> featureMap
, ContainerDto
<FeatureDto
> features
,
754 TreeNode
<Feature
, UUID
> node
, TaxonPageDto pageDto
) {
756 Feature feature
= node
.getData();
758 FeatureDto featureDto
= new FeatureDto(feature
.getUuid(), feature
.getId(), feature
.getLabel());
759 features
.addItem(featureDto
);
761 List
<Distribution
> distributions
= new ArrayList
<>();
764 for (DescriptionElementBase fact
: featureMap
.get(feature
.getUuid())){
765 if (fact
.isInstanceOf(Distribution
.class)) {
766 distributions
.add(CdmBase
.deproxy(fact
, Distribution
.class));
768 //TODO how to handle CommonNames, do we also want to have a data structure
772 // a bit like for distribution??
773 handleFact(featureDto
, fact
, pageDto
);
777 handleDistributions(config
, featureDto
, distributions
, pageDto
);
778 //TODO really needed?
779 orderFacts(featureDto
);
782 ContainerDto
<FeatureDto
> childFeatures
= new ContainerDto
<>();
783 for (TreeNode
<Feature
,UUID
> child
: node
.getChildren()) {
784 handleFeatureNode(config
, featureMap
, childFeatures
, child
, pageDto
);
786 if (childFeatures
.getCount() > 0) {
787 featureDto
.setSubFeatures(childFeatures
);
791 //TODO not really used yet, only for distinguishing fact classes,
792 //needs discussion if needed and how to implement.
793 //we could also move compareTo methods to DTO classes but with this
794 //remove from having only data in the DTO, no logic
795 private void orderFacts(FeatureDto featureDto
) {
796 List
<IFactDto
> list
= featureDto
.getFacts().getItems();
797 Collections
.sort(list
, (f1
,f2
)->{
798 if (!f1
.getClass().equals(f2
.getClass())) {
799 return f1
.getClass().getSimpleName().compareTo(f2
.getClass().getSimpleName());
801 if (f1
instanceof FactDto
) {
802 FactDto fact1
= (FactDto
)f1
;
803 FactDto fact2
= (FactDto
)f2
;
805 // return fact1.getTypedLabel().toString().compareTo(fact2.getTypedLabel().toString());
807 } else if (f1
instanceof CommonNameDto
) {
809 CommonNameDto fact1
= (CommonNameDto
)f1
;
810 CommonNameDto fact2
= (CommonNameDto
)f2
;
819 private Map
<UUID
, Set
<DescriptionElementBase
>> loadFeatureMap(IDescribable
<?
> describable
) {
820 Map
<UUID
, Set
<DescriptionElementBase
>> featureMap
= new HashMap
<>();
823 for (DescriptionBase
<?
> description
: filterPublished(describable
.getDescriptions())) {
824 if (description
.isImageGallery()) {
827 for (DescriptionElementBase deb
: description
.getElements()) {
828 Feature feature
= deb
.getFeature();
829 if (featureMap
.get(feature
.getUuid()) == null) {
830 featureMap
.put(feature
.getUuid(), new HashSet
<>());
832 featureMap
.get(feature
.getUuid()).add(deb
);
838 private TreeNode
<Feature
, UUID
> createDefaultFeatureNode(IDescribable
<?
> describable
) {
839 TreeNode
<Feature
, UUID
> root
= new TreeNode
<>();
840 Set
<Feature
> requiredFeatures
= new HashSet
<>();
842 for (DescriptionBase
<?
> description
: describable
.getDescriptions()) {
843 if (description
.isImageGallery()) {
846 for (DescriptionElementBase deb
: description
.getElements()) {
847 Feature feature
= deb
.getFeature();
848 if (feature
!= null) { //null should not happen
849 requiredFeatures
.add(feature
);
853 List
<Feature
> sortedChildren
= new ArrayList
<>(requiredFeatures
);
854 Collections
.sort(sortedChildren
, (f1
,f2
) -> f1
.getTitleCache().compareTo(f2
.getTitleCache()));
855 sortedChildren
.stream().forEachOrdered(f
->root
.addChild(new TreeNode
<>(f
.getUuid(), f
)));
860 * Recursive call to a feature tree's feature node in order to creates a tree structure
861 * ordered in the same way as the according feature tree but only containing features
862 * that do really exist for the given taxon. If only a child node is required the parent
863 * node/feature is also considered to be required.<BR>
865 private TreeNode
<Feature
, UUID
> filterFeatureNode(TermNode
<Feature
> featureNode
,
866 Set
<UUID
> existingFeatureUuids
) {
868 //first filter children
869 List
<TreeNode
<Feature
, UUID
>> requiredChildNodes
= new ArrayList
<>();
870 for (TermNode
<Feature
> childNode
: featureNode
.getChildNodes()) {
871 TreeNode
<Feature
, UUID
> child
= filterFeatureNode(childNode
, existingFeatureUuids
);
873 requiredChildNodes
.add(child
);
877 //if any child is required or this node is required ....
878 if (!requiredChildNodes
.isEmpty() ||
879 featureNode
.getTerm() != null && existingFeatureUuids
.contains(featureNode
.getTerm().getUuid())) {
881 TreeNode
<Feature
,UUID
> result
= new TreeNode
<>();
882 //add this nodes data
883 Feature feature
= featureNode
.getTerm() == null ?
null : featureNode
.getTerm();
884 if (feature
!= null) {
885 result
.setNodeId(feature
.getUuid());
886 result
.setData(feature
);
889 requiredChildNodes
.stream().forEachOrdered(c
->result
.addChild(c
));
897 * Computes the (unsorted) set of features for which facts exist
898 * for the given taxon.
900 private Map
<UUID
, Feature
> getExistingFeatureUuids(IDescribable
<?
> describable
) {
901 Map
<UUID
, Feature
> result
= new HashMap
<>();
902 for (DescriptionBase
<?
> description
: filterPublished(describable
.getDescriptions())) {
903 if (description
.isImageGallery()) {
906 for (DescriptionElementBase deb
: description
.getElements()) {
907 Feature feature
= deb
.getFeature();
908 if (feature
!= null) { //null should not happen
909 result
.put(feature
.getUuid(), feature
);
916 private void handleDistributions(TaxonPageDtoConfiguration config
, FeatureDto featureDto
,
917 List
<Distribution
> distributions
, TaxonPageDto pageDto
) {
919 if (distributions
.isEmpty()) {
922 IDistributionService distributionService
= repository
.getDistributionService();
925 DistributionInfoConfiguration distributionConfig
= config
.getDistributionInfoConfiguration(featureDto
.getUuid());
926 CondensedDistributionConfiguration condensedConfig
= distributionConfig
.getCondensedDistributionConfiguration();
928 String statusColorsString
= distributionConfig
.getStatusColorsString();
931 //copied from DescriptionListController
933 boolean ignoreDistributionStatusUndefined
= true; //workaround until #9500 is fully implemented
934 distributionConfig
.setIgnoreDistributionStatusUndefined(ignoreDistributionStatusUndefined
);
935 boolean neverUseFallbackAreaAsParent
= true; //may become a service parameter in future
937 //fallbackArea markers include markers for fully hidden areas and fallback areas.
938 //The later are hidden markers on areas that have non-hidden subareas (#4408)
939 Set
<MarkerType
> fallbackAreaMarkerTypes
= distributionConfig
.getFallbackAreaMarkerTypeList();
940 if(!CdmUtils
.isNullSafeEmpty(fallbackAreaMarkerTypes
)){
941 condensedConfig
.fallbackAreaMarkers
= fallbackAreaMarkerTypes
.stream().map(mt
->mt
.getUuid()).collect(Collectors
.toSet());
944 Map
<PresenceAbsenceTerm
, Color
> distributionStatusColors
;
946 distributionStatusColors
= DistributionServiceUtilities
.buildStatusColorMap(
947 statusColorsString
, repository
.getTermService(), repository
.getVocabularyService());
948 } catch (JsonProcessingException e
) {
949 pageDto
.addMessage(MessagesDto
.NewErrorInstance("JsonProcessingException when reading distribution status colors", e
));
950 //TODO is null allowed?
951 distributionStatusColors
= null;
954 DistributionInfoDto dto
= distributionService
.composeDistributionInfoFor(distributionConfig
, distributions
,
955 neverUseFallbackAreaAsParent
,
956 distributionStatusColors
, LocaleContext
.getLanguages());
958 //should not be necessary anymore as distribution data is now loaded directly in DIstributionServiceImpl
959 //by calling PortalDtoLoader.loadBaseData() directly
960 // if (distributionConfig.isUseTreeDto() && dto.getTree() != null) {
961 // DistributionTreeDto tree = (DistributionTreeDto)dto.getTree();
962 // TreeNode<Set<DistributionDto>, NamedAreaDto> root = tree.getRootElement();
963 // //fill uuid->distribution map
964 // Map<UUID,Distribution> distributionMap = new HashMap<>();
965 // distributions.stream().forEach(d->distributionMap.put(d.getUuid(), d));
966 // handleDistributionDtoNode(distributionMap, root);
969 featureDto
.addFact(dto
);
972 //should not be necessary anymore as distribution data is now loaded directly in DIstributionServiceImpl
973 //by calling PortalDtoLoader.loadBaseData() directly
974 // private static void handleDistributionDtoNode(Map<UUID, Distribution> map,
975 // TreeNode<Set<DistributionDto>, NamedAreaDto> root) {
976 // if (root.getData() != null) {
977 // root.getData().stream().forEach(dto->{
978 // Distribution distr = map.get(dto.getUuid());
979 // loadBaseData(distr, dto);
980 // dto.setTimeperiod(distr.getTimeperiod() == null ? null : distr.getTimeperiod().toString());
985 // if (root.getChildren() != null) {
986 // root.getChildren().stream().forEach(c->handleDistributionDtoNode(map, c));
990 private FactDtoBase
handleFact(FeatureDto featureDto
, DescriptionElementBase fact
, TaxonPageDto pageDto
) {
992 Language localeLang
= null;
995 if (fact
.isInstanceOf(TextData
.class)) {
996 TextData td
= CdmBase
.deproxy(fact
, TextData
.class);
997 LanguageString ls
= td
.getPreferredLanguageString(localeLang
);
998 String text
= ls
== null ?
"" : CdmUtils
.Nz(ls
.getText());
1000 FactDto factDto
= new FactDto();
1001 featureDto
.addFact(factDto
);
1002 //TODO do we really need type information for textdata here?
1003 TypedLabel typedLabel
= new TypedLabel(text
);
1004 typedLabel
.setClassAndId(td
);
1005 factDto
.getTypedLabel().add(typedLabel
);
1006 loadBaseData(td
, factDto
);
1009 }else if (fact
.isInstanceOf(CommonTaxonName
.class)) {
1010 CommonTaxonName ctn
= CdmBase
.deproxy(fact
, CommonTaxonName
.class);
1011 CommonNameDto dto
= new CommonNameDto();
1012 featureDto
.addFact(dto
);
1014 Language lang
= ctn
.getLanguage();
1016 String langLabel
= getTermLabel(lang
, localeLang
);
1017 dto
.setLanguage(langLabel
);
1018 dto
.setLanguageUuid(lang
.getUuid());
1021 dto
.setLanguage("-");
1024 NamedArea area
= ctn
.getArea();
1026 String areaLabel
= getTermLabel(area
, localeLang
);
1027 dto
.setArea(areaLabel
);
1028 dto
.setAreaUUID(area
.getUuid());
1030 dto
.setName(ctn
.getName());
1031 loadBaseData(ctn
, dto
);
1032 //TODO sort all common names (not urgent as this is done by portal code)
1034 } else if (fact
.isInstanceOf(IndividualsAssociation
.class)) {
1035 IndividualsAssociation ia
= CdmBase
.deproxy(fact
, IndividualsAssociation
.class);
1036 IndividualsAssociationDto dto
= new IndividualsAssociationDto ();
1038 LanguageString description
= MultilanguageTextHelper
.getPreferredLanguageString(ia
.getDescription(), Arrays
.asList(localeLang
));
1039 if (description
!= null) {
1040 dto
.setDescritpion(description
.getText());
1042 SpecimenOrObservationBase
<?
> specimen
= ia
.getAssociatedSpecimenOrObservation();
1043 if (specimen
!= null) {
1044 //TODO what to use here??
1045 dto
.setOccurrence(specimen
.getTitleCache());
1046 dto
.setOccurrenceUuid(specimen
.getUuid());
1049 featureDto
.addFact(dto
);
1050 loadBaseData(ia
, dto
);
1052 } else if (fact
.isInstanceOf(TaxonInteraction
.class)) {
1053 TaxonInteraction ti
= CdmBase
.deproxy(fact
, TaxonInteraction
.class);
1054 TaxonInteractionDto dto
= new TaxonInteractionDto ();
1056 LanguageString description
= MultilanguageTextHelper
.getPreferredLanguageString(
1057 ti
.getDescription(), Arrays
.asList(localeLang
));
1058 if (description
!= null) {
1059 dto
.setDescritpion(description
.getText());
1061 Taxon taxon
= ti
.getTaxon2();
1062 if (taxon
!= null) {
1063 //TODO what to use here??
1064 dto
.setTaxon(taxon
.cacheStrategy().getTaggedTitle(taxon
));
1065 dto
.setTaxonUuid(taxon
.getUuid());
1067 featureDto
.addFact(dto
);
1068 loadBaseData(ti
, dto
);
1070 }else if (fact
.isInstanceOf(CategoricalData
.class)) {
1071 CategoricalData cd
= CdmBase
.deproxy(fact
, CategoricalData
.class);
1072 FactDto factDto
= new FactDto();
1073 featureDto
.addFact(factDto
);
1074 //TODO do we really need type information for textdata here?
1075 String label
= CategoricalDataFormatter
.NewInstance(null).format(cd
, localeLang
);
1076 TypedLabel typedLabel
= new TypedLabel(label
);
1077 typedLabel
.setClassAndId(cd
);
1078 factDto
.getTypedLabel().add(typedLabel
);
1080 loadBaseData(cd
, factDto
);
1082 }else if (fact
.isInstanceOf(QuantitativeData
.class)) {
1083 QuantitativeData qd
= CdmBase
.deproxy(fact
, QuantitativeData
.class);
1084 FactDto factDto
= new FactDto();
1085 featureDto
.addFact(factDto
);
1086 //TODO do we really need type information for textdata here?
1087 String label
= QuantitativeDataFormatter
.NewInstance(null).format(qd
, localeLang
);
1088 TypedLabel typedLabel
= new TypedLabel(label
);
1089 typedLabel
.setClassAndId(qd
);
1090 factDto
.getTypedLabel().add(typedLabel
);
1092 loadBaseData(qd
, factDto
);
1094 }else if (fact
.isInstanceOf(TemporalData
.class)) {
1095 TemporalData td
= CdmBase
.deproxy(fact
, TemporalData
.class);
1096 FactDto factDto
= new FactDto();
1097 featureDto
.addFact(factDto
);
1098 //TODO do we really need type information for textdata here?
1099 String label
= td
.toString();
1100 TypedLabel typedLabel
= new TypedLabel(label
);
1101 typedLabel
.setClassAndId(td
);
1102 factDto
.getTypedLabel().add(typedLabel
);
1104 loadBaseData(td
, factDto
);
1107 pageDto
.addMessage(MessagesDto
.NewWarnInstance("DescriptionElement type not yet handled: " + fact
.getClass().getSimpleName()));
1110 result
.setTimeperiod(fact
.getTimeperiod() == null ?
null : fact
.getTimeperiod().toString());
1114 private String
getTermLabel(TermBase term
, Language localeLang
) {
1118 Representation rep
= term
.getPreferredRepresentation(localeLang
);
1119 String label
= rep
== null ?
null : rep
.getLabel();
1120 label
= label
== null ? term
.getLabel() : label
;
1125 * Compares an existing last date and the last date of an entity
1126 * and returns the resulting last date.
1128 private LocalDateTime
getLastUpdated(LocalDateTime existingLastDate
, VersionableEntity dateToAddEntity
) {
1130 DateTime dateToAdd
= dateToAddEntity
.getUpdated() != null ? dateToAddEntity
.getUpdated() : dateToAddEntity
.getCreated();
1132 LocalDateTime javaLocalDateTimeOfEntity
= dateToAdd
== null ?
null:
1133 LocalDateTime
.of(dateToAdd
.getYear(), dateToAdd
.getMonthOfYear(),
1134 dateToAdd
.getDayOfMonth(), dateToAdd
.getHourOfDay(),
1135 dateToAdd
.getMinuteOfHour(), dateToAdd
.getSecondOfMinute());
1137 if (existingLastDate
== null) {
1138 return javaLocalDateTimeOfEntity
;
1139 }else if (javaLocalDateTimeOfEntity
== null || javaLocalDateTimeOfEntity
.compareTo(existingLastDate
) < 0) {
1140 return existingLastDate
;
1142 return javaLocalDateTimeOfEntity
;
1146 public static void loadBaseData(CdmBase cdmBase
, CdmBaseDto dto
) {
1147 dto
.setId(cdmBase
.getId());
1148 dto
.setUuid(cdmBase
.getUuid());
1150 loadAnnotatable(cdmBase
, dto
);
1151 loadSources(cdmBase
, dto
);
1152 //loadIdentifiable(cdmBase, dto);
1155 private static void loadSources(CdmBase cdmBase
, CdmBaseDto dto
) {
1156 if (dto
instanceof SingleSourcedDto
&& cdmBase
.isInstanceOf(SingleSourcedEntityBase
.class)) {
1157 //TODO other sourced
1158 SingleSourcedEntityBase sourced
= CdmBase
.deproxy(cdmBase
, SingleSourcedEntityBase
.class);
1159 SingleSourcedDto sourcedDto
= (SingleSourcedDto
)dto
;
1160 NamedSource source
= sourced
.getSource();
1161 if (source
!= null) { //TODO && !source.isEmpty() - does not exist yet
1162 SourceDto sourceDto
= makeSource(source
);
1163 sourcedDto
.setSource(sourceDto
);
1165 } else if (dto
instanceof SourcedDto
&& cdmBase
instanceof ISourceable
) {
1166 @SuppressWarnings("unchecked")
1167 ISourceable
<OriginalSourceBase
> sourced
= (ISourceable
<OriginalSourceBase
>)cdmBase
;
1168 SourcedDto sourcedDto
= (SourcedDto
)dto
;
1169 for (OriginalSourceBase source
: sourced
.getSources()) {
1170 SourceDto sourceDto
= makeSource(source
);
1171 sourcedDto
.addSource(sourceDto
);
1174 //load description sources for facts
1175 if (cdmBase
.isInstanceOf(DescriptionElementBase
.class)){
1176 DescriptionBase
<?
> db
= CdmBase
.deproxy(cdmBase
, DescriptionElementBase
.class).getInDescription();
1177 if (db
!= null) { //test sometime do not have a description for facts
1178 SourcedDto sourcedDto
= (SourcedDto
)dto
;
1179 for (OriginalSourceBase source
: db
.getSources()) {
1180 SourceDto sourceDto
= new SourceDto();
1181 loadSource(source
, sourceDto
);
1182 sourcedDto
.addSource(sourceDto
);
1188 private static SourceDto
makeSource(OriginalSourceBase source
) {
1189 if (source
== null) {
1192 SourceDto sourceDto
= new SourceDto();
1193 loadSource(source
, sourceDto
);
1197 private static void loadSource(OriginalSourceBase source
, SourceDto sourceDto
) {
1199 source
= CdmBase
.deproxy(source
);
1201 loadBaseData(source
, sourceDto
);
1203 ICdmBase linkedObject
= source
.getCitation();
1204 if (linkedObject
== null) {
1206 linkedObject
= source
.getCdmSource();
1209 //citation doi & uri & links
1210 Reference ref
= source
.getCitation();
1212 sourceDto
.setDoi(ref
.getDoiString());
1213 sourceDto
.setUri(ref
.getUri());
1214 sourceDto
.setUuid(ref
.getUuid());
1215 sourceDto
.setOriginalInfo(source
.getOriginalInfo());
1216 Set
<ExternalLink
> links
= ref
.getLinks();
1217 for (ExternalLink link
: links
) {
1218 if (link
.getUri() != null) {
1219 sourceDto
.addLink(link
.getUri());
1225 //TODO this probably does not use specimen or cdmSource if necessary,
1226 // also long citation is still preliminary
1227 String label
= OriginalSourceFormatter
.INSTANCE_LONG_CITATION
.format(source
);
1228 TypedLabel typedLabel
= new TypedLabel(source
, label
);
1229 sourceDto
.addLabel(typedLabel
);
1230 sourceDto
.setType(source
.getType() != null ? source
.getType().toString() : null);
1232 if (source
.isInstanceOf(NamedSourceBase
.class)) {
1233 NamedSourceBase ns
= CdmBase
.deproxy(source
, NamedSourceBase
.class);
1236 TaxonName name
= ns
.getNameUsedInSource();
1238 List
<TaggedText
> taggedName
= name
.cacheStrategy().getTaggedTitle(name
);
1240 sourceDto
.setNameInSource(taggedName
);
1241 sourceDto
.setNameInSourceUuid(name
.getUuid());
1245 if (source
.isInstanceOf(DescriptionElementSource
.class)) {
1246 DescriptionElementSource des
= CdmBase
.deproxy(source
, DescriptionElementSource
.class);
1247 if (linkedObject
== null) {
1248 linkedObject
= des
.getSpecimen();
1253 sourceDto
.setLinkedUuid(getUuid(linkedObject
));
1254 String linkedObjectStr
= linkedObject
== null ?
null : CdmBase
.deproxy(linkedObject
).getClass().getSimpleName();
1255 sourceDto
.setLinkedClass(linkedObjectStr
);
1258 private static UUID
getUuid(ICdmBase cdmBase
) {
1259 return cdmBase
== null ?
null : cdmBase
.getUuid();
1262 private static void loadAnnotatable(CdmBase cdmBase
, CdmBaseDto dto
) {
1263 if (dto
instanceof AnnotatableDto
&& cdmBase
.isInstanceOf(AnnotatableEntity
.class)) {
1264 AnnotatableEntity annotatable
= CdmBase
.deproxy(cdmBase
, AnnotatableEntity
.class);
1265 AnnotatableDto annotatableDto
= (AnnotatableDto
)dto
;
1267 for (Annotation annotation
: annotatable
.getAnnotations()) {
1268 if (annotation
.getAnnotationType() != null
1269 //TODO annotation type filter
1270 && annotation
.getAnnotationType().getUuid().equals(AnnotationType
.uuidEditorial
)
1271 && StringUtils
.isNotBlank(annotation
.getText())) {
1273 AnnotationDto annotationDto
= new AnnotationDto();
1274 annotatableDto
.addAnnotation(annotationDto
);
1275 //TODO id needed? but need to adapt dto and container then
1276 loadBaseData(annotation
, annotationDto
);
1277 annotationDto
.setText(annotation
.getText());
1278 UUID uuidAnnotationType
= annotation
.getAnnotationType() == null ?
null :annotation
.getAnnotationType().getUuid();
1279 annotationDto
.setTypeUuid(uuidAnnotationType
);
1280 //language etc. currently not yet used
1285 for (Marker marker
: annotatable
.getMarkers()) {
1286 if (marker
.getMarkerType() != null
1287 //TODO markertype filter
1288 // && marker.getMarkerType().getUuid().equals(AnnotationType.uuidEditorial)
1291 MarkerDto markerDto
= new MarkerDto();
1292 annotatableDto
.addMarker(markerDto
);
1293 //TODO id needed? but need to adapt dto and container then
1294 loadBaseData(marker
, markerDto
);
1295 if (marker
.getMarkerType() != null) {
1296 markerDto
.setTypeUuid(marker
.getMarkerType().getUuid());
1298 markerDto
.setType(marker
.getMarkerType().getTitleCache());
1300 markerDto
.setValue(marker
.getValue());