cleanup
[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 public static TaxonRelationshipFormatter NewInstance(){
61 return new TaxonRelationshipFormatter();
62 }
63
64 public static TaxonRelationshipFormatter INSTANCE(){
65 if (instance == null){
66 instance = NewInstance();
67 }
68 return instance;
69 }
70
71 private TaxonRelationshipFormatter(){
72
73 }
74
75 public List<TaggedText> getTaggedText(TaxonRelationship taxonRelationship, boolean reverse, List<Language> languages) {
76 return getTaggedText(taxonRelationship, reverse, languages, false);
77 }
78
79 public List<TaggedText> getTaggedText(TaxonRelationship taxonRelationship, boolean reverse,
80 List<Language> languages, boolean withoutName) {
81
82 if (taxonRelationship == null){
83 return null;
84 }
85
86 TaxonRelationshipType type = taxonRelationship.getType();
87 boolean isMisapplied = (type == null ? false : type.isMisappliedName() && reverse);
88 boolean isSynonym = type == null? false : type.isAnySynonym();
89
90 Taxon relatedTaxon = reverse? taxonRelationship.getFromTaxon()
91 : taxonRelationship.getToTaxon();
92
93 if (relatedTaxon == null){
94 return null;
95 }
96
97 String doubtfulTaxonStr = relatedTaxon.isDoubtful() ? DOUBTFUL_TAXON_MARKER : "";
98 String doubtfulRelationStr = taxonRelationship.isDoubtful() ? "?" : "";
99
100 TaxonName name = relatedTaxon.getName();
101
102 TaggedTextBuilder builder = new TaggedTextBuilder();
103
104 //rel symbol
105 String symbol = doubtfulRelationStr + getSymbol(type, reverse, languages);
106 builder.add(TagEnum.symbol, symbol);
107
108 //name
109 if (!withoutName){
110 if (isMisapplied){
111 //starting quote
112 String startQuote = " " + doubtfulTaxonStr + QUOTE_START;
113 builder.addSeparator(startQuote);
114
115 //name cache
116 List<TaggedText> nameCacheTags = getNameCacheTags(name);
117 builder.addAll(nameCacheTags);
118
119 //end quote
120 String endQuote = QUOTE_END;
121 builder.add(TagEnum.postSeparator, endQuote);
122 }else{
123 builder.addSeparator(" " + doubtfulTaxonStr);
124 //name full title cache
125 List<TaggedText> nameCacheTags = getNameTitleCacheTags(name);
126 builder.addAll(nameCacheTags);
127 }
128 }else{
129 if (isNotBlank(doubtfulTaxonStr)){
130 builder.addSeparator(" " + doubtfulTaxonStr);
131 }
132 }
133
134 //sec/sensu (+ Separatoren?)
135 if (isNotBlank(relatedTaxon.getAppendedPhrase())){
136 builder.addWhitespace();
137 builder.add(TagEnum.appendedPhrase, relatedTaxon.getAppendedPhrase());
138 }
139 List<TaggedText> secTags = getReferenceTags(relatedTaxon.getSec(), relatedTaxon.getSecMicroReference(),
140 /* isMisapplied,*/ false);
141 if (!secTags.isEmpty()) {
142 builder.addSeparator(isMisapplied? SENSU_SEPARATOR : SEC_SEPARATOR);
143 builder.addAll(secTags);
144 }else if (isBlank(relatedTaxon.getAppendedPhrase())) {
145 if (isMisapplied){
146 builder.addWhitespace();
147 //TODO type unclear sensuReference(?)
148 builder.add(TagEnum.appendedPhrase, AUCT);
149 }else{
150 builder.addSeparator(SEC_SEPARATOR + UNKNOWN_SEC);
151 }
152 }
153
154 // //, non author
155 if (isMisapplied && name != null){
156 if (isNotBlank(name.getAuthorshipCache())){
157 builder.addSeparator(NON_SEPARATOR);
158 builder.add(TagEnum.authors, name.getAuthorshipCache().trim());
159 }
160 }
161
162 List<TaggedText> relSecTags = getReferenceTags(taxonRelationship.getCitation(),
163 taxonRelationship.getCitationMicroReference(),true);
164 if (!relSecTags.isEmpty()){
165 builder.addSeparator(isSynonym ? SYN_SEC : isMisapplied ? ERR_SEC : REL_SEC);
166 builder.addAll(relSecTags);
167 }
168
169 return builder.getTaggedText();
170 }
171
172 private List<TaggedText> getReferenceTags(Reference ref, String detail, /*boolean isSensu,*/ boolean isRelation) {
173 List<TaggedText> result = new ArrayList<>();
174 String secRef;
175
176 if (ref != null){
177 TeamOrPersonBase<?> author = CdmBase.deproxy(ref.getAuthorship());
178
179 //TODO distinguish linked and unlinked usage,
180 // if reference is not linked short citation should only be used
181 // if both author and year exists, also initials should be added in this case
182 //
183 if (ref.isProtectedTitleCache() == false &&
184 author != null &&
185 isNotBlank(author.getTitleCache())){
186 if (author.isInstanceOf(Person.class)){
187 secRef = PersonDefaultCacheStrategy.INSTANCE().getFamilyTitle((Person)author);
188 }else{
189 //#9624
190 secRef = TeamDefaultCacheStrategy.INSTANCE_ET_AL_2().getFamilyTitle((Team)author);
191 }
192 if (isNotBlank(ref.getYear())){
193 secRef += " " + ref.getYear();
194 }
195 }else{
196 secRef = ref.getTitleCache();
197 }
198 TagEnum secType = /*isSensu? TagEnum.sensuReference : */ isRelation? TagEnum.relSecReference : TagEnum.secReference;
199 TaggedText refTag = TaggedText.NewInstance(secType, secRef);
200 refTag.setEntityReference(new TypedEntityReference<>(CdmBase.deproxy(ref).getClass(), ref.getUuid()));
201 result.add(refTag);
202 }
203 if (isNotBlank(detail)){
204 result.add(TaggedText.NewSeparatorInstance(DETAIL_SEPARATOR));
205 TagEnum detailType = /*isSensu? TagEnum.sensuMicroReference : */ isRelation? TagEnum.relSecMicroReference :TagEnum.secMicroReference;
206 TaggedText microTag = TaggedText.NewInstance(detailType, detail);
207 result.add(microTag);
208 }
209 return result;
210 }
211
212 private List<TaggedText> getNameCacheTags(TaxonName name) {
213 List<TaggedText> result = name.cacheStrategy().getTaggedName(name);
214 return result;
215 }
216
217 private List<TaggedText> getNameTitleCacheTags(TaxonName name) {
218
219 //TODO full title?
220 List<TaggedText> result = name.cacheStrategy().getTaggedFullTitle(name);
221 return result;
222 }
223
224 /**
225 * @param type the taxon relationship type
226 * @param reverse is the relationship used reverse
227 * @param languages list of preferred languages
228 * @return the symbol for the taxon relationship
229 */
230 private String getSymbol(TaxonRelationshipType type, boolean reverse, List<Language> languages) {
231 if (type == null){
232 return UNDEFINED_SYMBOL;
233 }
234
235 //symbol
236 String symbol = reverse? type.getInverseSymbol():type.getSymbol();
237 if (isNotBlank(symbol)){
238 return symbol;
239 }
240
241 boolean isSymmetric = type.isSymmetric();
242 //symmetric inverted symbol
243 String invertedSymbol = reverse? type.getSymbol() : type.getInverseSymbol();
244 if (isSymmetric && isNotBlank(invertedSymbol)){
245 return invertedSymbol;
246 }
247
248 //abbrev label
249 Representation representation = reverse? type.getPreferredRepresentation(languages): type.getPreferredInverseRepresentation(languages);
250 String abbrevLabel = representation.getAbbreviatedLabel();
251 if (isNotBlank(abbrevLabel)){
252 return abbrevLabel;
253 }
254
255 //symmetric inverted abbrev label
256 Representation invertedRepresentation = reverse? type.getPreferredInverseRepresentation(languages):type.getPreferredRepresentation(languages);
257 String invertedAbbrevLabel = invertedRepresentation.getAbbreviatedLabel();
258 if (isSymmetric && isNotBlank(invertedAbbrevLabel)){
259 return invertedAbbrevLabel;
260 }
261
262 //non symmetric inverted symbol
263 if (!isSymmetric && isNotBlank(invertedSymbol)){
264 return INVERT_SYMBOL + invertedSymbol;
265 }
266
267 //non symmetric inverted abbrev label
268 if (!isSymmetric && isNotBlank(invertedAbbrevLabel)){
269 return INVERT_SYMBOL + invertedAbbrevLabel;
270 }
271
272 return UNDEFINED_SYMBOL;
273 }
274
275 private boolean isNotBlank(String str) {
276 return StringUtils.isNotBlank(str);
277 }
278
279 private boolean isBlank(String str) {
280 return StringUtils.isBlank(str);
281 }
282 }