fixing bugs in test for botanicalNameCacheStrategy and zoologicalNameCacheStrategy
[cdmlib.git] / cdmlib-model / src / main / java / eu / etaxonomy / cdm / strategy / cache / name / NonViralNameDefaultCacheStrategy.java
index be3ba62506de53ae6c8bb35347a9b0215bd40c3e..fb8e23110e31a3d2df654ff92774e0c05b732ba3 100644 (file)
@@ -14,6 +14,7 @@ import java.util.List;
 import java.util.Set;\r
 import java.util.UUID;\r
 \r
+import org.apache.commons.lang.StringUtils;\r
 import org.apache.log4j.Logger;\r
 \r
 import eu.etaxonomy.cdm.common.CdmUtils;\r
@@ -22,22 +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.model.reference.ReferenceBase;\r
-import eu.etaxonomy.cdm.strategy.cache.reference.ReferenceBaseDefaultCacheStrategy;\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
@@ -70,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
@@ -146,156 +159,80 @@ 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
-               }\r
-               \r
-               if (nonViralName.isProtectedTitleCache()){\r
-                       return nonViralName.getTitleCache();\r
-               }\r
-               String result = "";\r
-               //Autonym\r
-               if (isAutonym(nonViralName)){\r
-                       String speciesPart = getSpeciesNameCache(nonViralName);\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
-                       INomenclaturalAuthor basAuthor = nonViralName.getBasionymAuthorTeam();\r
-                       String basAuthorPart = "";\r
-                       if (basAuthor != null){\r
-                               basAuthorPart = CdmUtils.Nz(basAuthor.getNomenclaturalTitle());\r
-                       }\r
-                       if (! "".equals(basAuthorPart)){\r
-                               authorPart = "("+ basAuthorPart +")" + authorPart;\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
-                       }\r
-                       \r
-                       result = CdmUtils.concat(" ", new String[]{speciesPart, authorPart, infraSpeciesSeparator, infraSpeciesPart});\r
-                       result = result.trim().replace("null", "");\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
-               return result;\r
-       }\r
-       \r
-       protected boolean nameIncludesAuthorship(NonViralName nonViralName){\r
-               Rank rank = nonViralName.getRank();\r
-               if (rank != null && rank.isSpeciesAggregate()){\r
-                       return false;\r
                }else{\r
-                       return true;\r
+                       String result = createString(tags);\r
+                       return result;\r
                }\r
        }\r
-       \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
-               //null\r
-               if (nonViralName == null){\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
-               //full title cache\r
-               if (nonViralName.isProtectedFullTitleCache() == true) {\r
-                       return nonViralName.getFullTitleCache();\r
-               }\r
-               \r
-               String result = "";\r
-               //title cache\r
-               String titleCache = nonViralName.getTitleCache();   // OLD: getTitleCache(nonViralName);\r
-               \r
-               String microReference = nonViralName.getNomenclaturalMicroReference();\r
-               INomenclaturalReference ref = nonViralName.getNomenclaturalReference();\r
-               String referenceBaseCache = null;\r
-               if (ref != null){\r
-                       INomenclaturalReference nomenclaturalReference = HibernateProxyHelper.deproxy(ref, INomenclaturalReference.class);\r
-                       nomenclaturalReference.setCacheStrategy(nomenclaturalReference.getType().getCacheStrategy());\r
-                       referenceBaseCache = nomenclaturalReference.getNomenclaturalCitation(microReference);\r
-               }\r
-               \r
-               //make nomenclatural status\r
-               String ncStatusCache = "";\r
-               Set<NomenclaturalStatus> ncStati = nonViralName.getStatus();\r
-               Iterator<NomenclaturalStatus> iterator = ncStati.iterator();\r
-               while (iterator.hasNext()) {\r
-                       NomenclaturalStatus ncStatus = (NomenclaturalStatus)iterator.next();\r
-                       NomenclaturalStatusType statusType =  ncStatus.getType();\r
-                       Language lang = Language.LATIN();\r
-                       Representation repr = statusType.getRepresentation(lang);\r
-                       ncStatusCache = ", " + repr.getAbbreviatedLabel();\r
-               }\r
-               String refConcat = " ";\r
-               if (referenceBaseCache != null && ! referenceBaseCache.trim().startsWith("in ")){\r
-                       refConcat = ", ";\r
-               }\r
-               result = CdmUtils.concat(refConcat, titleCache, referenceBaseCache);\r
-               result = CdmUtils.concat("", result, ncStatusCache);\r
-               return result;\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
         */\r
        public String getNameCache(T nonViralName) {\r
-               if (nonViralName == null){\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
-               String result;\r
-               Rank rank = nonViralName.getRank();\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
-               if (nonViralName.isProtectedNameCache()){\r
-                       result = nonViralName.getNameCache();\r
-               }else if (rank == null){\r
-                       result = getRanklessNameCache(nonViralName);\r
-               }else if (rank.isInfraSpecific()){\r
-                       result = getInfraSpeciesNameCache(nonViralName);\r
-               }else if (rank.isSpecies()){\r
-                       result = getSpeciesNameCache(nonViralName);\r
-               }else if (rank.isInfraGeneric()){\r
-                       result = getInfraGenusNameCache(nonViralName);\r
-               }else if (rank.isGenus()){\r
-                       result = getGenusOrUninomialNameCache(nonViralName);\r
-               }else if (rank.isSupraGeneric()){\r
-                       result = getGenusOrUninomialNameCache(nonViralName);\r
-               }else{ \r
-                       logger.warn("Name Strategy for Name (UUID: " + nonViralName.getUuid() +  ") not yet implemented");\r
-                       result = "";\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
@@ -359,176 +296,572 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
                }\r
                result = exAuthorString + authorString;\r
                return result;\r
\r
        }\r
        \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
+               }else{\r
+                       return true;\r
+               }\r
+       }\r
+       \r
+// ************* TAGGED NAME ***************************************/  \r
+\r
        /* (non-Javadoc)\r
-        * @see eu.etaxonomy.cdm.strategy.INameCacheStrategy#getTaggedName(eu.etaxonomy.cdm.model.common.CdmBase)\r
+        * @see eu.etaxonomy.cdm.strategy.cache.name.NameCacheStrategyBase#getTaggedFullTitle(eu.etaxonomy.cdm.model.name.TaxonNameBase)\r
         */\r
        @Override\r
