2 * Copyright (C) 2007 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
9 package eu
.etaxonomy
.cdm
.strategy
.cache
.name
;
11 import java
.util
.ArrayList
;
12 import java
.util
.Arrays
;
13 import java
.util
.Iterator
;
14 import java
.util
.List
;
16 import java
.util
.UUID
;
18 import org
.apache
.commons
.lang
.StringUtils
;
19 import org
.apache
.log4j
.Logger
;
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
;
43 * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName>
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
51 public class TaxonNameDefaultCacheStrategy
52 extends NameCacheStrategyBase
53 implements INonViralNameCacheStrategy
{
55 private static final Logger logger
= Logger
.getLogger(TaxonNameDefaultCacheStrategy
.class);
56 private static final long serialVersionUID
= -6577757501563212669L;
58 final static UUID uuid
= UUID
.fromString("1cdda0d1-d5bc-480f-bf08-40a510a2f223");
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
= " ";
67 protected String zooAuthorYearSeperator
= ", ";
70 public UUID
getUuid(){
74 // ************************** FACTORY ******************************/
76 public static TaxonNameDefaultCacheStrategy
NewInstance(){
77 return new TaxonNameDefaultCacheStrategy();
81 // ************ CONSTRUCTOR *******************/
83 protected TaxonNameDefaultCacheStrategy(){
87 /* **************** GETTER / SETTER **************************************/
90 * String that separates the NameCache part from the AuthorCache part
93 public String
getNameAuthorSeperator() {
94 return nameAuthorSeperator
;
96 public void setNameAuthorSeperator(String nameAuthorSeperator
) {
97 this.nameAuthorSeperator
= nameAuthorSeperator
;
102 * String the basionym author part starts with e.g. '('.
103 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymEnd() basionymEnd} attribute
106 public String
getBasionymStart() {
107 return basionymStart
;
109 public void setBasionymStart(String basionymStart
) {
110 this.basionymStart
= basionymStart
;
114 * String the basionym author part ends with e.g. ')'.
115 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymStart() basionymStart} attribute
118 public String
getBasionymEnd() {
121 public void setBasionymEnd(String basionymEnd
) {
122 this.basionymEnd
= basionymEnd
;
127 * String to separate ex author from author.
130 public String
getExAuthorSeperator() {
131 return exAuthorSeperator
;
133 public void setExAuthorSeperator(String exAuthorSeperator
) {
134 this.exAuthorSeperator
= exAuthorSeperator
;
139 * String that separates the basionym/original_combination author part from the combination author part
142 public CharSequence
getBasionymAuthorCombinationAuthorSeperator() {
143 return basionymAuthorCombinationAuthorSeperator
;
147 public void setBasionymAuthorCombinationAuthorSeperator( CharSequence basionymAuthorCombinationAuthorSeperator
) {
148 this.basionymAuthorCombinationAuthorSeperator
= basionymAuthorCombinationAuthorSeperator
;
151 public String
getZooAuthorYearSeperator() {
152 return zooAuthorYearSeperator
;
154 public void setZooAuthorYearSeperator(String authorYearSeperator
) {
155 this.zooAuthorYearSeperator
= authorYearSeperator
;
158 // ******************* Authorship ******************************/
161 public String
getAuthorshipCache(TaxonName taxonName
) {
162 if (taxonName
== null){
164 }else if (taxonName
.getNameType().isViral()){
166 }else if(taxonName
.isProtectedAuthorshipCache() == true) {
168 return taxonName
.getAuthorshipCache();
170 return getNonCacheAuthorshipCache(taxonName
);
175 * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorship field.
176 * @throws NullPointerException if nonViralName is null.
180 protected String
getNonCacheAuthorshipCache(TaxonName nonViralName
){
181 if (nonViralName
.getNameType().isZoological()){
182 return this.getZoologicalNonCacheAuthorshipCache(nonViralName
);
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
= "";
192 if (basionymAuthor
!= null || exBasionymAuthor
!= null){
193 basionymPart
= basionymStart
+ getAuthorAndExAuthor(basionymAuthor
, exBasionymAuthor
) + basionymEnd
;
195 if (combinationAuthor
!= null || exCombinationAuthor
!= null){
196 authorPart
= getAuthorAndExAuthor(combinationAuthor
, exCombinationAuthor
);
198 result
= CdmUtils
.concat(basionymAuthorCombinationAuthorSeperator
, basionymPart
, authorPart
);
199 // if ("".equals(result)){
206 protected String
getZoologicalNonCacheAuthorshipCache(TaxonName nonViralName
) {
207 if (nonViralName
== null){
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();
218 String basionymPart
= "";
219 String authorPart
= "";
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
;
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
);
232 result
= CdmUtils
.concat(basionymAuthorCombinationAuthorSeperator
, basionymPart
, authorPart
);
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))
246 * @param author the author
247 * @param exAuthor the ex-author
250 protected String
getAuthorAndExAuthor(INomenclaturalAuthor author
, INomenclaturalAuthor exAuthor
){
251 String authorString
= "";
252 String exAuthorString
= "";
254 authorString
= CdmUtils
.Nz(author
.getNomenclaturalTitle());
256 if (exAuthor
!= null){
257 exAuthorString
= CdmUtils
.Nz(exAuthor
.getNomenclaturalTitle());
258 exAuthorString
+= exAuthorSeperator
;
260 String result
= exAuthorString
+ authorString
;
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
272 protected boolean nameIncludesAuthorship(INonViralName nonViralName
){
273 Rank rank
= nonViralName
.getRank();
274 if (rank
!= null && rank
.isSpeciesAggregate()){
281 // ************* TAGGED NAME ***************************************/
284 public List
<TaggedText
> getTaggedFullTitle(TaxonName nonViralName
) {
285 List
<TaggedText
> tags
= new ArrayList
<>();
288 if (nonViralName
== null){
292 //protected full title cache
293 if (nonViralName
.isProtectedFullTitleCache()){
294 tags
.add(new TaggedText(TagEnum
.fullName
, nonViralName
.getFullTitleCache()));
299 // String titleCache = nonViralName.getTitleCache();
300 List
<TaggedText
> titleTags
= getTaggedTitle(nonViralName
);
301 tags
.addAll(titleTags
);
304 String microReference
= nonViralName
.getNomenclaturalMicroReference();
305 INomenclaturalReference ref
= nonViralName
.getNomenclaturalReference();
306 String referenceCache
= null;
308 Reference reference
= HibernateProxyHelper
.deproxy(ref
, Reference
.class);
309 referenceCache
= reference
.getNomenclaturalCitation(microReference
);
312 if (StringUtils
.isNotBlank(referenceCache
)){
313 if (! referenceCache
.trim().startsWith("in ")){
314 String refConcat
= ", ";
315 tags
.add(new TaggedText(TagEnum
.separator
, refConcat
));
317 tags
.add(new TaggedText(TagEnum
.reference
, referenceCache
));
320 addOriginalSpelling(tags
, nonViralName
);
322 //nomenclatural status
323 tags
.addAll(getNomStatusTags(nonViralName
, true, false));
330 * @param nonViralName
335 public List
<TaggedText
> getNomStatusTags(TaxonName nonViralName
, boolean includeSeparatorBefore
,
336 boolean includeSeparatorAfter
) {
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
);
351 if(!Language
.LATIN().equals(repr
.getLanguage())){
352 String message
= "No latin representation available for nom. status. " + statusType
.getTitleCache();
353 logger
.info(message
);
355 nomStatusStr
= repr
.getAbbreviatedLabel();
357 String message
= "No representation available for nom. status. " + statusType
.getTitleCache();
358 logger
.warn(message
);
359 nomStatusStr
= statusType
.getTitleCache();
361 }else if(StringUtils
.isNotBlank(ncStatus
.getRuleConsidered())){
362 nomStatusStr
= ncStatus
.getRuleConsidered();
364 String statusSeparator
= ", ";
365 if (includeSeparatorBefore
){
366 nomStatusTags
.add(new TaggedText(TagEnum
.separator
, statusSeparator
));
368 nomStatusTags
.add(new TaggedText(TagEnum
.nomStatus
, nomStatusStr
, new TypedEntityReference
<>(ncStatus
.getClass(), ncStatus
.getUuid())));
369 if (includeSeparatorAfter
){
370 nomStatusTags
.add(new TaggedText(TagEnum
.postSeparator
, ","));
373 return nomStatusTags
;
377 public List
<TaggedText
> getTaggedTitle(TaxonName taxonName
) {
378 if (taxonName
== null){
382 List
<TaggedText
> tags
= new ArrayList
<>();
384 if (taxonName
.isViral()){
385 return getViralTaggedTitle(taxonName
);
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()));
392 }else if (taxonName
.isHybridFormula()){
394 String hybridSeparator
= NonViralNameParserImplRegExBase
.hybridSign
;
395 boolean isFirst
= true;
396 List
<HybridRelationship
> rels
= taxonName
.getOrderedChildRelationships();
397 for (HybridRelationship rel
: rels
){
399 tags
.add(new TaggedText(TagEnum
.hybridSign
, hybridSeparator
));
402 tags
.addAll(getTaggedTitle(rel
.getParentName()));
405 }else if (taxonName
.isAutonym()){
407 tags
.addAll(handleTaggedAutonym(taxonName
));
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
));
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()));
431 if (StringUtils
.isNotBlank(viralName
.getAcronym())){
432 //this is not according to the code
433 tags
.add(new TaggedText(TagEnum
.name
, viralName
.getAcronym()));
440 * Returns the tag list of the name part (without author and reference).
441 * @param nonViralName
445 public List
<TaggedText
> getTaggedName(TaxonName nonViralName
) {
446 if (nonViralName
== null){
448 }else if (nonViralName
.getNameType().isViral()){
451 List
<TaggedText
> tags
= new ArrayList
<>();
452 Rank rank
= nonViralName
.getRank();
454 if (nonViralName
.isProtectedNameCache()){
455 tags
.add(new TaggedText(TagEnum
.name
, nonViralName
.getNameCache()));
456 }else if (nonViralName
.isHybridFormula()){
458 String hybridSeparator
= NonViralNameParserImplRegExBase
.hybridSign
;
459 boolean isFirst
= true;
460 List
<HybridRelationship
> rels
= nonViralName
.getOrderedChildRelationships();
461 for (HybridRelationship rel
: rels
){
463 tags
.add(new TaggedText(TagEnum
.hybridSign
, hybridSeparator
));
466 tags
.addAll(getTaggedName(rel
.getParentName()));
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
);
485 tags
= getRanklessTaggedNameCache(nonViralName
);
486 logger
.warn("Rank does not belong to a rank class: " + rank
.getTitleCache() + ". Used rankless nameCache for name " + nonViralName
.getUuid());
488 //TODO handle appended phrase here instead of different places, check first if this is true for all
498 //***************************** PRIVATES ***************************************/
501 private boolean isAggregateWithAuthorship(TaxonName nonViralName
, Rank rank
) {
505 return rank
.isSpeciesAggregate() && ( isNotBlank(nonViralName
.getAuthorshipCache()) || nonViralName
.getNomenclaturalReference() != null );
511 * Returns the tag list for an autonym taxon.
513 * @see NonViralName#isAutonym()
514 * @see BotanicalName#isAutonym()
515 * @param nonViralName
518 private List
<TaggedText
> handleTaggedAutonym(TaxonName nonViralName
) {
519 List
<TaggedText
> tags
= null;
520 if (nonViralName
.isInfraSpecific()){
522 tags
= getSpeciesTaggedNameCache(nonViralName
);
525 String authorCache
= getAuthorshipCache(nonViralName
);
526 if (StringUtils
.isNotBlank(authorCache
)){
527 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));
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 !!");
535 String infraSpeciesMarker
= nonViralName
.getRank().getAbbreviation();
536 if (nonViralName
.isTrinomHybrid()){
537 infraSpeciesMarker
= CdmUtils
.concat("", NOTHO
, infraSpeciesMarker
);
539 if (StringUtils
.isNotBlank(infraSpeciesMarker
)){
540 tags
.add(new TaggedText(TagEnum
.rank
, infraSpeciesMarker
));
545 String infraSpeciesPart
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet()).trim();
546 if (StringUtils
.isNotBlank(infraSpeciesPart
)){
547 tags
.add(new TaggedText(TagEnum
.name
, infraSpeciesPart
));
550 } else if (nonViralName
.isInfraGeneric()){
552 tags
=getGenusOrUninomialTaggedNameCache(nonViralName
);
555 String authorCache
= getAuthorshipCache(nonViralName
);
556 if (StringUtils
.isNotBlank(authorCache
)){
557 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));
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 !!");
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.)", "");
570 if (StringUtils
.isNotBlank(infraGenericMarker
)){
571 tags
.add(new TaggedText(TagEnum
.rank
, infraGenericMarker
));
576 String infraGenericPart
= CdmUtils
.Nz(nonViralName
.getInfraGenericEpithet()).trim();
577 if (StringUtils
.isNotBlank(infraGenericPart
)){
578 tags
.add(new TaggedText(TagEnum
.name
, infraGenericPart
));
588 * Returns the tag list for rankless taxa.
589 * @param nonViralName
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
));
599 String infraSpeciesEpi
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet());
600 if (StringUtils
.isNotBlank(infraSpeciesEpi
)){
601 tags
.add(new TaggedText(TagEnum
.name
, infraSpeciesEpi
));
604 //result += " (rankless)";
605 addAppendedTaggedPhrase(tags
, nonViralName
);
610 * Returns the tag list for the first epithet (including a hybrid sign if required).
611 * @param nonViralName
614 private List
<TaggedText
> getUninomialTaggedPart(INonViralName nonViralName
) {
615 List
<TaggedText
> tags
= new ArrayList
<>();
617 if (nonViralName
.isMonomHybrid()){
618 addHybridPrefix(tags
);
621 String uninomial
= CdmUtils
.Nz(nonViralName
.getGenusOrUninomial()).trim();
622 if (StringUtils
.isNotBlank(uninomial
)){
623 tags
.add(new TaggedText(TagEnum
.name
, uninomial
));
630 * Returns the tag list for an genus or higher taxon.
632 * @param nonViralName
635 protected List
<TaggedText
> getGenusOrUninomialTaggedNameCache(INonViralName nonViralName
){
636 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
637 addAppendedTaggedPhrase(tags
, nonViralName
);
642 * Returns the tag list for an infrageneric taxon (including species aggregates).
644 * @see #getSpeciesAggregateTaggedCache(NonViralName)
645 * @param nonViralName
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
);
655 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
658 String infraGenericMarker
;
661 infraGenericMarker
= rank
.getInfraGenericMarker();
662 if (rank
.equals(Rank
.SECTION_BOTANY()) || rank
.equals(Rank
.SUBSECTION_BOTANY())){
663 infraGenericMarker
= infraGenericMarker
.replace("(bot.)", "");
665 } catch (UnknownCdmTypeException e
) {
666 infraGenericMarker
= "'unhandled infrageneric rank'";
669 infraGenericMarker
= "'undefined infrageneric rank'";
671 String infraGenEpi
= CdmUtils
.Nz(nonViralName
.getInfraGenericEpithet()).trim();
672 if (nonViralName
.isBinomHybrid()){
673 infraGenericMarker
= CdmUtils
.concat("", NOTHO
, infraGenericMarker
);
676 addInfraGenericPart(nonViralName
, tags
, infraGenericMarker
, infraGenEpi
);
678 addAppendedTaggedPhrase(tags
, nonViralName
);
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
690 * @param infraGenericMarker
692 protected void addInfraGenericPart(
693 @SuppressWarnings("unused") INonViralName name
,
694 List
<TaggedText
> tags
,
695 String infraGenericMarker
,
696 String infraGenEpi
) {
698 tags
.add(new TaggedText(TagEnum
.rank
, infraGenericMarker
));
701 if (StringUtils
.isNotBlank(infraGenEpi
)){
702 tags
.add(new TaggedText(TagEnum
.name
, infraGenEpi
));
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
712 protected List
<TaggedText
> getSpeciesAggregateTaggedCache(INonViralName nonViralName
){
713 if (! isBlank(nonViralName
.getAuthorshipCache())){
714 List
<TaggedText
> result
= getSpeciesTaggedNameCache(nonViralName
);
719 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
721 addSpeciesAggregateTaggedEpithet(tags
, nonViralName
);
722 addAppendedTaggedPhrase(tags
, nonViralName
);
727 * Adds the aggregate tag to the tag list.
729 * @param nonViralName
731 private void addSpeciesAggregateTaggedEpithet(List
<TaggedText
> tags
, INonViralName nonViralName
) {
734 marker
= nonViralName
.getRank().getInfraGenericMarker();
735 } catch (UnknownCdmTypeException e
) {
736 marker
= "'unknown aggregat type'";
738 if (StringUtils
.isNotBlank(marker
)){
739 tags
.add(new TaggedText(TagEnum
.rank
, marker
));
745 * Returns the tag list for a species taxon.
746 * @param nonViralName
749 protected List
<TaggedText
> getSpeciesTaggedNameCache(INonViralName nonViralName
){
750 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
751 addAppendedTaggedPhrase(tags
, nonViralName
);
755 protected List
<TaggedText
> getInfraSpeciesTaggedNameCache(TaxonName name
){
756 if (name
.getNameType().isZoological()){
757 boolean includeMarker
=includeInfraSpecificMarkerForZooNames(name
);
758 return getInfraSpeciesTaggedNameCache(name
, includeMarker
);
760 return getInfraSpeciesTaggedNameCache(name
, true);
764 protected boolean includeInfraSpecificMarkerForZooNames(TaxonName name
){
765 return ! (name
.isAutonym()); //only exclude marker if autonym, see also ZooNameNoMarkerCacheStrategy
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
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
);
782 if (StringUtils
.isNotBlank(marker
)){
783 tags
.add(new TaggedText(TagEnum
.rank
, marker
));
787 String infrSpecEpi
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet());
789 infrSpecEpi
= infrSpecEpi
.trim().replace("null", "");
791 if (StringUtils
.isNotBlank(infrSpecEpi
)){
792 tags
.add(new TaggedText(TagEnum
.name
, infrSpecEpi
));
795 addAppendedTaggedPhrase(tags
, nonViralName
);
801 * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
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
810 * Creates the tag list for the genus and species part.
811 * @param nonViralName
814 private List
<TaggedText
> getGenusAndSpeciesTaggedPart(INonViralName nonViralName
) {
816 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
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
;
826 infrGenEpi
= "(" + infrGenEpi
+ ")";
827 tags
.add(new TaggedText(TagEnum
.name
, infrGenEpi
));
831 String specEpi
= CdmUtils
.Nz(nonViralName
.getSpecificEpithet()).trim();
832 if (! hasInfraGenericEpi
&& nonViralName
.isBinomHybrid() ||
833 hasInfraGenericEpi
&& nonViralName
.isTrinomHybrid()){
834 addHybridPrefix(tags
);
836 if (StringUtils
.isNotBlank(specEpi
)){
837 tags
.add(new TaggedText(TagEnum
.name
, specEpi
));
843 * Adds the tag for the appended phrase if an appended phrase exists
845 * @param nonViralName
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
));
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();
865 return taxonName
.getInfraSpecificEpithet();
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
));
876 }else if (taxonName
.isHybridFormula()){
878 String hybridSeparator
= NonViralNameParserImplRegExBase
.hybridSign
;
879 boolean isFirst
= true;
880 List
<HybridRelationship
> rels
= taxonName
.getOrderedChildRelationships();
881 for (HybridRelationship rel
: rels
){
883 tags
.add(new TaggedText(TagEnum
.hybridSign
, hybridSeparator
));
886 tags
.addAll(getTaggedTitle(rel
.getParentName()));
889 }else if (taxonName
.isAutonym()){
891 tags
.addAll(handleTaggedAutonym(taxonName
));
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
));