Merge branch 'release/5.11.0'
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / strategy / cache / name / TaxonNameDefaultCacheStrategy.java
1 /**
2 * Copyright (C) 2007 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.strategy.cache.name;
10
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Iterator;
14 import java.util.List;
15 import java.util.Set;
16 import java.util.UUID;
17
18 import org.apache.commons.lang.StringUtils;
19 import org.apache.log4j.Logger;
20
21 import eu.etaxonomy.cdm.common.CdmUtils;
22 import eu.etaxonomy.cdm.common.UTF8;
23 import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
24 import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
25 import eu.etaxonomy.cdm.model.common.Language;
26 import eu.etaxonomy.cdm.model.name.HybridRelationship;
27 import eu.etaxonomy.cdm.model.name.INonViralName;
28 import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
29 import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
30 import eu.etaxonomy.cdm.model.name.Rank;
31 import eu.etaxonomy.cdm.model.name.TaxonName;
32 import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
33 import eu.etaxonomy.cdm.model.reference.Reference;
34 import eu.etaxonomy.cdm.model.term.Representation;
35 import eu.etaxonomy.cdm.ref.TypedEntityReference;
36 import eu.etaxonomy.cdm.strategy.cache.TagEnum;
37 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
38 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
39 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImplRegExBase;
40
41
42 /**
43 * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName>
44 * interface.<BR>
45 * The method implements a cache strategy for botanical names so no method has to be overwritten by
46 * a subclass for botanic names.
47 * Where differing from this default botanic name strategy other subclasses should overwrite the
48 * existing methods, e.g. a CacheStrategy for zoological names should overwrite getAuthorAndExAuthor
49 * @author a.mueller
50 */
51 public class TaxonNameDefaultCacheStrategy
52 extends NameCacheStrategyBase
53 implements INonViralNameCacheStrategy {
54
55 private static final Logger logger = Logger.getLogger(TaxonNameDefaultCacheStrategy.class);
56 private static final long serialVersionUID = -6577757501563212669L;
57
58 final static UUID uuid = UUID.fromString("1cdda0d1-d5bc-480f-bf08-40a510a2f223");
59
60 protected String nameAuthorSeperator = " ";
61 protected String basionymStart = "(";
62 protected String basionymEnd = ")";
63 protected String exAuthorSeperator = " ex ";
64 private static String NOTHO = "notho";
65 protected CharSequence basionymAuthorCombinationAuthorSeperator = " ";
66
67 protected String zooAuthorYearSeperator = ", ";
68
69 @Override
70 public UUID getUuid(){
71 return uuid;
72 }
73
74 // ************************** FACTORY ******************************/
75
76 public static TaxonNameDefaultCacheStrategy NewInstance(){
77 return new TaxonNameDefaultCacheStrategy();
78 }
79
80
81 // ************ CONSTRUCTOR *******************/
82
83 protected TaxonNameDefaultCacheStrategy(){
84 super();
85 }
86
87 /* **************** GETTER / SETTER **************************************/
88
89 /**
90 * String that separates the NameCache part from the AuthorCache part
91 * @return
92 */
93 public String getNameAuthorSeperator() {
94 return nameAuthorSeperator;
95 }
96 public void setNameAuthorSeperator(String nameAuthorSeperator) {
97 this.nameAuthorSeperator = nameAuthorSeperator;
98 }
99
100
101 /**
102 * String the basionym author part starts with e.g. '('.
103 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymEnd() basionymEnd} attribute
104 * @return
105 */
106 public String getBasionymStart() {
107 return basionymStart;
108 }
109 public void setBasionymStart(String basionymStart) {
110 this.basionymStart = basionymStart;
111 }
112
113 /**
114 * String the basionym author part ends with e.g. ')'.
115 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymStart() basionymStart} attribute
116 * @return
117 */
118 public String getBasionymEnd() {
119 return basionymEnd;
120 }
121 public void setBasionymEnd(String basionymEnd) {
122 this.basionymEnd = basionymEnd;
123 }
124
125
126 /**
127 * String to separate ex author from author.
128 * @return
129 */
130 public String getExAuthorSeperator() {
131 return exAuthorSeperator;
132 }
133 public void setExAuthorSeperator(String exAuthorSeperator) {
134 this.exAuthorSeperator = exAuthorSeperator;
135 }
136
137
138 /**
139 * String that separates the basionym/original_combination author part from the combination author part
140 * @return
141 */
142 public CharSequence getBasionymAuthorCombinationAuthorSeperator() {
143 return basionymAuthorCombinationAuthorSeperator;
144 }
145
146
147 public void setBasionymAuthorCombinationAuthorSeperator( CharSequence basionymAuthorCombinationAuthorSeperator) {
148 this.basionymAuthorCombinationAuthorSeperator = basionymAuthorCombinationAuthorSeperator;
149 }
150
151 public String getZooAuthorYearSeperator() {
152 return zooAuthorYearSeperator;
153 }
154 public void setZooAuthorYearSeperator(String authorYearSeperator) {
155 this.zooAuthorYearSeperator = authorYearSeperator;
156 }
157
158 // ******************* Authorship ******************************/
159
160 @Override
161 public String getAuthorshipCache(TaxonName taxonName) {
162 if (taxonName == null){
163 return null;
164 }else if (taxonName.getNameType().isViral()){
165 return null;
166 }else if(taxonName.isProtectedAuthorshipCache() == true) {
167 //cache protected
168 return taxonName.getAuthorshipCache();
169 }else{
170 return getNonCacheAuthorshipCache(taxonName);
171 }
172 }
173
174 /**
175 * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorship field.
176 * @throws NullPointerException if nonViralName is null.
177 * @param taxonName
178 * @return
179 */
180 protected String getNonCacheAuthorshipCache(TaxonName nonViralName){
181 if (nonViralName.getNameType().isZoological()){
182 return this.getZoologicalNonCacheAuthorshipCache(nonViralName);
183 }else{
184 String result = "";
185 INomenclaturalAuthor combinationAuthor = nonViralName.getCombinationAuthorship();
186 INomenclaturalAuthor exCombinationAuthor = nonViralName.getExCombinationAuthorship();
187 INomenclaturalAuthor basionymAuthor = nonViralName.getBasionymAuthorship();
188 INomenclaturalAuthor exBasionymAuthor = nonViralName.getExBasionymAuthorship();
189 String basionymPart = "";
190 String authorPart = "";
191 //basionym
192 if (basionymAuthor != null || exBasionymAuthor != null){
193 basionymPart = basionymStart + getAuthorAndExAuthor(basionymAuthor, exBasionymAuthor) + basionymEnd;
194 }
195 if (combinationAuthor != null || exCombinationAuthor != null){
196 authorPart = getAuthorAndExAuthor(combinationAuthor, exCombinationAuthor);
197 }
198 result = CdmUtils.concat(basionymAuthorCombinationAuthorSeperator, basionymPart, authorPart);
199 // if ("".equals(result)){
200 // result = null;
201 // }
202 return result;
203 }
204 }
205
206 protected String getZoologicalNonCacheAuthorshipCache(TaxonName nonViralName) {
207 if (nonViralName == null){
208 return null;
209 }
210 String result = "";
211 INomenclaturalAuthor combinationAuthor = nonViralName.getCombinationAuthorship();
212 INomenclaturalAuthor exCombinationAuthor = nonViralName.getExCombinationAuthorship();
213 INomenclaturalAuthor basionymAuthor = nonViralName.getBasionymAuthorship();
214 INomenclaturalAuthor exBasionymAuthor = nonViralName.getExBasionymAuthorship();
215 Integer publicationYear = nonViralName.getPublicationYear();
216 Integer originalPublicationYear = nonViralName.getOriginalPublicationYear();
217
218 String basionymPart = "";
219 String authorPart = "";
220 //basionym
221 if (basionymAuthor != null || exBasionymAuthor != null || originalPublicationYear != null ){
222 String authorAndEx = getAuthorAndExAuthor(basionymAuthor, exBasionymAuthor);
223 String originalPublicationYearString = originalPublicationYear == null ? null : String.valueOf(originalPublicationYear);
224 String authorAndExAndYear = CdmUtils.concat(zooAuthorYearSeperator, authorAndEx, originalPublicationYearString );
225 basionymPart = basionymStart + authorAndExAndYear + basionymEnd;
226 }
227 if (combinationAuthor != null || exCombinationAuthor != null){
228 String authorAndEx = getAuthorAndExAuthor(combinationAuthor, exCombinationAuthor);
229 String publicationYearString = publicationYear == null ? null : String.valueOf(publicationYear);
230 authorPart = CdmUtils.concat(zooAuthorYearSeperator, authorAndEx, publicationYearString);
231 }
232 result = CdmUtils.concat(basionymAuthorCombinationAuthorSeperator, basionymPart, authorPart);
233 if (result == null){
234 result = "";
235 }
236 return result;
237 }
238
239
240 /**
241 * Returns the AuthorCache part for a combination of an author and an ex author. This applies on
242 * combination authors as well as on basionym/orginal combination authors.
243 * The correct order is exAuthor ex author though some botanist do not know about and do it the
244 * other way round. (see 46.4-46.6 ICBN (Vienna Code, 2006))
245 *
246 * @param author the author
247 * @param exAuthor the ex-author
248 * @return
249 */
250 protected String getAuthorAndExAuthor(INomenclaturalAuthor author, INomenclaturalAuthor exAuthor){
251 String authorString = "";
252 String exAuthorString = "";
253 if (author != null){
254 authorString = CdmUtils.Nz(author.getNomenclaturalTitle());
255 }
256 if (exAuthor != null){
257 exAuthorString = CdmUtils.Nz(exAuthor.getNomenclaturalTitle());
258 exAuthorString += exAuthorSeperator;
259 }
260 String result = exAuthorString + authorString;
261 return result;
262 }
263
264
265
266 /**
267 * Checks if the given name should include the author in it's cached version.<BR>
268 * This is usually the case but not for <i>species aggregates</i>.
269 * @param nonViralName
270 * @return
271 */
272 protected boolean nameIncludesAuthorship(INonViralName nonViralName){
273 Rank rank = nonViralName.getRank();
274 if (rank != null && rank.isSpeciesAggregate()){
275 return false;
276 }else{
277 return true;
278 }
279 }
280
281 // ************* TAGGED NAME ***************************************/
282
283 @Override
284 public List<TaggedText> getTaggedFullTitle(TaxonName nonViralName) {
285 List<TaggedText> tags = new ArrayList<>();
286
287 //null
288 if (nonViralName == null){
289 return null;
290 }
291
292 //protected full title cache
293 if (nonViralName.isProtectedFullTitleCache()){
294 tags.add(new TaggedText(TagEnum.fullName, nonViralName.getFullTitleCache()));
295 return tags;
296 }
297
298 //title cache
299 // String titleCache = nonViralName.getTitleCache();
300 List<TaggedText> titleTags = getTaggedTitle(nonViralName);
301 tags.addAll(titleTags);
302
303 //reference
304 String microReference = nonViralName.getNomenclaturalMicroReference();
305 INomenclaturalReference ref = nonViralName.getNomenclaturalReference();
306 String referenceCache = null;
307 if (ref != null){
308 Reference reference = HibernateProxyHelper.deproxy(ref, Reference.class);
309 referenceCache = reference.getNomenclaturalCitation(microReference);
310 }
311 //add to tags
312 if (StringUtils.isNotBlank(referenceCache)){
313 if (! referenceCache.trim().startsWith("in ")){
314 String refConcat = ", ";
315 tags.add(new TaggedText(TagEnum.separator, refConcat));
316 }
317 tags.add(new TaggedText(TagEnum.reference, referenceCache));
318 }
319
320 addOriginalSpelling(tags, nonViralName);
321
322 //nomenclatural status
323 tags.addAll(getNomStatusTags(nonViralName, true, false));
324 return tags;
325
326 }
327
328
329 /**
330 * @param nonViralName
331 * @param tags
332 * @return
333 */
334 @Override
335 public List<TaggedText> getNomStatusTags(TaxonName nonViralName, boolean includeSeparatorBefore,
336 boolean includeSeparatorAfter) {
337
338 Set<NomenclaturalStatus> ncStati = nonViralName.getStatus();
339 Iterator<NomenclaturalStatus> iterator = ncStati.iterator();
340 List<TaggedText> nomStatusTags = new ArrayList<>();
341 while (iterator.hasNext()) {
342 NomenclaturalStatus ncStatus = iterator.next();
343 // since the NewInstance method of nomencatural status allows null as parameter
344 // we have to check for null values here
345 String nomStatusStr = "not defined";
346 if(ncStatus.getType() != null){
347 NomenclaturalStatusType statusType = ncStatus.getType();
348 List<Language> prefLangs = Arrays.asList(new Language[]{Language.LATIN(), Language.DEFAULT()});
349 Representation repr = statusType.getPreferredRepresentation(prefLangs);
350 if (repr != null){
351 if(!Language.LATIN().equals(repr.getLanguage())){
352 String message = "No latin representation available for nom. status. " + statusType.getTitleCache();
353 logger.info(message);
354 }
355 nomStatusStr = repr.getAbbreviatedLabel();
356 }else{
357 String message = "No representation available for nom. status. " + statusType.getTitleCache();
358 logger.warn(message);
359 nomStatusStr = statusType.getTitleCache();
360 }
361 }else if(StringUtils.isNotBlank(ncStatus.getRuleConsidered())){
362 nomStatusStr = ncStatus.getRuleConsidered();
363 }
364 String statusSeparator = ", ";
365 if (includeSeparatorBefore){
366 nomStatusTags.add(new TaggedText(TagEnum.separator, statusSeparator));
367 }
368 nomStatusTags.add(new TaggedText(TagEnum.nomStatus, nomStatusStr, new TypedEntityReference<>(ncStatus.getClass(), ncStatus.getUuid())));
369 if (includeSeparatorAfter){
370 nomStatusTags.add(new TaggedText(TagEnum.postSeparator, ","));
371 }
372 }
373 return nomStatusTags;
374 }
375
376 @Override
377 public List<TaggedText> getTaggedTitle(TaxonName taxonName) {
378 if (taxonName == null){
379 return null;
380 }
381
382 List<TaggedText> tags = new ArrayList<>();
383
384 if (taxonName.isViral()){
385 return getViralTaggedTitle(taxonName);
386 }
387 //TODO how to handle protected fullTitleCache here?
388 if (taxonName.isProtectedTitleCache()){
389 //protected title cache
390 tags.add(new TaggedText(TagEnum.name, taxonName.getTitleCache()));
391 return tags;
392 }else if (taxonName.isHybridFormula()){
393 //hybrid formula
394 String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
395 boolean isFirst = true;
396 List<HybridRelationship> rels = taxonName.getOrderedChildRelationships();
397 for (HybridRelationship rel: rels){
398 if (! isFirst){
399 tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
400 }
401 isFirst = false;
402 tags.addAll(getTaggedTitle(rel.getParentName()));
403 }
404 return tags;
405 }else if (taxonName.isAutonym()){
406 //Autonym
407 tags.addAll(handleTaggedAutonym(taxonName));
408 }else{ //not Autonym
409 List<TaggedText> nameTags = getTaggedName(taxonName);
410 tags.addAll(nameTags);
411 String authorCache = getAuthorshipCache(taxonName);
412 if (StringUtils.isNotBlank(authorCache)){
413 tags.add(new TaggedText(TagEnum.authors, authorCache));
414 }
415 }
416
417 return tags;
418 }
419
420 /**
421 * @param taxonName
422 * @return
423 */
424 private List<TaggedText> getViralTaggedTitle(TaxonName viralName) {
425 List<TaggedText> tags = new ArrayList<>();
426 if (viralName.isProtectedTitleCache()){
427 //protected title cache
428 tags.add(new TaggedText(TagEnum.name, viralName.getTitleCache()));
429 return tags;
430 }else{
431 if (StringUtils.isNotBlank(viralName.getAcronym())){
432 //this is not according to the code
433 tags.add(new TaggedText(TagEnum.name, viralName.getAcronym()));
434 }
435 return tags;
436 }
437 }
438
439 /**
440 * Returns the tag list of the name part (without author and reference).
441 * @param nonViralName
442 * @return
443 */
444 @Override
445 public List<TaggedText> getTaggedName(TaxonName nonViralName) {
446 if (nonViralName == null){
447 return null;
448 }else if (nonViralName.getNameType().isViral()){
449 return null;
450 }
451 List<TaggedText> tags = new ArrayList<>();
452 Rank rank = nonViralName.getRank();
453
454 if (nonViralName.isProtectedNameCache()){
455 tags.add(new TaggedText(TagEnum.name, nonViralName.getNameCache()));
456 }else if (nonViralName.isHybridFormula()){
457 //hybrid formula
458 String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
459 boolean isFirst = true;
460 List<HybridRelationship> rels = nonViralName.getOrderedChildRelationships();
461 for (HybridRelationship rel: rels){
462 if (! isFirst){
463 tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
464 }
465 isFirst = false;
466 tags.addAll(getTaggedName(rel.getParentName()));
467 }
468 return tags;
469
470 }else if (rank == null){
471 tags = getRanklessTaggedNameCache(nonViralName);
472 // }else if (nonViralName.isInfragenericUnranked()){
473 // result = getUnrankedInfragenericNameCache(nonViralName);
474 }else if (rank.isInfraSpecific()){
475 tags = getInfraSpeciesTaggedNameCache(nonViralName);
476 }else if (rank.isSpecies() || isAggregateWithAuthorship(nonViralName, rank) ){ //exception see #4288
477 tags = getSpeciesTaggedNameCache(nonViralName);
478 }else if (rank.isInfraGeneric()){
479 tags = getInfraGenusTaggedNameCache(nonViralName);
480 }else if (rank.isGenus()){
481 tags = getGenusOrUninomialTaggedNameCache(nonViralName);
482 }else if (rank.isSupraGeneric()){
483 tags = getGenusOrUninomialTaggedNameCache(nonViralName);
484 }else{
485 tags = getRanklessTaggedNameCache(nonViralName);
486 logger.warn("Rank does not belong to a rank class: " + rank.getTitleCache() + ". Used rankless nameCache for name " + nonViralName.getUuid());
487 }
488 //TODO handle appended phrase here instead of different places, check first if this is true for all
489 //cases
490
491 return tags;
492
493 }
494
495
496
497
498 //***************************** PRIVATES ***************************************/
499
500
501 private boolean isAggregateWithAuthorship(TaxonName nonViralName, Rank rank) {
502 if (rank == null){
503 return false;
504 }else{
505 return rank.isSpeciesAggregate() && ( isNotBlank(nonViralName.getAuthorshipCache()) || nonViralName.getNomenclaturalReference() != null );
506 }
507 }
508
509
510 /**
511 * Returns the tag list for an autonym taxon.
512 *
513 * @see NonViralName#isAutonym()
514 * @see BotanicalName#isAutonym()
515 * @param nonViralName
516 * @return
517 */
518 private List<TaggedText> handleTaggedAutonym(TaxonName nonViralName) {
519 List<TaggedText> tags = null;
520 if (nonViralName.isInfraSpecific()){
521 //species part
522 tags = getSpeciesTaggedNameCache(nonViralName);
523
524 //author
525 String authorCache = getAuthorshipCache(nonViralName);
526 if (StringUtils.isNotBlank(authorCache)){
527 tags.add(new TaggedText(TagEnum.authors, authorCache));
528 }
529
530 //infra species marker
531 if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){
532 //TODO handle exception
533 logger.warn("Rank for autonym does not exist or is not lower than species !!");
534 }else{
535 String infraSpeciesMarker = nonViralName.getRank().getAbbreviation();
536 if (nonViralName.isTrinomHybrid()){
537 infraSpeciesMarker = CdmUtils.concat("", NOTHO, infraSpeciesMarker);
538 }
539 if (StringUtils.isNotBlank(infraSpeciesMarker)){
540 tags.add(new TaggedText(TagEnum.rank, infraSpeciesMarker));
541 }
542 }
543
544 //infra species
545 String infraSpeciesPart = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim();
546 if (StringUtils.isNotBlank(infraSpeciesPart)){
547 tags.add(new TaggedText(TagEnum.name, infraSpeciesPart));
548 }
549
550 } else if (nonViralName.isInfraGeneric()){
551 //genus part
552 tags =getGenusOrUninomialTaggedNameCache(nonViralName);
553
554 //author
555 String authorCache = getAuthorshipCache(nonViralName);
556 if (StringUtils.isNotBlank(authorCache)){
557 tags.add(new TaggedText(TagEnum.authors, authorCache));
558 }
559
560 //infra species marker
561 if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraGeneric()){
562 //TODO handle exception
563 logger.warn("Rank for autonym does not exist or is not lower than species !!");
564 }else{
565 Rank rank = nonViralName.getRank();
566 String infraGenericMarker = rank.getAbbreviation();
567 if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
568 infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
569 }
570 if (StringUtils.isNotBlank(infraGenericMarker)){
571 tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
572 }
573 }
574
575 //infra genus
576 String infraGenericPart = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
577 if (StringUtils.isNotBlank(infraGenericPart)){
578 tags.add(new TaggedText(TagEnum.name, infraGenericPart));
579 }
580 }
581
582 return tags;
583 }
584
585
586
587 /**
588 * Returns the tag list for rankless taxa.
589 * @param nonViralName
590 * @return
591 */
592 protected List<TaggedText> getRanklessTaggedNameCache(INonViralName nonViralName){
593 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
594 String speciesEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
595 if (StringUtils.isNotBlank(speciesEpi)){
596 tags.add(new TaggedText(TagEnum.name, speciesEpi));
597 }
598
599 String infraSpeciesEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
600 if (StringUtils.isNotBlank(infraSpeciesEpi)){
601 tags.add(new TaggedText(TagEnum.name, infraSpeciesEpi));
602 }
603
604 //result += " (rankless)";
605 addAppendedTaggedPhrase(tags, nonViralName);
606 return tags;
607 }
608
609 /**
610 * Returns the tag list for the first epithet (including a hybrid sign if required).
611 * @param nonViralName
612 * @return
613 */
614 private List<TaggedText> getUninomialTaggedPart(INonViralName nonViralName) {
615 List<TaggedText> tags = new ArrayList<>();
616
617 if (nonViralName.isMonomHybrid()){
618 addHybridPrefix(tags);
619 }
620
621 String uninomial = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
622 if (StringUtils.isNotBlank(uninomial)){
623 tags.add(new TaggedText(TagEnum.name, uninomial));
624 }
625
626 return tags;
627 }
628
629 /**
630 * Returns the tag list for an genus or higher taxon.
631 *
632 * @param nonViralName
633 * @return
634 */
635 protected List<TaggedText> getGenusOrUninomialTaggedNameCache(INonViralName nonViralName){
636 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
637 addAppendedTaggedPhrase(tags, nonViralName);
638 return tags;
639 }
640
641 /**
642 * Returns the tag list for an infrageneric taxon (including species aggregates).
643 *
644 * @see #getSpeciesAggregateTaggedCache(NonViralName)
645 * @param nonViralName
646 * @return
647 */
648 protected List<TaggedText> getInfraGenusTaggedNameCache(INonViralName nonViralName){
649 Rank rank = nonViralName.getRank();
650 if (rank != null && rank.isSpeciesAggregate() && isBlank(nonViralName.getAuthorshipCache())){
651 return getSpeciesAggregateTaggedCache(nonViralName);
652 }
653
654 //genus
655 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
656
657 //marker
658 String infraGenericMarker;
659 if (rank != null){
660 try {
661 infraGenericMarker = rank.getInfraGenericMarker();
662 if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
663 infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
664 }
665 } catch (UnknownCdmTypeException e) {
666 infraGenericMarker = "'unhandled infrageneric rank'";
667 }
668 }else{
669 infraGenericMarker = "'undefined infrageneric rank'";
670 }
671 String infraGenEpi = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
672 if (nonViralName.isBinomHybrid()){
673 infraGenericMarker = CdmUtils.concat("", NOTHO, infraGenericMarker);
674 }
675
676 addInfraGenericPart(nonViralName, tags, infraGenericMarker, infraGenEpi);
677
678 addAppendedTaggedPhrase(tags, nonViralName);
679 return tags;
680 }
681
682
683 /**
684 * Default implementation for the infrageneric part of a name.
685 * This is usually the infrageneric marker and the infrageneric epitheton. But may be
686 * implemented differently e.g. for zoological names the infrageneric epitheton
687 * may be surrounded by brackets and the marker left out.
688 * @param nonViralName
689 * @param tags
690 * @param infraGenericMarker
691 */
692 protected void addInfraGenericPart(
693 @SuppressWarnings("unused") INonViralName name,
694 List<TaggedText> tags,
695 String infraGenericMarker,
696 String infraGenEpi) {
697 //add marker
698 tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
699
700 //add epitheton
701 if (StringUtils.isNotBlank(infraGenEpi)){
702 tags.add(new TaggedText(TagEnum.name, infraGenEpi));
703 }
704 }
705
706 /**
707 * Returns the tag list for a species aggregate (or similar) taxon.<BR>
708 * Possible ranks for a <i>species aggregate</i> are "aggr.", "species group", ...
709 * @param nonViralName
710 * @return
711 */
712 protected List<TaggedText> getSpeciesAggregateTaggedCache(INonViralName nonViralName){
713 if (! isBlank(nonViralName.getAuthorshipCache())){
714 List<TaggedText> result = getSpeciesTaggedNameCache(nonViralName);
715 return result;
716 }
717
718
719 List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
720
721 addSpeciesAggregateTaggedEpithet(tags, nonViralName);
722 addAppendedTaggedPhrase(tags, nonViralName);
723 return tags;
724 }
725
726 /**
727 * Adds the aggregate tag to the tag list.
728 * @param tags
729 * @param nonViralName
730 */
731 private void addSpeciesAggregateTaggedEpithet(List<TaggedText> tags, INonViralName nonViralName) {
732 String marker;
733 try {
734 marker = nonViralName.getRank().getInfraGenericMarker();
735 } catch (UnknownCdmTypeException e) {
736 marker = "'unknown aggregat type'";
737 }
738 if (StringUtils.isNotBlank(marker)){
739 tags.add(new TaggedText(TagEnum.rank, marker));
740 }
741 }
742
743
744 /**
745 * Returns the tag list for a species taxon.
746 * @param nonViralName
747 * @return
748 */
749 protected List<TaggedText> getSpeciesTaggedNameCache(INonViralName nonViralName){
750 List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
751 addAppendedTaggedPhrase(tags, nonViralName);
752 return tags;
753 }
754
755 protected List<TaggedText> getInfraSpeciesTaggedNameCache(TaxonName name){
756 if (name.getNameType().isZoological()){
757 boolean includeMarker =includeInfraSpecificMarkerForZooNames(name);
758 return getInfraSpeciesTaggedNameCache(name, includeMarker);
759 }else{
760 return getInfraSpeciesTaggedNameCache(name, true);
761 }
762 }
763
764 protected boolean includeInfraSpecificMarkerForZooNames(TaxonName name){
765 return ! (name.isAutonym()); //only exclude marker if autonym, see also ZooNameNoMarkerCacheStrategy
766 }
767
768 /**
769 * Creates the tag list for an infraspecific taxon. If include is true the result will contain
770 * the infraspecific marker (e.g. "var.")
771 * @param nonViralName
772 * @param includeMarker
773 * @return
774 */
775 protected List<TaggedText> getInfraSpeciesTaggedNameCache(INonViralName nonViralName, boolean includeMarker){
776 List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
777 if (includeMarker || nonViralName.isTrinomHybrid()){
778 String marker = (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
779 if (nonViralName.isTrinomHybrid()){
780 marker = CdmUtils.concat("", NOTHO, marker);
781 }
782 if (StringUtils.isNotBlank(marker)){
783 tags.add(new TaggedText(TagEnum.rank, marker));
784 }
785
786 }
787 String infrSpecEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
788
789 infrSpecEpi = infrSpecEpi.trim().replace("null", "");
790
791 if (StringUtils.isNotBlank(infrSpecEpi)){
792 tags.add(new TaggedText(TagEnum.name, infrSpecEpi));
793 }
794
795 addAppendedTaggedPhrase(tags, nonViralName);
796 return tags;
797 }
798
799
800 /**
801 * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
802 * @param tags
803 */
804 private void addHybridPrefix(List<TaggedText> tags) {
805 tags.add(new TaggedText(TagEnum.hybridSign, NonViralNameParserImplRegExBase.hybridSign));
806 tags.add(new TaggedText(TagEnum.separator, "")); //no whitespace separator
807 }
808
809 /**
810 * Creates the tag list for the genus and species part.
811 * @param nonViralName
812 * @return
813 */
814 private List<TaggedText> getGenusAndSpeciesTaggedPart(INonViralName nonViralName) {
815 //Uninomial
816 List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
817
818 //InfraGenericEpi
819 boolean hasInfraGenericEpi = StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet());
820 if (hasInfraGenericEpi){
821 String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();
822 if (nonViralName.isBinomHybrid()){
823 //maybe not correct but not really expected to happen
824 infrGenEpi = UTF8.HYBRID + infrGenEpi;
825 }
826 infrGenEpi = "(" + infrGenEpi + ")";
827 tags.add(new TaggedText(TagEnum.name, infrGenEpi));
828 }
829
830 //Species Epi
831 String specEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
832 if (! hasInfraGenericEpi && nonViralName.isBinomHybrid() ||
833 hasInfraGenericEpi && nonViralName.isTrinomHybrid()){
834 addHybridPrefix(tags);
835 }
836 if (StringUtils.isNotBlank(specEpi)){
837 tags.add(new TaggedText(TagEnum.name, specEpi));
838 }
839 return tags;
840 }
841
842 /**
843 * Adds the tag for the appended phrase if an appended phrase exists
844 * @param tags
845 * @param nonViralName
846 */
847 protected void addAppendedTaggedPhrase(List<TaggedText> tags, INonViralName nonViralName){
848 String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();
849 if (StringUtils.isNotEmpty(appendedPhrase)){
850 tags.add(new TaggedText(TagEnum.name, appendedPhrase));
851 }
852 }
853
854
855 @Override
856 public String getLastEpithet(TaxonName taxonName) {
857 Rank rank = taxonName.getRank();
858 if(rank.isGenus() || rank.isSupraGeneric()) {
859 return taxonName.getGenusOrUninomial();
860 } else if(rank.isInfraGeneric()) {
861 return taxonName.getInfraGenericEpithet();
862 } else if(rank.isSpecies()) {
863 return taxonName.getSpecificEpithet();
864 } else {
865 return taxonName.getInfraSpecificEpithet();
866 }
867 }
868
869 @Override
870 protected List<TaggedText> doGetTaggedTitle(TaxonName taxonName) {
871 List<TaggedText> tags = new ArrayList<>();
872 if (taxonName.getNameType().isViral()){
873 String acronym = taxonName.getAcronym();
874 tags.add(new TaggedText(TagEnum.name, acronym));
875 return tags;
876 }else if (taxonName.isHybridFormula()){
877 //hybrid formula
878 String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
879 boolean isFirst = true;
880 List<HybridRelationship> rels = taxonName.getOrderedChildRelationships();
881 for (HybridRelationship rel: rels){
882 if (! isFirst){
883 tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
884 }
885 isFirst = false;
886 tags.addAll(getTaggedTitle(rel.getParentName()));
887 }
888 return tags;
889 }else if (taxonName.isAutonym()){
890 //Autonym
891 tags.addAll(handleTaggedAutonym(taxonName));
892 }else{ //not Autonym
893 List<TaggedText> nameTags = getTaggedName(taxonName);
894 tags.addAll(nameTags);
895 String authorCache = getAuthorshipCache(taxonName);
896 if (StringUtils.isNotBlank(authorCache)){
897 tags.add(new TaggedText(TagEnum.authors, authorCache));
898 }
899 }
900 return tags;
901 }
902
903 }