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
.codehaus
.plexus
.util
.StringUtils
;
16 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
17 import eu
.etaxonomy
.cdm
.common
.UTF8
;
18 import eu
.etaxonomy
.cdm
.model
.agent
.Person
;
19 import eu
.etaxonomy
.cdm
.model
.agent
.Team
;
20 import eu
.etaxonomy
.cdm
.model
.agent
.TeamOrPersonBase
;
21 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
22 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
23 import eu
.etaxonomy
.cdm
.model
.common
.Representation
;
24 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
25 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
26 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
27 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
28 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
29 import eu
.etaxonomy
.cdm
.ref
.TypedEntityReference
;
30 import eu
.etaxonomy
.cdm
.strategy
.cache
.TagEnum
;
31 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
32 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedTextBuilder
;
33 import eu
.etaxonomy
.cdm
.strategy
.cache
.agent
.TeamDefaultCacheStrategy
;
40 public class TaxonRelationshipFormatter
{
45 private static final String DOUBTFUL_TAXON_MARKER
= "?" + UTF8
.NARROW_NO_BREAK
;
46 private static final String REL_SEC
= ", rel. sec. ";
47 private static final String ERR_SEC
= ", err. sec. ";
48 private static final String SYN_SEC
= ", syn. sec. ";
49 private static final String UNKNOWN_SEC
= "???";
50 private static final String NON_SEPARATOR
= ", non ";
51 private static final String QUOTE_START
= "\""; //TODO
52 private static final String QUOTE_END
= "\""; //TODO
53 private static final String AUCT
= "auct.";
54 private static final String SENSU_SEPARATOR
= " sensu ";
55 private static final String SEC_SEPARATOR
= " sec. ";
56 private static final String DETAIL_SEPARATOR
= ": ";
57 private static final String INVERT_SYMBOL
= "<-"; //TODO
58 private static final String UNDEFINED_SYMBOL
= "??"; //TODO
60 public List
<TaggedText
> getTaggedText(TaxonRelationship taxonRelationship
, boolean reverse
, List
<Language
> languages
) {
62 if (taxonRelationship
== null){
66 TaxonRelationshipType type
= taxonRelationship
.getType();
67 boolean isMisapplied
= type
== null ?
false : type
.isMisappliedNameOrInvalidDesignation() && reverse
;
68 boolean isSynonym
= type
== null?
false : type
.isAnySynonym();
71 Taxon relatedTaxon
= reverse? taxonRelationship
.getFromTaxon()
72 : taxonRelationship
.getToTaxon();
74 if (relatedTaxon
== null){
78 String doubtfulTaxonStr
= relatedTaxon
.isDoubtful() ? DOUBTFUL_TAXON_MARKER
: "";
79 String doubtfulRelationStr
= taxonRelationship
.isDoubtful() ?
"?" : "";
82 TaxonName name
= relatedTaxon
.getName();
84 TaggedTextBuilder builder
= new TaggedTextBuilder();
87 String symbol
= doubtfulRelationStr
+ getSymbol(type
, reverse
, languages
);
88 builder
.add(TagEnum
.symbol
, symbol
);
93 String startQuote
= " " + doubtfulTaxonStr
+ QUOTE_START
;
94 builder
.addSeparator(startQuote
);
97 List
<TaggedText
> nameCacheTags
= getNameCacheTags(name
);
98 builder
.addAll(nameCacheTags
);
101 String endQuote
= QUOTE_END
;
102 builder
.add(TagEnum
.postSeparator
, endQuote
);
104 builder
.addSeparator(" " + doubtfulTaxonStr
);
105 //name full title cache
106 List
<TaggedText
> nameCacheTags
= getNameTitleCacheTags(name
);
107 builder
.addAll(nameCacheTags
);
111 //sec/sensu (+ Separatoren?)
112 if (isNotBlank(relatedTaxon
.getAppendedPhrase())){
113 builder
.addWhitespace();
114 builder
.add(TagEnum
.appendedPhrase
, relatedTaxon
.getAppendedPhrase());
116 List
<TaggedText
> secTags
= getSensuTags(relatedTaxon
.getSec(), relatedTaxon
.getSecMicroReference(),
117 /* isMisapplied,*/ false);
118 if (!secTags
.isEmpty()) {
119 builder
.addSeparator(isMisapplied? SENSU_SEPARATOR
: SEC_SEPARATOR
);
120 builder
.addAll(secTags
);
121 }else if (isBlank(relatedTaxon
.getAppendedPhrase())) {
123 builder
.addWhitespace();
124 //TODO type unclear sensuReference(?)
125 builder
.add(TagEnum
.appendedPhrase
, AUCT
);
127 builder
.addSeparator(SEC_SEPARATOR
+ UNKNOWN_SEC
);
132 if (isMisapplied
&& name
!= null){
133 if (isNotBlank(name
.getAuthorshipCache())){
134 builder
.addSeparator(NON_SEPARATOR
);
135 builder
.add(TagEnum
.authors
, name
.getAuthorshipCache().trim());
139 List
<TaggedText
> relSecTags
= getSensuTags(taxonRelationship
.getCitation(),
140 taxonRelationship
.getCitationMicroReference(),true);
141 if (!relSecTags
.isEmpty()){
142 builder
.addSeparator(isSynonym ? SYN_SEC
: isMisapplied ? ERR_SEC
: REL_SEC
);
143 builder
.addAll(relSecTags
);
146 return builder
.getTaggedText();
149 private List
<TaggedText
> getSensuTags(Reference ref
, String detail
, /*boolean isSensu,*/ boolean isRelation
) {
150 List
<TaggedText
> result
= new ArrayList
<>();
154 TeamOrPersonBase
<?
> author
= ref
.getAuthorship();
155 //TODO distinguish linked and unlinked usage,
156 // if reference is not linked short citation should only be used
157 // if both author and year exists, also initials should be added in this case
159 if (ref
.isProtectedTitleCache() == false &&
161 isNotBlank(author
.getTitleCache())){
162 //TODO move to authorFormatter
163 String familyNames
= getFamilyNames(author
);
164 if (isNotBlank(familyNames
)){
165 secRef
= familyNames
;
167 secRef
= ref
.getAuthorship().getTitleCache();
169 if (isNotBlank(ref
.getYear())){
170 secRef
+= " " + ref
.getYear();
173 secRef
= ref
.getTitleCache();
175 TagEnum secType
= /*isSensu? TagEnum.sensuReference : */ isRelation? TagEnum
.relSecReference
: TagEnum
.secReference
;
176 TaggedText refTag
= TaggedText
.NewInstance(secType
, secRef
);
177 refTag
.setEntityReference(new TypedEntityReference
<>(CdmBase
.deproxy(ref
).getClass(), ref
.getUuid()));
180 if (isNotBlank(detail
)){
181 result
.add(TaggedText
.NewSeparatorInstance(DETAIL_SEPARATOR
));
182 TagEnum detailType
= /*isSensu? TagEnum.sensuMicroReference : */ isRelation? TagEnum
.relSecMicroReference
:TagEnum
.secMicroReference
;
183 TaggedText microTag
= TaggedText
.NewInstance(detailType
, detail
);
184 result
.add(microTag
);
193 private String
getFamilyNames(TeamOrPersonBase
<?
> author
) {
194 if (author
.isInstanceOf(Person
.class)){
195 Person person
= CdmBase
.deproxy(author
, Person
.class);
196 return isNotBlank(person
.getFamilyName())? person
.getFamilyName() : null;
198 Team team
= CdmBase
.deproxy(author
, Team
.class);
199 String result
= null;
200 int n
= team
.getTeamMembers().size();
202 if (team
.isHasMoreMembers()){
205 for (Person member
: team
.getTeamMembers()){
206 String name
= isNotBlank(member
.getFamilyName())? member
.getFamilyName(): member
.getTitleCache();
207 String separator
= index
< n ? TeamDefaultCacheStrategy
.STD_TEAM_CONCATINATION
: TeamDefaultCacheStrategy
.FINAL_TEAM_CONCATINATION
;
208 result
= CdmUtils
.concat(separator
, result
, name
);
211 if (team
.isHasMoreMembers()){
213 result
+= TeamDefaultCacheStrategy
.ET_AL_TEAM_CONCATINATION_FULL
+ "al.";
224 private List
<TaggedText
> getNameCacheTags(TaxonName name
) {
225 List
<TaggedText
> result
= name
.getCacheStrategy().getTaggedName(name
);
229 private List
<TaggedText
> getNameTitleCacheTags(TaxonName name
) {
232 List
<TaggedText
> result
= name
.getCacheStrategy().getTaggedFullTitle(name
);
238 * @param type the taxon relationship type
239 * @param reverse is the relationship used reverse
240 * @param languages list of preferred languages
241 * @return the symbol for the taxon relationship
243 private String
getSymbol(TaxonRelationshipType type
, boolean reverse
, List
<Language
> languages
) {
245 return UNDEFINED_SYMBOL
;
249 String symbol
= reverse? type
.getInverseSymbol():type
.getSymbol();
250 if (isNotBlank(symbol
)){
254 boolean isSymmetric
= type
.isSymmetric();
255 //symmetric inverted symbol
256 String invertedSymbol
= reverse? type
.getSymbol() : type
.getInverseSymbol();
257 if (isSymmetric
&& isNotBlank(invertedSymbol
)){
258 return invertedSymbol
;
262 Representation representation
= reverse? type
.getPreferredRepresentation(languages
): type
.getPreferredInverseRepresentation(languages
);
263 String abbrevLabel
= representation
.getAbbreviatedLabel();
264 if (isNotBlank(abbrevLabel
)){
268 //symmetric inverted abbrev label
269 Representation invertedRepresentation
= reverse? type
.getPreferredInverseRepresentation(languages
):type
.getPreferredRepresentation(languages
);
270 String invertedAbbrevLabel
= invertedRepresentation
.getAbbreviatedLabel();
271 if (isSymmetric
&& isNotBlank(invertedAbbrevLabel
)){
272 return invertedAbbrevLabel
;
275 //non symmetric inverted symbol
276 if (!isSymmetric
&& isNotBlank(invertedSymbol
)){
277 return INVERT_SYMBOL
+ invertedSymbol
;
280 //non symmetric inverted abbrev label
281 if (!isSymmetric
&& isNotBlank(invertedAbbrevLabel
)){
282 return INVERT_SYMBOL
+ invertedAbbrevLabel
;
285 return UNDEFINED_SYMBOL
;
288 private boolean isNotBlank(String str
) {
289 return StringUtils
.isNotBlank(str
);
292 private boolean isBlank(String str
) {
293 return StringUtils
.isBlank(str
);