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
.List
;
13 import java
.util
.UUID
;
15 import org
.apache
.log4j
.Logger
;
17 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
18 import eu
.etaxonomy
.cdm
.common
.UTF8
;
19 import eu
.etaxonomy
.cdm
.model
.agent
.INomenclaturalAuthor
;
20 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationship
;
21 import eu
.etaxonomy
.cdm
.model
.name
.INonViralName
;
22 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
23 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
24 import eu
.etaxonomy
.cdm
.strategy
.cache
.TagEnum
;
25 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
26 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
27 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImplRegExBase
;
31 * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName>
33 * The method implements a cache strategy for botanical names so no method has to be overwritten by
34 * a subclass for botanic names.
35 * Where differing from this default botanic name strategy other subclasses should overwrite the
36 * existing methods, e.g. a CacheStrategy for zoological names should overwrite getAuthorAndExAuthor
39 public class TaxonNameDefaultCacheStrategy
40 extends NameCacheStrategyBase
41 implements INonViralNameCacheStrategy
{
43 private static final Logger logger
= Logger
.getLogger(TaxonNameDefaultCacheStrategy
.class);
44 private static final long serialVersionUID
= -6577757501563212669L;
46 final static UUID uuid
= UUID
.fromString("1cdda0d1-d5bc-480f-bf08-40a510a2f223");
48 protected String nameAuthorSeperator
= " ";
49 protected String basionymStart
= "(";
50 protected String basionymEnd
= ")";
51 protected String exAuthorSeperator
= " ex ";
52 private static String NOTHO
= "notho";
53 protected CharSequence basionymAuthorCombinationAuthorSeperator
= " ";
55 protected String zooAuthorYearSeperator
= ", ";
58 public UUID
getUuid(){
62 // ************************** FACTORY ******************************/
64 public static TaxonNameDefaultCacheStrategy
NewInstance(){
65 return new TaxonNameDefaultCacheStrategy();
69 // ************ CONSTRUCTOR *******************/
71 protected TaxonNameDefaultCacheStrategy(){
75 /* **************** GETTER / SETTER **************************************/
78 * String that separates the NameCache part from the AuthorCache part
81 public String
getNameAuthorSeperator() {
82 return nameAuthorSeperator
;
84 public void setNameAuthorSeperator(String nameAuthorSeperator
) {
85 this.nameAuthorSeperator
= nameAuthorSeperator
;
90 * String the basionym author part starts with e.g. '('.
91 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymEnd() basionymEnd} attribute
94 public String
getBasionymStart() {
97 public void setBasionymStart(String basionymStart
) {
98 this.basionymStart
= basionymStart
;
102 * String the basionym author part ends with e.g. ')'.
103 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymStart() basionymStart} attribute
106 public String
getBasionymEnd() {
109 public void setBasionymEnd(String basionymEnd
) {
110 this.basionymEnd
= basionymEnd
;
115 * String to separate ex author from author.
118 public String
getExAuthorSeperator() {
119 return exAuthorSeperator
;
121 public void setExAuthorSeperator(String exAuthorSeperator
) {
122 this.exAuthorSeperator
= exAuthorSeperator
;
127 * String that separates the basionym/original_combination author part from the combination author part
130 public CharSequence
getBasionymAuthorCombinationAuthorSeperator() {
131 return basionymAuthorCombinationAuthorSeperator
;
135 public void setBasionymAuthorCombinationAuthorSeperator( CharSequence basionymAuthorCombinationAuthorSeperator
) {
136 this.basionymAuthorCombinationAuthorSeperator
= basionymAuthorCombinationAuthorSeperator
;
139 public String
getZooAuthorYearSeperator() {
140 return zooAuthorYearSeperator
;
142 public void setZooAuthorYearSeperator(String authorYearSeperator
) {
143 this.zooAuthorYearSeperator
= authorYearSeperator
;
146 // ******************* Authorship ******************************/
149 public String
getAuthorshipCache(TaxonName taxonName
) {
150 if (taxonName
== null){
152 }else if (taxonName
.isViral()){
154 }else if(taxonName
.isProtectedAuthorshipCache() == true) {
156 return taxonName
.getAuthorshipCache();
158 return getNonCacheAuthorshipCache(taxonName
);
163 * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorship field.
164 * @throws NullPointerException if nonViralName is null.
168 protected String
getNonCacheAuthorshipCache(TaxonName nonViralName
){
169 if (nonViralName
.getNameType().isZoological()){
170 return this.getZoologicalNonCacheAuthorshipCache(nonViralName
);
173 INomenclaturalAuthor combinationAuthor
= nonViralName
.getCombinationAuthorship();
174 INomenclaturalAuthor exCombinationAuthor
= nonViralName
.getExCombinationAuthorship();
175 INomenclaturalAuthor basionymAuthor
= nonViralName
.getBasionymAuthorship();
176 INomenclaturalAuthor exBasionymAuthor
= nonViralName
.getExBasionymAuthorship();
177 String basionymPart
= "";
178 String authorPart
= "";
180 if (basionymAuthor
!= null || exBasionymAuthor
!= null){
181 basionymPart
= basionymStart
+ getAuthorAndExAuthor(basionymAuthor
, exBasionymAuthor
) + basionymEnd
;
183 if (combinationAuthor
!= null || exCombinationAuthor
!= null){
184 authorPart
= getAuthorAndExAuthor(combinationAuthor
, exCombinationAuthor
);
186 result
= CdmUtils
.concat(basionymAuthorCombinationAuthorSeperator
, basionymPart
, authorPart
);
187 // if ("".equals(result)){
194 protected String
getZoologicalNonCacheAuthorshipCache(TaxonName nonViralName
) {
195 if (nonViralName
== null){
199 INomenclaturalAuthor combinationAuthor
= nonViralName
.getCombinationAuthorship();
200 INomenclaturalAuthor exCombinationAuthor
= nonViralName
.getExCombinationAuthorship();
201 INomenclaturalAuthor basionymAuthor
= nonViralName
.getBasionymAuthorship();
202 INomenclaturalAuthor exBasionymAuthor
= nonViralName
.getExBasionymAuthorship();
203 Integer publicationYear
= nonViralName
.getPublicationYear();
204 Integer originalPublicationYear
= nonViralName
.getOriginalPublicationYear();
206 String basionymPart
= "";
207 String authorPart
= "";
209 if (basionymAuthor
!= null || exBasionymAuthor
!= null || originalPublicationYear
!= null ){
210 String authorAndEx
= getAuthorAndExAuthor(basionymAuthor
, exBasionymAuthor
);
211 String originalPublicationYearString
= originalPublicationYear
== null ?
null : String
.valueOf(originalPublicationYear
);
212 String authorAndExAndYear
= CdmUtils
.concat(zooAuthorYearSeperator
, authorAndEx
, originalPublicationYearString
);
213 basionymPart
= basionymStart
+ authorAndExAndYear
+ basionymEnd
;
215 if (combinationAuthor
!= null || exCombinationAuthor
!= null){
216 String authorAndEx
= getAuthorAndExAuthor(combinationAuthor
, exCombinationAuthor
);
217 String publicationYearString
= publicationYear
== null ?
null : String
.valueOf(publicationYear
);
218 authorPart
= CdmUtils
.concat(zooAuthorYearSeperator
, authorAndEx
, publicationYearString
);
220 result
= CdmUtils
.concat(basionymAuthorCombinationAuthorSeperator
, basionymPart
, authorPart
);
229 * Returns the AuthorCache part for a combination of an author and an ex author. This applies on
230 * combination authors as well as on basionym/orginal combination authors.
231 * The correct order is exAuthor ex author though some botanist do not know about and do it the
232 * other way round. (see 46.4-46.6 ICBN (Vienna Code, 2006))
234 protected String
getAuthorAndExAuthor(INomenclaturalAuthor author
, INomenclaturalAuthor exAuthor
){
235 String authorString
= "";
236 String exAuthorString
= "";
238 authorString
= getNomAuthorTitle(author
);
240 if (exAuthor
!= null){
241 exAuthorString
= getNomAuthorTitle(exAuthor
);
242 exAuthorString
+= exAuthorSeperator
;
244 String result
= exAuthorString
+ authorString
;
248 private String
getNomAuthorTitle(INomenclaturalAuthor author
) {
249 return CdmUtils
.Nz(author
.getNomenclaturalTitleCache());
253 * Checks if the given name should include the author in it's cached version.<BR>
254 * This is usually the case but not for <i>species aggregates</i>.
255 * @param nonViralName
258 protected boolean nameIncludesAuthorship(INonViralName nonViralName
){
259 Rank rank
= nonViralName
.getRank();
260 if (rank
!= null && rank
.isSpeciesAggregate()){
267 // ************* TAGGED NAME ***************************************/
270 protected List
<TaggedText
> doGetTaggedTitle(TaxonName taxonName
) {
271 List
<TaggedText
> tags
= new ArrayList
<>();
272 if (taxonName
.getNameType().isViral()){
273 String acronym
= taxonName
.getAcronym();
274 if (isNotBlank(taxonName
.getAcronym())){
275 //this is not according to the code
276 tags
.add(new TaggedText(TagEnum
.name
, acronym
));
279 }else if (taxonName
.isHybridFormula()){
281 String hybridSeparator
= NonViralNameParserImplRegExBase
.hybridSign
;
282 boolean isFirst
= true;
283 List
<HybridRelationship
> rels
= taxonName
.getOrderedChildRelationships();
284 for (HybridRelationship rel
: rels
){
286 tags
.add(new TaggedText(TagEnum
.hybridSign
, hybridSeparator
));
289 tags
.addAll(getTaggedTitle(rel
.getParentName()));
292 }else if (taxonName
.isAutonym()){
294 tags
.addAll(handleTaggedAutonym(taxonName
));
296 List
<TaggedText
> nameTags
= getTaggedName(taxonName
);
297 tags
.addAll(nameTags
);
298 String authorCache
= getAuthorshipCache(taxonName
);
299 if (isNotBlank(authorCache
)){
300 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));
307 * Returns the tag list of the name part (without author and reference).
312 public List
<TaggedText
> getTaggedName(TaxonName taxonName
) {
313 if (taxonName
== null){
315 }else if (taxonName
.isViral()){
318 List
<TaggedText
> tags
= new ArrayList
<>();
319 Rank rank
= taxonName
.getRank();
321 if (taxonName
.isProtectedNameCache()){
322 tags
.add(new TaggedText(TagEnum
.name
, taxonName
.getNameCache()));
323 }else if (taxonName
.isHybridFormula()){
325 String hybridSeparator
= NonViralNameParserImplRegExBase
.hybridSign
;
326 boolean isFirst
= true;
327 List
<HybridRelationship
> rels
= taxonName
.getOrderedChildRelationships();
328 for (HybridRelationship rel
: rels
){
330 tags
.add(new TaggedText(TagEnum
.hybridSign
, hybridSeparator
));
333 tags
.addAll(getTaggedName(rel
.getParentName()));
337 }else if (rank
== null){
338 tags
= getRanklessTaggedNameCache(taxonName
);
339 // }else if (nonViralName.isInfragenericUnranked()){
340 // result = getUnrankedInfragenericNameCache(nonViralName);
341 }else if (rank
.isInfraSpecific()){
342 tags
= getInfraSpeciesTaggedNameCache(taxonName
);
343 }else if (rank
.isSpecies() || isAggregateWithAuthorship(taxonName
, rank
) ){ //exception see #4288
344 tags
= getSpeciesTaggedNameCache(taxonName
);
345 }else if (rank
.isInfraGeneric()){
346 tags
= getInfraGenusTaggedNameCache(taxonName
);
347 }else if (rank
.isGenus()){
348 tags
= getGenusOrUninomialTaggedNameCache(taxonName
);
349 }else if (rank
.isSupraGeneric()){
350 tags
= getGenusOrUninomialTaggedNameCache(taxonName
);
352 tags
= getRanklessTaggedNameCache(taxonName
);
353 logger
.warn("Rank does not belong to a rank class: " + rank
.getTitleCache() + ". Used rankless nameCache for name " + taxonName
.getUuid());
355 //TODO handle appended phrase here instead of different places, check first if this is true for all
361 //***************************** PRIVATES ***************************************/
363 private boolean isAggregateWithAuthorship(TaxonName nonViralName
, Rank rank
) {
367 return rank
.isSpeciesAggregate() && ( isNotBlank(nonViralName
.getAuthorshipCache()) || nonViralName
.getNomenclaturalReference() != null );
372 * Returns the tag list for an autonym taxon.
374 * @see NonViralName#isAutonym()
375 * @see BotanicalName#isAutonym()
376 * @param nonViralName
379 private List
<TaggedText
> handleTaggedAutonym(TaxonName nonViralName
) {
380 List
<TaggedText
> tags
= null;
381 if (nonViralName
.isInfraSpecific()){
383 tags
= getSpeciesTaggedNameCache(nonViralName
);
386 String authorCache
= getAuthorshipCache(nonViralName
);
387 if (isNotBlank(authorCache
)){
388 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));
391 //infra species marker
392 if (nonViralName
.getRank() == null || !nonViralName
.getRank().isInfraSpecific()){
393 //TODO handle exception
394 logger
.warn("Rank for autonym does not exist or is not lower than species !!");
396 String infraSpeciesMarker
= nonViralName
.getRank().getAbbreviation();
397 if (nonViralName
.isTrinomHybrid()){
398 infraSpeciesMarker
= CdmUtils
.concat("", NOTHO
, infraSpeciesMarker
);
400 if (isNotBlank(infraSpeciesMarker
)){
401 tags
.add(new TaggedText(TagEnum
.rank
, infraSpeciesMarker
));
406 String infraSpeciesPart
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet()).trim();
407 if (isNotBlank(infraSpeciesPart
)){
408 tags
.add(new TaggedText(TagEnum
.name
, infraSpeciesPart
));
411 } else if (nonViralName
.isInfraGeneric()){
413 tags
=getGenusOrUninomialTaggedNameCache(nonViralName
);
416 String authorCache
= getAuthorshipCache(nonViralName
);
417 if (isNotBlank(authorCache
)){
418 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));
421 //infra species marker
422 if (nonViralName
.getRank() == null || !nonViralName
.getRank().isInfraGeneric()){
423 //TODO handle exception
424 logger
.warn("Rank for autonym does not exist or is not lower than species !!");
426 Rank rank
= nonViralName
.getRank();
427 String infraGenericMarker
= rank
.getAbbreviation();
428 if (rank
.equals(Rank
.SECTION_BOTANY()) || rank
.equals(Rank
.SUBSECTION_BOTANY())){
429 infraGenericMarker
= infraGenericMarker
.replace("(bot.)", "");
431 if (isNotBlank(infraGenericMarker
)){
432 tags
.add(new TaggedText(TagEnum
.rank
, infraGenericMarker
));
437 String infraGenericPart
= CdmUtils
.Nz(nonViralName
.getInfraGenericEpithet()).trim();
438 if (isNotBlank(infraGenericPart
)){
439 tags
.add(new TaggedText(TagEnum
.name
, infraGenericPart
));
449 * Returns the tag list for rankless taxa.
450 * @param nonViralName
453 protected List
<TaggedText
> getRanklessTaggedNameCache(INonViralName nonViralName
){
454 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
455 String speciesEpi
= CdmUtils
.Nz(nonViralName
.getSpecificEpithet()).trim();
456 if (isNotBlank(speciesEpi
)){
457 tags
.add(new TaggedText(TagEnum
.name
, speciesEpi
));
460 String infraSpeciesEpi
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet());
461 if (isNotBlank(infraSpeciesEpi
)){
462 tags
.add(new TaggedText(TagEnum
.name
, infraSpeciesEpi
));
465 //result += " (rankless)";
466 addAppendedTaggedPhrase(tags
, nonViralName
);
471 * Returns the tag list for the first epithet (including a hybrid sign if required).
472 * @param nonViralName
475 private List
<TaggedText
> getUninomialTaggedPart(INonViralName nonViralName
) {
476 List
<TaggedText
> tags
= new ArrayList
<>();
478 if (nonViralName
.isMonomHybrid()){
479 addHybridPrefix(tags
);
482 String uninomial
= CdmUtils
.Nz(nonViralName
.getGenusOrUninomial()).trim();
483 if (isNotBlank(uninomial
)){
484 tags
.add(new TaggedText(TagEnum
.name
, uninomial
));
491 * Returns the tag list for an genus or higher taxon.
493 * @param nonViralName
496 protected List
<TaggedText
> getGenusOrUninomialTaggedNameCache(INonViralName nonViralName
){
497 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
498 addAppendedTaggedPhrase(tags
, nonViralName
);
503 * Returns the tag list for an infrageneric taxon (including species aggregates).
505 * @see #getSpeciesAggregateTaggedCache(NonViralName)
506 * @param nonViralName
509 protected List
<TaggedText
> getInfraGenusTaggedNameCache(INonViralName nonViralName
){
510 Rank rank
= nonViralName
.getRank();
511 if (rank
!= null && rank
.isSpeciesAggregate() && isBlank(nonViralName
.getAuthorshipCache())){
512 return getSpeciesAggregateTaggedCache(nonViralName
);
516 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
519 String infraGenericMarker
;
522 infraGenericMarker
= rank
.getInfraGenericMarker();
523 if (rank
.equals(Rank
.SECTION_BOTANY()) || rank
.equals(Rank
.SUBSECTION_BOTANY())){
524 infraGenericMarker
= infraGenericMarker
.replace("(bot.)", "");
526 } catch (UnknownCdmTypeException e
) {
527 infraGenericMarker
= "'unhandled infrageneric rank'";
530 infraGenericMarker
= "'undefined infrageneric rank'";
532 String infraGenEpi
= CdmUtils
.Nz(nonViralName
.getInfraGenericEpithet()).trim();
533 if (nonViralName
.isBinomHybrid()){
534 infraGenericMarker
= CdmUtils
.concat("", NOTHO
, infraGenericMarker
);
537 addInfraGenericPart(nonViralName
, tags
, infraGenericMarker
, infraGenEpi
);
539 addAppendedTaggedPhrase(tags
, nonViralName
);
545 * Default implementation for the infrageneric part of a name.
546 * This is usually the infrageneric marker and the infrageneric epitheton. But may be
547 * implemented differently e.g. for zoological names the infrageneric epitheton
548 * may be surrounded by brackets and the marker left out.
549 * @param nonViralName
551 * @param infraGenericMarker
553 protected void addInfraGenericPart(
554 @SuppressWarnings("unused") INonViralName name
,
555 List
<TaggedText
> tags
,
556 String infraGenericMarker
,
557 String infraGenEpi
) {
559 tags
.add(new TaggedText(TagEnum
.rank
, infraGenericMarker
));
562 if (isNotBlank(infraGenEpi
)){
563 tags
.add(new TaggedText(TagEnum
.name
, infraGenEpi
));
568 * Returns the tag list for a species aggregate (or similar) taxon.<BR>
569 * Possible ranks for a <i>species aggregate</i> are "aggr.", "species group", ...
570 * @param nonViralName
573 protected List
<TaggedText
> getSpeciesAggregateTaggedCache(INonViralName nonViralName
){
574 if (! isBlank(nonViralName
.getAuthorshipCache())){
575 List
<TaggedText
> result
= getSpeciesTaggedNameCache(nonViralName
);
580 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
582 addSpeciesAggregateTaggedEpithet(tags
, nonViralName
);
583 addAppendedTaggedPhrase(tags
, nonViralName
);
588 * Adds the aggregate tag to the tag list.
590 * @param nonViralName
592 private void addSpeciesAggregateTaggedEpithet(List
<TaggedText
> tags
, INonViralName nonViralName
) {
595 marker
= nonViralName
.getRank().getInfraGenericMarker();
596 } catch (UnknownCdmTypeException e
) {
597 marker
= "'unknown aggregat type'";
599 if (isNotBlank(marker
)){
600 tags
.add(new TaggedText(TagEnum
.rank
, marker
));
606 * Returns the tag list for a species taxon.
607 * @param nonViralName
610 protected List
<TaggedText
> getSpeciesTaggedNameCache(INonViralName nonViralName
){
611 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
612 addAppendedTaggedPhrase(tags
, nonViralName
);
616 protected List
<TaggedText
> getInfraSpeciesTaggedNameCache(TaxonName name
){
617 if (name
.getNameType().isZoological()){
618 boolean includeMarker
=includeInfraSpecificMarkerForZooNames(name
);
619 return getInfraSpeciesTaggedNameCache(name
, includeMarker
);
621 return getInfraSpeciesTaggedNameCache(name
, true);
625 protected boolean includeInfraSpecificMarkerForZooNames(TaxonName name
){
626 return ! (name
.isAutonym()); //only exclude marker if autonym, see also ZooNameNoMarkerCacheStrategy
630 * Creates the tag list for an infraspecific taxon. If include is true the result will contain
631 * the infraspecific marker (e.g. "var.")
632 * @param nonViralName
633 * @param includeMarker
636 protected List
<TaggedText
> getInfraSpeciesTaggedNameCache(INonViralName nonViralName
, boolean includeMarker
){
637 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
638 if (includeMarker
|| nonViralName
.isTrinomHybrid()){
639 String marker
= (nonViralName
.getRank().getAbbreviation()).trim().replace("null", "");
640 if (nonViralName
.isTrinomHybrid()){
641 marker
= CdmUtils
.concat("", NOTHO
, marker
);
643 if (isNotBlank(marker
)){
644 tags
.add(new TaggedText(TagEnum
.rank
, marker
));
648 String infrSpecEpi
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet());
650 infrSpecEpi
= infrSpecEpi
.trim().replace("null", "");
652 if (isNotBlank(infrSpecEpi
)){
653 tags
.add(new TaggedText(TagEnum
.name
, infrSpecEpi
));
656 addAppendedTaggedPhrase(tags
, nonViralName
);
662 * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
665 private void addHybridPrefix(List
<TaggedText
> tags
) {
666 tags
.add(new TaggedText(TagEnum
.hybridSign
, NonViralNameParserImplRegExBase
.hybridSign
));
667 tags
.add(new TaggedText(TagEnum
.separator
, "")); //no whitespace separator
671 * Creates the tag list for the genus and species part.
672 * @param nonViralName
675 private List
<TaggedText
> getGenusAndSpeciesTaggedPart(INonViralName nonViralName
) {
677 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
680 boolean hasInfraGenericEpi
= isNotBlank(nonViralName
.getInfraGenericEpithet());
681 if (hasInfraGenericEpi
){
682 String infrGenEpi
= nonViralName
.getInfraGenericEpithet().trim();
683 if (nonViralName
.isBinomHybrid()){
684 //maybe not correct but not really expected to happen
685 infrGenEpi
= UTF8
.HYBRID
+ infrGenEpi
;
687 infrGenEpi
= "(" + infrGenEpi
+ ")";
688 tags
.add(new TaggedText(TagEnum
.name
, infrGenEpi
));
692 String specEpi
= CdmUtils
.Nz(nonViralName
.getSpecificEpithet()).trim();
693 if (! hasInfraGenericEpi
&& nonViralName
.isBinomHybrid() ||
694 hasInfraGenericEpi
&& nonViralName
.isTrinomHybrid()){
695 addHybridPrefix(tags
);
697 if (isNotBlank(specEpi
)){
698 tags
.add(new TaggedText(TagEnum
.name
, specEpi
));
704 * Adds the tag for the appended phrase if an appended phrase exists
706 * @param nonViralName
708 protected void addAppendedTaggedPhrase(List
<TaggedText
> tags
, INonViralName nonViralName
){
709 String appendedPhrase
= nonViralName
==null ?
null : nonViralName
.getAppendedPhrase();
710 if (isNotBlank(appendedPhrase
)){
711 tags
.add(new TaggedText(TagEnum
.name
, appendedPhrase
));
716 public String
getLastEpithet(TaxonName taxonName
) {
717 Rank rank
= taxonName
.getRank();
718 if(rank
.isGenus() || rank
.isSupraGeneric()) {
719 return taxonName
.getGenusOrUninomial();
720 } else if(rank
.isInfraGeneric()) {
721 return taxonName
.getInfraGenericEpithet();
722 } else if(rank
.isSpecies()) {
723 return taxonName
.getSpecificEpithet();
725 return taxonName
.getInfraSpecificEpithet();