fixing bugs in test for botanicalNameCacheStrategy and zoologicalNameCacheStrategy
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / strategy / cache / name / NonViralNameDefaultCacheStrategy.java
index f5e6404b31916b58db0d8708eb7b477f22f21611..fb8e23110e31a3d2df654ff92774e0c05b732ba3 100644 (file)
@@ -23,20 +23,26 @@ import eu.etaxonomy.cdm.model.agent.INomenclaturalAuthor;
 import eu.etaxonomy.cdm.model.agent.Team;\r
 import eu.etaxonomy.cdm.model.common.Language;\r
 import eu.etaxonomy.cdm.model.common.Representation;\r
+import eu.etaxonomy.cdm.model.name.BotanicalName;\r
+import eu.etaxonomy.cdm.model.name.HybridRelationship;\r
 import eu.etaxonomy.cdm.model.name.NomenclaturalStatus;\r
 import eu.etaxonomy.cdm.model.name.NomenclaturalStatusType;\r
 import eu.etaxonomy.cdm.model.name.NonViralName;\r
 import eu.etaxonomy.cdm.model.name.Rank;\r
 import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;\r
+import eu.etaxonomy.cdm.strategy.TagEnum;\r
+import eu.etaxonomy.cdm.strategy.TaggedText;\r
 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;\r
+import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImplRegExBase;\r
 \r
 \r
 /**\r
- * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName> interface.\r
- * The method actually implements a cache strategy for botanical names so no method has to be overwritten by\r
+ * This class is a default implementation for the INonViralNameCacheStrategy<T extends NonViralName> \r
+ * interface.<BR>\r
+ * The method implements a cache strategy for botanical names so no method has to be overwritten by\r
  * a subclass for botanic names.\r
- * Where differing from this Default BotanicNameCacheStrategy other subclasses should overwrite the existing methods\r
- * e.g. a CacheStrategy for zoological names should overwrite getAuthorAndExAuthor\r
+ * Where differing from this default botanic name strategy other subclasses should overwrite the\r
+ * existing methods, e.g. a CacheStrategy for zoological names should overwrite getAuthorAndExAuthor\r
  * @author a.mueller\r
  */\r
 /**\r
@@ -69,6 +75,14 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
                return new NonViralNameDefaultCacheStrategy();\r
        }\r
        \r
+       /**\r
+        * Factory method\r
+        * @return NonViralNameDefaultCacheStrategy A new instance of  NonViralNameDefaultCacheStrategy\r
+        */\r
+       public static <T extends NonViralName<?>> NonViralNameDefaultCacheStrategy<T> NewInstance(Class<T> clazz){\r
+               return new NonViralNameDefaultCacheStrategy<T>();\r
+       }\r
+       \r
        /**\r
         * Constructor\r
         */\r
@@ -145,82 +159,153 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
        }\r
 \r
 \r
