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
.DistributionDto
;
38 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.DistributionInfoDto
;
39 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.DistributionTreeDto
;
40 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.FactDto
;
41 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.FactDtoBase
;
42 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.FeatureDto
;
43 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.IFactDto
;
44 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.IndividualsAssociationDto
;
45 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.MarkerDto
;
46 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.MessagesDto
;
47 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.MessagesDto
.MessageType
;
48 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.NamedAreaDto
;
49 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.SingleSourcedDto
;
50 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.SourceDto
;
51 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.SourcedDto
;
52 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonBaseDto
;
53 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonInteractionDto
;
54 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
;
55 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.ConceptRelationDTO
;
56 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.HomotypicGroupDTO
;
57 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.KeyDTO
;
58 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.MediaDTO
;
59 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.MediaRepresentationDTO
;
60 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.NameRelationDTO
;
61 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.SpecimenDTO
;
62 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.TaxonNodeAgentsRelDTO
;
63 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.TaxonPageDto
.TaxonNodeDTO
;
64 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.config
.DistributionInfoConfiguration
;
65 import eu
.etaxonomy
.cdm
.api
.dto
.portal
.config
.TaxonPageDtoConfiguration
;
66 import eu
.etaxonomy
.cdm
.api
.service
.exception
.TypeDesignationSetException
;
67 import eu
.etaxonomy
.cdm
.api
.service
.geo
.DistributionServiceUtilities
;
68 import eu
.etaxonomy
.cdm
.api
.service
.geo
.IDistributionService
;
69 import eu
.etaxonomy
.cdm
.api
.service
.l10n
.LocaleContext
;
70 import eu
.etaxonomy
.cdm
.api
.service
.name
.TypeDesignationSetContainer
;
71 import eu
.etaxonomy
.cdm
.api
.service
.name
.TypeDesignationSetFormatter
;
72 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
73 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
74 import eu
.etaxonomy
.cdm
.common
.TreeNode
;
75 import eu
.etaxonomy
.cdm
.compare
.taxon
.TaxonComparator
;
76 import eu
.etaxonomy
.cdm
.format
.common
.TypedLabel
;
77 import eu
.etaxonomy
.cdm
.format
.description
.CategoricalDataFormatter
;
78 import eu
.etaxonomy
.cdm
.format
.description
.QuantitativeDataFormatter
;
79 import eu
.etaxonomy
.cdm
.format
.description
.distribution
.CondensedDistributionConfiguration
;
80 import eu
.etaxonomy
.cdm
.format
.reference
.OriginalSourceFormatter
;
81 import eu
.etaxonomy
.cdm
.format
.taxon
.TaxonRelationshipFormatter
;
82 import eu
.etaxonomy
.cdm
.model
.common
.AnnotatableEntity
;
83 import eu
.etaxonomy
.cdm
.model
.common
.Annotation
;
84 import eu
.etaxonomy
.cdm
.model
.common
.AnnotationType
;
85 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
86 import eu
.etaxonomy
.cdm
.model
.common
.ICdmBase
;
87 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
88 import eu
.etaxonomy
.cdm
.model
.common
.LanguageString
;
89 import eu
.etaxonomy
.cdm
.model
.common
.Marker
;
90 import eu
.etaxonomy
.cdm
.model
.common
.MarkerType
;
91 import eu
.etaxonomy
.cdm
.model
.common
.MultilanguageTextHelper
;
92 import eu
.etaxonomy
.cdm
.model
.common
.SingleSourcedEntityBase
;
93 import eu
.etaxonomy
.cdm
.model
.common
.VersionableEntity
;
94 import eu
.etaxonomy
.cdm
.model
.description
.CategoricalData
;
95 import eu
.etaxonomy
.cdm
.model
.description
.CommonTaxonName
;
96 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementBase
;
97 import eu
.etaxonomy
.cdm
.model
.description
.DescriptionElementSource
;
98 import eu
.etaxonomy
.cdm
.model
.description
.Distribution
;
99 import eu
.etaxonomy
.cdm
.model
.description
.Feature
;
100 import eu
.etaxonomy
.cdm
.model
.description
.IndividualsAssociation
;
101 import eu
.etaxonomy
.cdm
.model
.description
.PolytomousKey
;
102 import eu
.etaxonomy
.cdm
.model
.description
.PresenceAbsenceTerm
;
103 import eu
.etaxonomy
.cdm
.model
.description
.QuantitativeData
;
104 import eu
.etaxonomy
.cdm
.model
.description
.TaxonDescription
;
105 import eu
.etaxonomy
.cdm
.model
.description
.TaxonInteraction
;
106 import eu
.etaxonomy
.cdm
.model
.description
.TemporalData
;
107 import eu
.etaxonomy
.cdm
.model
.description
.TextData
;
108 import eu
.etaxonomy
.cdm
.model
.location
.NamedArea
;
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
.SpecimenTypeDesignation
;
117 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
118 import eu
.etaxonomy
.cdm
.model
.name
.TypeDesignationBase
;
119 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
120 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
121 import eu
.etaxonomy
.cdm
.model
.reference
.ISourceable
;
122 import eu
.etaxonomy
.cdm
.model
.reference
.NamedSource
;
123 import eu
.etaxonomy
.cdm
.model
.reference
.NamedSourceBase
;
124 import eu
.etaxonomy
.cdm
.model
.reference
.OriginalSourceBase
;
125 import eu
.etaxonomy
.cdm
.model
.taxon
.Classification
;
126 import eu
.etaxonomy
.cdm
.model
.taxon
.Synonym
;
127 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
128 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonBase
;
129 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNode
;
130 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeAgentRelation
;
131 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonNodeStatus
;
132 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
133 import eu
.etaxonomy
.cdm
.model
.term
.Representation
;
134 import eu
.etaxonomy
.cdm
.model
.term
.TermBase
;
135 import eu
.etaxonomy
.cdm
.model
.term
.TermNode
;
136 import eu
.etaxonomy
.cdm
.model
.term
.TermTree
;
137 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedCacheHelper
;
138 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
139 import eu
.etaxonomy
.cdm
.strategy
.cache
.taxon
.TaxonBaseDefaultCacheStrategy
;
142 * Loads the portal dto from a taxon instance.
143 * Maybe later also supports loading from persistence.
148 public class PortalDtoLoader
{
150 private static final Logger logger
= LogManager
.getLogger();
152 private ICdmRepository repository
;
154 public PortalDtoLoader(ICdmRepository repository
) {
155 this.repository
= repository
;
158 public TaxonPageDto
load(Taxon taxon
, TaxonPageDtoConfiguration config
) {
159 TaxonPageDto result
= new TaxonPageDto();
161 TaxonName name
= taxon
.getName();
164 //TODO supplementalData for name
165 loadBaseData(taxon
, result
);
166 result
.setLastUpdated(getLastUpdated(null, taxon
));
167 result
.setNameLabel(name
!= null? name
.getTitleCache() : "");
168 result
.setLabel(CdmUtils
.Nz(taxon
.getTitleCache()));
169 // result.setTypedTaxonLabel(getTypedTaxonLabel(taxon, config));
170 result
.setTaggedLabel(getTaggedTaxon(taxon
, config
));
171 handleRelatedNames(taxon
.getName(), result
, config
);
173 loadTaxonNodes(taxon
, result
, config
);
174 loadSynonyms(taxon
, result
, config
);
175 loadConceptRelations(taxon
, result
, config
);
176 loadFacts(taxon
, result
, config
);
177 loadMedia(taxon
, result
, config
);
178 loadSpecimens(taxon
, result
, config
);
179 loadKeys(taxon
, result
, config
);
184 private List
<TaggedText
> getTaggedTaxon(TaxonBase
<?
> taxon
, TaxonPageDtoConfiguration config
) {
185 // List<TypedLabel> result = new ArrayList<>();
186 TaxonBaseDefaultCacheStrategy
<TaxonBase
<?
>> formatter
= new TaxonBaseDefaultCacheStrategy
<>();
187 List
<TaggedText
> tags
= formatter
.getTaggedTitle(taxon
);
191 private void loadKeys(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
192 ContainerDto
<KeyDTO
> container
= new ContainerDto
<>();
194 //TODO other key types, but type must not be null, otherwise NPE
195 Pager
<PolytomousKey
> keys
= repository
.getIdentificationKeyService().findKeysConvering(taxon
, PolytomousKey
.class, null, null, null);
196 for (PolytomousKey key
: keys
.getRecords()) {
197 KeyDTO dto
= new KeyDTO();
198 loadBaseData(key
, dto
);
199 dto
.setLabel(key
.getTitleCache());
200 dto
.setKeyClass(key
.getClass().getSimpleName());
201 container
.addItem(dto
);
203 if (container
.getCount() > 0) {
204 result
.setKeys(container
);
208 private void loadSpecimens(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
209 //TODO load specimen from multiple places
211 ContainerDto
<SpecimenDTO
> container
= new ContainerDto
<>();
213 List
<SpecimenOrObservationBase
<?
>> specimens
= new ArrayList
<>();
214 for (TaxonDescription taxonDescription
: taxon
.getDescriptions()) {
215 if (taxonDescription
.isImageGallery()) {
218 for (DescriptionElementBase el
: taxonDescription
.getElements()) {
219 if (el
.isInstanceOf(IndividualsAssociation
.class)) {
220 IndividualsAssociation indAss
= CdmBase
.deproxy(el
, IndividualsAssociation
.class);
221 SpecimenOrObservationBase
<?
> specimen
= indAss
.getAssociatedSpecimenOrObservation();
222 specimens
.add(specimen
);
226 List
<SpecimenOrObservationBase
<?
>> typeSpecimens
= loadTypeSpecimen(taxon
.getName(), config
);
227 specimens
.addAll(typeSpecimens
);
228 for (TaxonName syn
: taxon
.getSynonymNames()) {
229 typeSpecimens
= loadTypeSpecimen(syn
, config
);
230 specimens
.addAll(typeSpecimens
);
233 for (SpecimenOrObservationBase
<?
> specimen
: specimens
) {
234 SpecimenDTO dto
= new SpecimenDTO();
235 loadBaseData(specimen
, dto
);
236 dto
.setLabel(specimen
.getTitleCache());
237 container
.addItem(dto
);
239 if (container
.getCount() > 0 ) {
240 result
.setSpecimens(container
);
244 private List
<SpecimenOrObservationBase
<?
>> loadTypeSpecimen(TaxonName name
, TaxonPageDtoConfiguration config
) {
245 List
<SpecimenOrObservationBase
<?
>> result
= new ArrayList
<>();
246 for (SpecimenTypeDesignation desig
: name
.getSpecimenTypeDesignations()){
247 DerivedUnit specimen
= desig
.getTypeSpecimen();
248 if (specimen
!= null) {
249 result
.add(specimen
);
255 private void loadMedia(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
257 ContainerDto
<MediaDTO
> container
= new ContainerDto
<TaxonPageDto
.MediaDTO
>();
259 List
<Media
> medias
= new ArrayList
<>();
260 for (TaxonDescription taxonDescription
: taxon
.getDescriptions()) {
261 if (!taxonDescription
.isImageGallery()) {
265 List
<Media
> newMedia
= taxonDescription
.getElements().stream()
266 .filter(el
->el
.isInstanceOf(TextData
.class))
267 .map(el
->CdmBase
.deproxy(el
, TextData
.class))
269 .flatMap(td
->td
.getMedia().stream())
270 .collect(Collectors
.toList())
272 medias
.addAll(newMedia
);
274 //TODO collect media from elsewhere
275 for (Media media
: medias
) {
276 MediaDTO dto
= new TaxonPageDto
.MediaDTO();
277 loadBaseData(media
, dto
);
278 dto
.setLabel(media
.getTitleCache());
279 ContainerDto
<MediaRepresentationDTO
> representations
= new ContainerDto
<>();
280 for (MediaRepresentation rep
: media
.getRepresentations()) {
281 MediaRepresentationDTO repDto
= new MediaRepresentationDTO();
282 loadBaseData(rep
, dto
);
283 repDto
.setMimeType(rep
.getMimeType());
284 repDto
.setSuffix(rep
.getSuffix());
285 if (!rep
.getParts().isEmpty()) {
286 //TODO handle message if n(parts) > 1
287 MediaRepresentationPart part
= rep
.getParts().get(0);
288 repDto
.setUri(part
.getUri());
289 repDto
.setClazz(part
.getClass());
290 repDto
.setSize(part
.getSize());
291 if (part
.isInstanceOf(ImageFile
.class)) {
292 ImageFile image
= CdmBase
.deproxy(part
, ImageFile
.class);
293 repDto
.setHeight(image
.getHeight());
294 repDto
.setWidth(image
.getWidth());
296 //TODO AudioFile etc.
298 representations
.addItem(repDto
);
300 if (representations
.getCount() > 0) {
301 dto
.setRepresentations(representations
);
303 //TODO load representation data
304 container
.addItem(dto
);
307 if (container
.getCount() > 0) {
308 result
.setMedia(container
);
312 private void loadTaxonNodes(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
313 ContainerDto
<TaxonNodeDTO
> container
= new ContainerDto
<TaxonPageDto
.TaxonNodeDTO
>();
314 for (TaxonNode node
: taxon
.getTaxonNodes()) {
315 TaxonNodeDTO dto
= new TaxonNodeDTO();
316 loadBaseData(node
, dto
);
318 Classification classification
= node
.getClassification();
319 if (classification
!= null) {
320 dto
.setClassificationUuid(node
.getClassification().getUuid());
321 dto
.setClassificationLabel(classification
.getName().getText());
324 Language language
= Language
.DEFAULT();
327 TaxonNodeStatus status
= node
.getStatus();
328 if (status
!= null) {
329 dto
.setStatus(status
.getLabel(language
));
332 Map
<Language
, LanguageString
> statusNote
= node
.getStatusNote();
333 if (statusNote
!= null) {
334 //TODO handle fallback lang
335 LanguageString statusNoteStr
= statusNote
.get(language
);
336 if (statusNoteStr
== null && statusNote
.size() > 0) {
337 statusNoteStr
= statusNote
.entrySet().iterator().next().getValue();
339 if (statusNoteStr
!= null) {
340 dto
.setStatusNote(statusNoteStr
.getText());
344 Set
<TaxonNodeAgentRelation
> agents
= node
.getAgentRelations();
345 if (!agents
.isEmpty()) {
346 for (TaxonNodeAgentRelation rel
: agents
) {
347 TaxonNodeAgentsRelDTO agentDto
= new TaxonNodeAgentsRelDTO();
348 loadBaseData(rel
, agentDto
);
351 if (rel
.getAgent() != null) {
352 agentDto
.setAgent(rel
.getAgent().getFullTitle());
353 agentDto
.setAgentUuid(rel
.getAgent().getUuid());
354 //TODO compute preferred external link
355 agentDto
.setAgentLink(null);
357 if (rel
.getType() != null) {
358 agentDto
.setType(rel
.getType().getTitleCache());
359 agentDto
.setTypeUuid(rel
.getType().getUuid());
361 dto
.addAgent(agentDto
);
364 container
.addItem(dto
);
366 if (container
.getCount() > 0) {
367 result
.setTaxonNodes(container
);
373 private void loadSynonyms(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
374 // List<HomotypicalGroup> homotypicGroups = taxon.getHomotypicSynonymyGroups();
376 TaxonComparator comparator
= new TaxonComparator();
378 TaxonName name
= taxon
.getName();
380 //TODO depending on config add/remove accepted name
382 //TODO check publish flag
385 List
<Synonym
> homotypicSynonmys
= taxon
.getHomotypicSynonymsByHomotypicGroup(comparator
);
386 TaxonPageDto
.HomotypicGroupDTO homotypicGroupDto
= new TaxonPageDto
.HomotypicGroupDTO();
387 if (!homotypicSynonmys
.isEmpty()) {
388 loadBaseData(name
.getHomotypicalGroup(), homotypicGroupDto
);
390 for (Synonym syn
: homotypicSynonmys
) {
391 loadSynonymsInGroup(homotypicGroupDto
, syn
, config
);
395 handleTypification(name
.getHomotypicalGroup(), homotypicGroupDto
, result
, config
);
396 result
.setHomotypicSynonyms(homotypicGroupDto
);
398 //heterotypic synonyms
399 List
<HomotypicalGroup
> heteroGroups
= taxon
.getHeterotypicSynonymyGroups();
400 if (heteroGroups
.isEmpty()) {
403 ContainerDto
<HomotypicGroupDTO
> heteroContainer
= new ContainerDto
<>();
404 result
.setHeterotypicSynonymGroups(heteroContainer
);
406 for (HomotypicalGroup hg
: heteroGroups
) {
407 TaxonPageDto
.HomotypicGroupDTO hgDto
= new TaxonPageDto
.HomotypicGroupDTO();
408 loadBaseData(taxon
.getName().getHomotypicalGroup(), hgDto
);
409 heteroContainer
.addItem(hgDto
);
411 List
<Synonym
> heteroSyns
= taxon
.getSynonymsInGroup(hg
, comparator
);
412 for (Synonym syn
: heteroSyns
) {
413 loadSynonymsInGroup(hgDto
, syn
, config
);
415 handleTypification(hg
, hgDto
, result
, config
);
419 private void handleTypification(HomotypicalGroup homotypicalGroup
, HomotypicGroupDTO hgDto
,
420 TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
422 boolean withCitation
= true;
423 boolean withStartingTypeLabel
= true;
424 boolean withNameIfAvailable
= false;
425 TypeDesignationSetFormatter formatter
= new TypeDesignationSetFormatter(
426 withCitation
, withStartingTypeLabel
, withNameIfAvailable
);
427 Set
<TypeDesignationBase
<?
>> desigs
= homotypicalGroup
.getTypeDesignations();
429 TypeDesignationSetContainer manager
= TypeDesignationSetContainer
.NewDefaultInstance((Set
)desigs
);
430 List
<TaggedText
> tags
= formatter
.toTaggedText(manager
);
431 String label
= TaggedCacheHelper
.createString(tags
);
432 hgDto
.setTypes(label
);
433 hgDto
.setTaggedTypes(tags
);
434 // hgDto.setTypedTypes(null);
436 } catch (TypeDesignationSetException e
) {
437 result
.addMessage(new MessagesDto(MessageType
.ERROR
, "Error when creating type designation information"));
441 private void loadConceptRelations(Taxon taxon
, TaxonPageDto result
, TaxonPageDtoConfiguration config
) {
444 ContainerDto
<ConceptRelationDTO
> conceptRelContainer
= new ContainerDto
<>();
445 TaxonRelationshipFormatter taxRelFormatter
= TaxonRelationshipFormatter
.INSTANCE();
448 Set
<TaxonRelationship
> misappliedRels
= taxon
.getMisappliedNameRelations();
449 for (TaxonRelationship rel
: misappliedRels
) {
450 boolean inverse
= true;
451 boolean withoutName
= false;
452 loadConceptRelation(taxRelFormatter
, rel
, conceptRelContainer
, inverse
, withoutName
);
455 //... pro parte Synonyms
456 Set
<TaxonRelationship
> proParteRels
= taxon
.getProParteAndPartialSynonymRelations();
457 for (TaxonRelationship rel
: proParteRels
) {
458 boolean inverse
= true;
459 boolean withoutName
= false;
460 loadConceptRelation(taxRelFormatter
, rel
, conceptRelContainer
, inverse
, withoutName
);
463 //TODO MAN and pp from this taxon
466 Set
<TaxonRelationship
> toRels
= taxon
.getRelationsToThisTaxon();
467 for (TaxonRelationship rel
: toRels
) {
468 boolean inverse
= true;
469 boolean withoutName
= false;
470 loadConceptRelation(taxRelFormatter
, rel
, conceptRelContainer
, inverse
, withoutName
);
474 Set
<TaxonRelationship
> fromRels
= taxon
.getRelationsFromThisTaxon();
475 for (TaxonRelationship rel
: fromRels
) {
476 boolean inverse
= false;
477 boolean withoutName
= false;
478 loadConceptRelation(taxRelFormatter
, rel
, conceptRelContainer
, inverse
, withoutName
);
481 if (conceptRelContainer
.getCount() > 0) {
482 result
.setConceptRelations(conceptRelContainer
);
486 private void loadConceptRelation(TaxonRelationshipFormatter taxRelFormatter
, TaxonRelationship rel
, ContainerDto
<ConceptRelationDTO
> conceptRelContainer
, boolean inverse
,
487 boolean withoutName
) {
488 List
<Language
> languages
= Arrays
.asList(new Language
[] {Language
.DEFAULT()}); // TODO config.locales;
489 List
<TaggedText
> tags
= taxRelFormatter
.getTaggedText(rel
, inverse
, languages
, withoutName
);
490 String relLabel
= TaggedCacheHelper
.createString(tags
);
491 ConceptRelationDTO dto
= new TaxonPageDto
.ConceptRelationDTO();
492 loadBaseData(rel
, dto
);
493 Taxon relTaxon
= inverse ? rel
.getFromTaxon() : rel
.getToTaxon();
494 dto
.setRelTaxonId(relTaxon
.getId());
495 dto
.setRelTaxonUuid(relTaxon
.getUuid());
496 dto
.setRelTaxonLabel(relTaxon
.getTitleCache());
497 dto
.setLabel(relLabel
);
498 conceptRelContainer
.addItem(dto
);
501 private void loadSynonymsInGroup(TaxonPageDto
.HomotypicGroupDTO hgDto
, Synonym syn
, TaxonPageDtoConfiguration config
) {
502 TaxonBaseDto synDto
= new TaxonBaseDto();
503 loadBaseData(syn
, synDto
);
504 synDto
.setLabel(syn
.getTitleCache());
505 synDto
.setTaggedLabel(getTaggedTaxon(syn
, config
));
506 if (syn
.getName() != null) {
507 synDto
.setNameLabel(syn
.getName().getTitleCache());
508 handleRelatedNames(syn
.getName(), synDto
, config
);
512 hgDto
.addSynonym(synDto
);
515 private void handleRelatedNames(TaxonName name
, TaxonBaseDto taxonDto
, TaxonPageDtoConfiguration config
) {
516 //exclusions TODO handle via config
517 Set
<UUID
> excludedTypes
= new HashSet
<>(); //both directions
518 excludedTypes
.add(NameRelationshipType
.uuidBasionym
);
519 excludedTypes
.add(NameRelationshipType
.uuidReplacedSynonym
);
520 Set
<UUID
> excludedFromTypes
= new HashSet
<>(excludedTypes
);
521 Set
<UUID
> excludedToTypes
= new HashSet
<>(excludedTypes
);
524 //TODO config.getLocales();
525 Language locale
= Language
.DEFAULT();
527 for (NameRelationship rel
: name
.getRelationsFromThisName()) {
528 TaxonName relatedName
= rel
.getToName();
529 if (relatedName
== null || rel
.getType() == null || excludedFromTypes
.contains(rel
.getType().getUuid())) {
532 NameRelationDTO dto
= new NameRelationDTO();
533 loadBaseData(rel
, dto
);
535 dto
.setNameUuid(relatedName
.getUuid());
536 dto
.setNameLabel(relatedName
.getTaggedName());
538 dto
.setRelTypeUuid(rel
.getType().getUuid());
539 Representation rep
= rel
.getType().getPreferredRepresentation(locale
);
540 dto
.setRelType(rep
== null ? rel
.getType().toString() : rep
.getLabel());
542 dto
.setInverse(false);
543 taxonDto
.addRelatedName(dto
);
547 for (NameRelationship rel
: name
.getRelationsToThisName()) {
548 TaxonName relatedName
= rel
.getFromName();
549 if (relatedName
== null || rel
.getType() == null || excludedFromTypes
.contains(rel
.getType().getUuid())) {
552 NameRelationDTO dto
= new NameRelationDTO();
553 loadBaseData(rel
, dto
);
555 dto
.setNameUuid(relatedName
.getUuid());
556 dto
.setNameLabel(relatedName
.getTaggedName());
558 dto
.setRelTypeUuid(rel
.getType().getUuid());
559 Representation rep
= rel
.getType().getPreferredInverseRepresentation(Arrays
.asList(new Language
[] {locale
}));
560 dto
.setRelType(rep
== null ? rel
.getType().toString() : rep
.getLabel());
562 dto
.setInverse(true);
563 taxonDto
.addRelatedName(dto
);
567 private void loadFacts(Taxon taxon
, TaxonPageDto taxonPageDto
, TaxonPageDtoConfiguration config
) {
569 //compute the features that do exist for this taxon
570 Map
<UUID
, Feature
> existingFeatureUuids
= getExistingFeatureUuids(taxon
);
572 //filter, sort and structure according to feature tree
573 TreeNode
<Feature
, UUID
> filteredRootNode
;
574 if (config
.getFeatureTree() != null) {
577 TermTree
<Feature
> featureTree
= repository
.getTermTreeService().find(config
.getFeatureTree());
578 filteredRootNode
= filterFeatureNode(featureTree
.getRoot(), existingFeatureUuids
.keySet());
580 filteredRootNode
= createDefaultFeatureNode(taxon
);
583 //load facts per feature
584 Map
<UUID
,Set
<DescriptionElementBase
>> featureMap
= loadFeatureMap(taxon
);
587 if (!filteredRootNode
.getChildren().isEmpty()) {
588 ContainerDto
<FeatureDto
> features
= new ContainerDto
<>();
589 for (TreeNode
<Feature
,UUID
> node
: filteredRootNode
.getChildren()) {
590 handleFeatureNode(taxon
, config
, featureMap
, features
, node
);
592 taxonPageDto
.setFactualData(features
);
596 private void handleFeatureNode(Taxon taxon
, TaxonPageDtoConfiguration config
,
597 Map
<UUID
, Set
<DescriptionElementBase
>> featureMap
, ContainerDto
<FeatureDto
> features
,
598 TreeNode
<Feature
, UUID
> node
) {
600 Feature feature
= node
.getData();
602 FeatureDto featureDto
= new FeatureDto(feature
.getUuid(), feature
.getId(), feature
.getLabel());
603 features
.addItem(featureDto
);
605 List
<Distribution
> distributions
= new ArrayList
<>();
608 for (DescriptionElementBase fact
: featureMap
.get(feature
.getUuid())){
609 if (fact
.isInstanceOf(Distribution
.class)) {
610 distributions
.add(CdmBase
.deproxy(fact
, Distribution
.class));
612 //TODO how to handle CommonNames, do we also want to have a data structure
616 // a bit like for distribution??
617 handleFact(featureDto
, fact
);
621 handleDistributions(config
, featureDto
, taxon
, distributions
);
622 //TODO really needed?
623 orderFacts(featureDto
);
626 ContainerDto
<FeatureDto
> childFeatures
= new ContainerDto
<>();
627 for (TreeNode
<Feature
,UUID
> child
: node
.getChildren()) {
628 handleFeatureNode(taxon
, config
, featureMap
, childFeatures
, child
);
630 if (childFeatures
.getCount() > 0) {
631 featureDto
.setSubFeatures(childFeatures
);
635 //TODO not really used yet, only for distinguishing fact classes,
636 //needs discussion if needed and how to implement.
637 //we could also move compareTo methods to DTO classes but with this
638 //remove from having only data in the DTO, no logic
639 private void orderFacts(FeatureDto featureDto
) {
640 List
<IFactDto
> list
= featureDto
.getFacts().getItems();
641 Collections
.sort(list
, (f1
,f2
)->{
642 if (!f1
.getClass().equals(f2
.getClass())) {
643 return f1
.getClass().getSimpleName().compareTo(f2
.getClass().getSimpleName());
645 if (f1
instanceof FactDto
) {
646 FactDto fact1
= (FactDto
)f1
;
647 FactDto fact2
= (FactDto
)f2
;
649 // return fact1.getTypedLabel().toString().compareTo(fact2.getTypedLabel().toString());
651 } else if (f1
instanceof CommonNameDto
) {
653 CommonNameDto fact1
= (CommonNameDto
)f1
;
654 CommonNameDto fact2
= (CommonNameDto
)f2
;
663 private Map
<UUID
, Set
<DescriptionElementBase
>> loadFeatureMap(Taxon taxon
) {
664 Map
<UUID
, Set
<DescriptionElementBase
>> featureMap
= new HashMap
<>();
667 for (TaxonDescription taxonDescription
: taxon
.getDescriptions()) {
668 if (taxonDescription
.isImageGallery()) {
671 for (DescriptionElementBase deb
: taxonDescription
.getElements()) {
672 Feature feature
= deb
.getFeature();
673 if (featureMap
.get(feature
.getUuid()) == null) {
674 featureMap
.put(feature
.getUuid(), new HashSet
<>());
676 featureMap
.get(feature
.getUuid()).add(deb
);
682 private TreeNode
<Feature
, UUID
> createDefaultFeatureNode(Taxon taxon
) {
683 TreeNode
<Feature
, UUID
> root
= new TreeNode
<>();
684 Set
<Feature
> requiredFeatures
= new HashSet
<>();
686 for (TaxonDescription taxonDescription
: taxon
.getDescriptions()) {
687 if (taxonDescription
.isImageGallery()) {
690 for (DescriptionElementBase deb
: taxonDescription
.getElements()) {
691 Feature feature
= deb
.getFeature();
692 if (feature
!= null) { //null should not happen
693 requiredFeatures
.add(feature
);
697 List
<Feature
> sortedChildren
= new ArrayList
<>(requiredFeatures
);
698 Collections
.sort(sortedChildren
, (f1
,f2
) -> f1
.getTitleCache().compareTo(f2
.getTitleCache()));
699 sortedChildren
.stream().forEachOrdered(f
->root
.addChild(new TreeNode
<>(f
.getUuid(), f
)));
704 * Recursive call to a feature tree's feature node in order to creates a tree structure
705 * ordered in the same way as the according feature tree but only containing features
706 * that do really exist for the given taxon. If only a child node is required the parent
707 * node/feature is also considered to be required.<BR>
709 private TreeNode
<Feature
, UUID
> filterFeatureNode(TermNode
<Feature
> featureNode
,
710 Set
<UUID
> existingFeatureUuids
) {
712 //first filter children
713 List
<TreeNode
<Feature
, UUID
>> requiredChildNodes
= new ArrayList
<>();
714 for (TermNode
<Feature
> childNode
: featureNode
.getChildNodes()) {
715 TreeNode
<Feature
, UUID
> child
= filterFeatureNode(childNode
, existingFeatureUuids
);
717 requiredChildNodes
.add(child
);
721 //if any child is required or this node is required ....
722 if (!requiredChildNodes
.isEmpty() ||
723 featureNode
.getTerm() != null && existingFeatureUuids
.contains(featureNode
.getTerm().getUuid())) {
724 TreeNode
<Feature
,UUID
> result
= new TreeNode
<>();
725 //add this nodes data
726 Feature feature
= featureNode
.getTerm() == null ?
null : featureNode
.getTerm();
727 if (feature
!= null) {
728 result
.setNodeId(feature
.getUuid());
729 result
.setData(feature
);
732 requiredChildNodes
.stream().forEachOrdered(c
->result
.addChild(c
));
740 * Computes the (unsorted) set of features for which facts exist
741 * for the given taxon.
743 private Map
<UUID
, Feature
> getExistingFeatureUuids(Taxon taxon
) {
744 Map
<UUID
, Feature
> result
= new HashMap
<>();
745 for (TaxonDescription taxonDescription
: taxon
.getDescriptions()) {
746 if (taxonDescription
.isImageGallery()) {
749 for (DescriptionElementBase deb
: taxonDescription
.getElements()) {
750 Feature feature
= deb
.getFeature();
751 if (feature
!= null) { //null should not happen
752 result
.put(feature
.getUuid(), feature
);
759 private void handleDistributions(TaxonPageDtoConfiguration config
, FeatureDto featureDto
,
760 Taxon taxon
, List
<Distribution
> distributions
) {
762 if (distributions
.isEmpty()) {
765 IDistributionService distributionService
= repository
.getDistributionService();
768 DistributionInfoConfiguration distributionConfig
= config
.getDistributionInfoConfiguration();
769 CondensedDistributionConfiguration condensedConfig
= distributionConfig
.getCondensedDistrConfig();
771 String statusColorsString
= distributionConfig
.getStatusColorsString();
774 //copied from DescriptionListController
776 boolean ignoreDistributionStatusUndefined
= true; //workaround until #9500 is fully implemented
777 distributionConfig
.setIgnoreDistributionStatusUndefined(ignoreDistributionStatusUndefined
);
778 boolean fallbackAsParent
= true; //may become a service parameter in future
780 DistributionInfoDto dto
;
782 //hiddenArea markers include markers for fully hidden areas and fallback areas. The later
783 //are hidden markers on areas that have non-hidden subareas (#4408)
784 Set
<MarkerType
> hiddenAreaMarkerTypes
= distributionConfig
.getHiddenAreaMarkerTypeList();
785 if(hiddenAreaMarkerTypes
!= null && !hiddenAreaMarkerTypes
.isEmpty()){
786 condensedConfig
.hiddenAndFallbackAreaMarkers
= hiddenAreaMarkerTypes
.stream().map(mt
->mt
.getUuid()).collect(Collectors
.toSet());
789 List
<String
> initStrategy
= null;
791 Map
<PresenceAbsenceTerm
, Color
> distributionStatusColors
;
793 distributionStatusColors
= DistributionServiceUtilities
.buildStatusColorMap(
794 statusColorsString
, repository
.getTermService(), repository
.getVocabularyService());
795 } catch (JsonProcessingException e
) {
796 logger
.error("JsonProcessingException when reading distribution status colors");
797 //TODO is null allowed?
798 distributionStatusColors
= null;
801 dto
= distributionService
.composeDistributionInfoFor(distributionConfig
, taxon
.getUuid(),
803 distributionStatusColors
, LocaleContext
.getLanguages(),
806 if (distributionConfig
.isUseTreeDto() && dto
.getTree() != null) {
807 DistributionTreeDto tree
= (DistributionTreeDto
)dto
.getTree();
808 TreeNode
<Set
<DistributionDto
>, NamedAreaDto
> root
= tree
.getRootElement();
809 //fill uuid->distribution map
810 Map
<UUID
,Distribution
> distributionMap
= new HashMap
<>();
811 distributions
.stream().forEach(d
->distributionMap
.put(d
.getUuid(), d
));
812 handleDistributionDtoNode(distributionMap
, root
);
815 featureDto
.addFact(dto
);
818 private void handleDistributionDtoNode(Map
<UUID
, Distribution
> map
,
819 TreeNode
<Set
<DistributionDto
>, NamedAreaDto
> root
) {
820 if (root
.getData() != null) {
821 root
.getData().stream().forEach(d
->{
822 Distribution distr
= map
.get(d
.getUuid());
823 loadBaseData(distr
, d
);
824 d
.setTimeperiod(distr
.getTimeperiod() == null ?
null : distr
.getTimeperiod().toString());
829 if (root
.getChildren() != null) {
830 root
.getChildren().stream().forEach(c
->handleDistributionDtoNode(map
, c
));
834 private FactDtoBase
handleFact(FeatureDto featureDto
, DescriptionElementBase fact
) {
836 Language localeLang
= null;
839 if (fact
.isInstanceOf(TextData
.class)) {
840 TextData td
= CdmBase
.deproxy(fact
, TextData
.class);
841 LanguageString ls
= td
.getPreferredLanguageString(localeLang
);
842 String text
= ls
== null ?
"" : CdmUtils
.Nz(ls
.getText());
844 FactDto factDto
= new FactDto();
845 featureDto
.addFact(factDto
);
846 //TODO do we really need type information for textdata here?
847 TypedLabel typedLabel
= new TypedLabel(text
);
848 typedLabel
.setClassAndId(td
);
849 factDto
.getTypedLabel().add(typedLabel
);
850 loadBaseData(td
, factDto
);
853 }else if (fact
.isInstanceOf(CommonTaxonName
.class)) {
854 CommonTaxonName ctn
= CdmBase
.deproxy(fact
, CommonTaxonName
.class);
855 CommonNameDto dto
= new CommonNameDto();
856 featureDto
.addFact(dto
);
858 Language lang
= ctn
.getLanguage();
860 String langLabel
= getTermLabel(lang
, localeLang
);
861 dto
.setLanguage(langLabel
);
862 dto
.setLanguageUuid(lang
.getUuid());
865 dto
.setLanguage("-");
868 NamedArea area
= ctn
.getArea();
870 String areaLabel
= getTermLabel(area
, localeLang
);
871 dto
.setArea(areaLabel
);
872 dto
.setAreaUUID(area
.getUuid());
874 dto
.setName(ctn
.getName());
875 loadBaseData(ctn
, dto
);
876 //TODO sort all common names
878 } else if (fact
.isInstanceOf(IndividualsAssociation
.class)) {
879 IndividualsAssociation ia
= CdmBase
.deproxy(fact
, IndividualsAssociation
.class);
880 IndividualsAssociationDto dto
= new IndividualsAssociationDto ();
882 LanguageString description
= MultilanguageTextHelper
.getPreferredLanguageString(ia
.getDescription(), Arrays
.asList(localeLang
));
883 if (description
!= null) {
884 dto
.setDescritpion(description
.getText());
886 SpecimenOrObservationBase
<?
> specimen
= ia
.getAssociatedSpecimenOrObservation();
887 if (specimen
!= null) {
888 //TODO what to use here??
889 dto
.setOccurrence(specimen
.getTitleCache());
890 dto
.setOccurrenceUuid(specimen
.getUuid());
893 featureDto
.addFact(dto
);
894 loadBaseData(ia
, dto
);
896 } else if (fact
.isInstanceOf(TaxonInteraction
.class)) {
897 TaxonInteraction ti
= CdmBase
.deproxy(fact
, TaxonInteraction
.class);
898 TaxonInteractionDto dto
= new TaxonInteractionDto ();
900 LanguageString description
= MultilanguageTextHelper
.getPreferredLanguageString(
901 ti
.getDescription(), Arrays
.asList(localeLang
));
902 if (description
!= null) {
903 dto
.setDescritpion(description
.getText());
905 Taxon taxon
= ti
.getTaxon2();
907 //TODO what to use here??
908 dto
.setTaxon(taxon
.cacheStrategy().getTaggedTitle(taxon
));
909 dto
.setTaxonUuid(taxon
.getUuid());
911 featureDto
.addFact(dto
);
912 loadBaseData(ti
, dto
);
914 }else if (fact
.isInstanceOf(CategoricalData
.class)) {
915 CategoricalData cd
= CdmBase
.deproxy(fact
, CategoricalData
.class);
916 FactDto factDto
= new FactDto();
917 featureDto
.addFact(factDto
);
918 //TODO do we really need type information for textdata here?
919 String label
= CategoricalDataFormatter
.NewInstance(null).format(cd
, localeLang
);
920 TypedLabel typedLabel
= new TypedLabel(label
);
921 typedLabel
.setClassAndId(cd
);
922 factDto
.getTypedLabel().add(typedLabel
);
924 loadBaseData(cd
, factDto
);
926 }else if (fact
.isInstanceOf(QuantitativeData
.class)) {
927 QuantitativeData qd
= CdmBase
.deproxy(fact
, QuantitativeData
.class);
928 FactDto factDto
= new FactDto();
929 featureDto
.addFact(factDto
);
930 //TODO do we really need type information for textdata here?
931 String label
= QuantitativeDataFormatter
.NewInstance(null).format(qd
, localeLang
);
932 TypedLabel typedLabel
= new TypedLabel(label
);
933 typedLabel
.setClassAndId(qd
);
934 factDto
.getTypedLabel().add(typedLabel
);
936 loadBaseData(qd
, factDto
);
938 }else if (fact
.isInstanceOf(TemporalData
.class)) {
939 TemporalData td
= CdmBase
.deproxy(fact
, TemporalData
.class);
940 FactDto factDto
= new FactDto();
941 featureDto
.addFact(factDto
);
942 //TODO do we really need type information for textdata here?
943 String label
= td
.toString();
944 TypedLabel typedLabel
= new TypedLabel(label
);
945 typedLabel
.setClassAndId(td
);
946 factDto
.getTypedLabel().add(typedLabel
);
948 loadBaseData(td
, factDto
);
952 logger
.warn("DescriptionElement type not yet handled: " + fact
.getClass().getSimpleName());
955 result
.setTimeperiod(fact
.getTimeperiod() == null ?
null : fact
.getTimeperiod().toString());
959 private String
getTermLabel(TermBase term
, Language localeLang
) {
963 Representation rep
= term
.getPreferredRepresentation(localeLang
);
964 String label
= rep
== null ?
null : rep
.getLabel();
965 label
= label
== null ? term
.getLabel() : label
;
970 * Compares an existing last date and the last date of an entity
971 * and returns the resulting last date.
973 private LocalDateTime
getLastUpdated(LocalDateTime existingLastDate
, VersionableEntity dateToAddEntity
) {
975 DateTime dateToAdd
= dateToAddEntity
.getUpdated() != null ? dateToAddEntity
.getUpdated() : dateToAddEntity
.getCreated();
977 LocalDateTime javaLocalDateTimeOfEntity
= dateToAdd
== null ?
null:
978 LocalDateTime
.of(dateToAdd
.getYear(), dateToAdd
.getMonthOfYear(),
979 dateToAdd
.getDayOfMonth(), dateToAdd
.getHourOfDay(),
980 dateToAdd
.getMinuteOfHour(), dateToAdd
.getSecondOfMinute());
982 if (existingLastDate
== null) {
983 return javaLocalDateTimeOfEntity
;
984 }else if (javaLocalDateTimeOfEntity
== null || javaLocalDateTimeOfEntity
.compareTo(existingLastDate
) < 0) {
985 return existingLastDate
;
987 return javaLocalDateTimeOfEntity
;
991 private void loadBaseData(CdmBase cdmBase
, CdmBaseDto dto
) {
992 dto
.setId(cdmBase
.getId());
993 dto
.setUuid(cdmBase
.getUuid());
995 loadAnnotatable(cdmBase
, dto
);
996 loadSources(cdmBase
, dto
);
997 //loadIdentifiable(cdmBase, dto);
1000 private void loadSources(CdmBase cdmBase
, CdmBaseDto dto
) {
1001 if (dto
instanceof SingleSourcedDto
&& cdmBase
.isInstanceOf(SingleSourcedEntityBase
.class)) {
1002 //TODO other sourced
1003 SingleSourcedEntityBase sourced
= CdmBase
.deproxy(cdmBase
, SingleSourcedEntityBase
.class);
1004 SingleSourcedDto sourcedDto
= (SingleSourcedDto
)dto
;
1005 NamedSource source
= sourced
.getSource();
1006 SourceDto sourceDto
= new SourceDto();
1007 loadSource(source
, sourceDto
);
1008 sourcedDto
.setSource(sourceDto
);
1009 } else if (dto
instanceof SourcedDto
&& cdmBase
instanceof ISourceable
) {
1010 @SuppressWarnings("unchecked")
1011 ISourceable
<OriginalSourceBase
> sourced
= (ISourceable
<OriginalSourceBase
>)cdmBase
;
1012 SourcedDto sourcedDto
= (SourcedDto
)dto
;
1013 for (OriginalSourceBase source
: sourced
.getSources()) {
1014 SourceDto sourceDto
= new SourceDto();
1015 loadSource(source
, sourceDto
);
1016 sourcedDto
.addSource(sourceDto
);
1021 private void loadSource(OriginalSourceBase source
, SourceDto sourceDto
) {
1023 source
= CdmBase
.deproxy(source
);
1025 loadBaseData(source
, sourceDto
);
1027 ICdmBase linkedObject
= source
.getCitation();
1028 if (linkedObject
== null) {
1030 linkedObject
= source
.getCdmSource();
1033 //citation uuid & doi
1034 if (source
.getCitation()!= null) {
1035 sourceDto
.setDoi(source
.getCitation().getDoiString());
1039 //TODO this creates only the short form and probably also does not use
1040 // specimen or cdmSource if necessary
1041 String label
= OriginalSourceFormatter
.INSTANCE
.format(source
);
1042 TypedLabel typedLabel
= new TypedLabel(source
, label
);
1043 sourceDto
.addLabel(typedLabel
);
1044 sourceDto
.setType(source
.getType() != null ? source
.getType().toString() : null);
1046 if (source
.isInstanceOf(NamedSourceBase
.class)) {
1047 NamedSourceBase ns
= CdmBase
.deproxy(source
, NamedSourceBase
.class);
1050 TaxonName name
= ns
.getNameUsedInSource();
1052 List
<TaggedText
> taggedName
= name
.cacheStrategy().getTaggedTitle(name
);
1054 sourceDto
.setNameInSource(taggedName
);
1055 sourceDto
.setNameInSourceUuid(name
.getUuid());
1059 if (source
.isInstanceOf(DescriptionElementSource
.class)) {
1060 DescriptionElementSource des
= CdmBase
.deproxy(source
, DescriptionElementSource
.class);
1061 if (linkedObject
== null) {
1062 linkedObject
= des
.getSpecimen();
1067 sourceDto
.setLinkedUuid(getUuid(linkedObject
));
1068 String linkedObjectStr
= linkedObject
== null ?
null : CdmBase
.deproxy(linkedObject
).getClass().getSimpleName();
1069 sourceDto
.setLinkedClass(linkedObjectStr
);
1072 private UUID
getUuid(ICdmBase cdmBase
) {
1073 return cdmBase
== null ?
null : cdmBase
.getUuid();
1076 private void loadAnnotatable(CdmBase cdmBase
, CdmBaseDto dto
) {
1077 if (dto
instanceof AnnotatableDto
&& cdmBase
.isInstanceOf(AnnotatableEntity
.class)) {
1078 AnnotatableEntity annotatable
= CdmBase
.deproxy(cdmBase
, AnnotatableEntity
.class);
1079 AnnotatableDto annotatableDto
= (AnnotatableDto
)dto
;
1081 for (Annotation annotation
: annotatable
.getAnnotations()) {
1082 if (annotation
.getAnnotationType() != null
1083 //TODO annotation type filter
1084 && annotation
.getAnnotationType().getUuid().equals(AnnotationType
.uuidEditorial
)
1085 && StringUtils
.isNotBlank(annotation
.getText())) {
1087 AnnotationDto annotationDto
= new AnnotationDto();
1088 annotatableDto
.addAnnotation(annotationDto
);
1089 //TODO id needed? but need to adapt dto and container then
1090 loadBaseData(annotation
, annotationDto
);
1091 annotationDto
.setText(annotation
.getText());
1092 UUID uuidAnnotationType
= annotation
.getAnnotationType() == null ?
null :annotation
.getAnnotationType().getUuid();
1093 annotationDto
.setTypeUuid(uuidAnnotationType
);
1094 //language etc. currently not yet used
1099 for (Marker marker
: annotatable
.getMarkers()) {
1100 if (marker
.getMarkerType() != null
1101 //TODO markertype filter
1102 // && marker.getMarkerType().getUuid().equals(AnnotationType.uuidEditorial)
1105 MarkerDto markerDto
= new MarkerDto();
1106 annotatableDto
.addMarker(markerDto
);
1107 //TODO id needed? but need to adapt dto and container then
1108 loadBaseData(marker
, markerDto
);
1109 if (marker
.getMarkerType() != null) {
1110 markerDto
.setTypeUuid(marker
.getMarkerType().getUuid());
1112 markerDto
.setType(marker
.getMarkerType().getTitleCache());
1114 markerDto
.setValue(marker
.getValue());