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
.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
.common
.Representation
;
23 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
24 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
25 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
26 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationship
;
27 import eu
.etaxonomy
.cdm
.model
.taxon
.TaxonRelationshipType
;
28 import eu
.etaxonomy
.cdm
.ref
.TypedEntityReference
;
29 import eu
.etaxonomy
.cdm
.strategy
.cache
.TagEnum
;
30 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
37 public class TaxonRelationshipFormatter
{
39 private static final String REL_SEC
= ", rel. sec. ";
40 private static final String ERR_SEC
= ", err. sec. ";
41 private static final String SYN_SEC
= ", syn. sec. ";
42 private static final String UNKNOWN_SEC
= "???";
43 private static final String NON_SEPARATOR
= ", non ";
44 private static final String QUOTE_START
= "\""; //TODO
45 private static final String QUOTE_END
= "\""; //TODO
46 private static final String AUCT
= "auct.";
47 private static final String SENSU_SEPARATOR
= " sensu ";
48 private static final String SEC_SEPARATOR
= " sec. ";
49 private static final String DETAIL_SEPARATOR
= ": ";
50 private static final String INVERT_SYMBOL
= "<-"; //TODO
51 private static final String UNDEFINED_SYMBOL
= "??"; //TODO
53 public List
<TaggedText
> getTaggedText(TaxonRelationship taxonRelationship
, boolean reverse
, List
<Language
> languages
) {
55 if (taxonRelationship
== null){
59 TaxonRelationshipType type
= taxonRelationship
.getType();
60 boolean isMisapplied
= type
== null ?
false : type
.isAnyMisappliedName() && reverse
;
61 boolean isSynonym
= type
== null?
false : type
.isAnySynonym();
63 Taxon relatedTaxon
= reverse? taxonRelationship
.getFromTaxon()
64 : taxonRelationship
.getToTaxon();
66 if (relatedTaxon
== null){
69 TaxonName name
= relatedTaxon
.getName();
71 List
<TaggedText
> tags
= new ArrayList
<>();
74 String symbol
= getSymbol(type
, reverse
, languages
);
75 tags
.add(TaggedText
.NewInstance(TagEnum
.symbol
, symbol
));
78 tags
.add(TaggedText
.NewWhitespaceInstance());
83 String startQuote
= QUOTE_START
;
84 tags
.add(TaggedText
.NewSeparatorInstance(startQuote
));
87 List
<TaggedText
> nameCacheTags
= getNameCacheTags(name
);
88 tags
.addAll(nameCacheTags
);
91 String endQuote
= QUOTE_END
;
92 tags
.add(TaggedText
.NewSeparatorInstance(endQuote
));
96 List
<TaggedText
> nameCacheTags
= getNameTitleCacheTags(name
);
97 tags
.addAll(nameCacheTags
);
101 //sensu (+ Separatoren?)
102 if (isNotBlank(relatedTaxon
.getAppendedPhrase())){
103 tags
.add(TaggedText
.NewWhitespaceInstance());
104 tags
.add(TaggedText
.NewInstance(TagEnum
.appendedPhrase
, relatedTaxon
.getAppendedPhrase()));
106 List
<TaggedText
> secTags
= getSensuTags(relatedTaxon
.getSec(), relatedTaxon
.getSecMicroReference(), isMisapplied
);
107 if (!secTags
.isEmpty()) {
108 tags
.add(TaggedText
.NewSeparatorInstance(isMisapplied? SENSU_SEPARATOR
: SEC_SEPARATOR
));
109 tags
.addAll(secTags
);
110 }else if (isBlank(relatedTaxon
.getAppendedPhrase())) {
112 tags
.add(TaggedText
.NewWhitespaceInstance());
113 //TODO type unclear sensuReference(?)
114 tags
.add(TaggedText
.NewInstance(TagEnum
.authors
, AUCT
));
116 tags
.add(TaggedText
.NewSeparatorInstance(SEC_SEPARATOR
+ UNKNOWN_SEC
));
121 if (isMisapplied
&& name
!= null){
122 if (name
.getCombinationAuthorship() != null){
123 tags
.add(TaggedText
.NewSeparatorInstance(NON_SEPARATOR
));
124 //TODO add nom. ref. author tags
125 }else if (isNotBlank(name
.getAuthorshipCache())){
126 tags
.add(TaggedText
.NewSeparatorInstance(NON_SEPARATOR
));
127 tags
.add(TaggedText
.NewInstance(TagEnum
.authors
, name
.getAuthorshipCache().trim()));
131 //TODO tagEnum for relSec?
132 List
<TaggedText
> relSecTags
= getSensuTags
/*getCitationTags*/(taxonRelationship
.getCitation(),
133 taxonRelationship
.getCitationMicroReference(), false);
134 if (!relSecTags
.isEmpty()){
135 TaggedText relSecSeparatorToag
= TaggedText
.NewSeparatorInstance(isSynonym ? SYN_SEC
: isMisapplied ? ERR_SEC
: REL_SEC
);
136 tags
.add(relSecSeparatorToag
);
137 tags
.addAll(relSecTags
);
143 private List
<TaggedText
> getSensuTags(Reference ref
, String detail
, boolean isSensu
) {
144 List
<TaggedText
> result
= new ArrayList
<>();
148 TeamOrPersonBase
<?
> author
= ref
.getAuthorship();
149 //TODO distinguish linked and unlinked usage,
150 // if reference is not linked short citation should only be used
151 // if both author and year exists, also initials should be added in this case
153 if (ref
.isProtectedTitleCache() == false &&
155 isNotBlank(author
.getTitleCache())){
156 //TODO move to authorFormatter
157 String familyNames
= getFamilyNames(author
);
158 if (isNotBlank(familyNames
)){
159 secRef
= familyNames
;
161 secRef
= ref
.getAuthorship().getTitleCache();
163 if (isNotBlank(ref
.getYear())){
164 secRef
+= " " + ref
.getYear();
167 secRef
= ref
.getTitleCache();
169 TagEnum secType
= isSensu? TagEnum
.sensuReference
: TagEnum
.secReference
;
170 TaggedText refTag
= TaggedText
.NewInstance(secType
, secRef
);
171 refTag
.setEntityReference(new TypedEntityReference
<>(CdmBase
.deproxy(ref
.getClass()), ref
.getUuid(), secRef
));
174 if (isNotBlank(detail
)){
175 result
.add(TaggedText
.NewSeparatorInstance(DETAIL_SEPARATOR
));
176 //TODO do we need a sensu micro reference??
177 TagEnum detailType
= isSensu? TagEnum
.sensuMicroReference
: TagEnum
.secMicroReference
;
178 TaggedText microTag
= TaggedText
.NewInstance(detailType
, detail
);
179 result
.add(microTag
);
188 private String
getFamilyNames(TeamOrPersonBase
<?
> author
) {
189 if (author
.isInstanceOf(Person
.class)){
190 Person person
= CdmBase
.deproxy(author
, Person
.class);
191 return isNotBlank(person
.getFamilyName())? person
.getFamilyName() : null;
193 Team team
= CdmBase
.deproxy(author
, Team
.class);
194 String result
= null;
195 int n
= team
.getTeamMembers().size();
197 if (team
.isHasMoreMembers()){
200 for (Person member
: team
.getTeamMembers()){
201 String name
= isNotBlank(member
.getFamilyName())? member
.getFamilyName(): member
.getTitleCache();
202 String separator
= index
< n ?
", " : " & ";
203 result
= CdmUtils
.concat(separator
, result
, name
);
206 if (team
.isHasMoreMembers()){
217 * @param secMicroReference
220 private List
<TaggedText
> getCitationTags(Reference ref
, String secMicroReference
) {
221 List
<TaggedText
> result
= new ArrayList
<>();
225 //copied from TaxonBaseDefaultCacheStrategy
226 if (ref
.isProtectedTitleCache() == false &&
227 ref
.getCacheStrategy() != null &&
228 ref
.getAuthorship() != null &&
229 isNotBlank(ref
.getAuthorship().getTitleCache()) &&
230 isNotBlank(ref
.getYear())){
231 secRef
= ref
.getCacheStrategy().getCitation(ref
);
233 secRef
= ref
.getTitleCache();
235 //TODO do we need a sensuReference type?
236 TaggedText refTag
= TaggedText
.NewInstance(TagEnum
.secReference
, secRef
);
237 refTag
.setEntityReference(new TypedEntityReference
<>(ref
.getClass(), ref
.getUuid(), secRef
));
240 if (isNotBlank(secMicroReference
)){
241 result
.add(TaggedText
.NewSeparatorInstance(DETAIL_SEPARATOR
));
242 TaggedText microTag
= TaggedText
.NewInstance(TagEnum
.secMicroReference
, secMicroReference
);
243 result
.add(microTag
);
253 private List
<TaggedText
> getNameCacheTags(TaxonName name
) {
254 List
<TaggedText
> result
= name
.getCacheStrategy().getTaggedName(name
);
258 private List
<TaggedText
> getNameTitleCacheTags(TaxonName name
) {
261 List
<TaggedText
> result
= name
.getCacheStrategy().getTaggedTitle(name
);
267 * @param type the taxon relationship type
268 * @param reverse is the relationship used reverse
269 * @param languages list of preferred languages
270 * @return the symbol for the taxon relationship
272 private String
getSymbol(TaxonRelationshipType type
, boolean reverse
, List
<Language
> languages
) {
274 return UNDEFINED_SYMBOL
;
278 String symbol
= reverse? type
.getInverseSymbol():type
.getSymbol();
279 if (isNotBlank(symbol
)){
283 boolean isSymmetric
= type
.isSymmetric();
284 //symmetric inverted symbol
285 String invertedSymbol
= reverse? type
.getSymbol() : type
.getInverseSymbol();
286 if (isSymmetric
&& isNotBlank(invertedSymbol
)){
287 return invertedSymbol
;
291 Representation representation
= reverse? type
.getPreferredRepresentation(languages
): type
.getPreferredInverseRepresentation(languages
);
292 String abbrevLabel
= representation
.getAbbreviatedLabel();
293 if (isNotBlank(abbrevLabel
)){
297 //symmetric inverted abbrev label
298 Representation invertedRepresentation
= reverse? type
.getPreferredInverseRepresentation(languages
):type
.getPreferredRepresentation(languages
);
299 String invertedAbbrevLabel
= invertedRepresentation
.getAbbreviatedLabel();
300 if (isSymmetric
&& isNotBlank(invertedAbbrevLabel
)){
301 return invertedAbbrevLabel
;
304 //non symmetric inverted symbol
305 if (!isSymmetric
&& isNotBlank(invertedSymbol
)){
306 return INVERT_SYMBOL
+ invertedSymbol
;
309 //non symmetric inverted abbrev label
310 if (!isSymmetric
&& isNotBlank(invertedAbbrevLabel
)){
311 return INVERT_SYMBOL
+ invertedAbbrevLabel
;
314 return UNDEFINED_SYMBOL
;
317 private boolean isNotBlank(String str
) {
318 return StringUtils
.isNotBlank(str
);
321 private boolean isBlank(String str
) {
322 return StringUtils
.isBlank(str
);