-       public void setBasionymAuthorCombinationAuthorSeperator(\r
-                       CharSequence basionymAuthorCombinationAuthorSeperator) {\r
+       public void setBasionymAuthorCombinationAuthorSeperator( CharSequence basionymAuthorCombinationAuthorSeperator) {\r
                BasionymAuthorCombinationAuthorSeperator = basionymAuthorCombinationAuthorSeperator;\r
        }\r
 \r
        \r
 //** *****************************************************************************************/\r
-       \r
-       \r
+\r
        /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.strategy.INameCacheStrategy#getNameCache()\r
+        * @see eu.etaxonomy.cdm.strategy.cache.name.NameCacheStrategyBase#getTitleCache(eu.etaxonomy.cdm.model.name.TaxonNameBase)\r
         */\r
        @Override\r
        public String getTitleCache(T nonViralName) {\r
-               if (nonViralName == null){\r
+               List<TaggedText> tags = getTaggedTitle(nonViralName);\r
+               if (tags == null){\r
                        return null;\r
+               }else{\r
+                       String result = createString(tags);\r
+                       return result;\r
                }\r
-               \r
-               if (nonViralName.isProtectedTitleCache()){\r
-                       return nonViralName.getTitleCache();\r
+       }\r
+\r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.strategy.cache.name.NameCacheStrategyBase#getFullTitleCache(eu.etaxonomy.cdm.model.name.TaxonNameBase)\r
+        */\r
+       @Override\r
+       public String getFullTitleCache(T nonViralName) {\r
+               List<TaggedText> tags = getTaggedFullTitle(nonViralName);\r
+               if (tags == null){\r
+                       return null;\r
+               }else{\r
+                       String result = createString(tags);\r
+                       return result;\r
                }\r
-               String result = "";\r
-               //Autonym\r
-               if (nonViralName.isAutonym()){\r
-                       result = handleAutonym(nonViralName);\r
-               }else{ //not Autonym\r
-                       String nameCache = nonViralName.getNameCache();  //OLD: CdmUtils.Nz(getNameCache(nonViralName));\r
-                       if (nameIncludesAuthorship(nonViralName)){\r
-                               String authorCache = CdmUtils.Nz(getAuthorshipCache(nonViralName));\r
-                               result = CdmUtils.concat(NameAuthorSeperator, nameCache, authorCache);\r
-                       }else{\r
-                               result = nameCache;\r
+       }\r
+\r
+       \r
+       /**\r
+        * Generates and returns the "name cache" (only scientific name without author teams and year).\r
+        * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy#getNameCache(eu.etaxonomy.cdm.model.name.TaxonNameBase)\r
+        */\r
+       public String getNameCache(T nonViralName) {\r
+               List<TaggedText> tags = getTaggedName(nonViralName);\r
+               if (tags == null){\r
+                       return null;\r
+               }else{\r
+                       String result = createString(tags);\r
+                       return result;\r
+               }\r
+       }\r
+       \r
+\r
+       /**\r
+        * Creates a string from tagged text.\r
+        * @param tags\r
+        * @return\r
+        */\r
+       protected static String createString(List<TaggedText> tags) {\r
+               StringBuffer result = new StringBuffer();\r
+               \r
+               boolean isSeparator;\r
+               boolean wasSeparator = true;  //true for start tag\r
+               for (TaggedText tag: tags){\r
+                       isSeparator = tag.getType().equals(TagEnum.separator);\r
+                       if (! wasSeparator && ! isSeparator ){\r
+                               result.append(" ");\r
                        }\r
+                       result.append(tag.getText());\r
+                       wasSeparator = isSeparator;\r
                }\r
-               return result;\r
+               return result.toString();\r
        }\r
+       \r
+// ******************* Authorship ******************************/      \r
+       \r
 \r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.strategy.cache.INonViralNameCacheStrategy#getAuthorCache(eu.etaxonomy.cdm.model.name.NonViralName)\r
+        */\r
+       public String getAuthorshipCache(T nonViralName) {\r
+               if (nonViralName == null){\r
+                       return null;\r
+               }\r
+               //cache protected\r
+               if (nonViralName.isProtectedAuthorshipCache() == true) {\r
+                       return nonViralName.getAuthorshipCache();\r
+               }\r
+               return getNonCacheAuthorshipCache(nonViralName);\r
 \r
+       }\r
+       \r
        /**\r
+        * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorshipfield.\r
+        * @throws NullPointerException if nonViralName is null.\r
         * @param nonViralName\r
-        * @param speciesPart\r
         * @return\r
         */\r
-       private String handleAutonym(T nonViralName) {\r
-               String result;\r
-               String speciesPart = getSpeciesNameCache(nonViralName);\r
-               //TODO should this include basionym authors and ex authors\r
-               INomenclaturalAuthor author = nonViralName.getCombinationAuthorTeam();\r
+       protected String getNonCacheAuthorshipCache(T nonViralName){\r
+               String result = "";\r
+               INomenclaturalAuthor combinationAuthor = nonViralName.getCombinationAuthorTeam();\r
+               INomenclaturalAuthor exCombinationAuthor = nonViralName.getExCombinationAuthorTeam();\r
+               INomenclaturalAuthor basionymAuthor = nonViralName.getBasionymAuthorTeam();\r
+               INomenclaturalAuthor exBasionymAuthor = nonViralName.getExBasionymAuthorTeam();\r
+               String basionymPart = "";\r
                String authorPart = "";\r
-               if (author != null){\r
-                       authorPart = CdmUtils.Nz(author.getNomenclaturalTitle());\r
+               //basionym\r
+               if (basionymAuthor != null || exBasionymAuthor != null){\r
+                       basionymPart = BasionymStart + getAuthorAndExAuthor(basionymAuthor, exBasionymAuthor) + BasionymEnd;\r
                }\r
-               INomenclaturalAuthor basAuthor = nonViralName.getBasionymAuthorTeam();\r
-               String basAuthorPart = "";\r
-               if (basAuthor != null){\r
-                       basAuthorPart = CdmUtils.Nz(basAuthor.getNomenclaturalTitle());\r
+               if (combinationAuthor != null || exCombinationAuthor != null){\r
+                       authorPart = getAuthorAndExAuthor(combinationAuthor, exCombinationAuthor);\r
                }\r
-               if (! "".equals(basAuthorPart)){\r
-                       authorPart = "("+ basAuthorPart +")" + authorPart;\r
+               result = CdmUtils.concat(BasionymAuthorCombinationAuthorSeperator, basionymPart, authorPart);\r
+               return result;\r
+       }\r
+       \r
+       /**\r
+        * Returns the AuthorCache part for a combination of an author and an ex author. This applies on combination authors\r
+        * as well as on basionym/orginal combination authors.\r
+        * @param author the author\r
+        * @param exAuthor the ex-author\r
+        * @return\r
+        */\r
+       protected String getAuthorAndExAuthor(INomenclaturalAuthor author, INomenclaturalAuthor exAuthor){\r
+               String result = "";\r
+               String authorString = "";\r
+               String exAuthorString = "";\r
+               if (author != null){\r
+                       authorString = CdmUtils.Nz(author.getNomenclaturalTitle());\r
                }\r
-               String infraSpeciesPart = (CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()));\r
-\r
-               String infraSpeciesSeparator = "";\r
-               if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){\r
-                       //TODO handle exception\r
-                       logger.warn("Rank for autonym does not exist or is not lower than species !!");\r
-               }else{\r
-                       infraSpeciesSeparator = nonViralName.getRank().getAbbreviation();\r
+               if (exAuthor != null){\r
+                       exAuthorString = CdmUtils.Nz(exAuthor.getNomenclaturalTitle());\r
                }\r
-               \r
-               result = CdmUtils.concat(" ", new String[]{speciesPart, authorPart, infraSpeciesSeparator, infraSpeciesPart});\r
-               result = result.trim().replace("null", "");\r
+               if (exAuthorString.length() > 0 ){\r
+                       exAuthorString = exAuthorString + ExAuthorSeperator;\r
+               }\r
+               result = exAuthorString + authorString;\r
                return result;\r
        }\r
        \r
-       protected boolean nameIncludesAuthorship(NonViralName nonViralName){\r
+       \r
+       /**\r
+        * Checks if the given name should include the author in it's cached version.<BR>\r
+        * This is usually the case but not for <i>species aggregates</i>.\r
+        * @param nonViralName\r
+        * @return\r
+        */\r
+       protected boolean nameIncludesAuthorship(NonViralName<?> nonViralName){\r
                Rank rank = nonViralName.getRank();\r
                if (rank != null && rank.isSpeciesAggregate()){\r
                        return false;\r
@@ -229,25 +314,33 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
                }\r
        }\r
        \r
-       \r
+// ************* TAGGED NAME ***************************************/  \r
 \r
-       \r
-       \r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.strategy.cache.name.NameCacheStrategyBase#getTaggedFullTitle(eu.etaxonomy.cdm.model.name.TaxonNameBase)\r
+        */\r
        @Override\r
-       public String getFullTitleCache(T nonViralName) {\r
+       public List<TaggedText> getTaggedFullTitle(T nonViralName) {\r
+               List<TaggedText> tags = new ArrayList<TaggedText>();\r
+               \r
                //null\r
                if (nonViralName == null){\r
                        return null;\r
                }\r
-               //full title cache\r
-               if (nonViralName.isProtectedFullTitleCache() == true) {\r
-                       return nonViralName.getFullTitleCache();\r
+               \r
+               //protected full title cache\r
+               if (nonViralName.isProtectedFullTitleCache()){\r
+                       tags.add(new TaggedText(TagEnum.fullName, nonViralName.getFullTitleCache()));\r
+                       return tags;\r
                }\r
                \r
-               String result = "";\r
                //title cache\r
-               String titleCache = nonViralName.getTitleCache();   // OLD: getTitleCache(nonViralName);\r
+//             String titleCache = nonViralName.getTitleCache();\r
+               List<TaggedText> titleTags = getTaggedTitle(nonViralName);\r
+               tags.addAll(titleTags);\r
                \r
+               \r
+               //reference\r
                String microReference = nonViralName.getNomenclaturalMicroReference();\r
                INomenclaturalReference ref = nonViralName.getNomenclaturalReference();\r
                String referenceBaseCache = null;\r
@@ -256,11 +349,19 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
                        nomenclaturalReference.setCacheStrategy(nomenclaturalReference.getType().getCacheStrategy());\r
                        referenceBaseCache = nomenclaturalReference.getNomenclaturalCitation(microReference);\r
                }\r
+                       //add to tags\r
+               if (StringUtils.isNotBlank(referenceBaseCache)){\r
+                       if (! referenceBaseCache.trim().startsWith("in ")){\r
+                               String refConcat = ", ";\r
+                               tags.add(new TaggedText(TagEnum.separator, refConcat));\r
+                       }\r
+                       tags.add(new TaggedText(TagEnum.reference, referenceBaseCache));\r
+               }\r
                \r
-               //make nomenclatural status\r
-               String ncStatusCache = "";\r
+               //nomenclatural status\r
                Set<NomenclaturalStatus> ncStati = nonViralName.getStatus();\r
                Iterator<NomenclaturalStatus> iterator = ncStati.iterator();\r
+               List<TaggedText> nomStatusTags = new ArrayList<TaggedText>();\r
                while (iterator.hasNext()) {\r
                        NomenclaturalStatus ncStatus = (NomenclaturalStatus)iterator.next();\r
                        // since the NewInstance method of nomencatural status allows null as parameter\r
@@ -280,124 +381,422 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
                        }else if(ncStatus.getRuleConsidered() != null && ! ncStatus.getRuleConsidered().equals("")){\r
                                suffix = ncStatus.getRuleConsidered();\r
                        }\r
-                       ncStatusCache = ", " + suffix;\r
+                       String statusSeparator = ", ";\r
+                       nomStatusTags.add(new TaggedText(TagEnum.separator, statusSeparator));\r
+                       nomStatusTags.add(new TaggedText(TagEnum.nomStatus, suffix));\r
                }\r
-               String refConcat = " ";\r
-               if (referenceBaseCache != null && ! referenceBaseCache.trim().startsWith("in ")){\r
-                       refConcat = ", ";\r
+               tags.addAll(nomStatusTags);\r
+               return tags;\r
+               \r
+       }\r
+               \r
+       /* (non-Javadoc)\r
+        * @see eu.etaxonomy.cdm.strategy.cache.name.NameCacheStrategyBase#getTaggedTitle(eu.etaxonomy.cdm.model.name.TaxonNameBase)\r
+        */\r
+       public List<TaggedText> getTaggedTitle(T nonViralName) {\r
+               if (nonViralName == null){\r
+                       return null;\r
                }\r
-               result = CdmUtils.concat(refConcat, titleCache, referenceBaseCache);\r
-               result = CdmUtils.concat("", result, ncStatusCache);\r
-               return result;\r
+\r
+               List<TaggedText> tags = new ArrayList<TaggedText>();\r
+               \r
+               //TODO how to handle protected fullTitleCache here?\r
+               \r
+               if (nonViralName.isProtectedTitleCache()){\r
+                       //protected title cache\r
+                       tags.add(new TaggedText(TagEnum.name, nonViralName.getTitleCache()));\r
+                       return tags;\r
+               }else if (nonViralName.isHybridFormula()){\r
+                       //hybrid formula\r
+                       String hybridSeparator = NonViralNameParserImplRegExBase.hybridSign;\r
+                       boolean isFirst = true;\r
+                       List<HybridRelationship> rels = nonViralName.getOrderedChildRelationships();\r
+                       for (HybridRelationship rel: rels){\r
+                               if (! isFirst){\r
+                                       tags.add(new TaggedText(TagEnum.hybridSign, hybridSeparator));\r
+                               }\r
+                               isFirst = false;\r
+                               tags.addAll(getTaggedTitle((T)rel.getParentName()));\r
+                       }\r
+                       return tags;\r
+               }else if (nonViralName.isAutonym()){\r
+                       //Autonym\r
+                       tags.addAll(handleTaggedAutonym(nonViralName));\r
+               }else{ //not Autonym\r
+//                     String nameCache = nonViralName.getNameCache();  //OLD: CdmUtils.Nz(getNameCache(nonViralName));\r
+                       List<TaggedText> nameTags = getTaggedName(nonViralName);\r
+                       tags.addAll(nameTags);\r
+                       if (nameIncludesAuthorship(nonViralName)){\r
+                               String authorCache = getAuthorshipCache(nonViralName);\r
+                               if (StringUtils.isNotBlank(authorCache)){\r
+                                       tags.add(new TaggedText(TagEnum.authors, authorCache));\r
+                               }\r
+                       }\r
+               }\r
+               return tags;\r
+\r
        }\r
        \r
        \r
        /**\r
-        * Generates and returns the "name cache" (only scientific name without author teams and year).\r
-        * @see eu.etaxonomy.cdm.strategy.cache.name.INameCacheStrategy#getNameCache(eu.etaxonomy.cdm.model.name.TaxonNameBase)\r
+        * Returns the tag list of the name part (without author and reference).\r
+        * @param nonViralName\r
+        * @return\r
         */\r
-       public String getNameCache(T nonViralName) {\r
+       public List<TaggedText> getTaggedName(T nonViralName) {\r
                if (nonViralName == null){\r
                        return null;\r
                }\r
-               String result;\r
+               List<TaggedText> tags = new ArrayList<TaggedText>();\r
                Rank rank = nonViralName.getRank();\r
                \r
                if (nonViralName.isProtectedNameCache()){\r
-                       result = nonViralName.getNameCache();\r
+                       tags.add(new TaggedText(TagEnum.name, nonViralName.getNameCache()));\r
                }else if (rank == null){\r
-                       result = getRanklessNameCache(nonViralName);\r
+                       tags = getRanklessTaggedNameCache(nonViralName);\r
+//             }else if (nonViralName.isInfragenericUnranked()){\r
+//                     result = getUnrankedInfragenericNameCache(nonViralName);\r
                }else if (rank.isInfraSpecific()){\r
-                       result = getInfraSpeciesNameCache(nonViralName);\r
+                       tags = getInfraSpeciesTaggedNameCache(nonViralName);\r
                }else if (rank.isSpecies()){\r
-                       result = getSpeciesNameCache(nonViralName);\r
+                       tags = getSpeciesTaggedNameCache(nonViralName);\r
                }else if (rank.isInfraGeneric()){\r
-                       result = getInfraGenusNameCache(nonViralName);\r
+                       tags = getInfraGenusTaggedNameCache(nonViralName);\r
                }else if (rank.isGenus()){\r
-                       result = getGenusOrUninomialNameCache(nonViralName);\r
+                       tags = getGenusOrUninomialTaggedNameCache(nonViralName);\r
                }else if (rank.isSupraGeneric()){\r
-                       result = getGenusOrUninomialNameCache(nonViralName);\r
+                       tags = getGenusOrUninomialTaggedNameCache(nonViralName);\r
                }else{ \r
                        logger.warn("Name Strategy for Name (UUID: " + nonViralName.getUuid() +  ") not yet implemented");\r
-                       result = "";\r
                }\r
-               return result;\r
+               //TODO handle appended phrase here instead of different places, check first if this is true for all\r
+               //cases\r
+               \r
+               return tags;\r
+\r
        }\r
+\r
+\r
        \r
 \r
-       /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.strategy.cache.INonViralNameCacheStrategy#getAuthorCache(eu.etaxonomy.cdm.model.name.NonViralName)\r
+//***************************** PRIVATES ***************************************/\r
+       \r
+\r
+       /**\r
+        * Returns the tag list for an autonym taxon.\r
+        * \r
+        * @see NonViralName#isAutonym()\r
+        * @see BotanicalName#isAutonym()\r
+        * @param nonViralName\r
+        * @return\r
         */\r
-       public String getAuthorshipCache(T nonViralName) {\r
-               if (nonViralName == null){\r
-                       return null;\r
+       private List<TaggedText> handleTaggedAutonym(T nonViralName) {\r
+               \r
+               //species part\r
+               List<TaggedText> tags = getSpeciesTaggedNameCache(nonViralName);\r
+               \r
+               //author\r
+               //TODO should this include basionym authors and ex authors\r
+               INomenclaturalAuthor author = nonViralName.getCombinationAuthorTeam();\r
+               String authorPart = "";\r
+               if (author != null){\r
+                       authorPart = CdmUtils.Nz(author.getNomenclaturalTitle());\r
                }\r
-               //cache protected\r
-               if (nonViralName.isProtectedAuthorshipCache() == true) {\r
-                       return nonViralName.getAuthorshipCache();\r
+               INomenclaturalAuthor basAuthor = nonViralName.getBasionymAuthorTeam();\r
+               String basAuthorPart = "";\r
+               if (basAuthor != null){\r
+                       basAuthorPart = CdmUtils.Nz(basAuthor.getNomenclaturalTitle());\r
                }\r
-               return getNonCacheAuthorshipCache(nonViralName);\r
+               if (! "".equals(basAuthorPart)){\r
+                       authorPart = "("+ basAuthorPart +") " + authorPart;\r
+               }\r
+               if (StringUtils.isNotBlank(authorPart)){\r
+                       tags.add(new TaggedText(TagEnum.authors, authorPart));\r
+               }\r
+               \r
+               \r
+               //infra species marker\r
+               if (nonViralName.getRank() == null || !nonViralName.getRank().isInfraSpecific()){\r
+                       //TODO handle exception\r
+                       logger.warn("Rank for autonym does not exist or is not lower than species !!");\r
+               }else{\r
+                       String infraSpeciesMarker = nonViralName.getRank().getAbbreviation();\r
+                       if (StringUtils.isNotBlank(infraSpeciesMarker)){\r
+                               tags.add(new TaggedText(TagEnum.rank, infraSpeciesMarker));\r
+                       }\r
+               }\r
+               \r
+               //infra species\r
+               String infraSpeciesPart = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim().replace("null", "");\r
+               if (StringUtils.isNotBlank(infraSpeciesPart)){\r
+                       tags.add(new TaggedText(TagEnum.name, infraSpeciesPart));\r
+               }\r
+               \r
+               return tags;\r
+       }\r
+       \r
+       \r
+       \r
+       /**\r
+        * Returns the tag list for rankless taxa.\r
+        * @param nonViralName\r
+        * @return\r
+        */\r
+       protected List<TaggedText> getRanklessTaggedNameCache(NonViralName<?> nonViralName){\r
+               List<TaggedText> tags = getUninomialTaggedPart(nonViralName);\r
+               String speciesEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim().replace("null", "");\r
+               if (StringUtils.isNotBlank(speciesEpi)){\r
+                       tags.add(new TaggedText(TagEnum.name, speciesEpi));\r
+               }\r
+               \r
+               String infraSpeciesEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet()).trim().replace("null", "");\r
+               if (StringUtils.isNotBlank(infraSpeciesEpi)){\r
+                       tags.add(new TaggedText(TagEnum.name, infraSpeciesEpi));\r
+               }\r
+               \r
+               //result += " (rankless)";\r
+               addAppendedTaggedPhrase(tags, nonViralName);\r
+               return tags;                    \r
+       }\r
 \r
+       /**\r
+        * Returns the tag list for the first epithet (including a hybrid sign if required).\r
+        * @param nonViralName\r
+        * @return\r
+        */\r
+       private List<TaggedText> getUninomialTaggedPart(NonViralName<?> nonViralName) {\r
+               List<TaggedText> tags = new ArrayList<TaggedText>();\r
+               \r
+               if (nonViralName.isMonomHybrid()){\r
+                       addHybridPrefix(tags);\r
+               }\r
+               \r
+               String uninomial = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim().replace("null", "");\r
+               if (StringUtils.isNotBlank(uninomial)){\r
+                       tags.add(new TaggedText(TagEnum.name, uninomial));\r
+               }\r
+                       \r
+               return tags;\r
        }\r
        \r
        /**\r
-        * Returns the authorshipcache string for the atomized authorship fields. Does not use the authorshipfield.\r
-        * @throws NullPointerException if nonViralName is null.\r
+        * Returns the tag list for an genus or higher taxon.\r
+        * \r
         * @param nonViralName\r
         * @return\r
         */\r
-       protected String getNonCacheAuthorshipCache(T nonViralName){\r
-               String result = "";\r
-               INomenclaturalAuthor combinationAuthor = nonViralName.getCombinationAuthorTeam();\r
-               INomenclaturalAuthor exCombinationAuthor = nonViralName.getExCombinationAuthorTeam();\r
-               INomenclaturalAuthor basionymAuthor = nonViralName.getBasionymAuthorTeam();\r
-               INomenclaturalAuthor exBasionymAuthor = nonViralName.getExBasionymAuthorTeam();\r
-               String basionymPart = "";\r
-               String authorPart = "";\r
-               //basionym\r
-               if (basionymAuthor != null || exBasionymAuthor != null){\r
-                       basionymPart = BasionymStart + getAuthorAndExAuthor(basionymAuthor, exBasionymAuthor) + BasionymEnd;\r
+       protected List<TaggedText> getGenusOrUninomialTaggedNameCache(NonViralName<?> nonViralName){\r
+               List<TaggedText> tags = getUninomialTaggedPart(nonViralName);\r
+               addAppendedTaggedPhrase(tags, nonViralName);\r
+               return tags;\r
+       }\r
+       \r
+       /**\r
+        * Returns the tag list for an infrageneric taxon (including species aggregates).\r
+        * \r
+        * @see #getSpeciesAggregateTaggedCache(NonViralName)\r
+        * @param nonViralName\r
+        * @return\r
+        */\r
+       protected List<TaggedText> getInfraGenusTaggedNameCache(NonViralName<?> nonViralName){\r
+               Rank rank = nonViralName.getRank();\r
+               if (rank.isSpeciesAggregate()){\r
+                       return getSpeciesAggregateTaggedCache(nonViralName);\r
                }\r
-               if (combinationAuthor != null || exCombinationAuthor != null){\r
-                       authorPart = getAuthorAndExAuthor(combinationAuthor, exCombinationAuthor);\r
+               \r
+               //genus\r
+               List<TaggedText> tags = getUninomialTaggedPart(nonViralName);\r
+               \r
+               //marker\r
+               String infraGenericMarker = "'unhandled infrageneric rank'";\r
+               if (rank != null){\r
+                       try {\r
+                               infraGenericMarker = rank.getInfraGenericMarker();\r
+                       } catch (UnknownCdmTypeException e) {\r
+                               infraGenericMarker = "'unhandled infrageneric rank'";\r
+                       }\r
                }\r
-               result = CdmUtils.concat(BasionymAuthorCombinationAuthorSeperator, basionymPart, authorPart);\r
-               return result;\r
+               tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));\r
+               \r
+               \r
+               String infraGenEpi = CdmUtils.Nz(nonViralName.getInfraGenericEpithet()).trim().replace("null", "");\r
+               if (StringUtils.isNotBlank(infraGenEpi)){\r
+                       tags.add(new TaggedText(TagEnum.name, infraGenEpi));\r
+               }\r
+               \r
+               addAppendedTaggedPhrase(tags, nonViralName);\r
+               return tags;\r
        }\r
        \r
        /**\r
-        * Returns the AuthorCache part for a combination of an author and an ex author. This applies on combination authors\r
-        * as well as on basionym/orginal combination authors.\r
-        * @param author the author\r
-        * @param exAuthor the ex-author\r
+        * Returns the tag list for a species aggregate (or similar) taxon.<BR>\r
+        * Possible ranks for a <i>species aggregate</i> are "aggr.", "species group", ...      \r
+        * @param nonViralName\r
         * @return\r
         */\r
-       protected String getAuthorAndExAuthor(INomenclaturalAuthor author, INomenclaturalAuthor exAuthor){\r
-               String result = "";\r
-               String authorString = "";\r
-               String exAuthorString = "";\r
-               if (author != null){\r
-                       authorString = CdmUtils.Nz(author.getNomenclaturalTitle());\r
+       protected List<TaggedText> getSpeciesAggregateTaggedCache(NonViralName<?> nonViralName){\r
+               List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);\r
+               \r
+               addSpeciesAggregateTaggedEpithet(tags, nonViralName);\r
+               addAppendedTaggedPhrase(tags, nonViralName);\r
+               return tags;\r
+       }\r
+       \r
+       /**\r
+        * Adds the aggregate tag to the tag list.\r
+        * @param tags\r
+        * @param nonViralName\r
+        */\r
+       private void addSpeciesAggregateTaggedEpithet(List<TaggedText> tags, NonViralName<?> nonViralName) {\r
+               String marker;\r
+               try {\r
+                       marker = nonViralName.getRank().getInfraGenericMarker();\r
+               } catch (UnknownCdmTypeException e) {\r
+                       marker = "'unknown aggregat type'";\r
                }\r
-               if (exAuthor != null){\r
-                       exAuthorString = CdmUtils.Nz(exAuthor.getNomenclaturalTitle());\r
+               if (StringUtils.isNotBlank(marker)){\r
+                       tags.add(new TaggedText(TagEnum.rank, marker));\r
                }\r
-               if (exAuthorString.length() > 0 ){\r
-                       exAuthorString = exAuthorString + ExAuthorSeperator;\r
+       }\r
+\r
+       \r
+       /**\r
+        * Returns the tag list for a species taxon.\r
+        * @param nonViralName\r
+        * @return\r
+        */\r
+       protected List<TaggedText> getSpeciesTaggedNameCache(NonViralName<?> nonViralName){\r
+               List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);\r
+               addAppendedTaggedPhrase(tags, nonViralName);\r
+               return tags;\r
+       }\r
+\r
+       /**\r
+        * Creates the tag list for an infraspecific taxon. In include is true the result will contain\r
+        * @param nonViralName\r
+        * @return\r
+        */\r
+       protected List<TaggedText> getInfraSpeciesTaggedNameCache(NonViralName<?> nonViralName){\r
+               return getInfraSpeciesTaggedNameCache(nonViralName, true);\r
+       }\r
+       \r
+       /**\r
+        * Creates the tag list for an infraspecific taxon. In include is true the result will contain\r
+        * the infraspecific marker (e.g. "var.")\r
+        * @param nonViralName\r
+        * @param includeMarker\r
+        * @return\r
+        */\r
+       protected List<TaggedText> getInfraSpeciesTaggedNameCache(NonViralName<?> nonViralName, boolean includeMarker){\r
+               List<TaggedText> tags = getGenusAndSpeciesTaggedPart(nonViralName);\r
+               if (includeMarker){ \r
+                       String marker = (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");\r
+                       if (StringUtils.isNotBlank(marker)){\r
+                               tags.add(new TaggedText(TagEnum.rank, marker));\r
+                       }\r
+               }\r
+               String infrSpecEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());\r
+               if (nonViralName.isTrinomHybrid()){\r
+                       addHybridPrefix(tags);\r
+               }\r
+               \r
+               infrSpecEpi = infrSpecEpi.trim().replace("null", "");\r
+               if (StringUtils.isNotBlank(infrSpecEpi)){\r
+                       tags.add(new TaggedText(TagEnum.name, infrSpecEpi));\r
+               }\r
+               \r
+               addAppendedTaggedPhrase(tags, nonViralName);\r
+               return tags;\r
+       }\r
+\r
+\r
+       /**\r
+        * Adds a tag for the hybrid sign and an empty separator to avoid trailing whitespaces.\r
+        * @param tags\r
+        */\r
+       private void addHybridPrefix(List<TaggedText> tags) {\r
+               tags.add(new TaggedText(TagEnum.hybridSign, NonViralNameParserImplRegExBase.hybridSign));\r
+               tags.add(new TaggedText(TagEnum.separator, "")); //no whitespace separator\r
+       }\r
+\r
+       /**\r
+        * Creates the tag list for the genus and species part.\r
+        * @param nonViralName\r
+        * @return\r
+        */\r
+       private List<TaggedText> getGenusAndSpeciesTaggedPart(NonViralName<?> nonViralName) {\r
+               //Uninomial\r
+               List<TaggedText> tags = getUninomialTaggedPart(nonViralName);\r
+               \r
+               //InfraGenericEpi\r
+               boolean hasInfraGenericEpi = StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet());\r
+               if (hasInfraGenericEpi){\r
+                       String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();\r
+                       if (nonViralName.isBinomHybrid()){\r
+//                                     addHybridPrefix(tags);  FIXME hybridSign should be tag, but then we need to handle "(" ")" differently.\r
+                               infrGenEpi = NonViralNameParserImplRegExBase.hybridSign + infrGenEpi;\r
+                       }\r
+                       infrGenEpi = "(" + infrGenEpi + ")";\r
+                       tags.add(new TaggedText(TagEnum.name, infrGenEpi));\r
+               }\r
+\r
+               //Species Epi\r
+               String specEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim().replace("null", "");\r
+               if (! hasInfraGenericEpi && nonViralName.isBinomHybrid() || \r
+                               hasInfraGenericEpi && nonViralName.isTrinomHybrid()){\r
+                       addHybridPrefix(tags); \r
+               }\r
+               if (StringUtils.isNotBlank(specEpi)){\r
+                       tags.add(new TaggedText(TagEnum.name, specEpi));\r
+               }\r
+               return tags;\r
+       }\r
+       \r
+       /**\r
+        * Adds the tag for the appended phrase if an appended phrase exists\r
+        * @param tags\r
+        * @param nonViralName\r
+        */\r
+       protected void addAppendedTaggedPhrase(List<TaggedText> tags, NonViralName<?> nonViralName){\r
+               String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();\r
+               if (StringUtils.isNotEmpty(appendedPhrase)){\r
+                       tags.add(new TaggedText(TagEnum.name, appendedPhrase));\r
+               }\r
+       }\r
+\r
+       public String getLastEpithet(T taxonNameBase) {\r
+               Rank rank = taxonNameBase.getRank();\r
+               if(rank.isGenus() || rank.isSupraGeneric()) {\r
+                       return taxonNameBase.getGenusOrUninomial();\r
+               } else if(rank.isInfraGeneric()) {\r
+                       return taxonNameBase.getInfraGenericEpithet();\r
+               } else if(rank.isSpecies()) {\r
+                       return taxonNameBase.getSpecificEpithet();\r
+               } else {\r
+                       return taxonNameBase.getInfraSpecificEpithet();\r
                }\r
-               result = exAuthorString + authorString;\r
-               return result;\r
\r
        }\r
        \r
+// *************************** DEPRECATED ***************************************************/\r
        \r
+\r
+       //Old: may be replaced once getTagged(Full)Title is fully tested\r
        /* (non-Javadoc)\r
         * @see eu.etaxonomy.cdm.strategy.INameCacheStrategy#getTaggedName(eu.etaxonomy.cdm.model.common.CdmBase)\r
         */\r
        @Override\r
-       public List<Object> getTaggedName(T nonViralName) {\r
+       @Deprecated\r
+       public List<Object> getTaggedNameDeprecated(T nonViralName) {\r
                List<Object> tags = new ArrayList<Object>();\r
                \r
+               if (nonViralName.isProtectedNameCache() ||\r
+                               nonViralName.isProtectedAuthorshipCache() ||\r
+                               nonViralName.isProtectedFullTitleCache() ||\r
+                               nonViralName.isProtectedTitleCache()){\r
+                       tags.add(nonViralName.getTitleCache());\r
+                       return tags;\r
+               }\r
+               \r
                // Why does it make sense to add the nameCache in case of non existing genusOrUninomial?\r
 //             if (nonViralName.getGenusOrUninomial() == null){\r
 //                     tags.add(nonViralName.getNameCache());\r
@@ -421,6 +820,7 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
                        // --- strategy 1 --- \r
                                        \r
                        if (nonViralName.getRank().isSpeciesAggregate()){\r
+                               tags.add(nonViralName.getSpecificEpithet());\r
                                tags.add(getSpeciesAggregateEpithet(nonViralName));\r
                        }else{\r
                                tags.add(nonViralName.getRank());       \r
@@ -434,7 +834,7 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
                authorTeam.setTitleCache(nonViralName.getAuthorshipCache(), true);\r
                tags.add(authorTeam);\r
                \r
-               // Name is an autonym. Rank and infraspecific eitheton follow the author\r
+               // Name is an autonym. Rank and infraspecific epitheton follow the author\r
                if (nonViralName.isInfraSpecific() && nonViralName.getSpecificEpithet().equals(nonViralName.getInfraSpecificEpithet())){\r
                        tags.add(nonViralName.getRank());                       \r
                        tags.add(nonViralName.getInfraSpecificEpithet());                       \r
@@ -447,125 +847,21 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
                return tags;\r
        }\r
        \r
-\r
-//***************************** PRIVATES ***************************************/\r
-               \r
-               protected String getRanklessNameCache(NonViralName nonViralName){\r
-                       String result = "";\r
-                       result = (result + (CdmUtils.Nz(nonViralName.getGenusOrUninomial()))).trim().replace("null", "");\r
-                       result += " " + (CdmUtils.Nz(nonViralName.getSpecificEpithet())).trim();\r
-                       result += " " + (CdmUtils.Nz(nonViralName.getInfraSpecificEpithet())).trim();\r
-                       result = result.trim().replace("null", "");\r
-                       //result += " (rankless)";\r
-                       result = addAppendedPhrase(result, nonViralName);\r
-                       return result;                  \r
-               }\r
-       \r
        \r
-               protected String getGenusOrUninomialNameCache(NonViralName nonViralName){\r
-                       String result;\r
-                       result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();\r
-                       result = addAppendedPhrase(result, nonViralName).trim();\r
-                       return result;\r
-               }\r
-               \r
-               protected String getInfraGenusNameCache(NonViralName nonViralName){\r
-                       String result;\r
-                       Rank rank = nonViralName.getRank();\r
-                       if (rank.isSpeciesAggregate()){\r
-                               return getSpeciesAggregateCache(nonViralName);\r
-                       }\r
-                       String infraGenericMarker = "'unhandled infrageneric rank'";\r
-                       if (rank != null){\r
-                               try {\r
-                                       infraGenericMarker = rank.getInfraGenericMarker();\r
-                               } catch (UnknownCdmTypeException e) {\r
-                                       infraGenericMarker = "'unhandled infrageneric rank'";\r
-                               }\r
-                       }\r
-                       result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();\r
-                       result += " " + infraGenericMarker + " " + (CdmUtils.Nz(nonViralName.getInfraGenericEpithet())).trim().replace("null", "");\r
-                       result = addAppendedPhrase(result, nonViralName).trim();\r
-                       return result;\r
-               }\r
-\r
-//             aggr.|agg.|group\r
-               protected String getSpeciesAggregateCache(NonViralName nonViralName){\r
-                       String result = getGenusAndSpeciesPart(nonViralName);\r
-                       \r
-                       result += " " + getSpeciesAggregateEpithet(nonViralName);\r
-                       result = addAppendedPhrase(result, nonViralName).trim();\r
-                       return result;\r
-               }\r
-               \r
-               private String getSpeciesAggregateEpithet(NonViralName nonViralName) {\r
-                       String marker;\r
-                       try {\r
-                               marker = nonViralName.getRank().getInfraGenericMarker();\r
-                       } catch (UnknownCdmTypeException e) {\r
-                               marker = "'unknown aggregat type'";\r
-                       }\r
-                       return marker;\r
-               }\r
-               \r
-               protected String getSpeciesNameCache(NonViralName nonViralName){\r
-                       String result = getGenusAndSpeciesPart(nonViralName);\r
-                       result = addAppendedPhrase(result, nonViralName).trim();\r
-                       result = result.replace("\\s\\", " ");\r
-                       return result;\r
-               }\r
-               \r
-               \r
-               protected String getInfraSpeciesNameCache(NonViralName nonViralName){\r
-                       return getInfraSpeciesNameCache(nonViralName, true);\r
-               }\r
-               \r
-               protected String getInfraSpeciesNameCache(NonViralName nonViralName, boolean includeMarker){\r
-                       String result = getGenusAndSpeciesPart(nonViralName);\r
-                       if (includeMarker){ \r
-                               result += " " + (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");\r
-                       }\r
-                       result += " " + (CdmUtils.Nz(nonViralName.getInfraSpecificEpithet())).trim().replace("null", "");\r
-                       result = addAppendedPhrase(result, nonViralName).trim();\r
-                       return result;\r
-               }\r
-\r
-\r
-               private String getGenusAndSpeciesPart(NonViralName nonViralName) {\r
-                       String result;\r
-                       result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();\r
-                       if (StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet()) ){\r
-                               result += " (" + nonViralName.getInfraGenericEpithet().trim() + ")";\r
-                       }\r
-                       result += " " + (CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim()).replace("null", "");\r
-                       return result;\r
-               }\r
-\r
-               \r
-               protected String addAppendedPhrase(String resultString, NonViralName nonViralName){\r
-                       String appendedPhrase = nonViralName ==null ? null : nonViralName.getAppendedPhrase();\r
-                       if (resultString == null){\r
-                               return appendedPhrase;\r
-                       }else if(appendedPhrase == null || "".equals(appendedPhrase.trim())) {\r
-                               return resultString;\r
-                       }else if ("".equals(resultString)){\r
-                               return resultString + appendedPhrase;\r
-                       }else {\r
-                               return resultString + " " + appendedPhrase;\r
-                       }\r
-               }\r
-\r
+       /**\r
+        * @deprecated use only for {@link #getTaggedNameDeprecated(NonViralName)}. Delete when the later \r
+        * is deleted.\r
+        */\r
+       @Deprecated \r
+       private String getSpeciesAggregateEpithet(NonViralName<?> nonViralName) {\r
+               String marker;\r
+               try {\r
+                       marker = nonViralName.getRank().getInfraGenericMarker();\r
+               } catch (UnknownCdmTypeException e) {\r
+                       marker = "'unknown aggregat type'";\r
+               }\r
+               return marker;\r
+       }\r
 \r
-               public String getLastEpithet(T taxonNameBase) {\r
-                       Rank rank = taxonNameBase.getRank();\r
-                       if(rank.isGenus() || rank.isSupraGeneric()) {\r
-                               return taxonNameBase.getGenusOrUninomial();\r
-                       } else if(rank.isInfraGeneric()) {\r
-                               return taxonNameBase.getInfraGenericEpithet();\r
-                       } else if(rank.isSpecies()) {\r
-                               return taxonNameBase.getSpecificEpithet();\r
-                       } else {\r
-                               return taxonNameBase.getInfraSpecificEpithet();\r
-                       }\r
-               }\r
+       \r
 }\r