cleanup
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / strategy / cache / name / TaxonNameDefaultCacheStrategy.java
index 1f7499909f5ac10068667b65caefe47e9ab81ab9..d8f6cb9a1cb4801c3864812624c65f54efbd480f 100644 (file)
@@ -9,34 +9,27 @@
 package eu.etaxonomy.cdm.strategy.cache.name;
 
 import java.util.ArrayList;
-import java.util.Iterator;
 import java.util.List;
-import java.util.Set;
 import java.util.UUID;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
-import org.apache.commons.lang.StringUtils;
-import org.apache.log4j.Logger;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
 
 import eu.etaxonomy.cdm.common.CdmUtils;
 import eu.etaxonomy.cdm.common.UTF8;
-import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
 import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
-import eu.etaxonomy.cdm.model.common.Language;
-import eu.etaxonomy.cdm.model.common.Representation;
 import eu.etaxonomy.cdm.model.name.HybridRelationship;
 import eu.etaxonomy.cdm.model.name.INonViralName;
-import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;
-import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;
 import eu.etaxonomy.cdm.model.name.Rank;
 import eu.etaxonomy.cdm.model.name.TaxonName;
-import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
-import eu.etaxonomy.cdm.model.reference.Reference;
 import eu.etaxonomy.cdm.strategy.cache.TagEnum;
 import eu.etaxonomy.cdm.strategy.cache.TaggedText;
+import eu.etaxonomy.cdm.strategy.cache.TaggedTextBuilder;
 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
 import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImplRegExBase;
 
-
 /**
  * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName>
  * interface.<BR>
@@ -50,7 +43,7 @@ public class TaxonNameDefaultCacheStrategy
         extends NameCacheStrategyBase
         implements INonViralNameCacheStrategy {
 
-    private static final Logger logger = Logger.getLogger(TaxonNameDefaultCacheStrategy.class);
+    private static final Logger logger = LogManager.getLogger(TaxonNameDefaultCacheStrategy.class);
        private static final long serialVersionUID = -6577757501563212669L;
 
     final static UUID uuid = UUID.fromString("1cdda0d1-d5bc-480f-bf08-40a510a2f223");
@@ -64,10 +57,11 @@ public class TaxonNameDefaultCacheStrategy
 
     protected String zooAuthorYearSeperator = ", ";
 
-
+    private String cultivarStart = "'";
+    private String cultivarEnd = "'";
 
     @Override
-    public  UUID getUuid(){
+    public UUID getUuid(){
         return uuid;
     }
 
@@ -77,7 +71,6 @@ public class TaxonNameDefaultCacheStrategy
         return new TaxonNameDefaultCacheStrategy();
     }
 
-
 // ************ CONSTRUCTOR *******************/
 
     protected TaxonNameDefaultCacheStrategy(){
@@ -97,7 +90,6 @@ public class TaxonNameDefaultCacheStrategy
         this.nameAuthorSeperator = nameAuthorSeperator;
     }
 
-
     /**
      * String the basionym author part starts with e.g. '('.
      * This should correspond with the {@link TaxonNameDefaultCacheStrategy#getBasionymEnd() basionymEnd} attribute
@@ -122,10 +114,8 @@ public class TaxonNameDefaultCacheStrategy
         this.basionymEnd = basionymEnd;
     }
 
-
     /**
      * String to separate ex author from author.
-     * @return
      */
     public String getExAuthorSeperator() {
         return exAuthorSeperator;
@@ -134,16 +124,12 @@ public class TaxonNameDefaultCacheStrategy
         this.exAuthorSeperator = exAuthorSeperator;
     }
 
-
     /**
      * String that separates the basionym/original_combination author part from the combination author part
-     * @return
      */
     public CharSequence getBasionymAuthorCombinationAuthorSeperator() {
         return basionymAuthorCombinationAuthorSeperator;
     }
-
-
     public void setBasionymAuthorCombinationAuthorSeperator( CharSequence basionymAuthorCombinationAuthorSeperator) {
         this.basionymAuthorCombinationAuthorSeperator = basionymAuthorCombinationAuthorSeperator;
     }
@@ -155,19 +141,13 @@ public class TaxonNameDefaultCacheStrategy
         this.zooAuthorYearSeperator = authorYearSeperator;
     }
 