-       public List<Object> getTaggedName(T nonViralName) {\r
-               List<Object> tags = new ArrayList<Object>();\r
-               tags.add(nonViralName.getGenusOrUninomial());\r
-               if (nonViralName.isSpecies() || nonViralName.isInfraSpecific()){\r
-                       tags.add(nonViralName.getSpecificEpithet());                    \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
                \r
-               // No autonym \r
-               if (nonViralName.isInfraSpecific() && ! nonViralName.getSpecificEpithet().equals(nonViralName.getInfraSpecificEpithet())){\r
-                       tags.add(nonViralName.getRank());                       \r
-                       tags.add(nonViralName.getInfraSpecificEpithet());                       \r
+               //protected full title cache\r
+               if (nonViralName.isProtectedFullTitleCache()){\r
+                       tags.add(new TaggedText(TagEnum.fullName, nonViralName.getFullTitleCache()));\r
+                       return tags;\r
                }\r
                \r
-               if (nonViralName.isInfraGeneric()){\r
-                       //TODO choose right strategy or generic approach?\r
-                       // --- strategy 1 --- \r
-                       tags.add(nonViralName.getRank());                       \r
-                       tags.add(nonViralName.getInfraGenericEpithet());                        \r
-                       // --- strategy 2 --- \r
-//                     tags.add('('+nvn.getInfraGenericEpithet()+')'); \r
+               //title cache\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
+               if (ref != null){\r
+                       INomenclaturalReference nomenclaturalReference = HibernateProxyHelper.deproxy(ref, INomenclaturalReference.class);\r
+                       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
-               Team authorTeam = Team.NewInstance();\r
-               authorTeam.setProtectedTitleCache(true);\r
-               authorTeam.setTitleCache(nonViralName.getAuthorshipCache());\r
-               tags.add(authorTeam);\r
                \r
-               // Name is an autonym. Rank and infraspecific eitheton follow the author\r
-               if (nonViralName.isInfraSpecific() && nonViralName.getSpecificEpithet().equals(nonViralName.getInfraSpecificEpithet())){\r
-                       tags.add(nonViralName.getRank());                       \r
-                       tags.add(nonViralName.getInfraSpecificEpithet());                       \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
+                       // we have to check for null values here\r
+                       String suffix = "not defined";\r
+                       if(ncStatus.getType() != null){\r
+                               NomenclaturalStatusType statusType =  ncStatus.getType();\r
+                               Language lang = Language.LATIN();\r
+                               Representation repr = statusType.getRepresentation(lang);\r
+                               if (repr != null){\r
+                                       suffix = repr.getAbbreviatedLabel();\r
+                               }else{\r
+                                       String message = "No latin representation available for nom. status. " + statusType.getTitleCache();\r
+                                       logger.warn(message);\r
+                                       throw new IllegalStateException(message);\r
+                               }\r
+                       }else if(ncStatus.getRuleConsidered() != null && ! ncStatus.getRuleConsidered().equals("")){\r
+                               suffix = ncStatus.getRuleConsidered();\r
+                       }\r
+                       String statusSeparator = ", ";\r
+                       nomStatusTags.add(new TaggedText(TagEnum.separator, statusSeparator));\r
+                       nomStatusTags.add(new TaggedText(TagEnum.nomStatus, suffix));\r
                }\r
+               tags.addAll(nomStatusTags);\r
+               return tags;\r
                \r
-               if(! "".equals(nonViralName.getAppendedPhrase())){\r
-                       tags.add(nonViralName.getAppendedPhrase());\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
+\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
-       /************** PRIVATES ****************/\r
+       \r
+       /**\r
+        * Returns the tag list of the name part (without author and reference).\r
+        * @param nonViralName\r
+        * @return\r
+        */\r
+       public List<TaggedText> getTaggedName(T nonViralName) {\r
+               if (nonViralName == null){\r
+                       return null;\r
+               }\r
+               List<TaggedText> tags = new ArrayList<TaggedText>();\r
+               Rank rank = nonViralName.getRank();\r
                \r
-               protected String getRanklessNameCache(NonViralName nonViralName){\r
-                       String result = "";\r
-                       result = (result + (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
+               if (nonViralName.isProtectedNameCache()){\r
+                       tags.add(new TaggedText(TagEnum.name, nonViralName.getNameCache()));\r
+               }else if (rank == null){\r
+                       tags = getRanklessTaggedNameCache(nonViralName);\r
+//             }else if (nonViralName.isInfragenericUnranked()){\r
+//                     result = getUnrankedInfragenericNameCache(nonViralName);\r
+               }else if (rank.isInfraSpecific()){\r
+                       tags = getInfraSpeciesTaggedNameCache(nonViralName);\r
+               }else if (rank.isSpecies()){\r
+                       tags = getSpeciesTaggedNameCache(nonViralName);\r
+               }else if (rank.isInfraGeneric()){\r
+                       tags = getInfraGenusTaggedNameCache(nonViralName);\r
+               }else if (rank.isGenus()){\r
+                       tags = getGenusOrUninomialTaggedNameCache(nonViralName);\r
+               }else if (rank.isSupraGeneric()){\r
+                       tags = getGenusOrUninomialTaggedNameCache(nonViralName);\r
+               }else{ \r
+                       logger.warn("Name Strategy for Name (UUID: " + nonViralName.getUuid() +  ") not yet implemented");\r
                }\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
+//***************************** PRIVATES ***************************************/\r
        \r
-               protected String getGenusOrUninomialNameCache(NonViralName nonViralName){\r
-                       String result;\r
-                       result = CdmUtils.Nz(nonViralName.getGenusOrUninomial());\r
-                       result = addAppendedPhrase(result, nonViralName).trim();\r
-                       return result;\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
+       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
+               INomenclaturalAuthor basAuthor = nonViralName.getBasionymAuthorTeam();\r
+               String basAuthorPart = "";\r
+               if (basAuthor != null){\r
+                       basAuthorPart = CdmUtils.Nz(basAuthor.getNomenclaturalTitle());\r
+               }\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
-               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 = "xxx.";\r
-                       if (rank != null){\r
-                               try {\r
-                                       infraGenericMarker = rank.getInfraGenericMarker();\r
-                               } catch (UnknownCdmTypeException e) {\r
-                                       infraGenericMarker = "'unhandled infrageneric rank'";\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
-                       result = CdmUtils.Nz(nonViralName.getGenusOrUninomial());\r
-                       result += " " + infraGenericMarker + " " + (CdmUtils.Nz(nonViralName.getInfraGenericEpithet())).trim().replace("null", "");\r
-                       result = addAppendedPhrase(result, nonViralName).trim();\r
-                       return result;\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
-//             aggr.|agg.|group\r
-               protected String getSpeciesAggregateCache(NonViralName nonViralName){\r
-                       String result;\r
-                       result = CdmUtils.Nz(nonViralName.getGenusOrUninomial());\r
-                       result += " " + CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim().replace("null", "");\r
-                       String marker;\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 tag list for an genus or higher taxon.\r
+        * \r
+        * @param nonViralName\r
+        * @return\r
+        */\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
+               \r
+               //genus\r
+               List<TaggedText> tags = getUninomialTaggedPart(nonViralName);\r
+               \r
+               //marker\r
+               String infraGenericMarker = "'unhandled infrageneric rank'";\r
+               if (rank != null){\r
                        try {\r
-                               marker = nonViralName.getRank().getInfraGenericMarker();\r
+                               infraGenericMarker = rank.getInfraGenericMarker();\r
                        } catch (UnknownCdmTypeException e) {\r
-                               marker = "'unknown aggregat type'";\r
+                               infraGenericMarker = "'unhandled infrageneric rank'";\r
                        }\r
-                       result += " " + marker;\r
-                       result = addAppendedPhrase(result, nonViralName).trim();\r
-                       return result;\r
                }\r
+               tags.add(new TaggedText(TagEnum.rank, infraGenericMarker));\r
                \r
-               protected String getSpeciesNameCache(NonViralName nonViralName){\r
-                       String result;\r
-                       result = CdmUtils.Nz(nonViralName.getGenusOrUninomial());\r
-                       result += " " + CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim().replace("null", "");\r
-                       result = addAppendedPhrase(result, nonViralName).trim();\r
-                       result = result.replace("\\s\\", " ");\r
-                       return result;\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 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 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 (StringUtils.isNotBlank(marker)){\r
+                       tags.add(new TaggedText(TagEnum.rank, marker));\r
+               }\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
-               protected String getInfraSpeciesNameCache(NonViralName nonViralName){\r
-                       return getInfraSpeciesNameCache(nonViralName, true);\r
+               infrSpecEpi = infrSpecEpi.trim().replace("null", "");\r
+               if (StringUtils.isNotBlank(infrSpecEpi)){\r
+                       tags.add(new TaggedText(TagEnum.name, infrSpecEpi));\r
                }\r
                \r
-               protected String getInfraSpeciesNameCache(NonViralName nonViralName, boolean includeMarker){\r
-                       String result;\r
-                       result = CdmUtils.Nz(nonViralName.getGenusOrUninomial());\r
-                       result += " " + (CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim()).replace("null", "");\r
-                       if (includeMarker){ \r
-                               result += " " + (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");\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
-                       result += " " + (CdmUtils.Nz(nonViralName.getInfraSpecificEpithet())).trim().replace("null", "");\r
-                       result = addAppendedPhrase(result, nonViralName).trim();\r
-                       return result;\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
+       }\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
+       @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
+//             }else{\r
                \r
-               /**\r
-                * @param name\r
-                * @return true, if name has Rank, Rank is below species and species epithet equals infraSpeciesEpithtet, else false\r
-                */\r
-               protected boolean isAutonym(NonViralName nonViralName){\r
-                       if (nonViralName != null && nonViralName.getRank() != null && nonViralName.getSpecificEpithet() != null && nonViralName.getInfraSpecificEpithet() != null && \r
-                                       nonViralName.getRank().isInfraSpecific() && nonViralName.getSpecificEpithet().trim().equals(nonViralName.getInfraSpecificEpithet().trim())){\r
-                               return true;\r
+               if (nonViralName.getGenusOrUninomial() != null) {\r
+                       tags.add(nonViralName.getGenusOrUninomial());\r
+               }\r
+               if (nonViralName.isSpecies() || nonViralName.isInfraSpecific()){\r
+                       tags.add(nonViralName.getSpecificEpithet());                    \r
+               }\r
+               \r
+               // No autonym \r
+               if (nonViralName.isInfraSpecific() && ! nonViralName.getSpecificEpithet().equals(nonViralName.getInfraSpecificEpithet())){\r
+                       tags.add(nonViralName.getRank());                       \r
+                       tags.add(nonViralName.getInfraSpecificEpithet());                       \r
+               }\r
+               \r
+               if (nonViralName.isInfraGeneric()){\r
+                       //TODO choose right strategy or generic approach?\r
+                       // --- strategy 1 --- \r
+                                       \r
+                       if (nonViralName.getRank().isSpeciesAggregate()){\r
+                               tags.add(nonViralName.getSpecificEpithet());\r
+                               tags.add(getSpeciesAggregateEpithet(nonViralName));\r
                        }else{\r
-                               return false;\r
+                               tags.add(nonViralName.getRank());       \r
+                               tags.add(nonViralName.getInfraGenericEpithet());        \r
                        }\r
+                       // --- strategy 2 --- \r
+//                     tags.add('('+nvn.getInfraGenericEpithet()+')'); \r
                }\r
+               Team authorTeam = Team.NewInstance();\r
+               authorTeam.setProtectedTitleCache(true);\r
+               authorTeam.setTitleCache(nonViralName.getAuthorshipCache(), true);\r
+               tags.add(authorTeam);\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
+               // 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
                }\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
+               if(! "".equals(nonViralName.getAppendedPhrase())&& (nonViralName.getAppendedPhrase() != null)){\r
+                       tags.add(nonViralName.getAppendedPhrase());\r
                }\r
+               \r
+               return tags;\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
+       \r
 }\r