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
;
14 import java
.util
.regex
.Matcher
;
15 import java
.util
.regex
.Pattern
;
17 import org
.apache
.logging
.log4j
.LogManager
;
18 import org
.apache
.logging
.log4j
.Logger
;
20 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
21 import eu
.etaxonomy
.cdm
.common
.UTF8
;
22 import eu
.etaxonomy
.cdm
.model
.agent
.INomenclaturalAuthor
;
23 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationship
;
24 import eu
.etaxonomy
.cdm
.model
.name
.INonViralName
;
25 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
26 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
27 import eu
.etaxonomy
.cdm
.strategy
.cache
.TagEnum
;
28 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
29 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedTextBuilder
;
30 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
31 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImplRegExBase
;
34 * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName>
36 * The method implements a cache strategy for botanical names so no method has to be overwritten by
37 * a subclass for botanic names.
38 * Where differing from this default botanic name strategy other subclasses should overwrite the
39 * existing methods, e.g. a CacheStrategy for zoological names should overwrite getAuthorAndExAuthor
42 public class TaxonNameDefaultCacheStrategy
43 extends NameCacheStrategyBase
44 implements INonViralNameCacheStrategy
{
46 private static final Logger logger
= LogManager
.getLogger(TaxonNameDefaultCacheStrategy
.class);
47 private static final long serialVersionUID
= -6577757501563212669L;
49 final static UUID uuid
= UUID
.fromString("1cdda0d1-d5bc-480f-bf08-40a510a2f223");
51 protected String nameAuthorSeperator
= " ";
52 protected String basionymStart
= "(";
53 protected String basionymEnd
= ")";
54 protected String exAuthorSeperator
= " ex ";
55 private static String NOTHO
= "notho";
56 protected CharSequence basionymAuthorCombinationAuthorSeperator
= " ";
58 protected String zooAuthorYearSeperator
= ", ";
60 private String cultivarStart
= "'";
61 private String cultivarEnd
= "'";
64 public UUID
getUuid(){
68 // ************************** FACTORY ******************************/
70 public static TaxonNameDefaultCacheStrategy
NewInstance(){
71 return new TaxonNameDefaultCacheStrategy();
74 // ************ CONSTRUCTOR *******************/
76 protected TaxonNameDefaultCacheStrategy(){
80 /* **************** GETTER / SETTER **************************************/
83 * String that separates the NameCache part from the AuthorCache part
86 public String
getNameAuthorSeperator() {
87 return nameAuthorSeperator
;
89 public void setNameAuthorSeperator(String nameAuthorSeperator
) {
90 this.nameAuthorSeperator
= nameAuthorSeperator
;
94 * String the basionym author part starts with e.g. '('.
95 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymEnd() basionymEnd} attribute
98 public String
getBasionymStart() {
101 public void setBasionymStart(String basionymStart
) {
102 this.basionymStart
= basionymStart
;
106 * String the basionym author part ends with e.g. ')'.
107 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymStart() basionymStart} attribute
110 public String
getBasionymEnd() {
113 public void setBasionymEnd(String basionymEnd
) {
114 this.basionymEnd
= basionymEnd
;
118 * String to separate ex author from author.
120 public String
getExAuthorSeperator() {
121 return exAuthorSeperator
;
123 public void setExAuthorSeperator(String exAuthorSeperator
) {
124 this.exAuthorSeperator
= exAuthorSeperator
;
128 * String that separates the basionym/original_combination author part from the combination author part
130 public CharSequence
getBasionymAuthorCombinationAuthorSeperator() {
131 return basionymAuthorCombinationAuthorSeperator
;
133 public void setBasionymAuthorCombinationAuthorSeperator( CharSequence basionymAuthorCombinationAuthorSeperator
) {
134 this.basionymAuthorCombinationAuthorSeperator
= basionymAuthorCombinationAuthorSeperator
;
137 public String
getZooAuthorYearSeperator() {
138 return zooAuthorYearSeperator
;
140 public void setZooAuthorYearSeperator(String authorYearSeperator
) {
141 this.zooAuthorYearSeperator
= authorYearSeperator
;
144 // ******************* Authorship ******************************/
147 public String
getAuthorshipCache(TaxonName taxonName
) {
148 if (taxonName
== null){
150 }else if (taxonName
.isViral()){
152 }else if(taxonName
.isProtectedAuthorshipCache() == true) {
154 return taxonName
.getAuthorshipCache();
156 return getNonCacheAuthorshipCache(taxonName
);
161 * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorship field.
162 * @throws NullPointerException if nonViralName is null.
166 protected String
getNonCacheAuthorshipCache(TaxonName nonViralName
){
167 if (nonViralName
.getNameType().isZoological()){
168 return this.getZoologicalNonCacheAuthorshipCache(nonViralName
);
171 INomenclaturalAuthor combinationAuthor
= nonViralName
.getCombinationAuthorship();
172 INomenclaturalAuthor exCombinationAuthor
= nonViralName
.getExCombinationAuthorship();
173 INomenclaturalAuthor basionymAuthor
= nonViralName
.getBasionymAuthorship();
174 INomenclaturalAuthor exBasionymAuthor
= nonViralName
.getExBasionymAuthorship();
175 if (isCultivar(nonViralName
) ){
176 exCombinationAuthor
= null;
177 basionymAuthor
= null;
178 exBasionymAuthor
= null;
181 String basionymPart
= "";
182 String authorPart
= "";
184 if (basionymAuthor
!= null || exBasionymAuthor
!= null){
185 basionymPart
= basionymStart
+ getAuthorAndExAuthor(basionymAuthor
, exBasionymAuthor
) + basionymEnd
;
187 if (combinationAuthor
!= null || exCombinationAuthor
!= null){
188 authorPart
= getAuthorAndExAuthor(combinationAuthor
, exCombinationAuthor
);
190 result
= CdmUtils
.concat(basionymAuthorCombinationAuthorSeperator
, basionymPart
, authorPart
);
191 // if ("".equals(result)){
198 private boolean isCultivar(TaxonName name
) {
199 return name
.isCultivar() || isNotBlank(name
.getCultivarEpithet()) || isNotBlank(name
.getCultivarGroupEpithet());
202 protected String
getZoologicalNonCacheAuthorshipCache(TaxonName nonViralName
) {
203 if (nonViralName
== null){
207 INomenclaturalAuthor combinationAuthor
= nonViralName
.getCombinationAuthorship();
208 INomenclaturalAuthor exCombinationAuthor
= nonViralName
.getExCombinationAuthorship();
209 INomenclaturalAuthor basionymAuthor
= nonViralName
.getBasionymAuthorship();
210 INomenclaturalAuthor exBasionymAuthor
= nonViralName
.getExBasionymAuthorship();
211 Integer publicationYear
= nonViralName
.getPublicationYear();
212 Integer originalPublicationYear
= nonViralName
.getOriginalPublicationYear();
214 String basionymPart
= "";
215 String authorPart
= "";
217 if (basionymAuthor
!= null || exBasionymAuthor
!= null || originalPublicationYear
!= null ){
218 String authorAndEx
= getAuthorAndExAuthor(basionymAuthor
, exBasionymAuthor
);
219 String originalPublicationYearString
= originalPublicationYear
== null ?
null : String
.valueOf(originalPublicationYear
);
220 String authorAndExAndYear
= CdmUtils
.concat(zooAuthorYearSeperator
, authorAndEx
, originalPublicationYearString
);
221 basionymPart
= basionymStart
+ authorAndExAndYear
+ basionymEnd
;
223 if (combinationAuthor
!= null || exCombinationAuthor
!= null){
224 String authorAndEx
= getAuthorAndExAuthor(combinationAuthor
, exCombinationAuthor
);
225 String publicationYearString
= publicationYear
== null ?
null : String
.valueOf(publicationYear
);
226 authorPart
= CdmUtils
.concat(zooAuthorYearSeperator
, authorAndEx
, publicationYearString
);
228 result
= CdmUtils
.concat(basionymAuthorCombinationAuthorSeperator
, basionymPart
, authorPart
);
237 * Returns the AuthorCache part for a combination of an author and an ex author. This applies on
238 * combination authors as well as on basionym/orginal combination authors.
239 * The correct order is exAuthor ex author though some botanist do not know about and do it the
240 * other way round. (see 46.4-46.6 ICBN (Vienna Code, 2006))
242 protected String
getAuthorAndExAuthor(INomenclaturalAuthor author
, INomenclaturalAuthor exAuthor
){
243 String authorString
= "";
244 String exAuthorString
= "";
246 authorString
= getNomAuthorTitle(author
);
248 if (exAuthor
!= null){
249 exAuthorString
= getNomAuthorTitle(exAuthor
);
250 exAuthorString
+= exAuthorSeperator
;
252 String result
= exAuthorString
+ authorString
;
256 private String
getNomAuthorTitle(INomenclaturalAuthor author
) {
257 return CdmUtils
.Nz(author
.getNomenclaturalTitleCache());
261 * Checks if the given name should include the author in it's cached version.<BR>
262 * This is usually the case but not for <i>species aggregates</i>.
263 * @param nonViralName
266 protected boolean nameIncludesAuthorship(INonViralName nonViralName
){
267 Rank rank
= nonViralName
.getRank();
268 if (rank
!= null && rank
.isSpeciesAggregate()){
275 // ************* TAGGED NAME ***************************************/
278 protected List
<TaggedText
> doGetTaggedTitle(TaxonName taxonName
) {
279 List
<TaggedText
> tags
= new ArrayList
<>();
280 if (taxonName
.getNameType().isViral()){
281 String acronym
= taxonName
.getAcronym();
282 if (isNotBlank(taxonName
.getAcronym())){
283 //this is not according to the code
284 tags
.add(new TaggedText(TagEnum
.name
, acronym
));
287 }else if (taxonName
.isHybridFormula()){
289 String hybridSeparator
= NonViralNameParserImplRegExBase
.hybridSign
;
290 boolean isFirst
= true;
291 List
<HybridRelationship
> rels
= taxonName
.getOrderedChildRelationships();
292 for (HybridRelationship rel
: rels
){
294 tags
.add(new TaggedText(TagEnum
.hybridSign
, hybridSeparator
));
297 tags
.addAll(getTaggedTitle(rel
.getParentName()));
300 }else if (taxonName
.isAutonym()){
302 tags
.addAll(handleTaggedAutonym(taxonName
, true));
304 List
<TaggedText
> nameTags
= getTaggedName(taxonName
);
305 tags
.addAll(nameTags
);
306 String authorCache
= getAuthorshipCache(taxonName
);
307 if (isNotBlank(authorCache
)){
308 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));
315 * Returns the tag list of the name part (without author and reference).
320 public List
<TaggedText
> getTaggedName(TaxonName taxonName
) {
321 if (taxonName
== null){
323 }else if (taxonName
.isViral()){
326 List
<TaggedText
> tags
= new ArrayList
<>();
327 Rank rank
= taxonName
.getRank();
329 if (taxonName
.isProtectedNameCache()){
330 tags
.add(new TaggedText(TagEnum
.name
, taxonName
.getNameCache()));
331 }else if (taxonName
.isHybridFormula()){
333 //TODO graft-chimera (see also cultivars)
334 String hybridSeparator
= NonViralNameParserImplRegExBase
.hybridSign
;
335 boolean isFirst
= true;
336 List
<HybridRelationship
> rels
= taxonName
.getOrderedChildRelationships();
337 for (HybridRelationship rel
: rels
){
339 tags
.add(new TaggedText(TagEnum
.hybridSign
, hybridSeparator
));
342 tags
.addAll(getTaggedName(rel
.getParentName()));
346 }else if (rank
== null){
347 tags
= getRanklessTaggedNameCache(taxonName
, true);
348 }else if (rank
.isCultivar()){
349 tags
= getCultivarTaggedNameCache(taxonName
);
350 }else if (rank
.isInfraSpecific()){
351 tags
= getInfraSpeciesTaggedNameCache(taxonName
);
352 }else if (rank
.isSpecies() || isAggregateWithAuthorship(taxonName
, rank
) ){ //exception see #4288
353 tags
= getSpeciesTaggedNameCache(taxonName
, true);
354 }else if (rank
.isInfraGeneric()){
355 tags
= getInfraGenusTaggedNameCache(taxonName
, true);
356 }else if (rank
.isGenus()){
357 tags
= getGenusOrUninomialTaggedNameCache(taxonName
, true);
358 }else if (rank
.isSupraGeneric()){
359 tags
= getGenusOrUninomialTaggedNameCache(taxonName
, true);
361 tags
= getRanklessTaggedNameCache(taxonName
, true);
362 logger
.warn("Rank does not belong to a rank class: " + rank
.getTitleCache() + ". Used rankless nameCache for name " + taxonName
.getUuid());
364 //TODO handle appended phrase here instead of different places, check first if this is true for all
370 //***************************** PRIVATES ***************************************/
372 private List
<TaggedText
> getCultivarTaggedNameCache(TaxonName taxonName
) {
373 List
<TaggedText
> scientificNameTags
;
374 TaggedTextBuilder builder
= TaggedTextBuilder
.NewInstance();
375 if (isNotBlank(taxonName
.getInfraSpecificEpithet())){
376 scientificNameTags
= getInfraSpeciesTaggedNameCache(taxonName
, false, false);
377 } else if (isNotBlank(taxonName
.getSpecificEpithet())){
378 scientificNameTags
= getSpeciesTaggedNameCache(taxonName
, false);
379 } else if (isNotBlank(taxonName
.getInfraGenericEpithet())){
380 scientificNameTags
= getInfraGenusTaggedNameCache(taxonName
, false);
381 } else /*if (isNotBlank(taxonName.getGenusOrUninomial())) */{
382 scientificNameTags
= getGenusOrUninomialTaggedNameCache(taxonName
, false);
385 UUID rankUuid
= taxonName
.getRank().getUuid();
386 boolean rankIsHandled
= true;
387 String cultivarStr
= null;
388 String groupStr
= taxonName
.getCultivarGroupEpithet();
389 if (rankUuid
.equals(Rank
.uuidCultivar
)){
390 cultivarStr
= surroundedCultivarEpithet(taxonName
.getCultivarEpithet());
391 if (isNotBlank(cultivarStr
) && isNotBlank(groupStr
)){
392 groupStr
= surroundGroupWithBracket(groupStr
);
394 cultivarStr
= CdmUtils
.concat(" ", groupStr
, cultivarStr
);
395 }else if (rankUuid
.equals(Rank
.uuidCultivarGroup
)){
396 cultivarStr
= CdmUtils
.concat(" ", groupStr
, checkHasGroupEpithet(groupStr
)?
null: "Group");
397 }else if (rankUuid
.equals(Rank
.uuidGrexICNCP
)){
398 cultivarStr
= CdmUtils
.concat(" ", groupStr
, checkHasGrexEpithet(groupStr
)?
null: "grex");
400 rankIsHandled
= false;
403 builder
.addAll(scientificNameTags
);
404 }else if (rankUuid
.equals(Rank
.uuidGraftChimaera
)){
405 //TODO not yet fully implemented
406 cultivarStr
= "+ " + CdmUtils
.concat(" ", taxonName
.getGenusOrUninomial(), surroundedCultivarEpithet(taxonName
.getCultivarEpithet()));
407 }else if (rankUuid
.equals(Rank
.uuidDenominationClass
)){
408 //TODO dummy implementation
409 cultivarStr
= CdmUtils
.concat(" ", taxonName
.getGenusOrUninomial(), surroundedCultivarEpithet(taxonName
.getCultivarEpithet()));
410 } else { //(!rankIsHandled)
411 throw new IllegalStateException("Unsupported rank " + taxonName
.getRank().getTitleCache() + " for cultivar.");
413 if (isNotBlank(cultivarStr
)){
414 builder
.add(TagEnum
.cultivar
, cultivarStr
);
417 List
<TaggedText
> tags
= builder
.getTaggedText();
418 addAppendedTaggedPhrase(tags
, taxonName
, true);
422 private String
surroundGroupWithBracket(String groupStr
) {
423 if (groupStr
.matches(NonViralNameParserImplRegExBase
.grex
+ "$")){
425 }else if (groupStr
.matches(".*" + NonViralNameParserImplRegExBase
.grex
+ ".+")){
426 Matcher matcher
= Pattern
.compile(NonViralNameParserImplRegExBase
.grex
+ "\\s*" ).matcher(groupStr
);
428 return groupStr
.substring(0, matcher
.end()) + "("+ groupStr
.substring(matcher
.end())+ ")";
430 return "("+ groupStr
+ ")";
434 private boolean checkHasGroupEpithet(String group
) {
436 String
[] splits
= group
== null?
new String
[0]: group
.split("\\s+");
437 if (splits
.length
<= 1){
439 }else if (splits
[0].matches(NonViralNameParserImplRegExBase
.group
)
440 || splits
[splits
.length
-1].matches(NonViralNameParserImplRegExBase
.group
)){
447 private boolean checkHasGrexEpithet(String grex
) {
448 String
[] splits
= grex
== null?
new String
[0]: grex
.split("\\s+");
449 if (splits
.length
<= 1){
451 }else if (splits
[splits
.length
-1].matches(NonViralNameParserImplRegExBase
.grex
)){
458 private String
surroundedCultivarEpithet(String cultivarEpi
) {
459 return cultivarStart
+ CdmUtils
.Nz(cultivarEpi
) + cultivarEnd
;
462 private boolean isAggregateWithAuthorship(TaxonName nonViralName
, Rank rank
) {
466 return rank
.isSpeciesAggregate() && ( isNotBlank(nonViralName
.getAuthorshipCache()) || nonViralName
.getNomenclaturalReference() != null );
471 * Returns the tag list for an autonym taxon.
473 * @see NonViralName#isAutonym()
474 * @see BotanicalName#isAutonym()
475 * @param nonViralName
478 private List
<TaggedText
> handleTaggedAutonym(TaxonName nonViralName
, boolean addAppended
) {
479 List
<TaggedText
> tags
= null;
480 if (nonViralName
.isInfraSpecific()){
482 tags
= getSpeciesTaggedNameCache(nonViralName
, addAppended
);
485 String authorCache
= getAuthorshipCache(nonViralName
);
486 if (isNotBlank(authorCache
)){
487 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));
490 //infra species marker
491 if (nonViralName
.getRank() == null || !nonViralName
.getRank().isInfraSpecific()){
492 //TODO handle exception
493 logger
.warn("Rank for autonym does not exist or is not lower than species !!");
495 String infraSpeciesMarker
= nonViralName
.getRank().getAbbreviation();
496 if (nonViralName
.isTrinomHybrid()){
497 infraSpeciesMarker
= CdmUtils
.concat("", NOTHO
, infraSpeciesMarker
);
499 if (isNotBlank(infraSpeciesMarker
)){
500 tags
.add(new TaggedText(TagEnum
.rank
, infraSpeciesMarker
));
505 String infraSpeciesPart
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet()).trim();
506 if (isNotBlank(infraSpeciesPart
)){
507 tags
.add(new TaggedText(TagEnum
.name
, infraSpeciesPart
));
510 } else if (nonViralName
.isInfraGeneric()){
512 tags
=getGenusOrUninomialTaggedNameCache(nonViralName
, addAppended
);
515 String authorCache
= getAuthorshipCache(nonViralName
);
516 if (isNotBlank(authorCache
)){
517 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));
520 //infra species marker
521 if (nonViralName
.getRank() == null || !nonViralName
.getRank().isInfraGeneric()){
522 //TODO handle exception
523 logger
.warn("Rank for autonym does not exist or is not lower than species !!");
525 Rank rank
= nonViralName
.getRank();
526 String infraGenericMarker
= rank
.getAbbreviation();
527 if (rank
.equals(Rank
.SECTION_BOTANY()) || rank
.equals(Rank
.SUBSECTION_BOTANY())){
528 infraGenericMarker
= infraGenericMarker
.replace("(bot.)", "");
530 if (isNotBlank(infraGenericMarker
)){
531 tags
.add(new TaggedText(TagEnum
.rank
, infraGenericMarker
));
536 String infraGenericPart
= CdmUtils
.Nz(nonViralName
.getInfraGenericEpithet()).trim();
537 if (isNotBlank(infraGenericPart
)){
538 tags
.add(new TaggedText(TagEnum
.name
, infraGenericPart
));
548 * Returns the tag list for rankless taxa.
549 * @param nonViralName
552 protected List
<TaggedText
> getRanklessTaggedNameCache(INonViralName nonViralName
, boolean addAppended
){
553 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
554 String speciesEpi
= CdmUtils
.Nz(nonViralName
.getSpecificEpithet()).trim();
555 if (isNotBlank(speciesEpi
)){
556 tags
.add(new TaggedText(TagEnum
.name
, speciesEpi
));
559 String infraSpeciesEpi
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet());
560 if (isNotBlank(infraSpeciesEpi
)){
561 tags
.add(new TaggedText(TagEnum
.name
, infraSpeciesEpi
));
564 //result += " (rankless)";
565 addAppendedTaggedPhrase(tags
, nonViralName
, addAppended
);
570 * Returns the tag list for the first epithet (including a hybrid sign if required).
571 * @param nonViralName
574 private List
<TaggedText
> getUninomialTaggedPart(INonViralName nonViralName
) {
575 List
<TaggedText
> tags
= new ArrayList
<>();
577 if (nonViralName
.isMonomHybrid()){
578 addHybridPrefix(tags
);
581 String uninomial
= CdmUtils
.Nz(nonViralName
.getGenusOrUninomial()).trim();
582 if (isNotBlank(uninomial
)){
583 tags
.add(new TaggedText(TagEnum
.name
, uninomial
));
590 * Returns the tag list for an genus or higher taxon.
592 * @param nonViralName
595 protected List
<TaggedText
> getGenusOrUninomialTaggedNameCache(INonViralName nonViralName
, boolean addAppended
){
596 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
597 addAppendedTaggedPhrase(tags
, nonViralName
, addAppended
);
602 * Returns the tag list for an infrageneric taxon (including species aggregates).
604 * @see #getSpeciesAggregateTaggedCache(NonViralName)
605 * @param nonViralName
608 protected List
<TaggedText
> getInfraGenusTaggedNameCache(INonViralName nonViralName
, boolean addAppended
){
609 Rank rank
= nonViralName
.getRank();
610 if (rank
!= null && rank
.isSpeciesAggregate() && isBlank(nonViralName
.getAuthorshipCache())){
611 return getSpeciesAggregateTaggedCache(nonViralName
, addAppended
);
615 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
618 String infraGenericMarker
;
621 infraGenericMarker
= rank
.getInfraGenericMarker();
622 if (rank
.equals(Rank
.SECTION_BOTANY()) || rank
.equals(Rank
.SUBSECTION_BOTANY())){
623 infraGenericMarker
= infraGenericMarker
.replace("(bot.)", "");
625 } catch (UnknownCdmTypeException e
) {
626 infraGenericMarker
= "'unhandled infrageneric rank'";
629 infraGenericMarker
= "'undefined infrageneric rank'";
631 String infraGenEpi
= CdmUtils
.Nz(nonViralName
.getInfraGenericEpithet()).trim();
632 if (nonViralName
.isBinomHybrid()){
633 infraGenericMarker
= CdmUtils
.concat("", NOTHO
, infraGenericMarker
);
636 addInfraGenericPart(nonViralName
, tags
, infraGenericMarker
, infraGenEpi
);
638 addAppendedTaggedPhrase(tags
, nonViralName
, addAppended
);
644 * Default implementation for the infrageneric part of a name.
645 * This is usually the infrageneric marker and the infrageneric epitheton. But may be
646 * implemented differently e.g. for zoological names the infrageneric epitheton
647 * may be surrounded by brackets and the marker left out.
648 * @param nonViralName
650 * @param infraGenericMarker
652 protected void addInfraGenericPart(
653 @SuppressWarnings("unused") INonViralName name
,
654 List
<TaggedText
> tags
,
655 String infraGenericMarker
,
656 String infraGenEpi
) {
658 tags
.add(new TaggedText(TagEnum
.rank
, infraGenericMarker
));
661 if (isNotBlank(infraGenEpi
)){
662 tags
.add(new TaggedText(TagEnum
.name
, infraGenEpi
));
667 * Returns the tag list for a species aggregate (or similar) taxon.<BR>
668 * Possible ranks for a <i>species aggregate</i> are "aggr.", "species group", ...
669 * @param nonViralName
672 protected List
<TaggedText
> getSpeciesAggregateTaggedCache(INonViralName nonViralName
, boolean addAppended
){
673 if (! isBlank(nonViralName
.getAuthorshipCache())){
674 List
<TaggedText
> result
= getSpeciesTaggedNameCache(nonViralName
, addAppended
);
678 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
680 addSpeciesAggregateTaggedEpithet(tags
, nonViralName
);
681 addAppendedTaggedPhrase(tags
, nonViralName
, addAppended
);
686 * Adds the aggregate tag to the tag list.
688 private void addSpeciesAggregateTaggedEpithet(List
<TaggedText
> tags
, INonViralName nonViralName
) {
691 marker
= nonViralName
.getRank().getInfraGenericMarker();
692 } catch (UnknownCdmTypeException e
) {
693 marker
= "'unknown aggregat type'";
695 if (isNotBlank(marker
)){
696 tags
.add(new TaggedText(TagEnum
.rank
, marker
));
701 * Returns the tag list for a species taxon.
703 protected List
<TaggedText
> getSpeciesTaggedNameCache(INonViralName nonViralName
, boolean addAppended
){
704 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
705 addAppendedTaggedPhrase(tags
, nonViralName
, addAppended
);
709 protected List
<TaggedText
> getInfraSpeciesTaggedNameCache(TaxonName name
){
710 if (name
.getNameType().isZoological()){
711 boolean includeMarker
= includeInfraSpecificMarkerForZooNames(name
);
712 return getInfraSpeciesTaggedNameCache(name
, includeMarker
, true);
714 return getInfraSpeciesTaggedNameCache(name
, true, true);
718 protected boolean includeInfraSpecificMarkerForZooNames(TaxonName name
){
719 return ! (name
.isAutonym()); //only exclude marker if autonym, see also ZooNameNoMarkerCacheStrategy
723 * Creates the tag list for an infraspecific taxon. If include is true the result will contain
724 * the infraspecific marker (e.g. "var.")
725 * @param nonViralName
726 * @param includeMarker
729 protected List
<TaggedText
> getInfraSpeciesTaggedNameCache(INonViralName nonViralName
,
730 boolean includeMarker
, boolean addAppended
){
731 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
732 if (includeMarker
|| nonViralName
.isTrinomHybrid()){
733 String marker
= (nonViralName
.getRank().getAbbreviation()).trim().replace("null", "");
734 if (nonViralName
.isTrinomHybrid()){
735 marker
= CdmUtils
.concat("", NOTHO
, marker
);
737 if (isNotBlank(marker
)){
738 tags
.add(new TaggedText(TagEnum
.rank
, marker
));
742 String infrSpecEpi
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet());
744 infrSpecEpi
= infrSpecEpi
.trim().replace("null", "");
746 if (isNotBlank(infrSpecEpi
)){
747 tags
.add(new TaggedText(TagEnum
.name
, infrSpecEpi
));
750 addAppendedTaggedPhrase(tags
, nonViralName
, addAppended
);
756 * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
759 private void addHybridPrefix(List
<TaggedText
> tags
) {
760 tags
.add(new TaggedText(TagEnum
.hybridSign
, NonViralNameParserImplRegExBase
.hybridSign
));
761 tags
.add(new TaggedText(TagEnum
.separator
, "")); //no whitespace separator
765 * Creates the tag list for the genus and species part.
766 * @param nonViralName
769 private List
<TaggedText
> getGenusAndSpeciesTaggedPart(INonViralName nonViralName
) {
771 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
774 boolean hasInfraGenericEpi
= isNotBlank(nonViralName
.getInfraGenericEpithet());
775 if (hasInfraGenericEpi
){
776 String infrGenEpi
= nonViralName
.getInfraGenericEpithet().trim();
777 if (nonViralName
.isBinomHybrid()){
778 //maybe not correct but not really expected to happen
779 infrGenEpi
= UTF8
.HYBRID
+ infrGenEpi
;
781 infrGenEpi
= "(" + infrGenEpi
+ ")";
782 tags
.add(new TaggedText(TagEnum
.name
, infrGenEpi
));
786 String specEpi
= CdmUtils
.Nz(nonViralName
.getSpecificEpithet()).trim();
787 if (! hasInfraGenericEpi
&& nonViralName
.isBinomHybrid() ||
788 hasInfraGenericEpi
&& nonViralName
.isTrinomHybrid()){
789 addHybridPrefix(tags
);
791 if (isNotBlank(specEpi
)){
792 tags
.add(new TaggedText(TagEnum
.name
, specEpi
));
798 * Adds the tag for the appended phrase if an appended phrase exists
800 * @param nonViralName
803 protected void addAppendedTaggedPhrase(List
<TaggedText
> tags
, INonViralName nonViralName
, boolean addAppended
){
807 String appendedPhrase
= nonViralName
==null ?
null : nonViralName
.getAppendedPhrase();
808 if (isNotBlank(appendedPhrase
)){
809 tags
.add(new TaggedText(TagEnum
.name
, appendedPhrase
));
814 public String
getLastEpithet(TaxonName taxonName
) {
815 Rank rank
= taxonName
.getRank();
816 if(rank
.isGenus() || rank
.isSupraGeneric()) {
817 return taxonName
.getGenusOrUninomial();
818 } else if(rank
.isInfraGeneric()) {
819 return taxonName
.getInfraGenericEpithet();
820 } else if(rank
.isSpecies()) {
821 return taxonName
.getSpecificEpithet();
823 return taxonName
.getInfraSpecificEpithet();