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
.Iterator
;
13 import java
.util
.List
;
15 import java
.util
.UUID
;
17 import org
.apache
.commons
.lang
.StringUtils
;
18 import org
.apache
.log4j
.Logger
;
20 import eu
.etaxonomy
.cdm
.common
.CdmUtils
;
21 import eu
.etaxonomy
.cdm
.common
.UTF8
;
22 import eu
.etaxonomy
.cdm
.hibernate
.HibernateProxyHelper
;
23 import eu
.etaxonomy
.cdm
.model
.agent
.INomenclaturalAuthor
;
24 import eu
.etaxonomy
.cdm
.model
.common
.Language
;
25 import eu
.etaxonomy
.cdm
.model
.name
.HybridRelationship
;
26 import eu
.etaxonomy
.cdm
.model
.name
.INonViralName
;
27 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatus
;
28 import eu
.etaxonomy
.cdm
.model
.name
.NomenclaturalStatusType
;
29 import eu
.etaxonomy
.cdm
.model
.name
.Rank
;
30 import eu
.etaxonomy
.cdm
.model
.name
.TaxonName
;
31 import eu
.etaxonomy
.cdm
.model
.reference
.INomenclaturalReference
;
32 import eu
.etaxonomy
.cdm
.model
.reference
.Reference
;
33 import eu
.etaxonomy
.cdm
.model
.term
.Representation
;
34 import eu
.etaxonomy
.cdm
.ref
.TypedEntityReference
;
35 import eu
.etaxonomy
.cdm
.strategy
.cache
.TagEnum
;
36 import eu
.etaxonomy
.cdm
.strategy
.cache
.TaggedText
;
37 import eu
.etaxonomy
.cdm
.strategy
.exceptions
.UnknownCdmTypeException
;
38 import eu
.etaxonomy
.cdm
.strategy
.parser
.NonViralNameParserImplRegExBase
;
42 * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName>
44 * The method implements a cache strategy for botanical names so no method has to be overwritten by
45 * a subclass for botanic names.
46 * Where differing from this default botanic name strategy other subclasses should overwrite the
47 * existing methods, e.g. a CacheStrategy for zoological names should overwrite getAuthorAndExAuthor
50 public class TaxonNameDefaultCacheStrategy
51 extends NameCacheStrategyBase
52 implements INonViralNameCacheStrategy
{
54 private static final Logger logger
= Logger
.getLogger(TaxonNameDefaultCacheStrategy
.class);
55 private static final long serialVersionUID
= -6577757501563212669L;
57 final static UUID uuid
= UUID
.fromString("1cdda0d1-d5bc-480f-bf08-40a510a2f223");
59 protected String nameAuthorSeperator
= " ";
60 protected String basionymStart
= "(";
61 protected String basionymEnd
= ")";
62 protected String exAuthorSeperator
= " ex ";
63 private static String NOTHO
= "notho";
64 protected CharSequence basionymAuthorCombinationAuthorSeperator
= " ";
66 protected String zooAuthorYearSeperator
= ", ";
69 public UUID
getUuid(){
73 // ************************** FACTORY ******************************/
75 public static TaxonNameDefaultCacheStrategy
NewInstance(){
76 return new TaxonNameDefaultCacheStrategy();
80 // ************ CONSTRUCTOR *******************/
82 protected TaxonNameDefaultCacheStrategy(){
86 /* **************** GETTER / SETTER **************************************/
89 * String that separates the NameCache part from the AuthorCache part
92 public String
getNameAuthorSeperator() {
93 return nameAuthorSeperator
;
95 public void setNameAuthorSeperator(String nameAuthorSeperator
) {
96 this.nameAuthorSeperator
= nameAuthorSeperator
;
101 * String the basionym author part starts with e.g. '('.
102 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymEnd() basionymEnd} attribute
105 public String
getBasionymStart() {
106 return basionymStart
;
108 public void setBasionymStart(String basionymStart
) {
109 this.basionymStart
= basionymStart
;
113 * String the basionym author part ends with e.g. ')'.
114 * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymStart() basionymStart} attribute
117 public String
getBasionymEnd() {
120 public void setBasionymEnd(String basionymEnd
) {
121 this.basionymEnd
= basionymEnd
;
126 * String to separate ex author from author.
129 public String
getExAuthorSeperator() {
130 return exAuthorSeperator
;
132 public void setExAuthorSeperator(String exAuthorSeperator
) {
133 this.exAuthorSeperator
= exAuthorSeperator
;
138 * String that separates the basionym/original_combination author part from the combination author part
141 public CharSequence
getBasionymAuthorCombinationAuthorSeperator() {
142 return basionymAuthorCombinationAuthorSeperator
;
146 public void setBasionymAuthorCombinationAuthorSeperator( CharSequence basionymAuthorCombinationAuthorSeperator
) {
147 this.basionymAuthorCombinationAuthorSeperator
= basionymAuthorCombinationAuthorSeperator
;
150 public String
getZooAuthorYearSeperator() {
151 return zooAuthorYearSeperator
;
153 public void setZooAuthorYearSeperator(String authorYearSeperator
) {
154 this.zooAuthorYearSeperator
= authorYearSeperator
;
157 // ******************* Authorship ******************************/
160 public String
getAuthorshipCache(TaxonName taxonName
) {
161 if (taxonName
== null){
163 }else if (taxonName
.getNameType().isViral()){
165 }else if(taxonName
.isProtectedAuthorshipCache() == true) {
167 return taxonName
.getAuthorshipCache();
169 return getNonCacheAuthorshipCache(taxonName
);
174 * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorship field.
175 * @throws NullPointerException if nonViralName is null.
179 protected String
getNonCacheAuthorshipCache(TaxonName nonViralName
){
180 if (nonViralName
.getNameType().isZoological()){
181 return this.getZoologicalNonCacheAuthorshipCache(nonViralName
);
184 INomenclaturalAuthor combinationAuthor
= nonViralName
.getCombinationAuthorship();
185 INomenclaturalAuthor exCombinationAuthor
= nonViralName
.getExCombinationAuthorship();
186 INomenclaturalAuthor basionymAuthor
= nonViralName
.getBasionymAuthorship();
187 INomenclaturalAuthor exBasionymAuthor
= nonViralName
.getExBasionymAuthorship();
188 String basionymPart
= "";
189 String authorPart
= "";
191 if (basionymAuthor
!= null || exBasionymAuthor
!= null){
192 basionymPart
= basionymStart
+ getAuthorAndExAuthor(basionymAuthor
, exBasionymAuthor
) + basionymEnd
;
194 if (combinationAuthor
!= null || exCombinationAuthor
!= null){
195 authorPart
= getAuthorAndExAuthor(combinationAuthor
, exCombinationAuthor
);
197 result
= CdmUtils
.concat(basionymAuthorCombinationAuthorSeperator
, basionymPart
, authorPart
);
198 // if ("".equals(result)){
205 protected String
getZoologicalNonCacheAuthorshipCache(TaxonName nonViralName
) {
206 if (nonViralName
== null){
210 INomenclaturalAuthor combinationAuthor
= nonViralName
.getCombinationAuthorship();
211 INomenclaturalAuthor exCombinationAuthor
= nonViralName
.getExCombinationAuthorship();
212 INomenclaturalAuthor basionymAuthor
= nonViralName
.getBasionymAuthorship();
213 INomenclaturalAuthor exBasionymAuthor
= nonViralName
.getExBasionymAuthorship();
214 Integer publicationYear
= nonViralName
.getPublicationYear();
215 Integer originalPublicationYear
= nonViralName
.getOriginalPublicationYear();
217 String basionymPart
= "";
218 String authorPart
= "";
220 if (basionymAuthor
!= null || exBasionymAuthor
!= null || originalPublicationYear
!= null ){
221 String authorAndEx
= getAuthorAndExAuthor(basionymAuthor
, exBasionymAuthor
);
222 String originalPublicationYearString
= originalPublicationYear
== null ?
null : String
.valueOf(originalPublicationYear
);
223 String authorAndExAndYear
= CdmUtils
.concat(zooAuthorYearSeperator
, authorAndEx
, originalPublicationYearString
);
224 basionymPart
= basionymStart
+ authorAndExAndYear
+ basionymEnd
;
226 if (combinationAuthor
!= null || exCombinationAuthor
!= null){
227 String authorAndEx
= getAuthorAndExAuthor(combinationAuthor
, exCombinationAuthor
);
228 String publicationYearString
= publicationYear
== null ?
null : String
.valueOf(publicationYear
);
229 authorPart
= CdmUtils
.concat(zooAuthorYearSeperator
, authorAndEx
, publicationYearString
);
231 result
= CdmUtils
.concat(basionymAuthorCombinationAuthorSeperator
, basionymPart
, authorPart
);
240 * Returns the AuthorCache part for a combination of an author and an ex author. This applies on
241 * combination authors as well as on basionym/orginal combination authors.
242 * The correct order is exAuthor ex author though some botanist do not know about and do it the
243 * other way round. (see 46.4-46.6 ICBN (Vienna Code, 2006))
245 * @param author the author
246 * @param exAuthor the ex-author
249 protected String
getAuthorAndExAuthor(INomenclaturalAuthor author
, INomenclaturalAuthor exAuthor
){
250 String authorString
= "";
251 String exAuthorString
= "";
253 authorString
= CdmUtils
.Nz(author
.getNomenclaturalTitle());
255 if (exAuthor
!= null){
256 exAuthorString
= CdmUtils
.Nz(exAuthor
.getNomenclaturalTitle());
257 exAuthorString
+= exAuthorSeperator
;
259 String result
= exAuthorString
+ authorString
;
266 * Checks if the given name should include the author in it's cached version.<BR>
267 * This is usually the case but not for <i>species aggregates</i>.
268 * @param nonViralName
271 protected boolean nameIncludesAuthorship(INonViralName nonViralName
){
272 Rank rank
= nonViralName
.getRank();
273 if (rank
!= null && rank
.isSpeciesAggregate()){
280 // ************* TAGGED NAME ***************************************/
283 public List
<TaggedText
> getTaggedFullTitle(TaxonName nonViralName
) {
284 List
<TaggedText
> tags
= new ArrayList
<>();
287 if (nonViralName
== null){
291 //protected full title cache
292 if (nonViralName
.isProtectedFullTitleCache()){
293 tags
.add(new TaggedText(TagEnum
.fullName
, nonViralName
.getFullTitleCache()));
298 // String titleCache = nonViralName.getTitleCache();
299 List
<TaggedText
> titleTags
= getTaggedTitle(nonViralName
);
300 tags
.addAll(titleTags
);
303 String microReference
= nonViralName
.getNomenclaturalMicroReference();
304 INomenclaturalReference ref
= nonViralName
.getNomenclaturalReference();
305 String referenceCache
= null;
307 Reference reference
= HibernateProxyHelper
.deproxy(ref
, Reference
.class);
308 referenceCache
= reference
.getNomenclaturalCitation(microReference
);
311 if (StringUtils
.isNotBlank(referenceCache
)){
312 if (! referenceCache
.trim().startsWith("in ")){
313 String refConcat
= ", ";
314 tags
.add(new TaggedText(TagEnum
.separator
, refConcat
));
316 tags
.add(new TaggedText(TagEnum
.reference
, referenceCache
));
319 addOriginalSpelling(tags
, nonViralName
);
321 //nomenclatural status
322 tags
.addAll(getNomStatusTags(nonViralName
, true, false));
329 * @param nonViralName
334 public List
<TaggedText
> getNomStatusTags(TaxonName nonViralName
, boolean includeSeparatorBefore
,
335 boolean includeSeparatorAfter
) {
337 Set
<NomenclaturalStatus
> ncStati
= nonViralName
.getStatus();
338 Iterator
<NomenclaturalStatus
> iterator
= ncStati
.iterator();
339 List
<TaggedText
> nomStatusTags
= new ArrayList
<>();
340 while (iterator
.hasNext()) {
341 NomenclaturalStatus ncStatus
= iterator
.next();
342 // since the NewInstance method of nomencatural status allows null as parameter
343 // we have to check for null values here
344 String nomStatusStr
= "not defined";
345 if(ncStatus
.getType() != null){
346 NomenclaturalStatusType statusType
= ncStatus
.getType();
347 Language lang
= Language
.LATIN();
348 Representation repr
= statusType
.getRepresentation(lang
);
350 nomStatusStr
= repr
.getAbbreviatedLabel();
352 String message
= "No latin representation available for nom. status. " + statusType
.getTitleCache();
353 logger
.warn(message
);
354 throw new IllegalStateException(message
);
356 }else if(StringUtils
.isNotBlank(ncStatus
.getRuleConsidered())){
357 nomStatusStr
= ncStatus
.getRuleConsidered();
359 String statusSeparator
= ", ";
360 if (includeSeparatorBefore
){
361 nomStatusTags
.add(new TaggedText(TagEnum
.separator
, statusSeparator
));
363 nomStatusTags
.add(new TaggedText(TagEnum
.nomStatus
, nomStatusStr
, new TypedEntityReference
<>(ncStatus
.getClass(), ncStatus
.getUuid())));
364 if (includeSeparatorAfter
){
365 nomStatusTags
.add(new TaggedText(TagEnum
.postSeparator
, ","));
368 return nomStatusTags
;
372 public List
<TaggedText
> getTaggedTitle(TaxonName taxonName
) {
373 if (taxonName
== null){
377 List
<TaggedText
> tags
= new ArrayList
<>();
379 if (taxonName
.isViral()){
380 return getViralTaggedTitle(taxonName
);
382 //TODO how to handle protected fullTitleCache here?
383 if (taxonName
.isProtectedTitleCache()){
384 //protected title cache
385 tags
.add(new TaggedText(TagEnum
.name
, taxonName
.getTitleCache()));
387 }else if (taxonName
.isHybridFormula()){
389 String hybridSeparator
= NonViralNameParserImplRegExBase
.hybridSign
;
390 boolean isFirst
= true;
391 List
<HybridRelationship
> rels
= taxonName
.getOrderedChildRelationships();
392 for (HybridRelationship rel
: rels
){
394 tags
.add(new TaggedText(TagEnum
.hybridSign
, hybridSeparator
));
397 tags
.addAll(getTaggedTitle(rel
.getParentName()));
400 }else if (taxonName
.isAutonym()){
402 tags
.addAll(handleTaggedAutonym(taxonName
));
404 List
<TaggedText
> nameTags
= getTaggedName(taxonName
);
405 tags
.addAll(nameTags
);
406 String authorCache
= getAuthorshipCache(taxonName
);
407 if (StringUtils
.isNotBlank(authorCache
)){
408 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));
419 private List
<TaggedText
> getViralTaggedTitle(TaxonName viralName
) {
420 List
<TaggedText
> tags
= new ArrayList
<>();
421 if (viralName
.isProtectedTitleCache()){
422 //protected title cache
423 tags
.add(new TaggedText(TagEnum
.name
, viralName
.getTitleCache()));
426 if (StringUtils
.isNotBlank(viralName
.getAcronym())){
427 //this is not according to the code
428 tags
.add(new TaggedText(TagEnum
.name
, viralName
.getAcronym()));
435 * Returns the tag list of the name part (without author and reference).
436 * @param nonViralName
440 public List
<TaggedText
> getTaggedName(TaxonName nonViralName
) {
441 if (nonViralName
== null){
443 }else if (nonViralName
.getNameType().isViral()){
446 List
<TaggedText
> tags
= new ArrayList
<>();
447 Rank rank
= nonViralName
.getRank();
449 if (nonViralName
.isProtectedNameCache()){
450 tags
.add(new TaggedText(TagEnum
.name
, nonViralName
.getNameCache()));
451 }else if (nonViralName
.isHybridFormula()){
453 String hybridSeparator
= NonViralNameParserImplRegExBase
.hybridSign
;
454 boolean isFirst
= true;
455 List
<HybridRelationship
> rels
= nonViralName
.getOrderedChildRelationships();
456 for (HybridRelationship rel
: rels
){
458 tags
.add(new TaggedText(TagEnum
.hybridSign
, hybridSeparator
));
461 tags
.addAll(getTaggedName(rel
.getParentName()));
465 }else if (rank
== null){
466 tags
= getRanklessTaggedNameCache(nonViralName
);
467 // }else if (nonViralName.isInfragenericUnranked()){
468 // result = getUnrankedInfragenericNameCache(nonViralName);
469 }else if (rank
.isInfraSpecific()){
470 tags
= getInfraSpeciesTaggedNameCache(nonViralName
);
471 }else if (rank
.isSpecies() || isAggregateWithAuthorship(nonViralName
, rank
) ){ //exception see #4288
472 tags
= getSpeciesTaggedNameCache(nonViralName
);
473 }else if (rank
.isInfraGeneric()){
474 tags
= getInfraGenusTaggedNameCache(nonViralName
);
475 }else if (rank
.isGenus()){
476 tags
= getGenusOrUninomialTaggedNameCache(nonViralName
);
477 }else if (rank
.isSupraGeneric()){
478 tags
= getGenusOrUninomialTaggedNameCache(nonViralName
);
480 tags
= getRanklessTaggedNameCache(nonViralName
);
481 logger
.warn("Rank does not belong to a rank class: " + rank
.getTitleCache() + ". Used rankless nameCache for name " + nonViralName
.getUuid());
483 //TODO handle appended phrase here instead of different places, check first if this is true for all
493 //***************************** PRIVATES ***************************************/
496 private boolean isAggregateWithAuthorship(TaxonName nonViralName
, Rank rank
) {
500 return rank
.isSpeciesAggregate() && ( isNotBlank(nonViralName
.getAuthorshipCache()) || nonViralName
.getNomenclaturalReference() != null );
506 * Returns the tag list for an autonym taxon.
508 * @see NonViralName#isAutonym()
509 * @see BotanicalName#isAutonym()
510 * @param nonViralName
513 private List
<TaggedText
> handleTaggedAutonym(TaxonName nonViralName
) {
514 List
<TaggedText
> tags
= null;
515 if (nonViralName
.isInfraSpecific()){
517 tags
= getSpeciesTaggedNameCache(nonViralName
);
520 String authorCache
= getAuthorshipCache(nonViralName
);
521 if (StringUtils
.isNotBlank(authorCache
)){
522 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));
526 //infra species marker
527 if (nonViralName
.getRank() == null || !nonViralName
.getRank().isInfraSpecific()){
528 //TODO handle exception
529 logger
.warn("Rank for autonym does not exist or is not lower than species !!");
531 String infraSpeciesMarker
= nonViralName
.getRank().getAbbreviation();
532 if (nonViralName
.isTrinomHybrid()){
533 infraSpeciesMarker
= CdmUtils
.concat("", NOTHO
, infraSpeciesMarker
);
535 if (StringUtils
.isNotBlank(infraSpeciesMarker
)){
536 tags
.add(new TaggedText(TagEnum
.rank
, infraSpeciesMarker
));
541 String infraSpeciesPart
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet()).trim();
542 if (StringUtils
.isNotBlank(infraSpeciesPart
)){
543 tags
.add(new TaggedText(TagEnum
.name
, infraSpeciesPart
));
546 } else if (nonViralName
.isInfraGeneric()){
548 tags
=getGenusOrUninomialTaggedNameCache(nonViralName
);
551 //infra species marker
552 if (nonViralName
.getRank() == null || !nonViralName
.getRank().isInfraGeneric()){
553 //TODO handle exception
554 logger
.warn("Rank for autonym does not exist or is not lower than species !!");
556 Rank rank
= nonViralName
.getRank();
557 String infraGenericMarker
= rank
.getAbbreviation();
558 if (rank
.equals(Rank
.SECTION_BOTANY()) || rank
.equals(Rank
.SUBSECTION_BOTANY())){
559 infraGenericMarker
= infraGenericMarker
.replace("(bot.)", "");
561 if (StringUtils
.isNotBlank(infraGenericMarker
)){
562 tags
.add(new TaggedText(TagEnum
.rank
, infraGenericMarker
));
567 String infraGenericPart
= CdmUtils
.Nz(nonViralName
.getInfraGenericEpithet()).trim();
568 if (StringUtils
.isNotBlank(infraGenericPart
)){
569 tags
.add(new TaggedText(TagEnum
.name
, infraGenericPart
));
580 * Returns the tag list for rankless taxa.
581 * @param nonViralName
584 protected List
<TaggedText
> getRanklessTaggedNameCache(INonViralName nonViralName
){
585 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
586 String speciesEpi
= CdmUtils
.Nz(nonViralName
.getSpecificEpithet()).trim();
587 if (StringUtils
.isNotBlank(speciesEpi
)){
588 tags
.add(new TaggedText(TagEnum
.name
, speciesEpi
));
591 String infraSpeciesEpi
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet());
592 if (StringUtils
.isNotBlank(infraSpeciesEpi
)){
593 tags
.add(new TaggedText(TagEnum
.name
, infraSpeciesEpi
));
596 //result += " (rankless)";
597 addAppendedTaggedPhrase(tags
, nonViralName
);
602 * Returns the tag list for the first epithet (including a hybrid sign if required).
603 * @param nonViralName
606 private List
<TaggedText
> getUninomialTaggedPart(INonViralName nonViralName
) {
607 List
<TaggedText
> tags
= new ArrayList
<>();
609 if (nonViralName
.isMonomHybrid()){
610 addHybridPrefix(tags
);
613 String uninomial
= CdmUtils
.Nz(nonViralName
.getGenusOrUninomial()).trim();
614 if (StringUtils
.isNotBlank(uninomial
)){
615 tags
.add(new TaggedText(TagEnum
.name
, uninomial
));
622 * Returns the tag list for an genus or higher taxon.
624 * @param nonViralName
627 protected List
<TaggedText
> getGenusOrUninomialTaggedNameCache(INonViralName nonViralName
){
628 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
629 addAppendedTaggedPhrase(tags
, nonViralName
);
634 * Returns the tag list for an infrageneric taxon (including species aggregates).
636 * @see #getSpeciesAggregateTaggedCache(NonViralName)
637 * @param nonViralName
640 protected List
<TaggedText
> getInfraGenusTaggedNameCache(INonViralName nonViralName
){
641 Rank rank
= nonViralName
.getRank();
642 if (rank
!= null && rank
.isSpeciesAggregate() && isBlank(nonViralName
.getAuthorshipCache())){
643 return getSpeciesAggregateTaggedCache(nonViralName
);
647 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
650 String infraGenericMarker
;
653 infraGenericMarker
= rank
.getInfraGenericMarker();
654 if (rank
.equals(Rank
.SECTION_BOTANY()) || rank
.equals(Rank
.SUBSECTION_BOTANY())){
655 infraGenericMarker
= infraGenericMarker
.replace("(bot.)", "");
657 } catch (UnknownCdmTypeException e
) {
658 infraGenericMarker
= "'unhandled infrageneric rank'";
661 infraGenericMarker
= "'undefined infrageneric rank'";
663 String infraGenEpi
= CdmUtils
.Nz(nonViralName
.getInfraGenericEpithet()).trim();
664 if (nonViralName
.isBinomHybrid()){
665 infraGenericMarker
= CdmUtils
.concat("", NOTHO
, infraGenericMarker
);
668 addInfraGenericPart(nonViralName
, tags
, infraGenericMarker
, infraGenEpi
);
670 addAppendedTaggedPhrase(tags
, nonViralName
);
676 * Default implementation for the infrageneric part of a name.
677 * This is usually the infrageneric marker and the infrageneric epitheton. But may be
678 * implemented differently e.g. for zoological names the infrageneric epitheton
679 * may be surrounded by brackets and the marker left out.
680 * @param nonViralName
682 * @param infraGenericMarker
684 protected void addInfraGenericPart(
685 @SuppressWarnings("unused") INonViralName name
,
686 List
<TaggedText
> tags
,
687 String infraGenericMarker
,
688 String infraGenEpi
) {
690 tags
.add(new TaggedText(TagEnum
.rank
, infraGenericMarker
));
693 if (StringUtils
.isNotBlank(infraGenEpi
)){
694 tags
.add(new TaggedText(TagEnum
.name
, infraGenEpi
));
699 * Returns the tag list for a species aggregate (or similar) taxon.<BR>
700 * Possible ranks for a <i>species aggregate</i> are "aggr.", "species group", ...
701 * @param nonViralName
704 protected List
<TaggedText
> getSpeciesAggregateTaggedCache(INonViralName nonViralName
){
705 if (! isBlank(nonViralName
.getAuthorshipCache())){
706 List
<TaggedText
> result
= getSpeciesTaggedNameCache(nonViralName
);
711 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
713 addSpeciesAggregateTaggedEpithet(tags
, nonViralName
);
714 addAppendedTaggedPhrase(tags
, nonViralName
);
719 * Adds the aggregate tag to the tag list.
721 * @param nonViralName
723 private void addSpeciesAggregateTaggedEpithet(List
<TaggedText
> tags
, INonViralName nonViralName
) {
726 marker
= nonViralName
.getRank().getInfraGenericMarker();
727 } catch (UnknownCdmTypeException e
) {
728 marker
= "'unknown aggregat type'";
730 if (StringUtils
.isNotBlank(marker
)){
731 tags
.add(new TaggedText(TagEnum
.rank
, marker
));
737 * Returns the tag list for a species taxon.
738 * @param nonViralName
741 protected List
<TaggedText
> getSpeciesTaggedNameCache(INonViralName nonViralName
){
742 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
743 addAppendedTaggedPhrase(tags
, nonViralName
);
747 protected List
<TaggedText
> getInfraSpeciesTaggedNameCache(TaxonName name
){
748 if (name
.getNameType().isZoological()){
749 boolean includeMarker
=includeInfraSpecificMarkerForZooNames(name
);
750 return getInfraSpeciesTaggedNameCache(name
, includeMarker
);
752 return getInfraSpeciesTaggedNameCache(name
, true);
756 protected boolean includeInfraSpecificMarkerForZooNames(TaxonName name
){
757 return ! (name
.isAutonym()); //only exclude marker if autonym, see also ZooNameNoMarkerCacheStrategy
761 * Creates the tag list for an infraspecific taxon. If include is true the result will contain
762 * the infraspecific marker (e.g. "var.")
763 * @param nonViralName
764 * @param includeMarker
767 protected List
<TaggedText
> getInfraSpeciesTaggedNameCache(INonViralName nonViralName
, boolean includeMarker
){
768 List
<TaggedText
> tags
= getGenusAndSpeciesTaggedPart(nonViralName
);
769 if (includeMarker
|| nonViralName
.isTrinomHybrid()){
770 String marker
= (nonViralName
.getRank().getAbbreviation()).trim().replace("null", "");
771 if (nonViralName
.isTrinomHybrid()){
772 marker
= CdmUtils
.concat("", NOTHO
, marker
);
774 if (StringUtils
.isNotBlank(marker
)){
775 tags
.add(new TaggedText(TagEnum
.rank
, marker
));
779 String infrSpecEpi
= CdmUtils
.Nz(nonViralName
.getInfraSpecificEpithet());
781 infrSpecEpi
= infrSpecEpi
.trim().replace("null", "");
783 if (StringUtils
.isNotBlank(infrSpecEpi
)){
784 tags
.add(new TaggedText(TagEnum
.name
, infrSpecEpi
));
787 addAppendedTaggedPhrase(tags
, nonViralName
);
793 * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.
796 private void addHybridPrefix(List
<TaggedText
> tags
) {
797 tags
.add(new TaggedText(TagEnum
.hybridSign
, NonViralNameParserImplRegExBase
.hybridSign
));
798 tags
.add(new TaggedText(TagEnum
.separator
, "")); //no whitespace separator
802 * Creates the tag list for the genus and species part.
803 * @param nonViralName
806 private List
<TaggedText
> getGenusAndSpeciesTaggedPart(INonViralName nonViralName
) {
808 List
<TaggedText
> tags
= getUninomialTaggedPart(nonViralName
);
811 boolean hasInfraGenericEpi
= StringUtils
.isNotBlank(nonViralName
.getInfraGenericEpithet());
812 if (hasInfraGenericEpi
){
813 String infrGenEpi
= nonViralName
.getInfraGenericEpithet().trim();
814 if (nonViralName
.isBinomHybrid()){
815 //maybe not correct but not really expected to happen
816 infrGenEpi
= UTF8
.HYBRID
+ infrGenEpi
;
818 infrGenEpi
= "(" + infrGenEpi
+ ")";
819 tags
.add(new TaggedText(TagEnum
.name
, infrGenEpi
));
823 String specEpi
= CdmUtils
.Nz(nonViralName
.getSpecificEpithet()).trim();
824 if (! hasInfraGenericEpi
&& nonViralName
.isBinomHybrid() ||
825 hasInfraGenericEpi
&& nonViralName
.isTrinomHybrid()){
826 addHybridPrefix(tags
);
828 if (StringUtils
.isNotBlank(specEpi
)){
829 tags
.add(new TaggedText(TagEnum
.name
, specEpi
));
835 * Adds the tag for the appended phrase if an appended phrase exists
837 * @param nonViralName
839 protected void addAppendedTaggedPhrase(List
<TaggedText
> tags
, INonViralName nonViralName
){
840 String appendedPhrase
= nonViralName
==null ?
null : nonViralName
.getAppendedPhrase();
841 if (StringUtils
.isNotEmpty(appendedPhrase
)){
842 tags
.add(new TaggedText(TagEnum
.name
, appendedPhrase
));
848 public String
getLastEpithet(TaxonName taxonName
) {
849 Rank rank
= taxonName
.getRank();
850 if(rank
.isGenus() || rank
.isSupraGeneric()) {
851 return taxonName
.getGenusOrUninomial();
852 } else if(rank
.isInfraGeneric()) {
853 return taxonName
.getInfraGenericEpithet();
854 } else if(rank
.isSpecies()) {
855 return taxonName
.getSpecificEpithet();
857 return taxonName
.getInfraSpecificEpithet();
866 protected List
<TaggedText
> doGetTaggedTitle(TaxonName taxonName
) {
867 List
<TaggedText
> tags
= new ArrayList
<>();
868 if (taxonName
.getNameType().isViral()){
869 String acronym
= taxonName
.getAcronym();
870 tags
.add(new TaggedText(TagEnum
.name
, acronym
));
872 }else if (taxonName
.isHybridFormula()){
874 String hybridSeparator
= NonViralNameParserImplRegExBase
.hybridSign
;
875 boolean isFirst
= true;
876 List
<HybridRelationship
> rels
= taxonName
.getOrderedChildRelationships();
877 for (HybridRelationship rel
: rels
){
879 tags
.add(new TaggedText(TagEnum
.hybridSign
, hybridSeparator
));
882 tags
.addAll(getTaggedTitle(rel
.getParentName()));
885 }else if (taxonName
.isAutonym()){
887 tags
.addAll(handleTaggedAutonym(taxonName
));
889 List
<TaggedText
> nameTags
= getTaggedName(taxonName
);
890 tags
.addAll(nameTags
);
891 String authorCache
= getAuthorshipCache(taxonName
);
892 if (StringUtils
.isNotBlank(authorCache
)){
893 tags
.add(new TaggedText(TagEnum
.authors
, authorCache
));