ref #9999 improve ReferencingObjectFormatter for CategoricalData, StateData and TextD...
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / format / ReferencingObjectFormatter.java
1 /**
2 * Copyright (C) 2021 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.format;
10
11 import java.lang.reflect.InvocationTargetException;
12 import java.lang.reflect.Method;
13 import java.util.Set;
14
15 import org.apache.commons.lang3.StringUtils;
16
17 import eu.etaxonomy.cdm.common.CdmUtils;
18 import eu.etaxonomy.cdm.format.ICdmFormatter.FormatKey;
19 import eu.etaxonomy.cdm.format.description.CategoricalDataFormatter;
20 import eu.etaxonomy.cdm.format.occurrences.DistanceStringFormatter;
21 import eu.etaxonomy.cdm.model.agent.AgentBase;
22 import eu.etaxonomy.cdm.model.common.CdmBase;
23 import eu.etaxonomy.cdm.model.common.IdentifiableEntity;
24 import eu.etaxonomy.cdm.model.common.IdentifiableSource;
25 import eu.etaxonomy.cdm.model.common.Language;
26 import eu.etaxonomy.cdm.model.common.LanguageString;
27 import eu.etaxonomy.cdm.model.common.LanguageStringBase;
28 import eu.etaxonomy.cdm.model.common.Marker;
29 import eu.etaxonomy.cdm.model.common.MarkerType;
30 import eu.etaxonomy.cdm.model.common.RelationshipBase;
31 import eu.etaxonomy.cdm.model.common.RelationshipTermBase;
32 import eu.etaxonomy.cdm.model.common.TimePeriod;
33 import eu.etaxonomy.cdm.model.description.CategoricalData;
34 import eu.etaxonomy.cdm.model.description.CommonTaxonName;
35 import eu.etaxonomy.cdm.model.description.DescriptionBase;
36 import eu.etaxonomy.cdm.model.description.DescriptionElementBase;
37 import eu.etaxonomy.cdm.model.description.DescriptionElementSource;
38 import eu.etaxonomy.cdm.model.description.Distribution;
39 import eu.etaxonomy.cdm.model.description.IDescribable;
40 import eu.etaxonomy.cdm.model.description.KeyStatement;
41 import eu.etaxonomy.cdm.model.description.PresenceAbsenceTerm;
42 import eu.etaxonomy.cdm.model.description.SpecimenDescription;
43 import eu.etaxonomy.cdm.model.description.StateData;
44 import eu.etaxonomy.cdm.model.description.TaxonDescription;
45 import eu.etaxonomy.cdm.model.description.TaxonInteraction;
46 import eu.etaxonomy.cdm.model.description.TaxonNameDescription;
47 import eu.etaxonomy.cdm.model.description.TextData;
48 import eu.etaxonomy.cdm.model.location.NamedArea;
49 import eu.etaxonomy.cdm.model.name.HomotypicalGroup;
50 import eu.etaxonomy.cdm.model.name.HybridRelationship;
51 import eu.etaxonomy.cdm.model.name.NameRelationship;
52 import eu.etaxonomy.cdm.model.name.NameTypeDesignation;
53 import eu.etaxonomy.cdm.model.name.NomenclaturalSource;
54 import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
55 import eu.etaxonomy.cdm.model.name.SpecimenTypeDesignation;
56 import eu.etaxonomy.cdm.model.name.TaxonName;
57 import eu.etaxonomy.cdm.model.name.TextualTypeDesignation;
58 import eu.etaxonomy.cdm.model.name.TypeDesignationBase;
59 import eu.etaxonomy.cdm.model.name.TypeDesignationStatusBase;
60 import eu.etaxonomy.cdm.model.occurrence.DeterminationEvent;
61 import eu.etaxonomy.cdm.model.occurrence.GatheringEvent;
62 import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
63 import eu.etaxonomy.cdm.model.permission.Group;
64 import eu.etaxonomy.cdm.model.permission.User;
65 import eu.etaxonomy.cdm.model.reference.NamedSource;
66 import eu.etaxonomy.cdm.model.reference.OriginalSourceBase;
67 import eu.etaxonomy.cdm.model.taxon.Classification;
68 import eu.etaxonomy.cdm.model.taxon.SecundumSource;
69 import eu.etaxonomy.cdm.model.taxon.Taxon;
70 import eu.etaxonomy.cdm.model.taxon.TaxonBase;
71 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
72 import eu.etaxonomy.cdm.model.taxon.TaxonRelationship;
73 import eu.etaxonomy.cdm.model.term.Representation;
74
75 /**
76 * @author a.mueller
77 * @date 19.03.2021
78 */
79 public class ReferencingObjectFormatter {
80
81 public static String format(CdmBase element, Language defaultLanguage) {
82 return format(element, null, defaultLanguage);
83 }
84
85 public static String format(CdmBase element, String target, Language defaultLanguage) {
86
87 String resultString = null;
88 if (element == null){
89 return null;
90 }else if (element instanceof IdentifiableEntity) {
91 resultString = ((IdentifiableEntity<?>) element).getTitleCache();
92 }else if (element instanceof OriginalSourceBase) {
93 OriginalSourceBase originalSource = (OriginalSourceBase) element;
94 // ISourceable sourcedObject = originalSource.getSourcedObj();
95 //due to #5743 the bidirectionality for sourced object had to be removed
96
97 String sourceObjectTitle = "sourced object data not available (#5743)";
98
99 //it is now possible for NomenclaturalSource as they link to the sourced name
100 if (originalSource instanceof NomenclaturalSource){
101 TaxonName sourcedName = ((NomenclaturalSource)originalSource).getSourcedName();
102 sourceObjectTitle = sourcedName == null ? "Source orphaned, not attached to a name" :
103 "for " + sourcedName.getTitleCache();
104 }else if (originalSource instanceof SecundumSource){
105 TaxonBase<?> sourcedTaxon = ((SecundumSource)originalSource).getSourcedTaxon();
106 sourceObjectTitle = sourcedTaxon == null ? "Source orphaned, not attached to a taxon" :
107 "for " + sourcedTaxon.getTitleCache();
108 }else if (originalSource instanceof DescriptionElementSource){
109 sourceObjectTitle = getCache((DescriptionElementSource)originalSource, defaultLanguage);
110 }else if (originalSource instanceof IdentifiableSource && isNotBlank(target) ){
111 sourceObjectTitle = "for " + target;
112 }else if (originalSource instanceof NamedSource && isNotBlank(target) ){
113 sourceObjectTitle = "for " + target;
114 }
115 resultString = CdmUtils.concat("; ", new String[]{originalSource.getIdNamespace(), originalSource.getIdInSource(), sourceObjectTitle});
116 }else if (element instanceof LanguageStringBase) {
117 resultString = ((LanguageStringBase) element).getText();
118 }else if (element instanceof DescriptionElementBase) {
119 resultString = getCache((DescriptionElementBase) element, defaultLanguage);
120 }else if (element instanceof StateData) {
121 resultString = getCache((StateData) element, defaultLanguage);
122 }else if (element instanceof RelationshipBase<?, ?, ?>) {
123 resultString = getCache((RelationshipBase<?, ?, ?>) element, defaultLanguage);
124 }else if (element instanceof TypeDesignationBase<?>) {
125 resultString = getCache((TypeDesignationBase<?>) element, defaultLanguage);
126 }else if (element instanceof HomotypicalGroup) {
127 resultString = getCache((HomotypicalGroup) element);
128 }else if (element instanceof TaxonNode) {
129 resultString = getCache((TaxonNode) element);
130 }else if (element instanceof DeterminationEvent) {
131 resultString = getCache((DeterminationEvent) element);
132 }else if (element instanceof NomenclaturalStatus) {
133 resultString = getCache((NomenclaturalStatus) element);
134 }else if (element instanceof GatheringEvent){
135 resultString = getCache((GatheringEvent) element);
136 }else if (element instanceof Marker) {
137 Marker marker = (Marker) element;
138 MarkerType type = marker.getMarkerType();
139 resultString = (type == null ? "- no marker type -" : marker.getMarkerType().getLabel()) + " (" + marker.getFlag() + ")";
140 }else if (element instanceof User) {
141 User user = (User) element;
142 resultString = user.getUsername();
143 }else if (element instanceof Group) {
144 Group group = (Group) element;
145 resultString = group.getName();
146 }else if (element instanceof KeyStatement) {
147 KeyStatement keyStatement = (KeyStatement) element;
148 resultString = getCache(keyStatement);
149 }else{
150 // TODO write return texts for HomotypicalGroup, etc.
151 resultString = element.toString();
152 }
153
154 if (resultString == null){
155 resultString = element.toString();
156 }
157 return resultString;
158 }
159
160 private static String getCache(DescriptionElementSource source, Language defaultLanguage) {
161 DescriptionElementBase sourcedElement = source.getSourcedElement();
162
163 if (sourcedElement == null){
164 return "Source orphaned, not attached to a fact";
165 }
166 String superLabel = getDescribedObjectLabel(sourcedElement.getInDescription());
167 String result = CdmUtils.concat(": ", DescriptionElementFormatter.format(sourcedElement, defaultLanguage), superLabel);
168 return result;
169 }
170
171 private static String getDescribedObjectLabel(DescriptionBase<?> inDescription) {
172 IDescribable<?> entity = inDescription.describedEntity();
173 if (entity != null){
174 return entity.getTitleCache();
175 }else{
176 return inDescription.getTitleCache();
177 }
178 }
179
180 private static String getCache(RelationshipBase<?, ?, ?> rel, Language defaultLanguage) {
181 rel = CdmBase.deproxy(rel);
182 RelationshipTermBase<?> type = rel.getType();
183 IdentifiableEntity<?> from;
184 IdentifiableEntity<?> to;
185 if (rel.isInstanceOf(NameRelationship.class)){
186 from = ((NameRelationship)rel).getFromName();
187 to = ((NameRelationship)rel).getToName();
188 }else if (rel.isInstanceOf(HybridRelationship.class)){
189 from = ((HybridRelationship)rel).getParentName();
190 to = ((HybridRelationship)rel).getHybridName();
191 }else if (rel.isInstanceOf(TaxonRelationship.class)){
192 from = ((TaxonRelationship)rel).getFromTaxon();
193 to = ((TaxonRelationship)rel).getToTaxon();
194 }else{
195 try {
196 Method fromMethod = rel.getClass().getMethod("getRelatedFrom");
197 Method toMethod = rel.getClass().getMethod("getRelatedFrom");
198 fromMethod.setAccessible(true);
199 toMethod.setAccessible(true);
200 from = (IdentifiableEntity<?>)fromMethod.invoke(rel);
201 to = (IdentifiableEntity<?>)toMethod.invoke(rel);
202 } catch (NoSuchMethodException | SecurityException | IllegalAccessException
203 | IllegalArgumentException | InvocationTargetException e) {
204 throw new RuntimeException(e);
205 }
206 }
207 String typeLabel = null;
208 if (type != null){
209 Representation typeRepr = type.getPreferredRepresentation(defaultLanguage);
210 if (typeRepr != null){
211 typeLabel = typeRepr.getAbbreviatedLabel();
212 }
213 if (isBlank(typeLabel) && typeRepr != null){
214 typeLabel = typeRepr.getLabel();
215 }
216 if (isBlank(typeLabel) ){
217 typeLabel = type.getSymbol();
218 }
219 if (isBlank(typeLabel)){
220 typeLabel = type.getTitleCache();
221 }
222 }
223 if (isBlank(typeLabel)){
224 typeLabel = "->";
225 }
226 String result = CdmUtils.concat(" ", new String[]{from == null ? null : from.getTitleCache(),
227 typeLabel, to == null? null : to.getTitleCache()});
228 return result;
229 }
230
231 private static String getCache(GatheringEvent gatheringEvent){
232 String ALTITUDE_PREFIX = "alt. ";
233 final String METER = "m";
234
235 String result = "";
236
237 //collector
238 AgentBase<?> collector = CdmBase.deproxy(gatheringEvent.getCollector());
239 String collectorStr = collector == null? null : collector.getTitleCache();
240 result = CdmUtils.concat(", ", result, collectorStr);
241
242 //gathering period
243 TimePeriod gatheringPeriod = gatheringEvent.getTimeperiod();
244 result = CdmUtils.concat(", ", result, (gatheringPeriod == null? null : gatheringPeriod.toString()));
245
246 //country
247 String strCountry = null;
248 NamedArea country = gatheringEvent.getCountry();
249 Representation repCountry = country == null ? null : country.getRepresentation(Language.DEFAULT());
250 strCountry = repCountry == null ? null: repCountry.getLabel();
251 result = CdmUtils.concat(", ", result, strCountry);
252
253 //locality
254 LanguageString locality = gatheringEvent.getLocality();
255 if (locality != null) {
256 result = CdmUtils.concat(", ", result, locality.getText());
257 }
258
259 //elevation
260 String elevationStr;
261 if (isNotBlank(gatheringEvent.getAbsoluteElevationText())){
262 elevationStr = gatheringEvent.getAbsoluteElevationText();
263 }else{
264 String text = gatheringEvent.getAbsoluteElevationText();
265 Integer min = gatheringEvent.getAbsoluteElevation();
266 Integer max = gatheringEvent.getAbsoluteElevationMax();
267 elevationStr = DistanceStringFormatter.distanceString(min, max, text, METER);
268 }
269 if (isNotBlank(elevationStr)){
270 result = CdmUtils.concat(", " , result, ALTITUDE_PREFIX);
271 result += elevationStr;
272 }
273
274 //exact locality
275 if (gatheringEvent.getExactLocation() != null){
276 String exactLocation = gatheringEvent.getExactLocation().toSexagesimalString(false, false);
277 result = CdmUtils.concat(", ", result, exactLocation);
278 }
279
280 return result;
281 }
282
283 private static String getCache(DeterminationEvent detEvent) {
284 //taxon
285 String taxonStr = null;
286 TaxonName taxonName = detEvent.getTaxonName();
287 TaxonBase<?> taxon = detEvent.getTaxon();
288 if (taxonName != null){
289 taxonStr = taxonName.getTitleCache();
290 }
291 if (isBlank(taxonStr) && taxon != null){
292 taxonStr = taxon.getTitleCache();
293 }
294 if (isBlank(taxonStr)){
295 taxonStr = "no or unlabled taxon";
296 }
297
298 //unit
299 SpecimenOrObservationBase<?> unit = detEvent.getIdentifiedUnit();
300 String unitStr;
301 if (unit != null){
302 unitStr = unit.getTitleCache();
303 if (isBlank(unitStr)){
304 unitStr = "Unlabled unit";
305 }
306 }else{
307 unitStr = "no unit";
308 }
309
310 String result = CdmUtils.concat(" determined as ", unitStr, taxonStr);
311
312 return result;
313 }
314
315 private static String getCache(TaxonNode taxonNode) {
316 String result = "";
317 Taxon taxon = taxonNode.getTaxon();
318 if (taxon != null){
319 result = taxon.getName() != null ? taxon.getName().getTitleCache() : taxon.getTitleCache();
320 }
321
322 final String invisible = "Invisible root of ";
323 String parentStr;
324 TaxonNode parentNode = taxonNode.getParent();
325 if (parentNode == null){
326 parentStr = invisible;
327 }else{
328 Taxon parentTaxon = parentNode.getTaxon();
329 if (parentTaxon == null){
330 parentStr = " (root of ";
331 }else{
332 TaxonName parentName = parentTaxon.getName();
333 if (parentName == null){
334 parentStr = " (child of " + parentTaxon.getTitleCache();
335 }else{
336 parentStr = " (child of " + parentName.getTitleCache();
337 }
338 parentStr += " in ";
339 }
340 }
341
342 //classification
343 Classification classification = taxonNode.getClassification();
344 String classificationStr ;
345 if (classification != null){
346 classificationStr = classification.getName() == null ? "classification:"+classification.getId() : classification.getName().getText();
347 if (isBlank(classificationStr)){
348 classificationStr = classification.toString();
349 }
350 }else{
351 classificationStr = "-no classification-"; //should not happen
352 }
353
354 result = CdmUtils.concat("", parentStr, classificationStr, parentStr == invisible? "" : ")");
355
356 return result;
357 }
358
359 private static String getCache(TypeDesignationBase<?> designation, Language defaultLanguage) {
360 designation = CdmBase.deproxy(designation);
361 //from
362 String fromString = null;
363 Set<TaxonName> from = designation.getTypifiedNames();
364 if(from != null){
365 for (TaxonName name : from){
366 fromString = CdmUtils.concat(",", fromString, name.getTitleCache());
367 }
368 }
369 //to
370 IdentifiableEntity<?> to = null;
371 String toStr = "";
372 if (designation.isInstanceOf(SpecimenTypeDesignation.class)){
373 to = ((SpecimenTypeDesignation)designation).getTypeSpecimen();
374 }else if (designation.isInstanceOf(NameTypeDesignation.class)){
375 to = ((NameTypeDesignation)designation).getTypeName();
376 }else if (designation.isInstanceOf(TextualTypeDesignation.class)){
377 toStr = ((TextualTypeDesignation)designation).getPreferredText(defaultLanguage);
378 }else{
379 throw new RuntimeException("Type Designation class not supported: " + designation.getClass().getName());
380 }
381 toStr = to == null? toStr : to.getTitleCache();
382 //status
383 String typeLabel = null;
384 TypeDesignationStatusBase<?> status = designation.getTypeStatus();
385 if (status != null){
386 Representation typeRepr = status.getPreferredRepresentation(defaultLanguage);
387 if (typeRepr != null){
388 typeLabel = typeRepr.getAbbreviatedLabel();
389 }
390 if (isBlank(typeLabel) && typeRepr != null){
391 typeLabel = typeRepr.getLabel();
392 }
393 if (isBlank(typeLabel) ){
394 typeLabel = status.getSymbol();
395 }
396 if (isBlank(typeLabel)){
397 typeLabel = status.getTitleCache();
398 }
399 }
400 if (isBlank(typeLabel)){
401 typeLabel = "->";
402 }
403 //concat
404 String result = CdmUtils.concat(" ", new String[]{fromString, typeLabel, toStr});
405 return result;
406 }
407
408 private static String getCache(HomotypicalGroup hg) {
409 String result = "";
410 for (TaxonName tnb : hg.getTypifiedNames()){
411 result = CdmUtils.concat(", ", result, tnb.getTitleCache());
412 }
413 if (isBlank(result)){
414 result = "No typified names";
415 }
416 return result;
417 }
418
419 private static String getCache(KeyStatement ks) {
420 String result = "";
421 LanguageString ls = ks.getPreferredLanguageString(Language.DEFAULT());
422 if (ls != null && CdmUtils.isNotBlank(ls.getText())){
423 result = ls.getText();
424 }else{
425 result = ks.toString();
426 }
427 return result;
428 }
429
430 private static String getCache(NomenclaturalStatus nomStatus) {
431 String result = nomStatus.getName().getTitleCache();
432 if (nomStatus.getType()!= null){
433 Representation rep = nomStatus.getType().getPreferredRepresentation(Language.DEFAULT());
434 if (rep != null){
435 result = CdmUtils.concat(": ", rep.getAbbreviatedLabel(), result);
436 }
437 }
438 return result;
439 }
440
441
442 private static boolean isNotBlank(String str){
443 return StringUtils.isNotBlank(str);
444 }
445
446 private static boolean isBlank(String str){
447 return StringUtils.isBlank(str);
448 }
449
450 //from taxeditor DescriptionHelper
451 private static String getCache(DescriptionElementBase element,
452 Language defaultLanguage) {
453
454 DescriptionBase<?> descr = element.getInDescription();
455 descr = CdmBase.deproxy(descr);
456
457 String mainElementLabel = mainElementLabel(descr);
458
459 String cache = null;
460 if (element instanceof TextData) {
461 LanguageString text = ((TextData) element).getPreferredLanguageString(defaultLanguage);
462 if (text != null) {
463 cache = text.getText();
464 }
465 cache = cache == null || isBlank(cache)? "empty" : StringUtils.truncate(cache, 20);
466 }else if (element instanceof CommonTaxonName) {
467 cache = ((CommonTaxonName) element).getName();
468 }else if (element instanceof TaxonInteraction) {
469 Taxon taxon2 = ((TaxonInteraction) element).getTaxon2();
470 if(taxon2 != null && taxon2.getName() != null){
471 cache = taxon2.getName().getTitleCache();
472 }else{
473 cache = "No taxon chosen";
474 }
475 }else if (element instanceof Distribution) {
476 Distribution distribution = (Distribution) element;
477
478
479 NamedArea area = distribution.getArea();
480 if(area != null){
481 cache = area.getLabel();
482
483 PresenceAbsenceTerm status = distribution.getStatus();
484 if (status == null){
485 cache += ", no status";
486 }else {
487 cache += ", " + status.getLabel();
488 }
489 }
490 }else if (element instanceof CategoricalData) {
491 CategoricalData categoricalData = (CategoricalData) element;
492
493 cache = CategoricalDataFormatter.NewInstance(new FormatKey[] {}).format(categoricalData);
494 }
495 String result = cache == null ? "" : cache;
496 result = concatWithMainElement(mainElementLabel, result);
497 return result;
498 }
499
500
501
502 private static String getCache(StateData stateData,
503 Language defaultLanguage) {
504 String cache = null;
505 if (stateData.getState()!= null) {
506 Representation rep = stateData.getState().getPreferredRepresentation(defaultLanguage);
507 if (rep != null) {
508 cache = rep.getLabel();
509 }
510 }
511 cache = isBlank(cache)? stateData.getUuid().toString() : cache;
512 DescriptionBase<?> desc = stateData.getCategoricalData() == null? null : stateData.getCategoricalData().getInDescription();
513 String mainElementLabel = mainElementLabel(desc);
514 return concatWithMainElement(mainElementLabel, cache);
515 }
516
517
518 /**
519 * Returns the label of the main element (taxon, specimen or name) for
520 * the given description.
521 */
522 private static String mainElementLabel(DescriptionBase<?> descr) {
523 String mainElementLabel = null;
524 if (descr != null){
525 if (descr.isInstanceOf(TaxonDescription.class)){
526 Taxon taxon = CdmBase.deproxy(descr, TaxonDescription.class).getTaxon();
527 if (taxon != null){
528 mainElementLabel = taxon.getTitleCache();
529 }
530 }else if (descr.isInstanceOf(SpecimenDescription.class)){
531 SpecimenOrObservationBase<?> specimen = CdmBase.deproxy(descr, SpecimenDescription.class).getDescribedSpecimenOrObservation();
532 if (specimen != null){
533 mainElementLabel = specimen.getTitleCache();
534 }
535 }else if (descr.isInstanceOf(TaxonNameDescription.class)){
536 TaxonName name = CdmBase.deproxy(descr, TaxonNameDescription.class).getTaxonName();
537 if (name != null){
538 mainElementLabel = name.getTitleCache();
539 }
540 }
541 }
542 return mainElementLabel;
543 }
544
545 private static String concatWithMainElement(String mainElementLabel, String result) {
546 if (isNotBlank(mainElementLabel)){
547 result = CdmUtils.concat(" ", result, "(" + mainElementLabel + ")");
548 }
549 return result;
550 }
551 }