ref #6682 include doubtful
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / format / taxon / TaxonRelationshipFormatter.java
1 /**
2 * Copyright (C) 2018 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.taxon;
10
11 import java.util.ArrayList;
12 import java.util.List;
13
14 import org.codehaus.plexus.util.StringUtils;
15
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;
31 import eu.etaxonomy.cdm.strategy.cache.TaggedTextBuilder;
32 import eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy;
33
34 /**
35 * @author a.mueller
36 * @since 13.08.2018
37 *
38 */
39 public class TaxonRelationshipFormatter {
40
41 private static final String REL_SEC = ", rel. sec. ";
42 private static final String ERR_SEC = ", err. sec. ";
43 private static final String SYN_SEC = ", syn. sec. ";
44 private static final String UNKNOWN_SEC = "???";
45 private static final String NON_SEPARATOR = ", non ";
46 private static final String QUOTE_START = "\""; //TODO
47 private static final String QUOTE_END = "\""; //TODO
48 private static final String AUCT = "auct.";
49 private static final String SENSU_SEPARATOR = " sensu ";
50 private static final String SEC_SEPARATOR = " sec. ";
51 private static final String DETAIL_SEPARATOR = ": ";
52 private static final String INVERT_SYMBOL = "<-"; //TODO
53 private static final String UNDEFINED_SYMBOL = "??"; //TODO
54
55 public List<TaggedText> getTaggedText(TaxonRelationship taxonRelationship, boolean reverse, List<Language> languages) {
56
57 if (taxonRelationship == null){
58 return null;
59 }
60
61 TaxonRelationshipType type = taxonRelationship.getType();
62 boolean isMisapplied = type == null ? false : type.isMisappliedNameOrInvalidDesignation() && reverse;
63 boolean isSynonym = type == null? false : type.isAnySynonym();
64
65
66 Taxon relatedTaxon = reverse? taxonRelationship.getFromTaxon()
67 : taxonRelationship.getToTaxon();
68
69 if (relatedTaxon == null){
70 return null;
71 }
72 boolean isDoubtful = taxonRelationship.isDoubtful() || relatedTaxon.isDoubtful();
73 String doubtfulStr = isDoubtful ? "?" : "";
74
75 TaxonName name = relatedTaxon.getName();
76
77 // List<TaggedText> tags = new ArrayList<>();
78 TaggedTextBuilder builder = new TaggedTextBuilder();
79
80 //rel symbol
81 String symbol = getSymbol(type, reverse, languages);
82 builder.add(TagEnum.symbol, symbol);
83
84 //name
85 if (isMisapplied){
86 //starting quote
87 String startQuote = " " + doubtfulStr + QUOTE_START;
88 builder.addSeparator(startQuote);// .add(TaggedText.NewSeparatorInstance(startQuote));
89
90 //name cache
91 List<TaggedText> nameCacheTags = getNameCacheTags(name);
92 builder.addAll(nameCacheTags);
93
94 //end quote
95 String endQuote = QUOTE_END;
96 builder.add(TagEnum.postSeparator, endQuote);
97 }else{
98 builder.addSeparator(" " + doubtfulStr);
99 //name full title cache
100 List<TaggedText> nameCacheTags = getNameTitleCacheTags(name);
101 builder.addAll(nameCacheTags);
102 }
103
104
105 //sensu (+ Separatoren?)
106 if (isNotBlank(relatedTaxon.getAppendedPhrase())){
107 builder.addWhitespace();
108 builder.add(TagEnum.appendedPhrase, relatedTaxon.getAppendedPhrase());
109 }
110 List<TaggedText> secTags = getSensuTags(relatedTaxon.getSec(), relatedTaxon.getSecMicroReference(),
111 /* isMisapplied,*/ false);
112 if (!secTags.isEmpty()) {
113 builder.addSeparator(isMisapplied? SENSU_SEPARATOR : SEC_SEPARATOR);
114 builder.addAll(secTags);
115 }else if (isBlank(relatedTaxon.getAppendedPhrase())) {
116 if (isMisapplied){
117 builder.addWhitespace();
118 //TODO type unclear sensuReference(?)
119 builder.add(TagEnum.appendedPhrase, AUCT);
120 }else{
121 builder.addSeparator(SEC_SEPARATOR + UNKNOWN_SEC);
122 }
123 }
124
125 // //, non author
126 if (isMisapplied && name != null){
127 if (name.getCombinationAuthorship() != null && isNotBlank(name.getCombinationAuthorship().getNomenclaturalTitle())){
128 builder.addSeparator(NON_SEPARATOR);
129 builder.add(TagEnum.authors, name.getCombinationAuthorship().getNomenclaturalTitle());
130 }else if (isNotBlank(name.getAuthorshipCache())){
131 builder.addSeparator(NON_SEPARATOR);
132 builder.add(TagEnum.authors, name.getAuthorshipCache().trim());
133 }
134 }
135
136 List<TaggedText> relSecTags = getSensuTags(taxonRelationship.getCitation(),
137 taxonRelationship.getCitationMicroReference(),true);
138 if (!relSecTags.isEmpty()){
139 builder.addSeparator(isSynonym ? SYN_SEC : isMisapplied ? ERR_SEC : REL_SEC);
140 builder.addAll(relSecTags);
141 }
142
143 return builder.getTaggedText();
144 }
145
146 private List<TaggedText> getSensuTags(Reference ref, String detail, /*boolean isSensu,*/ boolean isRelation) {
147 List<TaggedText> result = new ArrayList<>();
148 String secRef;
149
150 if (ref != null){
151 TeamOrPersonBase<?> author = ref.getAuthorship();
152 //TODO distinguish linked and unlinked usage,
153 // if reference is not linked short citation should only be used
154 // if both author and year exists, also initials should be added in this case
155 //
156 if (ref.isProtectedTitleCache() == false &&
157 author != null &&
158 isNotBlank(author.getTitleCache())){
159 //TODO move to authorFormatter
160 String familyNames = getFamilyNames(author);
161 if (isNotBlank(familyNames)){
162 secRef = familyNames;
163 }else{
164 secRef = ref.getAuthorship().getTitleCache();
165 }
166 if (isNotBlank(ref.getYear())){
167 secRef += " " + ref.getYear();
168 }
169 }else{
170 secRef = ref.getTitleCache();
171 }
172 TagEnum secType = /*isSensu? TagEnum.sensuReference : */ isRelation? TagEnum.relSecReference : TagEnum.secReference;
173 TaggedText refTag = TaggedText.NewInstance(secType, secRef);
174 refTag.setEntityReference(new TypedEntityReference<>(CdmBase.deproxy(ref).getClass(), ref.getUuid()));
175 result.add(refTag);
176 }
177 if (isNotBlank(detail)){
178 result.add(TaggedText.NewSeparatorInstance(DETAIL_SEPARATOR));
179 TagEnum detailType = /*isSensu? TagEnum.sensuMicroReference : */ isRelation? TagEnum.relSecMicroReference :TagEnum.secMicroReference;
180 TaggedText microTag = TaggedText.NewInstance(detailType, detail);
181 result.add(microTag);
182 }
183 return result;
184 }
185
186 /**
187 * @param author
188 * @return
189 */
190 private String getFamilyNames(TeamOrPersonBase<?> author) {
191 if (author.isInstanceOf(Person.class)){
192 Person person = CdmBase.deproxy(author, Person.class);
193 return isNotBlank(person.getFamilyName())? person.getFamilyName() : null;
194 }else{
195 Team team = CdmBase.deproxy(author, Team.class);
196 String result = null;
197 int n = team.getTeamMembers().size();
198 int index = 0;
199 if (team.isHasMoreMembers()){
200 n++;
201 }
202 for (Person member : team.getTeamMembers()){
203 String name = isNotBlank(member.getFamilyName())? member.getFamilyName(): member.getTitleCache();
204 String separator = index < n ? TeamDefaultCacheStrategy.STD_TEAM_CONCATINATION : TeamDefaultCacheStrategy.FINAL_TEAM_CONCATINATION;
205 result = CdmUtils.concat(separator, result, name);
206 index++;
207 }
208 if (team.isHasMoreMembers()){
209 //TODO or et al.???
210 result += TeamDefaultCacheStrategy.ET_AL_TEAM_CONCATINATION_FULL + "al.";
211 }
212 return result;
213 }
214 }
215
216
217 /**
218 * @param name
219 * @return
220 */
221 private List<TaggedText> getNameCacheTags(TaxonName name) {
222 List<TaggedText> result = name.getCacheStrategy().getTaggedName(name);
223 return result;
224 }
225
226 private List<TaggedText> getNameTitleCacheTags(TaxonName name) {
227
228 //TODO full title?
229 List<TaggedText> result = name.getCacheStrategy().getTaggedFullTitle(name);
230 return result;
231 }
232
233
234 /**
235 * @param type the taxon relationship type
236 * @param reverse is the relationship used reverse
237 * @param languages list of preferred languages
238 * @return the symbol for the taxon relationship
239 */
240 private String getSymbol(TaxonRelationshipType type, boolean reverse, List<Language> languages) {
241 if (type == null){
242 return UNDEFINED_SYMBOL;
243 }
244
245 //symbol
246 String symbol = reverse? type.getInverseSymbol():type.getSymbol();
247 if (isNotBlank(symbol)){
248 return symbol;
249 }
250
251 boolean isSymmetric = type.isSymmetric();
252 //symmetric inverted symbol
253 String invertedSymbol = reverse? type.getSymbol() : type.getInverseSymbol();
254 if (isSymmetric && isNotBlank(invertedSymbol)){
255 return invertedSymbol;
256 }
257
258 //abbrev label
259 Representation representation = reverse? type.getPreferredRepresentation(languages): type.getPreferredInverseRepresentation(languages);
260 String abbrevLabel = representation.getAbbreviatedLabel();
261 if (isNotBlank(abbrevLabel)){
262 return abbrevLabel;
263 }
264
265 //symmetric inverted abbrev label
266 Representation invertedRepresentation = reverse? type.getPreferredInverseRepresentation(languages):type.getPreferredRepresentation(languages);
267 String invertedAbbrevLabel = invertedRepresentation.getAbbreviatedLabel();
268 if (isSymmetric && isNotBlank(invertedAbbrevLabel)){
269 return invertedAbbrevLabel;
270 }
271
272 //non symmetric inverted symbol
273 if (!isSymmetric && isNotBlank(invertedSymbol)){
274 return INVERT_SYMBOL + invertedSymbol;
275 }
276
277 //non symmetric inverted abbrev label
278 if (!isSymmetric && isNotBlank(invertedAbbrevLabel)){
279 return INVERT_SYMBOL + invertedAbbrevLabel;
280 }
281
282 return UNDEFINED_SYMBOL;
283 }
284
285 private boolean isNotBlank(String str) {
286 return StringUtils.isNotBlank(str);
287 }
288
289 private boolean isBlank(String str) {
290 return StringUtils.isBlank(str);
291 }
292 }