2 * Copyright (C) 2021 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
6 * The contents of this file are subject to the Mozilla Public License Version 1.1
7 * See LICENSE.TXT at the top of this package for the full license terms.
9 package eu
.etaxonomy
.cdm
.format
;
11 import java
.lang
.reflect
.InvocationTargetException
;
12 import java
.lang
.reflect
.Method
;
15 import org
.apache
.commons
.lang3
.StringUtils
;
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
;
79 public class ReferencingObjectFormatter
{
81 public static String
format(CdmBase element
, Language defaultLanguage
) {
82 return format(element
, null, defaultLanguage
);
85 public static String
format(CdmBase element
, String target
, Language defaultLanguage
) {
87 String resultString
= 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
97 String sourceObjectTitle
= "sourced object data not available (#5743)";
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
;
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
);
150 // TODO write return texts for HomotypicalGroup, etc.
151 resultString
= element
.toString();
154 if (resultString
== null){
155 resultString
= element
.toString();
160 private static String
getCache(DescriptionElementSource source
, Language defaultLanguage
) {
161 DescriptionElementBase sourcedElement
= source
.getSourcedElement();
163 if (sourcedElement
== null){
164 return "Source orphaned, not attached to a fact";
166 String superLabel
= getDescribedObjectLabel(sourcedElement
.getInDescription());
167 String result
= CdmUtils
.concat(": ", DescriptionElementFormatter
.format(sourcedElement
, defaultLanguage
), superLabel
);
171 private static String
getDescribedObjectLabel(DescriptionBase
<?
> inDescription
) {
172 IDescribable
<?
> entity
= inDescription
.describedEntity();
174 return entity
.getTitleCache();
176 return inDescription
.getTitleCache();
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();
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
);
207 String typeLabel
= null;
209 Representation typeRepr
= type
.getPreferredRepresentation(defaultLanguage
);
210 if (typeRepr
!= null){
211 typeLabel
= typeRepr
.getAbbreviatedLabel();
213 if (isBlank(typeLabel
) && typeRepr
!= null){
214 typeLabel
= typeRepr
.getLabel();
216 if (isBlank(typeLabel
) ){
217 typeLabel
= type
.getSymbol();
219 if (isBlank(typeLabel
)){
220 typeLabel
= type
.getTitleCache();
223 if (isBlank(typeLabel
)){
226 String result
= CdmUtils
.concat(" ", new String
[]{from
== null ?
null : from
.getTitleCache(),
227 typeLabel
, to
== null?
null : to
.getTitleCache()});
231 private static String
getCache(GatheringEvent gatheringEvent
){
232 String ALTITUDE_PREFIX
= "alt. ";
233 final String METER
= "m";
238 AgentBase
<?
> collector
= CdmBase
.deproxy(gatheringEvent
.getCollector());
239 String collectorStr
= collector
== null?
null : collector
.getTitleCache();
240 result
= CdmUtils
.concat(", ", result
, collectorStr
);
243 TimePeriod gatheringPeriod
= gatheringEvent
.getTimeperiod();
244 result
= CdmUtils
.concat(", ", result
, (gatheringPeriod
== null?
null : gatheringPeriod
.toString()));
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
);
254 LanguageString locality
= gatheringEvent
.getLocality();
255 if (locality
!= null) {
256 result
= CdmUtils
.concat(", ", result
, locality
.getText());
261 if (isNotBlank(gatheringEvent
.getAbsoluteElevationText())){
262 elevationStr
= gatheringEvent
.getAbsoluteElevationText();
264 String text
= gatheringEvent
.getAbsoluteElevationText();
265 Integer min
= gatheringEvent
.getAbsoluteElevation();
266 Integer max
= gatheringEvent
.getAbsoluteElevationMax();
267 elevationStr
= DistanceStringFormatter
.distanceString(min
, max
, text
, METER
);
269 if (isNotBlank(elevationStr
)){
270 result
= CdmUtils
.concat(", " , result
, ALTITUDE_PREFIX
);
271 result
+= elevationStr
;
275 if (gatheringEvent
.getExactLocation() != null){
276 String exactLocation
= gatheringEvent
.getExactLocation().toSexagesimalString(false, false);
277 result
= CdmUtils
.concat(", ", result
, exactLocation
);
283 private static String
getCache(DeterminationEvent detEvent
) {
285 String taxonStr
= null;
286 TaxonName taxonName
= detEvent
.getTaxonName();
287 TaxonBase
<?
> taxon
= detEvent
.getTaxon();
288 if (taxonName
!= null){
289 taxonStr
= taxonName
.getTitleCache();
291 if (isBlank(taxonStr
) && taxon
!= null){
292 taxonStr
= taxon
.getTitleCache();
294 if (isBlank(taxonStr
)){
295 taxonStr
= "no or unlabled taxon";
299 SpecimenOrObservationBase
<?
> unit
= detEvent
.getIdentifiedUnit();
302 unitStr
= unit
.getTitleCache();
303 if (isBlank(unitStr
)){
304 unitStr
= "Unlabled unit";
310 String result
= CdmUtils
.concat(" determined as ", unitStr
, taxonStr
);
315 private static String
getCache(TaxonNode taxonNode
) {
317 Taxon taxon
= taxonNode
.getTaxon();
319 result
= taxon
.getName() != null ? taxon
.getName().getTitleCache() : taxon
.getTitleCache();
322 final String invisible
= "Invisible root of ";
324 TaxonNode parentNode
= taxonNode
.getParent();
325 if (parentNode
== null){
326 parentStr
= invisible
;
328 Taxon parentTaxon
= parentNode
.getTaxon();
329 if (parentTaxon
== null){
330 parentStr
= " (root of ";
332 TaxonName parentName
= parentTaxon
.getName();
333 if (parentName
== null){
334 parentStr
= " (child of " + parentTaxon
.getTitleCache();
336 parentStr
= " (child of " + parentName
.getTitleCache();
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();
351 classificationStr
= "-no classification-"; //should not happen
354 result
= CdmUtils
.concat("", parentStr
, classificationStr
, parentStr
== invisible?
"" : ")");
359 private static String
getCache(TypeDesignationBase
<?
> designation
, Language defaultLanguage
) {
360 designation
= CdmBase
.deproxy(designation
);
362 String fromString
= null;
363 Set
<TaxonName
> from
= designation
.getTypifiedNames();
365 for (TaxonName name
: from
){
366 fromString
= CdmUtils
.concat(",", fromString
, name
.getTitleCache());
370 IdentifiableEntity
<?
> to
= null;
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
);
379 throw new RuntimeException("Type Designation class not supported: " + designation
.getClass().getName());
381 toStr
= to
== null? toStr
: to
.getTitleCache();
383 String typeLabel
= null;
384 TypeDesignationStatusBase
<?
> status
= designation
.getTypeStatus();
386 Representation typeRepr
= status
.getPreferredRepresentation(defaultLanguage
);
387 if (typeRepr
!= null){
388 typeLabel
= typeRepr
.getAbbreviatedLabel();
390 if (isBlank(typeLabel
) && typeRepr
!= null){
391 typeLabel
= typeRepr
.getLabel();
393 if (isBlank(typeLabel
) ){
394 typeLabel
= status
.getSymbol();
396 if (isBlank(typeLabel
)){
397 typeLabel
= status
.getTitleCache();
400 if (isBlank(typeLabel
)){
404 String result
= CdmUtils
.concat(" ", new String
[]{fromString
, typeLabel
, toStr
});
408 private static String
getCache(HomotypicalGroup hg
) {
410 for (TaxonName tnb
: hg
.getTypifiedNames()){
411 result
= CdmUtils
.concat(", ", result
, tnb
.getTitleCache());
413 if (isBlank(result
)){
414 result
= "No typified names";
419 private static String
getCache(KeyStatement ks
) {
421 LanguageString ls
= ks
.getPreferredLanguageString(Language
.DEFAULT());
422 if (ls
!= null && CdmUtils
.isNotBlank(ls
.getText())){
423 result
= ls
.getText();
425 result
= ks
.toString();
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());
435 result
= CdmUtils
.concat(": ", rep
.getAbbreviatedLabel(), result
);
442 private static boolean isNotBlank(String str
){
443 return StringUtils
.isNotBlank(str
);
446 private static boolean isBlank(String str
){
447 return StringUtils
.isBlank(str
);
450 //from taxeditor DescriptionHelper
451 private static String
getCache(DescriptionElementBase element
,
452 Language defaultLanguage
) {
454 DescriptionBase
<?
> descr
= element
.getInDescription();
455 descr
= CdmBase
.deproxy(descr
);
457 String mainElementLabel
= mainElementLabel(descr
);
460 if (element
instanceof TextData
) {
461 LanguageString text
= ((TextData
) element
).getPreferredLanguageString(defaultLanguage
);
463 cache
= text
.getText();
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();
473 cache
= "No taxon chosen";
475 }else if (element
instanceof Distribution
) {
476 Distribution distribution
= (Distribution
) element
;
479 NamedArea area
= distribution
.getArea();
481 cache
= area
.getLabel();
483 PresenceAbsenceTerm status
= distribution
.getStatus();
485 cache
+= ", no status";
487 cache
+= ", " + status
.getLabel();
490 }else if (element
instanceof CategoricalData
) {
491 CategoricalData categoricalData
= (CategoricalData
) element
;
493 cache
= CategoricalDataFormatter
.NewInstance(new FormatKey
[] {}).format(categoricalData
);
495 String result
= cache
== null ?
"" : cache
;
496 result
= concatWithMainElement(mainElementLabel
, result
);
502 private static String
getCache(StateData stateData
,
503 Language defaultLanguage
) {
505 if (stateData
.getState()!= null) {
506 Representation rep
= stateData
.getState().getPreferredRepresentation(defaultLanguage
);
508 cache
= rep
.getLabel();
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
);
519 * Returns the label of the main element (taxon, specimen or name) for
520 * the given description.
522 private static String
mainElementLabel(DescriptionBase
<?
> descr
) {
523 String mainElementLabel
= null;
525 if (descr
.isInstanceOf(TaxonDescription
.class)){
526 Taxon taxon
= CdmBase
.deproxy(descr
, TaxonDescription
.class).getTaxon();
528 mainElementLabel
= taxon
.getTitleCache();
530 }else if (descr
.isInstanceOf(SpecimenDescription
.class)){
531 SpecimenOrObservationBase
<?
> specimen
= CdmBase
.deproxy(descr
, SpecimenDescription
.class).getDescribedSpecimenOrObservation();
532 if (specimen
!= null){
533 mainElementLabel
= specimen
.getTitleCache();
535 }else if (descr
.isInstanceOf(TaxonNameDescription
.class)){
536 TaxonName name
= CdmBase
.deproxy(descr
, TaxonNameDescription
.class).getTaxonName();
538 mainElementLabel
= name
.getTitleCache();
542 return mainElementLabel
;
545 private static String
concatWithMainElement(String mainElementLabel
, String result
) {
546 if (isNotBlank(mainElementLabel
)){
547 result
= CdmUtils
.concat(" ", result
, "(" + mainElementLabel
+ ")");