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