ref #9624, ref #7305, ref #9326 refactor team formatting all over cdmlib (including...
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / format / reference / NomenclaturalSourceFormatter.java
1 /**
2 * Copyright (C) 2021 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.reference;
10
11 import org.apache.log4j.Logger;
12
13 import eu.etaxonomy.cdm.common.CdmUtils;
14 import eu.etaxonomy.cdm.format.CdmFormatterBase;
15 import eu.etaxonomy.cdm.model.agent.Person;
16 import eu.etaxonomy.cdm.model.agent.Team;
17 import eu.etaxonomy.cdm.model.agent.TeamOrPersonBase;
18 import eu.etaxonomy.cdm.model.common.CdmBase;
19 import eu.etaxonomy.cdm.model.name.NomenclaturalSource;
20 import eu.etaxonomy.cdm.model.name.TaxonName;
21 import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
22 import eu.etaxonomy.cdm.model.reference.Reference;
23 import eu.etaxonomy.cdm.model.reference.ReferenceType;
24 import eu.etaxonomy.cdm.strategy.cache.agent.TeamDefaultCacheStrategy;
25 import eu.etaxonomy.cdm.strategy.cache.reference.ReferenceDefaultCacheStrategy;
26 import eu.etaxonomy.cdm.strategy.cache.reference.TitleWithoutYearAndAuthorHelper;
27
28 /**
29 * @author a.mueller
30 * @since 03.05.2021
31 */
32 public class NomenclaturalSourceFormatter extends CdmFormatterBase<NomenclaturalSource>{
33
34 private static final Logger logger = Logger.getLogger(NomenclaturalSourceFormatter.class);
35
36 private static final String beforeMicroReference = ": ";
37 private static final String afterInRefAuthor = ", ";
38
39 private static NomenclaturalSourceFormatter instance;
40
41 public static final NomenclaturalSourceFormatter INSTANCE() {
42 if (instance == null){
43 instance = new NomenclaturalSourceFormatter();
44 }
45 return instance;
46 }
47
48 @Override
49 public String format(NomenclaturalSource source){
50 if (source == null){
51 return null;
52 }
53 Reference reference = source.getCitation();
54 String microReference = source.getCitationMicroReference();
55 if (reference == null && isBlank(microReference)){
56 return null;
57 }
58 return format(reference, microReference);
59 }
60
61 /**
62 * Returns a formatted string containing the entire citation used for
63 * nomenclatural purposes based on the {@link Reference reference} supplied - including
64 * (abbreviated) title but not authors - and on the given details.<BR>
65 *
66 * @param reference
67 * the nomenclatural reference
68 * @param microReference the string with the details (generally pages)
69 * corresponding to the nomenclatural reference supplied
70 * as the first argument
71 * @return the formatted string representing the
72 * nomenclatural citation
73 * @see INomenclaturalReference#getNomenclaturalCitation(String)
74 * @see TaxonName#getNomenclaturalReference()
75 */
76 public String format(Reference reference, String microReference){
77 if (reference == null){
78 return CdmUtils.concat(beforeMicroReference, "-", microReference);
79 }
80
81 if (reference.isProtectedAbbrevTitleCache()){
82 String cache = reference.getAbbrevTitleCache();
83 return handleDetailAndYearForProtected(reference, cache, microReference);
84 }
85
86 String result = getTokenizedNomenclaturalTitel(reference);
87 //if no data is available and only titleCache is protected take the protected title
88 //this is to avoid empty cache if someone forgets to set also the abbrevTitleCache
89 //we need to think about handling protected not separate for abbrevTitleCache and titleCache
90 if (result.equals(INomenclaturalReference.MICRO_REFERENCE_TOKEN) && reference.isProtectedTitleCache() ){
91 String cache = reference.getTitleCache();
92 return handleDetailAndYearForProtected(reference, cache, microReference);
93 }
94
95 microReference = Nz(microReference);
96 if (isNotBlank(microReference)){
97 microReference = getBeforeMicroReference() + microReference;
98 if (microReference.endsWith(".") && result.contains(INomenclaturalReference.MICRO_REFERENCE_TOKEN + ".") ){
99 microReference = microReference.substring(0, microReference.length() - 1);
100 }
101 }
102 result = replaceMicroRefToken(microReference, result);
103 if (result.startsWith(". ")){ //only year available, remove '. '
104 result = result.substring(2);
105 }
106 return result;
107 }
108
109 private String getBeforeMicroReference(){
110 return beforeMicroReference;
111 }
112
113 /**
114 * Returns the nomenclatural title with micro reference represented as token
115 * which can later be replaced by the real data.
116 *
117 * @see INomenclaturalReference#MICRO_REFERENCE_TOKEN
118 */
119 private String getTokenizedNomenclaturalTitel(Reference ref) {
120 if (ReferenceDefaultCacheStrategy.isRealInRef(ref)){
121 return getTokenizedNomenclaturalTitelInRef(ref);
122 }else{
123 String result = getTitleWithoutYearAndAuthor(ref, true, true);
124 result += INomenclaturalReference.MICRO_REFERENCE_TOKEN;
125 result = addYear(result, ref, true);
126 return result;
127 }
128 }
129
130 private String replaceMicroRefToken(String microReference, String string) {
131 int index = string.indexOf(INomenclaturalReference.MICRO_REFERENCE_TOKEN);
132
133 if (index > -1){
134 String before = string.substring(0, index);
135 String after = string.substring(index + INomenclaturalReference.MICRO_REFERENCE_TOKEN.length() );
136 String localMicroReference = microReference.trim(); //needed ?
137 if (after.length() > 0){
138 if ( ("".equals(localMicroReference) && before.endsWith(after.substring(0,1)) || localMicroReference.endsWith(after.substring(0,1)))){
139 after = after.substring(1);
140 }
141 }
142 String result = before + localMicroReference + after;
143 return result;
144 }else{
145 return string;
146 }
147 }
148
149 private String handleDetailAndYearForProtected(Reference nomenclaturalReference, String cache, String microReference) {
150 String microRef = isNotBlank(microReference) ? getBeforeMicroReference() + microReference : "";
151 if (cache == null){
152 logger.warn("Cache is null. This should never be the case.");
153 cache = "";
154 }
155 String result = cache + (cache.contains(microRef) ? "" : microRef);
156
157 String date = nomenclaturalReference.getDatePublishedString();
158 if (isNotBlank(date) && ! result.contains(date)){
159 result = result + ReferenceDefaultCacheStrategy.beforeYear + date;
160 }
161 return result;
162 }
163
164 private String getTokenizedNomenclaturalTitelInRef(Reference thisRef) {
165 if (thisRef == null){
166 return null;
167 }
168
169 Reference inRef = CdmBase.deproxy(thisRef.getInReference(), Reference.class);
170 if (inRef != null && inRef.getInReference() != null && thisRef.getType() == ReferenceType.Section){
171 //this is a reference of type Section which has a in-in-Ref
172 //TODO maybe we do not need to restrict to type=Section only
173 return this.getTokenizedNomenclaturalTitelInInRef(thisRef);
174 }
175
176 String result;
177 //use generics's publication date if it exists
178 if (inRef == null || (thisRef.hasDatePublished() ) ){
179 result = inRef == null ? "" : getTitleWithoutYearAndAuthorGeneric(inRef, true);
180 //added //TODO unify with non-inRef references formatting
181
182 if (isNotBlank(thisRef.getVolume())){
183 result = result + " " + thisRef.getVolume();
184 }
185 //TODO series / edition
186
187 //end added
188 result += INomenclaturalReference.MICRO_REFERENCE_TOKEN;
189 result = addYear(result, thisRef, true);
190 }else{
191 //else use inRefs's publication date
192 result = inRef.getNomenclaturalCitation(INomenclaturalReference.MICRO_REFERENCE_TOKEN);
193 if (result != null){
194 result = result.replace(beforeMicroReference + INomenclaturalReference.MICRO_REFERENCE_TOKEN, INomenclaturalReference.MICRO_REFERENCE_TOKEN);
195 }
196 }
197 //FIXME: vol. etc., https://dev.e-taxonomy.eu/redmine/issues/2862
198
199 result = getInRefAuthorPart(thisRef.getInReference(), afterInRefAuthor) + result;
200 result = "in " + result;
201 return result;
202 }
203
204
205 /**
206 * For handling in-in-Ref case.
207 * Must only be called if a reference has inRef and inInRef
208 * @param section
209 * @return
210 */
211 private String getTokenizedNomenclaturalTitelInInRef(Reference ref) {
212 String result;
213
214 Reference inRef = CdmBase.deproxy(ref.getInReference(), Reference.class);
215 Reference inInRef = CdmBase.deproxy(inRef.getInReference(), Reference.class);
216
217 if (! ReferenceDefaultCacheStrategy.isNomRef(inInRef.getType())){
218 if (! ReferenceDefaultCacheStrategy.isNomRef(inRef.getType())){
219 logger.warn("Neither inReference nor inInReference is a "
220 + " nomenclatural reference. This is not correct or not handled yet."
221 + " Generic titleWithoutYearAndAuthor used instead");
222 result = getTitleWithoutYearAndAuthorGeneric(inInRef, true);
223 //FIXME: vol. etc., https://dev.e-taxonomy.eu/redmine/issues/2862 (comment taken from super.getTokenizedNomenclaturalTitel())
224 }else{
225 result = getTitleWithoutYearAndAuthor(inRef, true, true);
226 }
227 }else{
228 result = getTitleWithoutYearAndAuthor(inInRef, true, true);
229 }
230 result += INomenclaturalReference.MICRO_REFERENCE_TOKEN;
231
232 Reference dataReference = (ref.hasDatePublished() ? ref : inRef.hasDatePublished() ? inRef : inInRef);
233
234 result = addYear(result, dataReference, true);
235
236 result = getInRefAuthorPart(inInRef, afterInRefAuthor) + result;
237 if (! result.startsWith("in ")){
238 result = "in " + result;
239 }
240 return result;
241 }
242
243 /**
244 * See https://dev.e-taxonomy.eu/redmine/issues/8881
245 */
246 private String getInRefAuthorPart(Reference book, String seperator){
247 if (book == null){
248 return "";
249 }
250
251 TeamOrPersonBase<?> author = book.getAuthorship();
252 String result;
253 if (author == null){
254 result = "";
255 }else if(author.isInstanceOf(Person.class)){
256 Person person = CdmBase.deproxy(author, Person.class);
257 result = getInRefPerson(person);
258 }else{
259 Team team = CdmBase.deproxy(author, Team.class);
260 if (team.isProtectedNomenclaturalTitleCache()){
261 //not yet finally discussed may change in future
262 result = team.getNomenclaturalTitle();
263 }else if (team.isProtectedTitleCache()){
264 //not yet finally discussed may change in future
265 result = team.getTitleCache();
266 }else if (team.getTeamMembers().isEmpty()){
267 //not yet finally discussed may change in future
268 result = team.getTitleCache();
269 }else{
270 result = TeamDefaultCacheStrategy.INSTANCE_ET_AL_3().getFamilyTitle(team);
271 }
272 }
273
274 result = Nz(result);
275 if (! result.trim().equals("")){
276 result = result + seperator;
277 }
278 return result;
279 }
280
281 private String getInRefPerson(Person person) {
282 String result;
283 if (isNotBlank(person.getFamilyName())){
284 result = person.getFamilyName();
285 }else if (isNotBlank(person.getNomenclaturalTitle())){
286 result = person.getNomenclaturalTitle(); //TODO discuss if nomTitle is really better here then titleCache
287 }else{
288 result = person.getTitleCache(); //maybe remove everything behind a ","
289 }
290 return result;
291 }
292
293 /**
294 * @see ReferenceDefaultCacheStrategy#addYear(String, Reference, boolean)
295 */
296 private String addYear(String yearStr, Reference ref, boolean useFullDatePublished) {
297 return ReferenceDefaultCacheStrategy.addYear(yearStr, ref, useFullDatePublished);
298 }
299 private String getTitleWithoutYearAndAuthorGeneric(Reference ref, boolean isAbbrev){
300 return TitleWithoutYearAndAuthorHelper.getTitleWithoutYearAndAuthorGeneric(ref, isAbbrev);
301 }
302 private String getTitleWithoutYearAndAuthor(Reference ref, boolean isAbbrev, boolean isNomRef){
303 return TitleWithoutYearAndAuthorHelper.getTitleWithoutYearAndAuthor(ref, isAbbrev, isNomRef);
304 }
305 }