c3d2a2d3cfe76b57ab0c69b83d6e47cb3cf9d2c0
[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.apache.commons.lang3.StringUtils;
15
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;
34
35 /**
36 * Formatter for TaxonRelationships.
37 *
38 * @author a.mueller
39 * @since 13.08.2018
40 */
41 public class TaxonRelationshipFormatter {
42
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
57
58 private static TaxonRelationshipFormatter instance;
59
60 // ************************* FACTORY ************************/
61
62 public static TaxonRelationshipFormatter NewInstance(){
63 return new TaxonRelationshipFormatter();
64 }
65
66 public static TaxonRelationshipFormatter INSTANCE(){
67 if (instance == null){
68 instance = NewInstance();
69 }
70 return instance;
71 }
72
73 // ******************* CONSTRUCTOR ************************/
74
75 private TaxonRelationshipFormatter(){}
76
77 // ********************** METHODS ***************************/
78
79 public List<TaggedText> getTaggedText(TaxonRelationship taxonRelationship, boolean inverse, List<Language> languages) {
80 return getTaggedText(taxonRelationship, inverse, languages, false);
81 }
82
83 public List<TaggedText> getTaggedText(TaxonRelationship taxonRelationship, boolean inverse,
84 List<Language> languages, boolean withoutName) {
85
86 if (taxonRelationship == null){
87 return null;
88 }
89
90 TaxonRelationshipType type = taxonRelationship.getType();
91 boolean isMisapplied = (type == null ? false : type.isMisappliedName() && inverse);
92 boolean isSynonym = type == null? false : type.isAnySynonym();
93
94 Taxon relatedTaxon = inverse? taxonRelationship.getFromTaxon()
95 : taxonRelationship.getToTaxon();
96
97 if (relatedTaxon == null){
98 return null;
99 }
100
101 String doubtfulTaxonStr = relatedTaxon.isDoubtful() ? DOUBTFUL_TAXON_MARKER : "";
102 String doubtfulRelationStr = taxonRelationship.isDoubtful() ? "?" : "";
103
104 TaxonName name = relatedTaxon.getName();
105
106 TaggedTextBuilder builder = new TaggedTextBuilder();
107
108 //rel symbol
109 String symbol = doubtfulRelationStr + getSymbol(type, inverse, languages);
110 builder.add(TagEnum.symbol, symbol);
111
112 //name
113 if (!withoutName){
114 if (isMisapplied){
115 //starting quote
116 String startQuote = " " + doubtfulTaxonStr + QUOTE_START;
117 builder.addSeparator(startQuote);
118
119 //name cache
120 List<TaggedText> nameCacheTags = getNameCacheTags(name);
121 builder.addAll(nameCacheTags);
122
123 //end quote
124 String endQuote = QUOTE_END;
125 builder.add(TagEnum.postSeparator, endQuote);
126 }else{
127 builder.addSeparator(" " + doubtfulTaxonStr);
128 //name full title cache
129 List<TaggedText> nameCacheTags = getNameTitleCacheTags(name);
130 builder.addAll(nameCacheTags);
131 }
132 }else{
133 if (isNotBlank(doubtfulTaxonStr)){
134 builder.addSeparator(" " + doubtfulTaxonStr);
135 }
136 }
137
138 //sec/sensu (+ Separatoren?)
139 if (isNotBlank(relatedTaxon.getAppendedPhrase())){
140 builder.addWhitespace();
141 builder.add(TagEnum.appendedPhrase, relatedTaxon.getAppendedPhrase());
142 }
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())) {
149 if (isMisapplied){
150 builder.addWhitespace();
151 //TODO type unclear sensuReference(?)
152 builder.add(TagEnum.appendedPhrase, AUCT);
153 }else{
154 builder.addSeparator(SEC_SEPARATOR + UNKNOWN_SEC);
155 }
156 }
157
158 // //, non author
159 if (isMisapplied && name != null){
160 if (isNotBlank(name.getAuthorshipCache())){
161 builder.addSeparator(NON_SEPARATOR);
162 builder.add(TagEnum.authors, name.getAuthorshipCache().trim());
163 }
164 }
165
166 //p.p.
167 if (isMisapplied) {
168 if (isProParteMAN(type, inverse)) {
169 builder.addSeparator(", ");
170 symbol = "p.p.";
171 builder.add(TagEnum.symbol, symbol);
172 } else if (isPartialMAN(type, inverse)) {
173 builder.addSeparator(", ");
174 symbol = "part.";
175 builder.add(TagEnum.symbol, symbol);
176 }
177 }
178
179 //rel sec
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);
185 }
186
187 return builder.getTaggedText();
188 }
189
190 private List<TaggedText> getReferenceTags(Reference ref, String detail, /*boolean isSensu,*/ boolean isRelation) {
191 List<TaggedText> result = new ArrayList<>();
192 String secRef;
193
194 if (ref != null){
195 TeamOrPersonBase<?> author = CdmBase.deproxy(ref.getAuthorship());
196
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
200 //
201 if (ref.isProtectedTitleCache() == false &&
202 author != null &&
203 isNotBlank(author.getTitleCache())){
204 if (author.isInstanceOf(Person.class)){
205 secRef = PersonDefaultCacheStrategy.INSTANCE().getFamilyTitle((Person)author);
206 }else{
207 //#9624
208 secRef = TeamDefaultCacheStrategy.INSTANCE_ET_AL_2().getFamilyTitle((Team)author);
209 }
210 if (isNotBlank(ref.getYear())){
211 secRef += " " + ref.getYear();
212 }
213 }else{
214 secRef = ref.getTitleCache();
215 }
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()));
219 result.add(refTag);
220 }
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);
226 }
227 return result;
228 }
229
230 private List<TaggedText> getNameCacheTags(TaxonName name) {
231 List<TaggedText> result = name.cacheStrategy().getTaggedName(name);
232 return result;
233 }
234
235 private List<TaggedText> getNameTitleCacheTags(TaxonName name) {
236
237 //TODO full title?
238 List<TaggedText> result = name.cacheStrategy().getTaggedFullTitle(name);
239 return result;
240 }
241
242 /**
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
247 */
248 private String getSymbol(TaxonRelationshipType type, boolean inverse, List<Language> languages) {
249 if (type == null){
250 return UNDEFINED_SYMBOL;
251 }
252
253 //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();
259 }
260 return symbol;
261 }
262
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;
268 }
269
270 //abbrev label
271 Representation representation = inverse? type.getPreferredRepresentation(languages): type.getPreferredInverseRepresentation(languages);
272 String abbrevLabel = representation.getAbbreviatedLabel();
273 if (isNotBlank(abbrevLabel)){
274 return abbrevLabel;
275 }
276
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;
282 }
283
284 //non symmetric inverted symbol
285 if (!isSymmetric && isNotBlank(invertedSymbol)){
286 return INVERT_SYMBOL + invertedSymbol;
287 }
288
289 //non symmetric inverted abbrev label
290 if (!isSymmetric && isNotBlank(invertedAbbrevLabel)){
291 return INVERT_SYMBOL + invertedAbbrevLabel;
292 }
293
294 return UNDEFINED_SYMBOL;
295 }
296
297 private boolean isPartialMAN(TaxonRelationshipType type, boolean inverse) {
298 return inverse && type.getUuid().equals(TaxonRelationshipType.uuidPartialMisappliedNameFor);
299 }
300
301 private boolean isProParteMAN(TaxonRelationshipType type, boolean inverse) {
302 return inverse && type.getUuid().equals(TaxonRelationshipType.uuidProParteMisappliedNameFor);
303 }
304
305 private boolean isNotBlank(String str) {
306 return StringUtils.isNotBlank(str);
307 }
308
309 private boolean isBlank(String str) {
310 return StringUtils.isBlank(str);
311 }
312 }