-
-//** *****************************************************************************************/
-
-
-
-
 // ******************* Authorship ******************************/
 
     @Override
     public String getAuthorshipCache(TaxonName taxonName) {
         if (taxonName == null){
             return null;
-        }else if (taxonName.getNameType().isViral()){
+        }else if (taxonName.isViral()){
             return null;
         }else if(taxonName.isProtectedAuthorshipCache() == true) {
             //cache protected
@@ -192,6 +172,12 @@ public class TaxonNameDefaultCacheStrategy
             INomenclaturalAuthor exCombinationAuthor = nonViralName.getExCombinationAuthorship();
             INomenclaturalAuthor basionymAuthor = nonViralName.getBasionymAuthorship();
             INomenclaturalAuthor exBasionymAuthor = nonViralName.getExBasionymAuthorship();
+            if (isCultivar(nonViralName) ){
+                exCombinationAuthor = null;
+                basionymAuthor = null;
+                exBasionymAuthor = null;
+            }
+
             String basionymPart = "";
             String authorPart = "";
             //basionym
@@ -209,6 +195,10 @@ public class TaxonNameDefaultCacheStrategy
         }
     }
 
+    private boolean isCultivar(TaxonName name) {
+        return name.isCultivar() || isNotBlank(name.getCultivarEpithet()) || isNotBlank(name.getCultivarGroupEpithet());
+    }
+
     protected String getZoologicalNonCacheAuthorshipCache(TaxonName nonViralName) {
         if (nonViralName == null){
             return null;
@@ -248,26 +238,24 @@ public class TaxonNameDefaultCacheStrategy
      * combination authors as well as on basionym/orginal combination authors.
      * The correct order is exAuthor ex author though some botanist do not know about and do it the
      * other way round. (see 46.4-46.6 ICBN (Vienna Code, 2006))
-     *
-     * @param author the author
-     * @param exAuthor the ex-author
-     * @return
      */
     protected String getAuthorAndExAuthor(INomenclaturalAuthor author, INomenclaturalAuthor exAuthor){
         String authorString = "";
         String exAuthorString = "";
         if (author != null){
-            authorString = CdmUtils.Nz(author.getNomenclaturalTitle());
+            authorString = getNomAuthorTitle(author);
         }
         if (exAuthor != null){
-            exAuthorString = CdmUtils.Nz(exAuthor.getNomenclaturalTitle());
+            exAuthorString = getNomAuthorTitle(exAuthor);
             exAuthorString += exAuthorSeperator;
         }
         String result = exAuthorString + authorString;
         return result;
     }
 
-
+    private String getNomAuthorTitle(INomenclaturalAuthor author) {
+        return CdmUtils.Nz(author.getNomenclaturalTitleCache());
+    }
 
     /**
      * Checks if the given name should include the author in it's cached version.<BR>
@@ -287,109 +275,14 @@ public class TaxonNameDefaultCacheStrategy
 // ************* TAGGED NAME ***************************************/
 
     @Override
-    public List<TaggedText> getTaggedFullTitle(TaxonName nonViralName) {
+    protected List<TaggedText> doGetTaggedTitle(TaxonName taxonName) {
         List<TaggedText> tags = new ArrayList<>();
-
-        //null
-        if (nonViralName == null){
-            return null;
-        }
-
-        //protected full title cache
-        if (nonViralName.isProtectedFullTitleCache()){
-            tags.add(new TaggedText(TagEnum.fullName, nonViralName.getFullTitleCache()));
-            return tags;
-        }
-
-        //title cache
-//             String titleCache = nonViralName.getTitleCache();
-        List<TaggedText> titleTags = getTaggedTitle(nonViralName);
-        tags.addAll(titleTags);
-
-        //reference
-        String microReference = nonViralName.getNomenclaturalMicroReference();
-        INomenclaturalReference ref = nonViralName.getNomenclaturalReference();
-        String referenceCache = null;
-        if (ref != null){
-            Reference reference = HibernateProxyHelper.deproxy(ref, Reference.class);
-            referenceCache = reference.getNomenclaturalCitation(microReference);
-        }
-            //add to tags
-        if (StringUtils.isNotBlank(referenceCache)){
-            if (! referenceCache.trim().startsWith("in ")){
-                String refConcat = ", ";
-                tags.add(new TaggedText(TagEnum.separator, refConcat));
-            }
-            tags.add(new TaggedText(TagEnum.reference, referenceCache));
-        }
-
-        addOriginalSpelling(tags, nonViralName);
-
-        //nomenclatural status
-        tags.addAll(getNomStatusTags(nonViralName, true, false));
-        return tags;
-
-    }
-
-
-    /**
-     * @param nonViralName
-     * @param tags
-     * @return
-     */
-    @Override
-    public List<TaggedText> getNomStatusTags(TaxonName nonViralName, boolean includeSeparatorBefore,
-            boolean includeSeparatorAfter) {
-
-        Set<NomenclaturalStatus> ncStati = nonViralName.getStatus();
-        Iterator<NomenclaturalStatus> iterator = ncStati.iterator();
-        List<TaggedText> nomStatusTags = new ArrayList<>();
-        while (iterator.hasNext()) {
-            NomenclaturalStatus ncStatus = iterator.next();
-            // since the NewInstance method of nomencatural status allows null as parameter
-            // we have to check for null values here
-            String nomStatusStr = "not defined";
-            if(ncStatus.getType() != null){
-                NomenclaturalStatusType statusType =  ncStatus.getType();
-                Language lang = Language.LATIN();
-                Representation repr = statusType.getRepresentation(lang);
-                if (repr != null){
-                    nomStatusStr = repr.getAbbreviatedLabel();
-                }else{
-                    String message = "No latin representation available for nom. status. " + statusType.getTitleCache();
-                    logger.warn(message);
-                    throw new IllegalStateException(message);
-                }
-            }else if(StringUtils.isNotBlank(ncStatus.getRuleConsidered())){
-                nomStatusStr = ncStatus.getRuleConsidered();
-            }
-            String statusSeparator = ", ";
-            if (includeSeparatorBefore){
-                nomStatusTags.add(new TaggedText(TagEnum.separator, statusSeparator));
-            }
-            nomStatusTags.add(new TaggedText(TagEnum.nomStatus, nomStatusStr));
-            if (includeSeparatorAfter){
-                nomStatusTags.add(new TaggedText(TagEnum.postSeparator, ","));
+        if (taxonName.getNameType().isViral()){
+            String acronym = taxonName.getAcronym();
+            if (isNotBlank(taxonName.getAcronym())){
+                //this is not according to the code
+                tags.add(new TaggedText(TagEnum.name, acronym));
             }
-        }
-        return nomStatusTags;
-    }
-
-    @Override
-    public List<TaggedText> getTaggedTitle(TaxonName taxonName) {
-        if (taxonName == null){
-            return null;
-        }
-
-        List<TaggedText> tags = new ArrayList<>();
-
-        if (taxonName.isViral()){
-            return getViralTaggedTitle(taxonName);
-        }
-        //TODO how to handle protected fullTitleCache here?
-        if (taxonName.isProtectedTitleCache()){
-            //protected title cache
-            tags.add(new TaggedText(TagEnum.name, taxonName.getTitleCache()));
             return tags;
         }else if (taxonName.isHybridFormula()){
             //hybrid formula
@@ -406,60 +299,41 @@ public class TaxonNameDefaultCacheStrategy
             return tags;
         }else if (taxonName.isAutonym()){
             //Autonym
-            tags.addAll(handleTaggedAutonym(taxonName));
+            tags.addAll(handleTaggedAutonym(taxonName, true));
         }else{ //not Autonym
             List<TaggedText> nameTags = getTaggedName(taxonName);
             tags.addAll(nameTags);
             String authorCache = getAuthorshipCache(taxonName);
-            if (StringUtils.isNotBlank(authorCache)){
+            if (isNotBlank(authorCache)){
                 tags.add(new TaggedText(TagEnum.authors, authorCache));
             }
         }
-
         return tags;
     }
 
-    /**
-     * @param taxonName
-     * @return
-     */
-    private List<TaggedText> getViralTaggedTitle(TaxonName viralName) {
-        List<TaggedText> tags = new ArrayList<>();
-        if (viralName.isProtectedTitleCache()){
-            //protected title cache
-            tags.add(new TaggedText(TagEnum.name, viralName.getTitleCache()));
-            return tags;
-        }else{
-            if (StringUtils.isNotBlank(viralName.getAcronym())){
-                //this is not according to the code
-                tags.add(new TaggedText(TagEnum.name, viralName.getAcronym()));
-            }
-            return tags;
-        }
-    }
-
     /**
      * Returns the tag list of the name part (without author and reference).
-     * @param nonViralName
+     * @param taxonName
      * @return
      */
     @Override
-    public List<TaggedText> getTaggedName(TaxonName nonViralName) {
-        if (nonViralName == null){
+    public List<TaggedText> getTaggedName(TaxonName taxonName) {
+        if (taxonName == null){
             return null;
-        }else if (nonViralName.getNameType().isViral()){
+        }else if (taxonName.isViral()){
             return null;
         }
         List<TaggedText> tags = new ArrayList<>();
-        Rank rank = nonViralName.getRank();
+        Rank rank = taxonName.getRank();
 
-        if (nonViralName.isProtectedNameCache()){
-            tags.add(new TaggedText(TagEnum.name, nonViralName.getNameCache()));
-        }else if (nonViralName.isHybridFormula()){
+        if (taxonName.isProtectedNameCache()){
+            tags.add(new TaggedText(TagEnum.name, taxonName.getNameCache()));
+        }else if (taxonName.isHybridFormula()){
             //hybrid formula
+            //TODO graft-chimera (see also cultivars)
             String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
             boolean isFirst = true;
-            List<HybridRelationship> rels = nonViralName.getOrderedChildRelationships();
+            List<HybridRelationship> rels = taxonName.getOrderedChildRelationships();
             for (HybridRelationship rel: rels){
                 if (! isFirst){
                     tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
@@ -470,34 +344,120 @@ public class TaxonNameDefaultCacheStrategy
             return tags;
 
         }else if (rank == null){
-            tags = getRanklessTaggedNameCache(nonViralName);
-//             }else if (nonViralName.isInfragenericUnranked()){
-//                     result = getUnrankedInfragenericNameCache(nonViralName);
+            tags = getRanklessTaggedNameCache(taxonName, true);
+               }else if (rank.isCultivar()){
+                       tags = getCultivarTaggedNameCache(taxonName);
         }else if (rank.isInfraSpecific()){
-            tags = getInfraSpeciesTaggedNameCache(nonViralName);
-        }else if (rank.isSpecies() || isAggregateWithAuthorship(nonViralName, rank) ){ //exception see #4288
-            tags = getSpeciesTaggedNameCache(nonViralName);
+            tags = getInfraSpeciesTaggedNameCache(taxonName);
+        }else if (rank.isSpecies() || isAggregateWithAuthorship(taxonName, rank) ){ //exception see #4288
+            tags = getSpeciesTaggedNameCache(taxonName, true);
         }else if (rank.isInfraGeneric()){
-            tags = getInfraGenusTaggedNameCache(nonViralName);
+            tags = getInfraGenusTaggedNameCache(taxonName, true);
         }else if (rank.isGenus()){
-            tags = getGenusOrUninomialTaggedNameCache(nonViralName);
+            tags = getGenusOrUninomialTaggedNameCache(taxonName, true);
         }else if (rank.isSupraGeneric()){
-            tags = getGenusOrUninomialTaggedNameCache(nonViralName);
+            tags = getGenusOrUninomialTaggedNameCache(taxonName, true);
         }else{
-            logger.warn("Name Strategy for Name (UUID: " + nonViralName.getUuid() +  ") not yet implemented");
+            tags = getRanklessTaggedNameCache(taxonName, true);
+            logger.warn("Rank does not belong to a rank class: " + rank.getTitleCache() + ". Used rankless nameCache for name " + taxonName.getUuid());
         }
         //TODO handle appended phrase here instead of different places, check first if this is true for all
         //cases
 
         return tags;
+    }
 
+//***************************** PRIVATES ***************************************/
+
+    private List<TaggedText> getCultivarTaggedNameCache(TaxonName taxonName) {
+        List<TaggedText> scientificNameTags;
+        TaggedTextBuilder builder = TaggedTextBuilder.NewInstance();
+        if (isNotBlank(taxonName.getInfraSpecificEpithet())){
+            scientificNameTags = getInfraSpeciesTaggedNameCache(taxonName, false, false);
+        } else if (isNotBlank(taxonName.getSpecificEpithet())){
+            scientificNameTags = getSpeciesTaggedNameCache(taxonName, false);
+        } else if (isNotBlank(taxonName.getInfraGenericEpithet())){
+            scientificNameTags = getInfraGenusTaggedNameCache(taxonName, false);
+        } else /*if (isNotBlank(taxonName.getGenusOrUninomial())) */{
+            scientificNameTags = getGenusOrUninomialTaggedNameCache(taxonName, false);
+        }
+
+        UUID rankUuid = taxonName.getRank().getUuid();
+        boolean rankIsHandled = true;
+        String cultivarStr = null;
+        String groupStr = taxonName.getCultivarGroupEpithet();
+        if (rankUuid.equals(Rank.uuidCultivar)){
+            cultivarStr = surroundedCultivarEpithet(taxonName.getCultivarEpithet());
+            if (isNotBlank(cultivarStr) && isNotBlank(groupStr)){
+                groupStr = surroundGroupWithBracket(groupStr);
+            }
+            cultivarStr = CdmUtils.concat(" ", groupStr, cultivarStr);
+        }else if (rankUuid.equals(Rank.uuidCultivarGroup)){
+            cultivarStr = CdmUtils.concat(" ", groupStr, checkHasGroupEpithet(groupStr)? null: "Group");
+        }else if (rankUuid.equals(Rank.uuidGrexICNCP)){
+            cultivarStr = CdmUtils.concat(" ", groupStr, checkHasGrexEpithet(groupStr)? null: "grex");
+        }else{
+            rankIsHandled = false;
+        }
+        if (rankIsHandled){
+            builder.addAll(scientificNameTags);
+        }else if (rankUuid.equals(Rank.uuidGraftChimaera)){
+            //TODO not yet fully implemented
+            cultivarStr = "+ " + CdmUtils.concat(" ", taxonName.getGenusOrUninomial(), surroundedCultivarEpithet(taxonName.getCultivarEpithet()));
+        }else if (rankUuid.equals(Rank.uuidDenominationClass)){
+            //TODO dummy implementation
+            cultivarStr = CdmUtils.concat(" ", taxonName.getGenusOrUninomial(), surroundedCultivarEpithet(taxonName.getCultivarEpithet()));
+        } else { //(!rankIsHandled)
+            throw new IllegalStateException("Unsupported rank " + taxonName.getRank().getTitleCache() + " for cultivar.");
+        }
+        if (isNotBlank(cultivarStr)){
+            builder.add(TagEnum.cultivar, cultivarStr);
+        }
+
+        List<TaggedText> tags = builder.getTaggedText();
+        addAppendedTaggedPhrase(tags, taxonName, true);
+        return tags;
     }
 
+    private String surroundGroupWithBracket(String groupStr) {
+        if (groupStr.matches(NonViralNameParserImplRegExBase.grex + "$")){
+            return groupStr;
+        }else if (groupStr.matches(".*" + NonViralNameParserImplRegExBase.grex + ".+")){
+            Matcher matcher = Pattern.compile(NonViralNameParserImplRegExBase.grex + "\\s*" ).matcher(groupStr);
+            matcher.find();
+            return groupStr.substring(0, matcher.end()) + "("+ groupStr.substring(matcher.end())+ ")";
+        }else{
+            return "("+ groupStr + ")";
+        }
+    }
 
+    private boolean checkHasGroupEpithet(String group) {
 
+        String[] splits = group == null? new String[0]: group.split("\\s+");
+        if (splits.length <= 1){
+            return false;
+        }else if (splits[0].matches(NonViralNameParserImplRegExBase.group)
+                || splits[splits.length-1].matches(NonViralNameParserImplRegExBase.group)){
+            return true;
+        }else{
+            return false;
+        }
+    }
 
-//***************************** PRIVATES ***************************************/
+    private boolean checkHasGrexEpithet(String grex) {
+        String[] splits = grex == null? new String[0]: grex.split("\\s+");
+        if (splits.length <= 1){
+            return false;
+        }else if (splits[splits.length-1].matches(NonViralNameParserImplRegExBase.grex)){
+            return true;
+        }else{
+            return false;
+        }
+    }
 
+    private String surroundedCultivarEpithet(String cultivarEpi) {
+        return cultivarStart + CdmUtils.Nz(cultivarEpi) + cultivarEnd;
+    }
 
     private boolean isAggregateWithAuthorship(TaxonName nonViralName, Rank rank) {
                if (rank == null){
@@ -507,7 +467,6 @@ public class TaxonNameDefaultCacheStrategy
                }
        }
 
-
        /**
      * Returns the tag list for an autonym taxon.
      *
@@ -516,19 +475,18 @@ public class TaxonNameDefaultCacheStrategy
      * @param nonViralName
      * @return
      */
-    private List<TaggedText> handleTaggedAutonym(TaxonName nonViralName) {
+    private List<TaggedText> handleTaggedAutonym(TaxonName nonViralName, boolean addAppended) {
        List<TaggedText> tags = null;
        if (nonViralName.isInfraSpecific()){
                //species part
-               tags = getSpeciesTaggedNameCache(nonViralName);
+               tags = getSpeciesTaggedNameCache(nonViralName, addAppended);
 
                //author
                String authorCache = getAuthorshipCache(nonViralName);
-               if (StringUtils.isNotBlank(authorCache)){
+               if (isNotBlank(authorCache)){
                    tags.add(new TaggedText(TagEnum.authors, authorCache));
                }
 
-
                //infra species marker
                if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){
                    //TODO handle exception
@@ -538,21 +496,26 @@ public class TaxonNameDefaultCacheStrategy
                    if (nonViralName.isTrinomHybrid()){
                        infraSpeciesMarker = CdmUtils.concat("", NOTHO, infraSpeciesMarker);
                    }
-                   if (StringUtils.isNotBlank(infraSpeciesMarker)){
+                   if (isNotBlank(infraSpeciesMarker)){
                        tags.add(new TaggedText(TagEnum.rank, infraSpeciesMarker));
                    }
                }
 
                //infra species
                String infraSpeciesPart = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim();
-               if (StringUtils.isNotBlank(infraSpeciesPart)){
+               if (isNotBlank(infraSpeciesPart)){
                    tags.add(new TaggedText(TagEnum.name, infraSpeciesPart));
                }
 
         } else if (nonViralName.isInfraGeneric()){
                //genus part
-              tags =getGenusOrUninomialTaggedNameCache(nonViralName);
+              tags =getGenusOrUninomialTaggedNameCache(nonViralName, addAppended);
 
+              //author
+           String authorCache = getAuthorshipCache(nonViralName);
+           if (isNotBlank(authorCache)){
+               tags.add(new TaggedText(TagEnum.authors, authorCache));
+           }
 
                //infra species marker
                if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraGeneric()){
@@ -564,17 +527,16 @@ public class TaxonNameDefaultCacheStrategy
                 if (rank.equals(Rank.SECTION_BOTANY()) || rank.equals(Rank.SUBSECTION_BOTANY())){
                        infraGenericMarker = infraGenericMarker.replace("(bot.)", "");
                 }
-                   if (StringUtils.isNotBlank(infraGenericMarker)){
+                   if (isNotBlank(infraGenericMarker)){
                        tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
                    }
                }
 
-               //infra species
+               //infra genus
                String infraGenericPart = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim();
-               if (StringUtils.isNotBlank(infraGenericPart)){
+               if (isNotBlank(infraGenericPart)){
                    tags.add(new TaggedText(TagEnum.name, infraGenericPart));
                }
-
         }
 
         return tags;
@@ -587,20 +549,20 @@ public class TaxonNameDefaultCacheStrategy
      * @param nonViralName
      * @return
      */
-    protected List<TaggedText> getRanklessTaggedNameCache(INonViralName nonViralName){
+    protected List<TaggedText> getRanklessTaggedNameCache(INonViralName nonViralName, boolean addAppended){
         List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
         String speciesEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
-        if (StringUtils.isNotBlank(speciesEpi)){
+        if (isNotBlank(speciesEpi)){
             tags.add(new TaggedText(TagEnum.name, speciesEpi));
         }
 
         String infraSpeciesEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
-        if (StringUtils.isNotBlank(infraSpeciesEpi)){
+        if (isNotBlank(infraSpeciesEpi)){
             tags.add(new TaggedText(TagEnum.name, infraSpeciesEpi));
         }
 
         //result += " (rankless)";
-        addAppendedTaggedPhrase(tags, nonViralName);
+        addAppendedTaggedPhrase(tags, nonViralName, addAppended);
         return tags;
     }
 
@@ -617,7 +579,7 @@ public class TaxonNameDefaultCacheStrategy
         }
 
         String uninomial = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
-        if (StringUtils.isNotBlank(uninomial)){
+        if (isNotBlank(uninomial)){
             tags.add(new TaggedText(TagEnum.name, uninomial));
         }
 
@@ -630,9 +592,9 @@ public class TaxonNameDefaultCacheStrategy
      * @param nonViralName
      * @return
      */
-    protected List<TaggedText> getGenusOrUninomialTaggedNameCache(INonViralName nonViralName){
+    protected List<TaggedText> getGenusOrUninomialTaggedNameCache(INonViralName nonViralName, boolean addAppended){
         List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
-        addAppendedTaggedPhrase(tags, nonViralName);
+        addAppendedTaggedPhrase(tags, nonViralName, addAppended);
         return tags;
     }
 
@@ -643,10 +605,10 @@ public class TaxonNameDefaultCacheStrategy
      * @param nonViralName
      * @return
      */
-    protected List<TaggedText> getInfraGenusTaggedNameCache(INonViralName nonViralName){
+    protected List<TaggedText> getInfraGenusTaggedNameCache(INonViralName nonViralName, boolean addAppended){
         Rank rank = nonViralName.getRank();
         if (rank != null && rank.isSpeciesAggregate() && isBlank(nonViralName.getAuthorshipCache())){
-            return getSpeciesAggregateTaggedCache(nonViralName);
+            return getSpeciesAggregateTaggedCache(nonViralName, addAppended);
         }
 
         //genus
@@ -673,7 +635,7 @@ public class TaxonNameDefaultCacheStrategy
 
         addInfraGenericPart(nonViralName, tags, infraGenericMarker, infraGenEpi);
 
-        addAppendedTaggedPhrase(tags, nonViralName);
+        addAppendedTaggedPhrase(tags, nonViralName, addAppended);
         return tags;
     }
 
@@ -696,7 +658,7 @@ public class TaxonNameDefaultCacheStrategy
                tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));
 
                //add epitheton
-               if (StringUtils.isNotBlank(infraGenEpi)){
+               if (isNotBlank(infraGenEpi)){
             tags.add(new TaggedText(TagEnum.name, infraGenEpi));
         }
        }
@@ -707,24 +669,21 @@ public class TaxonNameDefaultCacheStrategy
      * @param nonViralName
      * @return
      */
-    protected List<TaggedText> getSpeciesAggregateTaggedCache(INonViralName nonViralName){
+    protected List<TaggedText> getSpeciesAggregateTaggedCache(INonViralName nonViralName, boolean addAppended){
         if (! isBlank(nonViralName.getAuthorshipCache())){
-               List<TaggedText> result = getSpeciesTaggedNameCache(nonViralName);
+               List<TaggedText> result = getSpeciesTaggedNameCache(nonViralName, addAppended);
                return result;
         }
 
-
        List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
 
         addSpeciesAggregateTaggedEpithet(tags, nonViralName);
-        addAppendedTaggedPhrase(tags, nonViralName);
+        addAppendedTaggedPhrase(tags, nonViralName, addAppended);
         return tags;
     }
 
     /**
      * Adds the aggregate tag to the tag list.
-     * @param tags
-     * @param nonViralName
      */
     private void addSpeciesAggregateTaggedEpithet(List<TaggedText> tags, INonViralName nonViralName) {
         String marker;
@@ -733,37 +692,33 @@ public class TaxonNameDefaultCacheStrategy
         } catch (UnknownCdmTypeException e) {
             marker = "'unknown aggregat type'";
         }
-        if (StringUtils.isNotBlank(marker)){
+        if (isNotBlank(marker)){
             tags.add(new TaggedText(TagEnum.rank, marker));
         }
     }
 
-
     /**
      * Returns the tag list for a species taxon.
-     * @param nonViralName
-     * @return
      */
-    protected List<TaggedText> getSpeciesTaggedNameCache(INonViralName nonViralName){
+    protected List<TaggedText> getSpeciesTaggedNameCache(INonViralName nonViralName, boolean addAppended){
         List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
-        addAppendedTaggedPhrase(tags, nonViralName);
+        addAppendedTaggedPhrase(tags, nonViralName, addAppended);
         return tags;
     }
 
-    /**
-     * Creates the tag list for an infraspecific taxon. In include is true the result will contain
-     * @param nonViralName
-     * @return
-     */
-    protected List<TaggedText> getInfraSpeciesTaggedNameCache(TaxonName nonViralName){
-        if (nonViralName.getNameType().isZoological()){
-            boolean includeMarker = ! (nonViralName.isAutonym());
-            return getInfraSpeciesTaggedNameCache(nonViralName, includeMarker);
+    protected List<TaggedText> getInfraSpeciesTaggedNameCache(TaxonName name){
+        if (name.getNameType().isZoological()){
+            boolean includeMarker = includeInfraSpecificMarkerForZooNames(name);
+            return getInfraSpeciesTaggedNameCache(name, includeMarker, true);
         }else{
-            return getInfraSpeciesTaggedNameCache(nonViralName, true);
+            return getInfraSpeciesTaggedNameCache(name, true, true);
         }
     }
 
+    protected boolean includeInfraSpecificMarkerForZooNames(TaxonName name){
+        return ! (name.isAutonym());  //only exclude marker if autonym, see also ZooNameNoMarkerCacheStrategy
+    }
+
     /**
      * Creates the tag list for an infraspecific taxon. If include is true the result will contain
      * the infraspecific marker (e.g. "var.")
@@ -771,14 +726,15 @@ public class TaxonNameDefaultCacheStrategy
      * @param includeMarker
      * @return
      */
-    protected List<TaggedText> getInfraSpeciesTaggedNameCache(INonViralName nonViralName, boolean includeMarker){
+    protected List<TaggedText> getInfraSpeciesTaggedNameCache(INonViralName nonViralName,
+            boolean includeMarker, boolean addAppended){
         List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);
         if (includeMarker || nonViralName.isTrinomHybrid()){
             String marker = (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
             if (nonViralName.isTrinomHybrid()){
                 marker = CdmUtils.concat("", NOTHO, marker);
             }
-            if (StringUtils.isNotBlank(marker)){
+            if (isNotBlank(marker)){
                 tags.add(new TaggedText(TagEnum.rank, marker));
             }
 
@@ -787,11 +743,11 @@ public class TaxonNameDefaultCacheStrategy
 
         infrSpecEpi = infrSpecEpi.trim().replace("null", "");
 
-        if (StringUtils.isNotBlank(infrSpecEpi)){
+        if (isNotBlank(infrSpecEpi)){
             tags.add(new TaggedText(TagEnum.name, infrSpecEpi));
         }
 
-        addAppendedTaggedPhrase(tags, nonViralName);
+        addAppendedTaggedPhrase(tags, nonViralName, addAppended);
         return tags;
     }
 
@@ -815,7 +771,7 @@ public class TaxonNameDefaultCacheStrategy
         List<TaggedText> tags = getUninomialTaggedPart(nonViralName);
 
         //InfraGenericEpi
-        boolean hasInfraGenericEpi = StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet());
+        boolean hasInfraGenericEpi = isNotBlank(nonViralName.getInfraGenericEpithet());
         if (hasInfraGenericEpi){
             String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();
             if (nonViralName.isBinomHybrid()){
@@ -832,7 +788,7 @@ public class TaxonNameDefaultCacheStrategy
                 hasInfraGenericEpi && nonViralName.isTrinomHybrid()){
             addHybridPrefix(tags);
         }
-        if (StringUtils.isNotBlank(specEpi)){
+        if (isNotBlank(specEpi)){
             tags.add(new TaggedText(TagEnum.name, specEpi));
         }
         return tags;
@@ -842,15 +798,18 @@ public class TaxonNameDefaultCacheStrategy
      * Adds the tag for the appended phrase if an appended phrase exists
      * @param tags
      * @param nonViralName
+     * @param addAppended
      */
-    protected void addAppendedTaggedPhrase(List<TaggedText> tags, INonViralName nonViralName){
+    protected void addAppendedTaggedPhrase(List<TaggedText> tags, INonViralName nonViralName, boolean addAppended){
+        if (!addAppended){
+            return;
+        }
         String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();
-        if (StringUtils.isNotEmpty(appendedPhrase)){
+        if (isNotBlank(appendedPhrase)){
             tags.add(new TaggedText(TagEnum.name, appendedPhrase));
         }
     }
 
-
        @Override
     public String getLastEpithet(TaxonName taxonName) {
         Rank rank = taxonName.getRank();
@@ -866,41 +825,4 @@ public class TaxonNameDefaultCacheStrategy
     }
 
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    protected List<TaggedText> doGetTaggedTitle(TaxonName taxonName) {
-        List<TaggedText> tags = new ArrayList<>();
-        if (taxonName.getNameType().isViral()){
-            String acronym = taxonName.getAcronym();
-            tags.add(new TaggedText(TagEnum.name, acronym));
-            return tags;
-        }else if (taxonName.isHybridFormula()){
-            //hybrid formula
-            String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;
-            boolean isFirst = true;
-            List<HybridRelationship> rels = taxonName.getOrderedChildRelationships();
-            for (HybridRelationship rel: rels){
-                if (! isFirst){
-                    tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));
-                }
-                isFirst = false;
-                tags.addAll(getTaggedTitle(rel.getParentName()));
-            }
-            return tags;
-        }else if (taxonName.isAutonym()){
-            //Autonym
-            tags.addAll(handleTaggedAutonym(taxonName));
-        }else{ //not Autonym
-             List<TaggedText> nameTags = getTaggedName(taxonName);
-            tags.addAll(nameTags);
-            String authorCache = getAuthorshipCache(taxonName);
-            if (StringUtils.isNotBlank(authorCache)){
-                tags.add(new TaggedText(TagEnum.authors, authorCache));
-            }
-        }
-        return tags;
-    }
-
 }