ref #10222 rename factualData to taxonFacts
[cdmlib.git] / cdmlib-services / src / main / java / eu / etaxonomy / cdm / api / service / portal / PortalDtoLoader.java
1 /**
2 * Copyright (C) 2023 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
5 *
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.
8 */
9 package eu.etaxonomy.cdm.api.service.portal;
10
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;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.UUID;
22 import java.util.stream.Collectors;
23
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;
28
29 import com.fasterxml.jackson.core.JsonProcessingException;
30
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.NamedAreaDto;
48 import eu.etaxonomy.cdm.api.dto.portal.SingleSourcedDto;
49 import eu.etaxonomy.cdm.api.dto.portal.SourceDto;
50 import eu.etaxonomy.cdm.api.dto.portal.SourcedDto;
51 import eu.etaxonomy.cdm.api.dto.portal.TaxonBaseDto;
52 import eu.etaxonomy.cdm.api.dto.portal.TaxonInteractionDto;
53 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto;
54 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.ConceptRelationDTO;
55 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.HomotypicGroupDTO;
56 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.KeyDTO;
57 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.MediaDTO;
58 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.MediaRepresentationDTO;
59 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.NameRelationDTO;
60 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.SpecimenDTO;
61 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.TaxonNodeAgentsRelDTO;
62 import eu.etaxonomy.cdm.api.dto.portal.TaxonPageDto.TaxonNodeDTO;
63 import eu.etaxonomy.cdm.api.dto.portal.config.DistributionInfoConfiguration;
64 import eu.etaxonomy.cdm.api.dto.portal.config.TaxonPageDtoConfiguration;
65 import eu.etaxonomy.cdm.api.service.geo.DistributionServiceUtilities;
66 import eu.etaxonomy.cdm.api.service.geo.IDistributionService;
67 import eu.etaxonomy.cdm.api.service.l10n.LocaleContext;
68 import eu.etaxonomy.cdm.api.service.name.TypeDesignationSetContainer;
69 import eu.etaxonomy.cdm.api.service.name.TypeDesignationSetFormatter;
70 import eu.etaxonomy.cdm.api.service.pager.Pager;
71 import eu.etaxonomy.cdm.common.CdmUtils;
72 import eu.etaxonomy.cdm.common.TreeNode;
73 import eu.etaxonomy.cdm.compare.taxon.TaxonComparator;
74 import eu.etaxonomy.cdm.format.common.TypedLabel;
75 import eu.etaxonomy.cdm.format.description.CategoricalDataFormatter;
76 import eu.etaxonomy.cdm.format.description.QuantitativeDataFormatter;
77 import eu.etaxonomy.cdm.format.description.distribution.CondensedDistributionConfiguration;
78 import eu.etaxonomy.cdm.format.reference.OriginalSourceFormatter;
79 import eu.etaxonomy.cdm.format.taxon.TaxonRelationshipFormatter;
80 import eu.etaxonomy.cdm.model.common.AnnotatableEntity;
81 import eu.etaxonomy.cdm.model.common.Annotation;
82 import eu.etaxonomy.cdm.model.common.AnnotationType;
83 import eu.etaxonomy.cdm.model.common.CdmBase;
84 import eu.etaxonomy.cdm.model.common.ICdmBase;
85 import eu.etaxonomy.cdm.model.common.Language;
86 import eu.etaxonomy.cdm.model.common.LanguageString;
87 import eu.etaxonomy.cdm.model.common.Marker;
88 import eu.etaxonomy.cdm.model.common.MarkerType;
89 import eu.etaxonomy.cdm.model.common.MultilanguageTextHelper;
90 import eu.etaxonomy.cdm.model.common.SingleSourcedEntityBase;
91 import eu.etaxonomy.cdm.model.common.VersionableEntity;
92 import eu.etaxonomy.cdm.model.description.CategoricalData;
93 import eu.etaxonomy.cdm.model.description.CommonTaxonName;
94 import eu.etaxonomy.cdm.model.description.DescriptionBase;
95 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
96 import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
97 import eu.etaxonomy.cdm.model.description.Distribution;
98 import eu.etaxonomy.cdm.model.description.Feature;
99 import eu.etaxonomy.cdm.model.description.IDescribable;
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.ExternalLink;
110 import eu.etaxonomy.cdm.model.media.ImageFile;
111 import eu.etaxonomy.cdm.model.media.Media;
112 import eu.etaxonomy.cdm.model.media.MediaRepresentation;
113 import eu.etaxonomy.cdm.model.media.MediaRepresentationPart;
114 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
115 import eu.etaxonomy.cdm.model.name.NameRelationship;
116 import eu.etaxonomy.cdm.model.name.NameRelationshipType;
117 import eu.etaxonomy.cdm.model.name.NomenclaturalSource;
118 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
119 import eu.etaxonomy.cdm.model.name.TaxonName;
120 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
121 import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
122 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
123 import eu.etaxonomy.cdm.model.reference.ISourceable;
124 import eu.etaxonomy.cdm.model.reference.NamedSource;
125 import eu.etaxonomy.cdm.model.reference.NamedSourceBase;
126 import eu.etaxonomy.cdm.model.reference.OriginalSourceBase;
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.taxon.TaxonBaseDefaultCacheStrategy;
142
143 /**
144 * Loads the portal dto from a taxon instance.
145 * Maybe later also supports loading from persistence.
146 *
147 * @author a.mueller
148 * @date 09.01.2023
149 */
150 public class PortalDtoLoader {
151
152 private static final Logger logger = LogManager.getLogger();
153
154 private ICdmRepository repository;
155
156 public PortalDtoLoader(ICdmRepository repository) {
157 this.repository = repository;
158 }
159
160 public TaxonPageDto load(Taxon taxon, TaxonPageDtoConfiguration config) {
161 TaxonPageDto result = new TaxonPageDto();
162
163 loadAcceptedTaxon(taxon, config, result);
164
165 loadTaxonNodes(taxon, result, config);
166 loadSynonyms(taxon, result, config);
167 loadConceptRelations(taxon, result, config);
168 loadFacts(taxon, result, config);
169 loadMedia(taxon, result, config);
170 loadSpecimens(taxon, result, config);
171 loadKeys(taxon, result, config);
172
173 return result;
174 }
175
176 private void loadAcceptedTaxon(Taxon taxon, TaxonPageDtoConfiguration config, TaxonPageDto result) {
177 try {
178 TaxonName name = taxon.getName();
179
180 //load 1:1
181 //TODO supplementalData for name
182 loadBaseData(taxon, result);
183 result.setLastUpdated(getLastUpdated(null, taxon));
184 result.setLabel(CdmUtils.Nz(taxon.getTitleCache()));
185 // result.setTypedTaxonLabel(getTypedTaxonLabel(taxon, config));
186 result.setTaggedLabel(getTaggedTaxon(taxon, config));
187 if (name != null) {
188 handleName(config, result, name, result);
189 }
190 } catch (Exception e) {
191 //e.printStackTrace();
192 result.addMessage(MessagesDto.NewErrorInstance("Error when loading accepted name data.", e));
193 }
194 }
195
196 private void handleName(TaxonPageDtoConfiguration config, TaxonBaseDto nameDto, TaxonName name, TaxonPageDto pageDto) {
197 nameDto.setNameLabel(name.getTitleCache());
198 handleRelatedNames(name, nameDto, config);
199 loadProtologues(name, nameDto);
200 nameDto.setNameUuid(name.getUuid());
201 nameDto.setNameType(name.getNameType().toString());
202 loadNameFacts(name, nameDto, config, pageDto);
203 nameDto.setTaggedName(name.getTaggedFullTitle());
204 }
205
206 private List<TaggedText> getTaggedTaxon(TaxonBase<?> taxon, TaxonPageDtoConfiguration config) {
207 // List<TypedLabel> result = new ArrayList<>();
208 TaxonBaseDefaultCacheStrategy<TaxonBase<?>> formatter = new TaxonBaseDefaultCacheStrategy<>();
209 List<TaggedText> tags = formatter.getTaggedTitle(taxon);
210 return tags;
211 }
212
213 private void loadKeys(Taxon taxon, TaxonPageDto result, TaxonPageDtoConfiguration config) {
214 try {
215 ContainerDto<KeyDTO> container = new ContainerDto<>();
216
217 //TODO other key types, but type must not be null, otherwise NPE
218 Pager<PolytomousKey> keys = repository.getIdentificationKeyService().findKeysConvering(taxon, PolytomousKey.class, null, null, null);
219 for (PolytomousKey key : keys.getRecords()) {
220 KeyDTO dto = new KeyDTO();
221 loadBaseData(key, dto);
222 dto.setLabel(key.getTitleCache());
223 dto.setKeyClass(key.getClass().getSimpleName());
224 container.addItem(dto);
225 }
226 if (container.getCount() > 0) {
227 result.setKeys(container);
228 }
229 } catch (Exception e) {
230 //e.printStackTrace();
231 result.addMessage(MessagesDto.NewErrorInstance("Error when loading identification key data.", e));
232 }
233 }
234
235 private void loadSpecimens(Taxon taxon, TaxonPageDto result, TaxonPageDtoConfiguration config) {
236 //TODO load specimen from multiple places
237
238 try {
239 ContainerDto<SpecimenDTO> container = new ContainerDto<>();
240
241 List<SpecimenOrObservationBase<?>> specimens = new ArrayList<>();
242 for (TaxonDescription taxonDescription : taxon.getDescriptions()) {
243 if (taxonDescription.isImageGallery()) {
244 continue;
245 }
246 for (DescriptionElementBase el : taxonDescription.getElements()) {
247 if (el.isInstanceOf(IndividualsAssociation.class)) {
248 IndividualsAssociation indAss = CdmBase.deproxy(el, IndividualsAssociation.class);
249 SpecimenOrObservationBase<?> specimen = indAss.getAssociatedSpecimenOrObservation();
250 specimens.add(specimen);
251 }
252 }
253 }
254 List<SpecimenOrObservationBase<?>> typeSpecimens = loadTypeSpecimen(taxon.getName(), config);
255 specimens.addAll(typeSpecimens);
256 for (TaxonName syn : taxon.getSynonymNames()) {
257 typeSpecimens = loadTypeSpecimen(syn, config);
258 specimens.addAll(typeSpecimens);
259 }
260
261 for (SpecimenOrObservationBase<?> specimen : specimens) {
262 SpecimenDTO dto = new SpecimenDTO();
263 loadBaseData(specimen, dto);
264 dto.setLabel(specimen.getTitleCache());
265 container.addItem(dto);
266 }
267 if (container.getCount() > 0 ) {
268 result.setSpecimens(container);
269 }
270 } catch (Exception e) {
271 //e.printStackTrace();
272 result.addMessage(MessagesDto.NewErrorInstance("Error when loading specimen data.", e));
273 }
274 }
275
276 private List<SpecimenOrObservationBase<?>> loadTypeSpecimen(TaxonName name, TaxonPageDtoConfiguration config) {
277 List<SpecimenOrObservationBase<?>> result = new ArrayList<>();
278 for (SpecimenTypeDesignation desig: name.getSpecimenTypeDesignations()){
279 DerivedUnit specimen = desig.getTypeSpecimen();
280 if (specimen != null) {
281 result.add(specimen);
282 }
283 }
284 return result;
285 }
286
287 private void loadMedia(Taxon taxon, TaxonPageDto result, TaxonPageDtoConfiguration config) {
288
289 try {
290 ContainerDto<MediaDTO> container = new ContainerDto<TaxonPageDto.MediaDTO>();
291
292 List<Media> medias = new ArrayList<>();
293 for (TaxonDescription taxonDescription : taxon.getDescriptions()) {
294 if (!taxonDescription.isImageGallery()) {
295 continue;
296 }
297
298 List<Media> newMedia = taxonDescription.getElements().stream()
299 .filter(el->el.isInstanceOf(TextData.class))
300 .map(el->CdmBase.deproxy(el, TextData.class))
301 .filter(td->true)
302 .flatMap(td->td.getMedia().stream())
303 .collect(Collectors.toList())
304 ;
305 medias.addAll(newMedia);
306 }
307 //TODO collect media from elsewhere
308 for (Media media : medias) {
309 MediaDTO dto = new TaxonPageDto.MediaDTO();
310 loadBaseData(media, dto);
311 dto.setLabel(media.getTitleCache());
312 ContainerDto<MediaRepresentationDTO> representations = new ContainerDto<>();
313 for (MediaRepresentation rep : media.getRepresentations()) {
314 MediaRepresentationDTO repDto = new MediaRepresentationDTO();
315 loadBaseData(rep, dto);
316 repDto.setMimeType(rep.getMimeType());
317 repDto.setSuffix(rep.getSuffix());
318 if (!rep.getParts().isEmpty()) {
319 //TODO handle message if n(parts) > 1
320 MediaRepresentationPart part = rep.getParts().get(0);
321 repDto.setUri(part.getUri());
322 repDto.setClazz(part.getClass());
323 repDto.setSize(part.getSize());
324 if (part.isInstanceOf(ImageFile.class)) {
325 ImageFile image = CdmBase.deproxy(part, ImageFile.class);
326 repDto.setHeight(image.getHeight());
327 repDto.setWidth(image.getWidth());
328 }
329 //TODO AudioFile etc.
330 }
331 representations.addItem(repDto);
332 }
333 if (representations.getCount() > 0) {
334 dto.setRepresentations(representations);
335 }
336 //TODO load representation data
337 container.addItem(dto);
338 }
339
340 if (container.getCount() > 0) {
341 result.setMedia(container);
342 }
343 } catch (Exception e) {
344 //e.printStackTrace();
345 result.addMessage(MessagesDto.NewErrorInstance("Error when loading media data.", e));
346 }
347 }
348
349 private void loadTaxonNodes(Taxon taxon, TaxonPageDto result, TaxonPageDtoConfiguration config) {
350 try {
351 ContainerDto<TaxonNodeDTO> container = new ContainerDto<TaxonPageDto.TaxonNodeDTO>();
352 for (TaxonNode node : taxon.getTaxonNodes()) {
353 TaxonNodeDTO dto = new TaxonNodeDTO();
354 loadBaseData(node, dto);
355 //classification
356 Classification classification = node.getClassification();
357 if (classification != null) {
358 dto.setClassificationUuid(node.getClassification().getUuid());
359 dto.setClassificationLabel(classification.getName().getText());
360 }
361 //TODO lang/locale
362 Language language = Language.DEFAULT();
363
364 //status
365 TaxonNodeStatus status = node.getStatus();
366 if (status != null) {
367 dto.setStatus(status.getLabel(language));
368 }
369 //statusNote
370 Map<Language, LanguageString> statusNote = node.getStatusNote();
371 if (statusNote != null) {
372 //TODO handle fallback lang
373 LanguageString statusNoteStr = statusNote.get(language);
374 if (statusNoteStr == null && statusNote.size() > 0) {
375 statusNoteStr = statusNote.entrySet().iterator().next().getValue();
376 }
377 if (statusNoteStr != null) {
378 dto.setStatusNote(statusNoteStr.getText());
379 }
380 }
381 //agent relations
382 Set<TaxonNodeAgentRelation> agents = node.getAgentRelations();
383 if (!agents.isEmpty()) {
384 for (TaxonNodeAgentRelation rel : agents) {
385 TaxonNodeAgentsRelDTO agentDto = new TaxonNodeAgentsRelDTO();
386 loadBaseData(rel, agentDto);
387
388 //TODO laod
389 if (rel.getAgent() != null) {
390 agentDto.setAgent(rel.getAgent().getFullTitle());
391 agentDto.setAgentUuid(rel.getAgent().getUuid());
392 //TODO compute preferred external link
393 agentDto.setAgentLink(null);
394 }
395 if (rel.getType() != null) {
396 agentDto.setType(rel.getType().getTitleCache());
397 agentDto.setTypeUuid(rel.getType().getUuid());
398 }
399 dto.addAgent(agentDto);
400 }
401 }
402 container.addItem(dto);
403 }
404 if (container.getCount() > 0) {
405 result.setTaxonNodes(container);
406 }
407 } catch (Exception e) {
408 // e.printStackTrace();
409 result.addMessage(MessagesDto.NewErrorInstance("Error when loading taxon node data.", e));
410 }
411 }
412
413 private void loadSynonyms(Taxon taxon, TaxonPageDto result, TaxonPageDtoConfiguration config) {
414
415 try {
416 // List<HomotypicalGroup> homotypicGroups = taxon.getHomotypicSynonymyGroups();
417
418 TaxonComparator comparator = new TaxonComparator();
419
420 TaxonName name = taxon.getName();
421
422 //TODO depending on config add/remove accepted name
423
424 //TODO check publish flag
425
426 //homotypic synonyms
427 List<Synonym> homotypicSynonmys = taxon.getHomotypicSynonymsByHomotypicGroup(comparator);
428 TaxonPageDto.HomotypicGroupDTO homotypicGroupDto = new TaxonPageDto.HomotypicGroupDTO();
429 if (homotypicSynonmys != null && !homotypicSynonmys.isEmpty()) {
430 loadBaseData(name.getHomotypicalGroup(), homotypicGroupDto);
431
432 for (Synonym syn : homotypicSynonmys) {
433 loadSynonymsInGroup(homotypicGroupDto, syn, config, result);
434 }
435 }
436 if (name != null) {
437 handleTypification(name.getHomotypicalGroup(), homotypicGroupDto, result, config);
438 }
439 result.setHomotypicSynonyms(homotypicGroupDto);
440
441 //heterotypic synonyms
442 List<HomotypicalGroup> heteroGroups = taxon.getHeterotypicSynonymyGroups();
443 if (heteroGroups.isEmpty()) {
444 return;
445 }
446 ContainerDto<HomotypicGroupDTO> heteroContainer = new ContainerDto<>();
447 result.setHeterotypicSynonymGroups(heteroContainer);
448
449 for (HomotypicalGroup hg : heteroGroups) {
450 TaxonPageDto.HomotypicGroupDTO hgDto = new TaxonPageDto.HomotypicGroupDTO();
451 loadBaseData(taxon.getName().getHomotypicalGroup(), hgDto);
452 heteroContainer.addItem(hgDto);
453
454 List<Synonym> heteroSyns = taxon.getSynonymsInGroup(hg, comparator);
455 for (Synonym syn : heteroSyns) {
456 loadSynonymsInGroup(hgDto, syn, config, result);
457 }
458 handleTypification(hg, hgDto, result, config);
459 }
460 } catch (Exception e) {
461 //e.printStackTrace();
462 result.addMessage(MessagesDto.NewErrorInstance("Error when loading synonym data.", e));
463 }
464 }
465
466 private void handleTypification(HomotypicalGroup homotypicalGroup, HomotypicGroupDTO hgDto,
467 TaxonPageDto result, TaxonPageDtoConfiguration config) {
468
469 boolean withCitation = true;
470 boolean withStartingTypeLabel = true;
471 boolean withNameIfAvailable = false;
472 TypeDesignationSetFormatter formatter = new TypeDesignationSetFormatter(
473 withCitation, withStartingTypeLabel, withNameIfAvailable);
474 Set<TypeDesignationBase<?>> desigs = homotypicalGroup.getTypeDesignations();
475 try {
476 TypeDesignationSetContainer manager = TypeDesignationSetContainer.NewDefaultInstance((Set)desigs);
477 List<TaggedText> tags = formatter.toTaggedText(manager);
478 String label = TaggedCacheHelper.createString(tags);
479 hgDto.setTypes(label);
480 hgDto.setTaggedTypes(tags);
481 // hgDto.setTypedTypes(null);
482
483 } catch (Exception e) {
484 // e.printStackTrace();
485 result.addMessage(MessagesDto.NewErrorInstance("Error when creating type designation information", e));
486 }
487 }
488
489 private void loadConceptRelations(Taxon taxon, TaxonPageDto result, TaxonPageDtoConfiguration config) {
490
491 try {
492 //concept relations
493 ContainerDto<ConceptRelationDTO> conceptRelContainer = new ContainerDto<>();
494 TaxonRelationshipFormatter taxRelFormatter = TaxonRelationshipFormatter.INSTANCE();
495
496 //... MAN
497 Set<TaxonRelationship> misappliedRels = taxon.getMisappliedNameRelations();
498 for (TaxonRelationship rel : misappliedRels) {
499 boolean inverse = true;
500 boolean withoutName = false;
501 loadConceptRelation(taxRelFormatter, rel, conceptRelContainer, inverse, withoutName);
502 }
503
504 //... pro parte Synonyms
505 Set<TaxonRelationship> proParteRels = taxon.getProParteAndPartialSynonymRelations();
506 for (TaxonRelationship rel : proParteRels) {
507 boolean inverse = true;
508 boolean withoutName = false;
509 loadConceptRelation(taxRelFormatter, rel, conceptRelContainer, inverse, withoutName);
510 }
511
512 //TODO MAN and pp from this taxon
513
514 //... to-relations
515 Set<TaxonRelationship> toRels = taxon.getRelationsToThisTaxon();
516 toRels.removeAll(misappliedRels);
517 toRels.removeAll(proParteRels);
518 for (TaxonRelationship rel : toRels) {
519 boolean inverse = true;
520 boolean withoutName = false;
521 loadConceptRelation(taxRelFormatter, rel, conceptRelContainer, inverse, withoutName);
522 }
523
524 //... from-relations
525 Set<TaxonRelationship> fromRels = taxon.getRelationsFromThisTaxon();
526 for (TaxonRelationship rel : fromRels) {
527 boolean inverse = false;
528 boolean withoutName = false;
529 loadConceptRelation(taxRelFormatter, rel, conceptRelContainer, inverse, withoutName);
530 }
531
532 if (conceptRelContainer.getCount() > 0) {
533 result.setConceptRelations(conceptRelContainer);
534 }
535 } catch (Exception e) {
536 //e.printStackTrace();
537 result.addMessage(MessagesDto.NewErrorInstance("Error when loading concept relation data.", e));
538 }
539 }
540
541 private void loadConceptRelation(TaxonRelationshipFormatter taxRelFormatter, TaxonRelationship rel, ContainerDto<ConceptRelationDTO> conceptRelContainer, boolean inverse,
542 boolean withoutName) {
543 List<Language> languages = Arrays.asList(new Language[] {Language.DEFAULT()}); // TODO config.locales;
544 List<TaggedText> tags = taxRelFormatter.getTaggedText(rel, inverse, languages, withoutName);
545 String relLabel = TaggedCacheHelper.createString(tags);
546 ConceptRelationDTO dto = new TaxonPageDto.ConceptRelationDTO();
547 loadBaseData(rel, dto);
548 Taxon relTaxon = inverse ? rel.getFromTaxon() : rel.getToTaxon();
549 dto.setRelTaxonId(relTaxon.getId());
550 dto.setRelTaxonUuid(relTaxon.getUuid());
551 dto.setRelTaxonLabel(relTaxon.getTitleCache());
552 dto.setLabel(relLabel);
553 if (rel.getType() != null) {
554 dto.setRelTypeUuid(rel.getType().getUuid());
555 }
556 for (TaxonNode node : relTaxon.getTaxonNodes()) {
557 Classification classification = node.getClassification();
558 if (classification != null) {
559 dto.addClassificationUuids(classification.getUuid());
560 }
561 }
562 conceptRelContainer.addItem(dto);
563 }
564
565 private void loadSynonymsInGroup(TaxonPageDto.HomotypicGroupDTO hgDto, Synonym syn,
566 TaxonPageDtoConfiguration config, TaxonPageDto pageDto) {
567
568 TaxonBaseDto synDto = new TaxonBaseDto();
569 loadBaseData(syn, synDto);
570 synDto.setLabel(syn.getTitleCache());
571 synDto.setTaggedLabel(getTaggedTaxon(syn, config));
572
573 if (syn.getName() != null) {
574 handleName(config, synDto, syn.getName(), pageDto);
575 synDto.setNameLabel(syn.getName().getTitleCache());
576 handleRelatedNames(syn.getName(), synDto, config);
577 loadProtologues(syn.getName(), synDto);
578 }
579
580 //TODO
581 hgDto.addSynonym(synDto);
582 }
583
584 private void loadProtologues(TaxonName name, TaxonBaseDto taxonBaseDto) {
585 NomenclaturalSource nomSource = name.getNomenclaturalSource();
586 if (nomSource != null) {
587 Set<ExternalLink> links = nomSource.getLinks();
588 for (ExternalLink link : links) {
589 if (link.getUri() != null) {
590 taxonBaseDto.addProtologue(link.getUri());
591 }
592 }
593 }
594 }
595
596 private void handleRelatedNames(TaxonName name, TaxonBaseDto taxonDto, TaxonPageDtoConfiguration config) {
597 //exclusions TODO handle via config
598 Set<UUID> excludedTypes = new HashSet<>(); //both directions
599 excludedTypes.add(NameRelationshipType.uuidBasionym);
600 excludedTypes.add(NameRelationshipType.uuidReplacedSynonym);
601 Set<UUID> excludedFromTypes = new HashSet<>(excludedTypes);
602 Set<UUID> excludedToTypes = new HashSet<>(excludedTypes);
603 //TODO non-types
604
605 //TODO config.getLocales();
606 Language locale = Language.DEFAULT();
607
608 for (NameRelationship rel : name.getRelationsFromThisName()) {
609 TaxonName relatedName = rel.getToName();
610 if (relatedName == null || rel.getType() == null || excludedFromTypes.contains(rel.getType().getUuid())) {
611 continue;
612 }
613 NameRelationDTO dto = new NameRelationDTO();
614 loadBaseData(rel, dto);
615 //name
616 dto.setNameUuid(relatedName.getUuid());
617 dto.setNameLabel(relatedName.getTaggedName());
618 //type
619 dto.setRelTypeUuid(rel.getType().getUuid());
620 Representation rep = rel.getType().getPreferredRepresentation(locale);
621 dto.setRelType(rep == null ? rel.getType().toString() : rep.getLabel());
622 //inverse
623 dto.setInverse(false);
624 //ruleConsidered
625 dto.setRuleConsidered(rel.getRuleConsidered());
626 taxonDto.addRelatedName(dto);
627 }
628
629 //to relations
630 for (NameRelationship rel : name.getRelationsToThisName()) {
631 TaxonName relatedName = rel.getFromName();
632 if (relatedName == null || rel.getType() == null || excludedFromTypes.contains(rel.getType().getUuid())) {
633 continue;
634 }
635 NameRelationDTO dto = new NameRelationDTO();
636 loadBaseData(rel, dto);
637 //name
638 dto.setNameUuid(relatedName.getUuid());
639 dto.setNameLabel(relatedName.getTaggedName());
640 //type
641 dto.setRelTypeUuid(rel.getType().getUuid());
642 Representation rep = rel.getType().getPreferredInverseRepresentation(Arrays.asList(new Language[] {locale}));
643 dto.setRelType(rep == null ? rel.getType().toString() : rep.getLabel());
644 //inverse
645 dto.setInverse(true);
646 taxonDto.addRelatedName(dto);
647 }
648 }
649
650 private void loadFacts(Taxon taxon, TaxonPageDto taxonPageDto, TaxonPageDtoConfiguration config) {
651
652 try {
653 //compute the features that do exist for this taxon
654 Map<UUID, Feature> existingFeatureUuids = getExistingFeatureUuids(taxon);
655
656 //filter, sort and structure according to feature tree
657 TreeNode<Feature, UUID> filteredRootNode;
658 if (config.getFeatureTree() != null) {
659
660 //TODO class cast
661 TermTree<Feature> featureTree = repository.getTermTreeService().find(config.getFeatureTree());
662 filteredRootNode = filterFeatureNode(featureTree.getRoot(), existingFeatureUuids.keySet());
663 } else {
664 filteredRootNode = createDefaultFeatureNode(taxon);
665 }
666
667 //load facts per feature
668 Map<UUID,Set<DescriptionElementBase>> featureMap = loadFeatureMap(taxon);
669
670 //load final result
671 if (!filteredRootNode.getChildren().isEmpty()) {
672 ContainerDto<FeatureDto> features = new ContainerDto<>();
673 for (TreeNode<Feature,UUID> node : filteredRootNode.getChildren()) {
674 handleFeatureNode(config, featureMap, features, node);
675 }
676 taxonPageDto.setTaxonFacts(features);
677 }
678 } catch (Exception e) {
679 //e.printStackTrace();
680 taxonPageDto.addMessage(MessagesDto.NewErrorInstance("Error when loading factual data.", e));
681 }
682 }
683
684 //TODO merge with loadFacts, it is almost the same, see //DIFFERENT
685 private void loadNameFacts(TaxonName name, TaxonBaseDto nameDto, TaxonPageDtoConfiguration config, TaxonPageDto pageDto) {
686
687 try {
688 //compute the features that do exist for this taxon
689 Map<UUID, Feature> existingFeatureUuids = getExistingFeatureUuids(name);
690
691 //filter, sort and structure according to feature tree
692 TreeNode<Feature, UUID> filteredRootNode;
693 if (config.getFeatureTree() != null) {
694
695 //TODO class cast
696 TermTree<Feature> featureTree = repository.getTermTreeService().find(config.getFeatureTree());
697 filteredRootNode = filterFeatureNode(featureTree.getRoot(), existingFeatureUuids.keySet());
698 } else {
699 filteredRootNode = createDefaultFeatureNode(name);
700 }
701
702 //load facts per feature
703 Map<UUID,Set<DescriptionElementBase>> featureMap = loadFeatureMap(name);
704
705 //load final result
706 if (!filteredRootNode.getChildren().isEmpty()) {
707 ContainerDto<FeatureDto> features = new ContainerDto<>();
708 for (TreeNode<Feature,UUID> node : filteredRootNode.getChildren()) {
709 handleFeatureNode(config, featureMap, features, node);
710 }
711 //DIFFERENT
712 nameDto.setNameFacts(features);
713 }
714 } catch (Exception e) {
715 //e.printStackTrace();
716 //DIFFERENT
717 pageDto.addMessage(MessagesDto.NewErrorInstance("Error when loading factual data.", e));
718 }
719 }
720
721 private void handleFeatureNode(TaxonPageDtoConfiguration config,
722 Map<UUID, Set<DescriptionElementBase>> featureMap, ContainerDto<FeatureDto> features,
723 TreeNode<Feature, UUID> node) {
724
725 Feature feature = node.getData();
726 //TODO locale
727 FeatureDto featureDto = new FeatureDto(feature.getUuid(), feature.getId(), feature.getLabel());
728 features.addItem(featureDto);
729
730 List<Distribution> distributions = new ArrayList<>();
731
732 //
733 for (DescriptionElementBase fact : featureMap.get(feature.getUuid())){
734 if (fact.isInstanceOf(Distribution.class)) {
735 distributions.add(CdmBase.deproxy(fact, Distribution.class));
736 }else {
737 //TODO how to handle CommonNames, do we also want to have a data structure
738 //with Language|
739 // -- Area|
740 // --name
741 // a bit like for distribution??
742 handleFact(featureDto, fact);
743 }
744 }
745
746 handleDistributions(config, featureDto, distributions);
747 //TODO really needed?
748 orderFacts(featureDto);
749
750 //children
751 ContainerDto<FeatureDto> childFeatures = new ContainerDto<>();
752 for (TreeNode<Feature,UUID> child : node.getChildren()) {
753 handleFeatureNode(config, featureMap, childFeatures, child);
754 }
755 if (childFeatures.getCount() > 0) {
756 featureDto.setSubFeatures(childFeatures);
757 }
758 }
759
760 //TODO not really used yet, only for distinguishing fact classes,
761 //needs discussion if needed and how to implement.
762 //we could also move compareTo methods to DTO classes but with this
763 //remove from having only data in the DTO, no logic
764 private void orderFacts(FeatureDto featureDto) {
765 List<IFactDto> list = featureDto.getFacts().getItems();
766 Collections.sort(list, (f1,f2)->{
767 if (!f1.getClass().equals(f2.getClass())) {
768 return f1.getClass().getSimpleName().compareTo(f2.getClass().getSimpleName());
769 }else {
770 if (f1 instanceof FactDto) {
771 FactDto fact1 = (FactDto)f1;
772 FactDto fact2 = (FactDto)f2;
773
774 // return fact1.getTypedLabel().toString().compareTo(fact2.getTypedLabel().toString());
775 return 0; //FIXME;
776 } else if (f1 instanceof CommonNameDto) {
777 int result = 0;
778 CommonNameDto fact1 = (CommonNameDto)f1;
779 CommonNameDto fact2 = (CommonNameDto)f2;
780 return 0; //FIXME
781 }
782 }
783 return 0; //FIXME
784 });
785
786 }
787
788 private Map<UUID, Set<DescriptionElementBase>> loadFeatureMap(IDescribable<?> describable) {
789 Map<UUID, Set<DescriptionElementBase>> featureMap = new HashMap<>();
790
791 //... load facts
792 for (DescriptionBase<?> description : describable.getDescriptions()) {
793 if (description.isImageGallery()) {
794 continue;
795 }
796 for (DescriptionElementBase deb : description.getElements()) {
797 Feature feature = deb.getFeature();
798 if (featureMap.get(feature.getUuid()) == null) {
799 featureMap.put(feature.getUuid(), new HashSet<>());
800 }
801 featureMap.get(feature.getUuid()).add(deb);
802 }
803 }
804 return featureMap;
805 }
806
807 private TreeNode<Feature, UUID> createDefaultFeatureNode(IDescribable<?> describable) {
808 TreeNode<Feature, UUID> root = new TreeNode<>();
809 Set<Feature> requiredFeatures = new HashSet<>();
810
811 for (DescriptionBase<?> description : describable.getDescriptions()) {
812 if (description.isImageGallery()) {
813 continue;
814 }
815 for (DescriptionElementBase deb : description.getElements()) {
816 Feature feature = deb.getFeature();
817 if (feature != null) { //null should not happen
818 requiredFeatures.add(feature);
819 }
820 }
821 }
822 List<Feature> sortedChildren = new ArrayList<>(requiredFeatures);
823 Collections.sort(sortedChildren, (f1,f2) -> f1.getTitleCache().compareTo(f2.getTitleCache()));
824 sortedChildren.stream().forEachOrdered(f->root.addChild(new TreeNode<>(f.getUuid(), f)));
825 return root;
826 }
827
828 /**
829 * Recursive call to a feature tree's feature node in order to creates a tree structure
830 * ordered in the same way as the according feature tree but only containing features
831 * that do really exist for the given taxon. If only a child node is required the parent
832 * node/feature is also considered to be required.<BR>
833 */
834 private TreeNode<Feature, UUID> filterFeatureNode(TermNode<Feature> featureNode,
835 Set<UUID> existingFeatureUuids) {
836
837 //first filter children
838 List<TreeNode<Feature, UUID>> requiredChildNodes = new ArrayList<>();
839 for (TermNode<Feature> childNode : featureNode.getChildNodes()) {
840 TreeNode<Feature, UUID> child = filterFeatureNode(childNode, existingFeatureUuids);
841 if (child != null) {
842 requiredChildNodes.add(child);
843 }
844 }
845
846 //if any child is required or this node is required ....
847 if (!requiredChildNodes.isEmpty() ||
848 featureNode.getTerm() != null && existingFeatureUuids.contains(featureNode.getTerm().getUuid())) {
849 TreeNode<Feature,UUID> result = new TreeNode<>();
850 //add this nodes data
851 Feature feature = featureNode.getTerm() == null ? null : featureNode.getTerm();
852 if (feature != null) {
853 result.setNodeId(feature.getUuid());
854 result.setData(feature);
855 }
856 //add child data
857 requiredChildNodes.stream().forEachOrdered(c->result.addChild(c));
858 return result;
859 }else {
860 return null;
861 }
862 }
863
864 /**
865 * Computes the (unsorted) set of features for which facts exist
866 * for the given taxon.
867 */
868 private Map<UUID, Feature> getExistingFeatureUuids(IDescribable<?> describable) {
869 Map<UUID, Feature> result = new HashMap<>();
870 for (DescriptionBase<?> description : describable.getDescriptions()) {
871 if (description.isImageGallery()) {
872 continue;
873 }
874 for (DescriptionElementBase deb : description.getElements()) {
875 Feature feature = deb.getFeature();
876 if (feature != null) { //null should not happen
877 result.put(feature.getUuid(), feature);
878 }
879 }
880 }
881 return result;
882 }
883
884 private void handleDistributions(TaxonPageDtoConfiguration config, FeatureDto featureDto,
885 List<Distribution> distributions) {
886
887 if (distributions.isEmpty()) {
888 return;
889 }
890 IDistributionService distributionService = repository.getDistributionService();
891
892 //configs
893 DistributionInfoConfiguration distributionConfig = config.getDistributionInfoConfiguration();
894 CondensedDistributionConfiguration condensedConfig = distributionConfig.getCondensedDistrConfig();
895
896 String statusColorsString = distributionConfig.getStatusColorsString();
897
898
899 //copied from DescriptionListController
900
901 boolean ignoreDistributionStatusUndefined = true; //workaround until #9500 is fully implemented
902 distributionConfig.setIgnoreDistributionStatusUndefined(ignoreDistributionStatusUndefined);
903 boolean fallbackAsParent = true; //may become a service parameter in future
904
905 DistributionInfoDto dto;
906
907 //hiddenArea markers include markers for fully hidden areas and fallback areas. The later
908 //are hidden markers on areas that have non-hidden subareas (#4408)
909 Set<MarkerType> hiddenAreaMarkerTypes = distributionConfig.getHiddenAreaMarkerTypeList();
910 if(hiddenAreaMarkerTypes != null && !hiddenAreaMarkerTypes.isEmpty()){
911 condensedConfig.hiddenAndFallbackAreaMarkers = hiddenAreaMarkerTypes.stream().map(mt->mt.getUuid()).collect(Collectors.toSet());
912 }
913
914 List<String> initStrategy = null;
915
916 Map<PresenceAbsenceTerm, Color> distributionStatusColors;
917 try {
918 distributionStatusColors = DistributionServiceUtilities.buildStatusColorMap(
919 statusColorsString, repository.getTermService(), repository.getVocabularyService());
920 } catch (JsonProcessingException e) {
921 logger.error("JsonProcessingException when reading distribution status colors");
922 //TODO is null allowed?
923 distributionStatusColors = null;
924 }
925
926 dto = distributionService.composeDistributionInfoFor(distributionConfig, distributions,
927 fallbackAsParent,
928 distributionStatusColors, LocaleContext.getLanguages());
929
930 if (distributionConfig.isUseTreeDto() && dto.getTree() != null) {
931 DistributionTreeDto tree = (DistributionTreeDto)dto.getTree();
932 TreeNode<Set<DistributionDto>, NamedAreaDto> root = tree.getRootElement();
933 //fill uuid->distribution map
934 Map<UUID,Distribution> distributionMap = new HashMap<>();
935 distributions.stream().forEach(d->distributionMap.put(d.getUuid(), d));
936 handleDistributionDtoNode(distributionMap, root);
937 }
938
939 featureDto.addFact(dto);
940 }
941
942 private void handleDistributionDtoNode(Map<UUID, Distribution> map,
943 TreeNode<Set<DistributionDto>, NamedAreaDto> root) {
944 if (root.getData() != null) {
945 root.getData().stream().forEach(d->{
946 Distribution distr = map.get(d.getUuid());
947 loadBaseData(distr, d);
948 d.setTimeperiod(distr.getTimeperiod() == null ? null : distr.getTimeperiod().toString());
949
950 });
951 }
952 //handle children
953 if (root.getChildren() != null) {
954 root.getChildren().stream().forEach(c->handleDistributionDtoNode(map, c));
955 }
956 }
957
958 private FactDtoBase handleFact(FeatureDto featureDto, DescriptionElementBase fact) {
959 //TODO locale
960 Language localeLang = null;
961
962 FactDtoBase result;
963 if (fact.isInstanceOf(TextData.class)) {
964 TextData td = CdmBase.deproxy(fact, TextData.class);
965 LanguageString ls = td.getPreferredLanguageString(localeLang);
966 String text = ls == null ? "" : CdmUtils.Nz(ls.getText());
967
968 FactDto factDto = new FactDto();
969 featureDto.addFact(factDto);
970 //TODO do we really need type information for textdata here?
971 TypedLabel typedLabel = new TypedLabel(text);
972 typedLabel.setClassAndId(td);
973 factDto.getTypedLabel().add(typedLabel);
974 loadBaseData(td, factDto);
975 //TODO
976 result = factDto;
977 }else if (fact.isInstanceOf(CommonTaxonName.class)) {
978 CommonTaxonName ctn = CdmBase.deproxy(fact, CommonTaxonName.class);
979 CommonNameDto dto = new CommonNameDto();
980 featureDto.addFact(dto);
981
982 Language lang = ctn.getLanguage();
983 if (lang != null) {
984 String langLabel = getTermLabel(lang, localeLang);
985 dto.setLanguage(langLabel);
986 dto.setLanguageUuid(lang.getUuid());
987 }else {
988 //TODO
989 dto.setLanguage("-");
990 }
991 //area
992 NamedArea area = ctn.getArea();
993 if (area != null) {
994 String areaLabel = getTermLabel(area, localeLang);
995 dto.setArea(areaLabel);
996 dto.setAreaUUID(area.getUuid());
997 }
998 dto.setName(ctn.getName());
999 loadBaseData(ctn, dto);
1000 //TODO sort all common names
1001 result = dto;
1002 } else if (fact.isInstanceOf(IndividualsAssociation.class)) {
1003 IndividualsAssociation ia = CdmBase.deproxy(fact, IndividualsAssociation.class);
1004 IndividualsAssociationDto dto = new IndividualsAssociationDto ();
1005
1006 LanguageString description = MultilanguageTextHelper.getPreferredLanguageString(ia.getDescription(), Arrays.asList(localeLang));
1007 if (description != null) {
1008 dto.setDescritpion(description.getText());
1009 }
1010 SpecimenOrObservationBase<?> specimen = ia.getAssociatedSpecimenOrObservation();
1011 if (specimen != null) {
1012 //TODO what to use here??
1013 dto.setOccurrence(specimen.getTitleCache());
1014 dto.setOccurrenceUuid(specimen.getUuid());
1015 }
1016
1017 featureDto.addFact(dto);
1018 loadBaseData(ia, dto);
1019 result = dto;
1020 } else if (fact.isInstanceOf(TaxonInteraction.class)) {
1021 TaxonInteraction ti = CdmBase.deproxy(fact, TaxonInteraction.class);
1022 TaxonInteractionDto dto = new TaxonInteractionDto ();
1023
1024 LanguageString description = MultilanguageTextHelper.getPreferredLanguageString(
1025 ti.getDescription(), Arrays.asList(localeLang));
1026 if (description != null) {
1027 dto.setDescritpion(description.getText());
1028 }
1029 Taxon taxon = ti.getTaxon2();
1030 if (taxon != null) {
1031 //TODO what to use here??
1032 dto.setTaxon(taxon.cacheStrategy().getTaggedTitle(taxon));
1033 dto.setTaxonUuid(taxon.getUuid());
1034 }
1035 featureDto.addFact(dto);
1036 loadBaseData(ti, dto);
1037 result = dto;
1038 }else if (fact.isInstanceOf(CategoricalData.class)) {
1039 CategoricalData cd = CdmBase.deproxy(fact, CategoricalData.class);
1040 FactDto factDto = new FactDto();
1041 featureDto.addFact(factDto);
1042 //TODO do we really need type information for textdata here?
1043 String label = CategoricalDataFormatter.NewInstance(null).format(cd, localeLang);
1044 TypedLabel typedLabel = new TypedLabel(label);
1045 typedLabel.setClassAndId(cd);
1046 factDto.getTypedLabel().add(typedLabel);
1047 //TODO
1048 loadBaseData(cd, factDto);
1049 result = factDto;
1050 }else if (fact.isInstanceOf(QuantitativeData.class)) {
1051 QuantitativeData qd = CdmBase.deproxy(fact, QuantitativeData.class);
1052 FactDto factDto = new FactDto();
1053 featureDto.addFact(factDto);
1054 //TODO do we really need type information for textdata here?
1055 String label = QuantitativeDataFormatter.NewInstance(null).format(qd, localeLang);
1056 TypedLabel typedLabel = new TypedLabel(label);
1057 typedLabel.setClassAndId(qd);
1058 factDto.getTypedLabel().add(typedLabel);
1059 //TODO
1060 loadBaseData(qd, factDto);
1061 result = factDto;
1062 }else if (fact.isInstanceOf(TemporalData.class)) {
1063 TemporalData td = CdmBase.deproxy(fact, TemporalData.class);
1064 FactDto factDto = new FactDto();
1065 featureDto.addFact(factDto);
1066 //TODO do we really need type information for textdata here?
1067 String label = td.toString();
1068 TypedLabel typedLabel = new TypedLabel(label);
1069 typedLabel.setClassAndId(td);
1070 factDto.getTypedLabel().add(typedLabel);
1071 //TODO
1072 loadBaseData(td, factDto);
1073 result = factDto;
1074 }else {
1075 // TODO
1076 logger.warn("DescriptionElement type not yet handled: " + fact.getClass().getSimpleName());
1077 return null;
1078 }
1079 result.setTimeperiod(fact.getTimeperiod() == null ? null : fact.getTimeperiod().toString());
1080 return result;
1081 }
1082
1083 private String getTermLabel(TermBase term, Language localeLang) {
1084 if (term == null) {
1085 return null;
1086 }
1087 Representation rep = term.getPreferredRepresentation(localeLang);
1088 String label = rep == null ? null : rep.getLabel();
1089 label = label == null ? term.getLabel() : label;
1090 return label;
1091 }
1092
1093 /**
1094 * Compares an existing last date and the last date of an entity
1095 * and returns the resulting last date.
1096 */
1097 private LocalDateTime getLastUpdated(LocalDateTime existingLastDate, VersionableEntity dateToAddEntity) {
1098
1099 DateTime dateToAdd = dateToAddEntity.getUpdated() != null ? dateToAddEntity.getUpdated() : dateToAddEntity.getCreated();
1100
1101 LocalDateTime javaLocalDateTimeOfEntity = dateToAdd == null ? null:
1102 LocalDateTime.of(dateToAdd.getYear(), dateToAdd.getMonthOfYear(),
1103 dateToAdd.getDayOfMonth(), dateToAdd.getHourOfDay(),
1104 dateToAdd.getMinuteOfHour(), dateToAdd.getSecondOfMinute());
1105
1106 if (existingLastDate == null) {
1107 return javaLocalDateTimeOfEntity;
1108 }else if (javaLocalDateTimeOfEntity == null || javaLocalDateTimeOfEntity.compareTo(existingLastDate) < 0) {
1109 return existingLastDate;
1110 }else {
1111 return javaLocalDateTimeOfEntity;
1112 }
1113 }
1114
1115 private void loadBaseData(CdmBase cdmBase, CdmBaseDto dto) {
1116 dto.setId(cdmBase.getId());
1117 dto.setUuid(cdmBase.getUuid());
1118
1119 loadAnnotatable(cdmBase, dto);
1120 loadSources(cdmBase, dto);
1121 //loadIdentifiable(cdmBase, dto);
1122 }
1123
1124 private void loadSources(CdmBase cdmBase, CdmBaseDto dto) {
1125 if (dto instanceof SingleSourcedDto && cdmBase.isInstanceOf(SingleSourcedEntityBase.class)) {
1126 //TODO other sourced
1127 SingleSourcedEntityBase sourced = CdmBase.deproxy(cdmBase, SingleSourcedEntityBase.class);
1128 SingleSourcedDto sourcedDto = (SingleSourcedDto)dto;
1129 NamedSource source = sourced.getSource();
1130 if (source != null) { //TODO && !source.isEmpty() - does not exist yet
1131 SourceDto sourceDto = new SourceDto();
1132 loadSource(source, sourceDto);
1133 sourcedDto.setSource(sourceDto);
1134 }
1135 } else if (dto instanceof SourcedDto && cdmBase instanceof ISourceable) {
1136 @SuppressWarnings("unchecked")
1137 ISourceable<OriginalSourceBase> sourced = (ISourceable<OriginalSourceBase>)cdmBase;
1138 SourcedDto sourcedDto = (SourcedDto)dto;
1139 for (OriginalSourceBase source : sourced.getSources()) {
1140 SourceDto sourceDto = new SourceDto();
1141 loadSource(source, sourceDto);
1142 sourcedDto.addSource(sourceDto);
1143 }
1144 }
1145 }
1146
1147 private void loadSource(OriginalSourceBase source, SourceDto sourceDto) {
1148
1149 source = CdmBase.deproxy(source);
1150 //base data
1151 loadBaseData(source, sourceDto);
1152
1153 ICdmBase linkedObject = source.getCitation();
1154 if (linkedObject == null) {
1155 //cdmsource
1156 linkedObject = source.getCdmSource();
1157 }
1158
1159 //citation uuid & doi
1160 if (source.getCitation()!= null) {
1161 sourceDto.setDoi(source.getCitation().getDoiString());
1162 }
1163
1164 //label
1165 //TODO this probably does not use specimen or cdmSource if necessary,
1166 // also long citation is still preliminary
1167 String label = OriginalSourceFormatter.INSTANCE_LONG_CITATION.format(source);
1168 TypedLabel typedLabel = new TypedLabel(source, label);
1169 sourceDto.addLabel(typedLabel);
1170 sourceDto.setType(source.getType() != null ? source.getType().toString() : null);
1171
1172 if (source.isInstanceOf(NamedSourceBase.class)) {
1173 NamedSourceBase ns = CdmBase.deproxy(source, NamedSourceBase.class);
1174
1175 //nameUsedInSource
1176 TaxonName name = ns.getNameUsedInSource();
1177 if (name != null) {
1178 List<TaggedText> taggedName = name.cacheStrategy().getTaggedTitle(name);
1179 //TODO nom status?
1180 sourceDto.setNameInSource(taggedName);
1181 sourceDto.setNameInSourceUuid(name.getUuid());
1182 }
1183
1184 //specimen uuid
1185 if (source.isInstanceOf(DescriptionElementSource.class)) {
1186 DescriptionElementSource des = CdmBase.deproxy(source, DescriptionElementSource.class);
1187 if (linkedObject == null) {
1188 linkedObject = des.getSpecimen();
1189 }
1190 }
1191 }
1192
1193 sourceDto.setLinkedUuid(getUuid(linkedObject));
1194 String linkedObjectStr = linkedObject == null ? null : CdmBase.deproxy(linkedObject).getClass().getSimpleName();
1195 sourceDto.setLinkedClass(linkedObjectStr);
1196 }
1197
1198 private UUID getUuid(ICdmBase cdmBase) {
1199 return cdmBase == null ? null : cdmBase.getUuid();
1200 }
1201
1202 private void loadAnnotatable(CdmBase cdmBase, CdmBaseDto dto) {
1203 if (dto instanceof AnnotatableDto && cdmBase.isInstanceOf(AnnotatableEntity.class)) {
1204 AnnotatableEntity annotatable = CdmBase.deproxy(cdmBase, AnnotatableEntity.class);
1205 AnnotatableDto annotatableDto = (AnnotatableDto)dto;
1206 //annotation
1207 for (Annotation annotation : annotatable.getAnnotations()) {
1208 if (annotation.getAnnotationType() != null
1209 //TODO annotation type filter
1210 && annotation.getAnnotationType().getUuid().equals(AnnotationType.uuidEditorial)
1211 && StringUtils.isNotBlank(annotation.getText())) {
1212
1213 AnnotationDto annotationDto = new AnnotationDto();
1214 annotatableDto.addAnnotation(annotationDto);
1215 //TODO id needed? but need to adapt dto and container then
1216 loadBaseData(annotation, annotationDto);
1217 annotationDto.setText(annotation.getText());
1218 UUID uuidAnnotationType = annotation.getAnnotationType() == null ? null :annotation.getAnnotationType().getUuid();
1219 annotationDto.setTypeUuid(uuidAnnotationType);
1220 //language etc. currently not yet used
1221 }
1222 }
1223
1224 //marker
1225 for (Marker marker : annotatable.getMarkers()) {
1226 if (marker.getMarkerType() != null
1227 //TODO markertype filter
1228 // && marker.getMarkerType().getUuid().equals(AnnotationType.uuidEditorial)
1229 ){
1230
1231 MarkerDto markerDto = new MarkerDto();
1232 annotatableDto.addMarker(markerDto);
1233 //TODO id needed? but need to adapt dto and container then
1234 loadBaseData(marker, markerDto);
1235 if (marker.getMarkerType() != null) {
1236 markerDto.setTypeUuid(marker.getMarkerType().getUuid());
1237 //TODO locale
1238 markerDto.setType(marker.getMarkerType().getTitleCache());
1239 }
1240 markerDto.setValue(marker.getValue());
1241 }
1242 }
1243 }
1244 }
1245 }