2 * Copyright (C) 2018 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
.taxon
;
11 import java
.util
.ArrayList
;
12 import java
.util
.List
;
14 import org
.apache
.commons
.lang3
.StringUtils
;
16 import eu
.etaxonomy
.cdm
.common
.UTF8
;
17 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
18 import eu
.etaxonomy
.cdm
.model
.agent
.Team
;
19 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
20 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
21 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
22 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
23 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
24 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
25 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
26 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
27 import eu
.etaxonomy
.cdm
.model
.term
.Representation
;
28 import eu
.etaxonomy
.cdm
.ref
.TypedEntityReference
;
29 import eu
.etaxonomy
.cdm
.strategy
.cache
.TagEnum
;
30 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
31 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedTextBuilder
;
32 import eu
.etaxonomy
.cdm
.strategy
.cache
.agent
.PersonDefaultCacheStrategy
;
33 import eu
.etaxonomy
.cdm
.strategy
.cache
.agent
.TeamDefaultCacheStrategy
;
36 * Formatter for TaxonRelationships.
41 public class TaxonRelationshipFormatter
{
43 private static final String DOUBTFUL_TAXON_MARKER
= "?" + UTF8
.NARROW_NO_BREAK
;
44 private static final String REL_SEC
= ", rel. sec. ";
45 private static final String ERR_SEC
= ", err. sec. ";
46 private static final String SYN_SEC
= ", syn. sec. ";
47 private static final String UNKNOWN_SEC
= "???";
48 private static final String NON_SEPARATOR
= ", non ";
49 private static final String QUOTE_START
= "\""; //TODO
50 private static final String QUOTE_END
= "\""; //TODO
51 private static final String AUCT
= "auct.";
52 private static final String SENSU_SEPARATOR
= " sensu ";
53 private static final String SEC_SEPARATOR
= " sec. ";
54 private static final String DETAIL_SEPARATOR
= ": ";
55 private static final String INVERT_SYMBOL
= "<-"; //TODO
56 private static final String UNDEFINED_SYMBOL
= "??"; //TODO
58 private static TaxonRelationshipFormatter instance
;
60 // ************************* FACTORY ************************/
62 public static TaxonRelationshipFormatter
NewInstance(){
63 return new TaxonRelationshipFormatter();
66 public static TaxonRelationshipFormatter
INSTANCE(){
67 if (instance
== null){
68 instance
= NewInstance();
73 // ******************* CONSTRUCTOR ************************/
75 private TaxonRelationshipFormatter(){}
77 // ********************** METHODS ***************************/
79 public List
<TaggedText
> getTaggedText(TaxonRelationship taxonRelationship
, boolean inverse
, List
<Language
> languages
) {
80 return getTaggedText(taxonRelationship
, inverse
, languages
, false);
83 public List
<TaggedText
> getTaggedText(TaxonRelationship taxonRelationship
, boolean inverse
,
84 List
<Language
> languages
, boolean withoutName
) {
86 if (taxonRelationship
== null){
90 TaxonRelationshipType type
= taxonRelationship
.getType();
91 boolean isMisapplied
= (type
== null ?
false : type
.isMisappliedName() && inverse
);
92 boolean isSynonym
= type
== null?
false : type
.isAnySynonym();
94 Taxon relatedTaxon
= inverse? taxonRelationship
.getFromTaxon()
95 : taxonRelationship
.getToTaxon();
97 if (relatedTaxon
== null){
101 String doubtfulTaxonStr
= relatedTaxon
.isDoubtful() ? DOUBTFUL_TAXON_MARKER
: "";
102 String doubtfulRelationStr
= taxonRelationship
.isDoubtful() ?
"?" : "";
104 TaxonName name
= relatedTaxon
.getName();
106 TaggedTextBuilder builder
= new TaggedTextBuilder();
109 String symbol
= doubtfulRelationStr
+ getSymbol(type
, inverse
, languages
);
110 builder
.add(TagEnum
.symbol
, symbol
);
116 String startQuote
= " " + doubtfulTaxonStr
+ QUOTE_START
;
117 builder
.addSeparator(startQuote
);
120 List
<TaggedText
> nameCacheTags
= getNameCacheTags(name
);
121 builder
.addAll(nameCacheTags
);
124 String endQuote
= QUOTE_END
;
125 builder
.add(TagEnum
.postSeparator
, endQuote
);
127 builder
.addSeparator(" " + doubtfulTaxonStr
);
128 //name full title cache
129 List
<TaggedText
> nameCacheTags
= getNameTitleCacheTags(name
);
130 builder
.addAll(nameCacheTags
);
133 if (isNotBlank(doubtfulTaxonStr
)){
134 builder
.addSeparator(" " + doubtfulTaxonStr
);
138 //sec/sensu (+ Separatoren?)
139 if (isNotBlank(relatedTaxon
.getAppendedPhrase())){
140 builder
.addWhitespace();
141 builder
.add(TagEnum
.appendedPhrase
, relatedTaxon
.getAppendedPhrase());
143 List
<TaggedText
> secTags
= getReferenceTags(relatedTaxon
.getSec(), relatedTaxon
.getSecMicroReference(),
144 /* isMisapplied,*/ false);
145 if (!secTags
.isEmpty()) {
146 builder
.addSeparator(isMisapplied? SENSU_SEPARATOR
: SEC_SEPARATOR
);
147 builder
.addAll(secTags
);
148 }else if (isBlank(relatedTaxon
.getAppendedPhrase())) {
150 builder
.addWhitespace();
151 //TODO type unclear sensuReference(?)
152 builder
.add(TagEnum
.appendedPhrase
, AUCT
);
154 builder
.addSeparator(SEC_SEPARATOR
+ UNKNOWN_SEC
);
159 if (isMisapplied
&& name
!= null){
160 if (isNotBlank(name
.getAuthorshipCache())){
161 builder
.addSeparator(NON_SEPARATOR
);
162 builder
.add(TagEnum
.authors
, name
.getAuthorshipCache().trim());
168 if (isProParteMAN(type
, inverse
)) {
169 builder
.addSeparator(", ");
171 builder
.add(TagEnum
.symbol
, symbol
);
172 } else if (isPartialMAN(type
, inverse
)) {
173 builder
.addSeparator(", ");
175 builder
.add(TagEnum
.symbol
, symbol
);
180 List
<TaggedText
> relSecTags
= getReferenceTags(taxonRelationship
.getCitation(),
181 taxonRelationship
.getCitationMicroReference(),true);
182 if (!relSecTags
.isEmpty()){
183 builder
.addSeparator(isSynonym ? SYN_SEC
: isMisapplied ? ERR_SEC
: REL_SEC
);
184 builder
.addAll(relSecTags
);
187 return builder
.getTaggedText();
190 private List
<TaggedText
> getReferenceTags(Reference ref
, String detail
, /*boolean isSensu,*/ boolean isRelation
) {
191 List
<TaggedText
> result
= new ArrayList
<>();
195 TeamOrPersonBase
<?
> author
= CdmBase
.deproxy(ref
.getAuthorship());
197 //TODO distinguish linked and unlinked usage,
198 // if reference is not linked short citation should only be used
199 // if both author and year exists, also initials should be added in this case
201 if (ref
.isProtectedTitleCache() == false &&
203 isNotBlank(author
.getTitleCache())){
204 if (author
.isInstanceOf(Person
.class)){
205 secRef
= PersonDefaultCacheStrategy
.INSTANCE().getFamilyTitle((Person
)author
);
208 secRef
= TeamDefaultCacheStrategy
.INSTANCE_ET_AL_2().getFamilyTitle((Team
)author
);
210 if (isNotBlank(ref
.getYear())){
211 secRef
+= " " + ref
.getYear();
214 secRef
= ref
.getTitleCache();
216 TagEnum secType
= /*isSensu? TagEnum.sensuReference : */ isRelation? TagEnum
.relSecReference
: TagEnum
.secReference
;
217 TaggedText refTag
= TaggedText
.NewInstance(secType
, secRef
);
218 refTag
.setEntityReference(new TypedEntityReference
<>(CdmBase
.deproxy(ref
).getClass(), ref
.getUuid()));
221 if (isNotBlank(detail
)){
222 result
.add(TaggedText
.NewSeparatorInstance(DETAIL_SEPARATOR
));
223 TagEnum detailType
= /*isSensu? TagEnum.sensuMicroReference : */ isRelation? TagEnum
.relSecMicroReference
:TagEnum
.secMicroReference
;
224 TaggedText microTag
= TaggedText
.NewInstance(detailType
, detail
);
225 result
.add(microTag
);
230 private List
<TaggedText
> getNameCacheTags(TaxonName name
) {
231 List
<TaggedText
> result
= name
.cacheStrategy().getTaggedName(name
);
235 private List
<TaggedText
> getNameTitleCacheTags(TaxonName name
) {
238 List
<TaggedText
> result
= name
.cacheStrategy().getTaggedFullTitle(name
);
243 * @param type the taxon relationship type
244 * @param inverse is the relationship used inverse
245 * @param languages list of preferred languages
246 * @return the symbol for the taxon relationship
248 private String
getSymbol(TaxonRelationshipType type
, boolean inverse
, List
<Language
> languages
) {
250 return UNDEFINED_SYMBOL
;
254 String symbol
= inverse? type
.getInverseSymbol():type
.getSymbol();
255 if (isNotBlank(symbol
)){
256 //handle p.p. MAN specific #10082
257 if (isProParteMAN(type
, inverse
) || isPartialMAN(type
, inverse
)) {
258 return TaxonRelationshipType
.MISAPPLIED_NAME_FOR().getInverseSymbol();
263 boolean isSymmetric
= type
.isSymmetric();
264 //symmetric inverted symbol
265 String invertedSymbol
= inverse? type
.getSymbol() : type
.getInverseSymbol();
266 if (isSymmetric
&& isNotBlank(invertedSymbol
)){
267 return invertedSymbol
;
271 Representation representation
= inverse? type
.getPreferredRepresentation(languages
): type
.getPreferredInverseRepresentation(languages
);
272 String abbrevLabel
= representation
.getAbbreviatedLabel();
273 if (isNotBlank(abbrevLabel
)){
277 //symmetric inverted abbrev label
278 Representation invertedRepresentation
= inverse? type
.getPreferredInverseRepresentation(languages
):type
.getPreferredRepresentation(languages
);
279 String invertedAbbrevLabel
= invertedRepresentation
.getAbbreviatedLabel();
280 if (isSymmetric
&& isNotBlank(invertedAbbrevLabel
)){
281 return invertedAbbrevLabel
;
284 //non symmetric inverted symbol
285 if (!isSymmetric
&& isNotBlank(invertedSymbol
)){
286 return INVERT_SYMBOL
+ invertedSymbol
;
289 //non symmetric inverted abbrev label
290 if (!isSymmetric
&& isNotBlank(invertedAbbrevLabel
)){
291 return INVERT_SYMBOL
+ invertedAbbrevLabel
;
294 return UNDEFINED_SYMBOL
;
297 private boolean isPartialMAN(TaxonRelationshipType type
, boolean inverse
) {
298 return inverse
&& type
.getUuid().equals(TaxonRelationshipType
.uuidPartialMisappliedNameFor
);
301 private boolean isProParteMAN(TaxonRelationshipType type
, boolean inverse
) {
302 return inverse
&& type
.getUuid().equals(TaxonRelationshipType
.uuidProParteMisappliedNameFor
);
305 private boolean isNotBlank(String str
) {
306 return StringUtils
.isNotBlank(str
);
309 private boolean isBlank(String str
) {
310 return StringUtils
.isBlank(str
);