commit 045113789693fcdbb05f1b86e133a3149b55442b
Author: Andreas Müller <a.mueller@bgbm.org>
Date:   Tue Dec 21 09:27:52 2010 +0000

    hybrid parsing and caching for monom, binom and trinom flags implemented

diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/cache/name/NonViralNameDefaultCacheStrategy.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/cache/name/NonViralNameDefaultCacheStrategy.java
index a59f176092..df9f44866b 100644
--- a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/cache/name/NonViralNameDefaultCacheStrategy.java
+++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/cache/name/NonViralNameDefaultCacheStrategy.java
@@ -29,6 +29,7 @@ import eu.etaxonomy.cdm.model.name.NonViralName;
 import eu.etaxonomy.cdm.model.name.Rank;
 import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
 import eu.etaxonomy.cdm.strategy.exceptions.UnknownCdmTypeException;
+import eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImplRegExBase;
 
 
 /**
@@ -506,10 +507,20 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
 	
 		protected String getGenusOrUninomialNameCache(NonViralName nonViralName){
 			String result;
-			result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
+			result = getUninomialPart(nonViralName);
 			result = addAppendedPhrase(result, nonViralName).trim();
 			return result;
 		}
+
+
+		private String getUninomialPart(NonViralName nonViralName) {
+			String result;
+			result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
+			if (nonViralName.isMonomHybrid()){
+				result = NonViralNameParserImplRegExBase.hybridSign + result; 
+			}
+			return result;
+		}
 		
 		protected String getInfraGenusNameCache(NonViralName nonViralName){
 			String result;
@@ -525,7 +536,7 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
 					infraGenericMarker = "'unhandled infrageneric rank'";
 				}
 			}
-			result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
+			result = getUninomialPart(nonViralName);
 			result += " " + infraGenericMarker + " " + (CdmUtils.Nz(nonViralName.getInfraGenericEpithet())).trim().replace("null", "");
 			result = addAppendedPhrase(result, nonViralName).trim();
 			return result;
@@ -567,7 +578,11 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
 			if (includeMarker){ 
 				result += " " + (nonViralName.getRank().getAbbreviation()).trim().replace("null", "");
 			}
-			result += " " + (CdmUtils.Nz(nonViralName.getInfraSpecificEpithet())).trim().replace("null", "");
+			String infrSpecEpi = CdmUtils.Nz(nonViralName.getInfraSpecificEpithet());
+			if (nonViralName.isTrinomHybrid()){
+				infrSpecEpi = NonViralNameParserImplRegExBase.hybridSign + infrSpecEpi; 
+			}
+			result += " " + (infrSpecEpi).trim().replace("null", "");
 			result = addAppendedPhrase(result, nonViralName).trim();
 			return result;
 		}
@@ -575,11 +590,25 @@ public class NonViralNameDefaultCacheStrategy<T extends NonViralName> extends Na
 
 		private String getGenusAndSpeciesPart(NonViralName nonViralName) {
 			String result;
-			result = CdmUtils.Nz(nonViralName.getGenusOrUninomial()).trim();
-			if (StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet()) ){
-				result += " (" + nonViralName.getInfraGenericEpithet().trim() + ")";
+			//Uninomial
+			result = getUninomialPart(nonViralName);
+			
+			//InfraGenericEpi
+			boolean hasInfraGenericEpi = StringUtils.isNotBlank(nonViralName.getInfraGenericEpithet());
+			if (hasInfraGenericEpi){
+				String infrGenEpi = nonViralName.getInfraGenericEpithet().trim();
+				if (nonViralName.isBinomHybrid()){
+					infrGenEpi = NonViralNameParserImplRegExBase.hybridSign + infrGenEpi; 
+				}
+				result += " (" + infrGenEpi + ")";
+			}
+			//Species Epi
+			String specEpi = CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim();
+			if (! hasInfraGenericEpi && nonViralName.isBinomHybrid() || 
+					hasInfraGenericEpi && nonViralName.isTrinomHybrid()){
+				specEpi = NonViralNameParserImplRegExBase.hybridSign +  specEpi; 
 			}
-			result += " " + (CdmUtils.Nz(nonViralName.getSpecificEpithet()).trim()).replace("null", "");
+			result += " " + (specEpi).replace("null", "");
 			return result;
 		}
 
diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImpl.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImpl.java
index ba9534da01..a30a3a082c 100644
--- a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImpl.java
+++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImpl.java
@@ -746,6 +746,7 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
 		// OLD: fullName = oWsRE.subst(fullName, " "); //substitute multiple whitespaces		   
 		fullNameString = fullNameString.trim();
 		
+		fullNameString = removeHybridBlanks(fullNameString);
 		String[] epi = pattern.split(fullNameString);
 		try {
 	    	//cultivars //TODO 2 implement cultivars
@@ -754,17 +755,17 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
 //		    }
 		    //hybrids //TODO 2 implement hybrids
 		    //else 
-		    if (hybridPattern.matcher(fullNameString).matches() ){
-		    	nameToBeFilled = parseHybrid(fullNameString);
-		    }
-		    else if (genusOrSupraGenusPattern.matcher(fullNameString).matches()){
+//		    if (hybridPattern.matcher(fullNameString).find() ){
+//		    	parseHybrid(nameToBeFilled, fullNameString, rank, makeEmpty);
+//		    } else 
+		      if (genusOrSupraGenusPattern.matcher(fullNameString).matches()){
 		    	//supraGeneric
 				if (rank != null && ! hasCheckRankProblem  && (rank.isSupraGeneric()|| rank.isGenus())){
 					nameToBeFilled.setRank(rank);
 					nameToBeFilled.setGenusOrUninomial(epi[0]);
 				} 
-				//genus or guess rank
-				else {
+				 //genus or guess rank
+				 else {
 					rank = guessUninomialRank(nameToBeFilled, epi[0]); 
 					nameToBeFilled.setRank(rank);
 					nameToBeFilled.setGenusOrUninomial(epi[0]);
@@ -774,28 +775,28 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
 				}
 				authorString = fullNameString.substring(epi[0].length());
 			}
-			//infra genus
-			else if (infraGenusPattern.matcher(fullNameString).matches()){
+			 //infra genus
+			 else if (infraGenusPattern.matcher(fullNameString).matches()){
 				nameToBeFilled.setRank(Rank.getRankByAbbreviation(epi[1]));
 				nameToBeFilled.setGenusOrUninomial(epi[0]);
 				nameToBeFilled.setInfraGenericEpithet(epi[2]);
 				authorString = fullNameString.substring(epi[0].length() + 1 + epi[1].length()+ 1 + epi[2].length());
 			}
-			//aggr. or group
-			else if (aggrOrGroupPattern.matcher(fullNameString).matches()){
+			 //aggr. or group
+			 else if (aggrOrGroupPattern.matcher(fullNameString).matches()){
 				nameToBeFilled.setRank(Rank.getRankByAbbreviation(epi[2]));
 				nameToBeFilled.setGenusOrUninomial(epi[0]);
 				nameToBeFilled.setSpecificEpithet(epi[1]);
 			}
-			//species
-			else if (speciesPattern.matcher(fullNameString).matches()){
+			 //species
+			 else if (speciesPattern.matcher(fullNameString).matches()){
 				nameToBeFilled.setRank(Rank.SPECIES());
 				nameToBeFilled.setGenusOrUninomial(epi[0]);
 				nameToBeFilled.setSpecificEpithet(epi[1]);
 				authorString = fullNameString.substring(epi[0].length() + 1 + epi[1].length());
 			}
-			//autonym
-			else if (autonymPattern.matcher(fullNameString).matches()){
+			 //autonym
+			 else if (autonymPattern.matcher(fullNameString).matches()){
 				nameToBeFilled.setRank(Rank.getRankByAbbreviation(epi[epi.length - 2]));
 				nameToBeFilled.setGenusOrUninomial(epi[0]);
 				nameToBeFilled.setSpecificEpithet(epi[1]);
@@ -804,8 +805,8 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
 				int lenInfraSpecies =  2 + epi[epi.length - 2].length() + epi[epi.length - 1].length();
 				authorString = fullNameString.substring(lenSpecies, fullNameString.length() - lenInfraSpecies);
 			}
-			//infraSpecies
-			else if (infraSpeciesPattern.matcher(fullNameString).matches()){
+			 //infraSpecies
+			 else if (infraSpeciesPattern.matcher(fullNameString).matches()){
 				String infraSpecRankEpi = epi[2];
 				String infraSpecEpi = epi[3];
 				if ("tax.".equals(infraSpecRankEpi)){
@@ -818,7 +819,7 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
 				nameToBeFilled.setInfraSpecificEpithet(infraSpecEpi);
 				authorString = fullNameString.substring(epi[0].length()+ 1 + epi[1].length() +1 + infraSpecRankEpi.length() + 1 + infraSpecEpi.length());
 			}//old infraSpecies
-			else if (oldInfraSpeciesPattern.matcher(fullNameString).matches()){
+			 else if (oldInfraSpeciesPattern.matcher(fullNameString).matches()){
 				boolean implemented = false;
 				if (implemented){
 					nameToBeFilled.setRank(Rank.getRankByNameOrAbbreviation(epi[2]));
@@ -844,6 +845,8 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
 				// END
 				logger.info("no applicable parsing rule could be found for \"" + fullNameString + "\"");
 		    }
+		    //hybrid bits	
+		    handleHybridBits(nameToBeFilled);
 			//authors
 		    if (nameToBeFilled != null && StringUtils.isNotBlank(authorString) ){ 
 				handleAuthors(nameToBeFilled, fullNameString, authorString);
@@ -865,6 +868,47 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
 		}
 	}
 
+	private void handleHybridBits(NonViralName nameToBeFilled) {
+		//uninomial
+		String uninomial = CdmUtils.Nz(nameToBeFilled.getGenusOrUninomial());
+		boolean isUninomialHybrid = uninomial.startsWith(hybridSign);
+		if (isUninomialHybrid){
+			nameToBeFilled.setMonomHybrid(true);
+			nameToBeFilled.setGenusOrUninomial(uninomial.replace(hybridSign, ""));
+		}
+		//infrageneric
+		String infrageneric = CdmUtils.Nz(nameToBeFilled.getInfraGenericEpithet());
+		boolean isInfraGenericHybrid = infrageneric.startsWith(hybridSign);
+		if (isInfraGenericHybrid){
+			nameToBeFilled.setBinomHybrid(true);
+			nameToBeFilled.setInfraGenericEpithet(infrageneric.replace(hybridSign, ""));
+		}
+		//species Epi
+		String speciesEpi = CdmUtils.Nz(nameToBeFilled.getSpecificEpithet());
+		boolean isSpeciesHybrid = speciesEpi.startsWith(hybridSign);
+		if (isSpeciesHybrid){
+			if (StringUtils.isBlank(infrageneric)){
+				nameToBeFilled.setBinomHybrid(true);
+			}else{
+				nameToBeFilled.setTrinomHybrid(true);
+			}
+			nameToBeFilled.setSpecificEpithet(speciesEpi.replace(hybridSign, ""));
+		}
+		//infra species
+		String infraSpeciesEpi = CdmUtils.Nz(nameToBeFilled.getInfraSpecificEpithet());
+		boolean isInfraSpeciesHybrid = infraSpeciesEpi.startsWith(hybridSign);
+		if (isInfraSpeciesHybrid){
+			nameToBeFilled.setTrinomHybrid(true);
+			nameToBeFilled.setInfraSpecificEpithet(infraSpeciesEpi.replace(hybridSign, ""));
+		}
+		
+	}
+
+	private String removeHybridBlanks(String fullNameString) {
+		fullNameString = fullNameString.replaceAll(hybridFull, " "+hybridSign).trim();
+		return fullNameString;
+	}
+
 	/**
 	 * Author parser for external use
 	 * @param nonViralName
@@ -1002,7 +1046,7 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
 			}
 		}else {
 			//TODO
-			logger.warn ("not yet implemented");
+			logger.warn ("Full author String parsable only for defined BotanicalNames or ZoologicalNames but this is " + clazz.getSimpleName());
 			throw new StringNotParsableException("fullAuthorString (" +fullAuthorString+") not parsable: ");
 		}
 		fullAuthorsChecked(fullAuthorString, authors, years);
@@ -1131,13 +1175,53 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
 	
 
 	//Parsing of the given full name that has been identified as hybrid already somewhere else.
-	private BotanicalName parseHybrid(String fullName){
+	private void parseHybrid(NonViralName nameToBeFilled, String fullNameString, Rank rank, boolean makeEmpty){
 	    logger.warn("parseHybrid --> function not yet implemented");
-	    BotanicalName result = BotanicalName.NewInstance(null);
-	    result.setTitleCache(fullName,true);
-	    return result;
+	    
+//	    String nonHybridName  = fullNameString;
+//	    boolean isMonomHybrid = isMonomHybrid(fullNameString);
+//	    if (isMonomHybrid){
+//	    	nonHybridName.replaceAll(hybrid, "");
+//	    }
+//	    
+//	    String[] split = nonHybridName.split("\\s");
+//	    parseFullName(nameToBeFilled, nonHybridName, rank, makeEmpty);
+//	    
+//	    nonHybridName = nonHybridName.replaceAll(hybrid, " ");
+//
+//	    boolean isBinomHybrid = isBinomHybrid(split);
+////	    boolean isTrinomHybrid = isTrinomHybrid(split);
+//	    
+//	    nonHybridName = nonHybridName.replaceAll(hybrid, " ");
+//	     
+//	    parseFullName(nameToBeFilled, nonHybridName, rank, makeEmpty);
+//	    nameToBeFilled.getTitleCache();
+//	    nameToBeFilled.setMonomHybrid(isMonomHybrid);
+//	    nameToBeFilled.setBinomHybrid(isBinomHybrid);
+//	    nameToBeFilled.setBinomHybrid(isTrinomHybrid);
+	    
+	    nameToBeFilled.setTitleCache(fullNameString,true);
+	    return;
     }
 	
+//	private boolean isBinomHybrid(String[] split) {
+//		if (){
+//			
+//		}
+//		return false;
+//	}
+
+	private boolean isMonomHybrid(String fullNameString) {
+		Matcher matcher = hybridPattern.matcher(fullNameString);
+		boolean find = matcher.find();
+		int start = matcher.start();
+		if (find == true && start == 0){
+			return true;
+		}else{
+			return false;
+		}
+	}
+
 //	// Parsing of the given full name that has been identified as a cultivar already somwhere else.
 //	// The ... cv. ... syntax is not covered here as it is not according the rules for naming cultivars.
 	public BotanicalName parseCultivar(String fullName)	throws StringNotParsableException{
diff --git a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplRegExBase.java b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplRegExBase.java
index e63f24b0d1..6726568f84 100644
--- a/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplRegExBase.java
+++ b/cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplRegExBase.java
@@ -22,13 +22,15 @@ import org.apache.log4j.Logger;
 public abstract class NonViralNameParserImplRegExBase  {
 	@SuppressWarnings("unused")
 	private static final Logger logger = Logger.getLogger(NonViralNameParserImplRegExBase.class);
-	
+
 	// good intro: http://java.sun.com/docs/books/tutorial/essential/regex/index.html
     
     //splitter
     protected static String epiSplitter = "(\\s+|\\(|\\))"; //( ' '+| '(' | ')' )
     protected static Pattern pattern = Pattern.compile(epiSplitter); 
-    
+
+	public static final String hybridSign = "\u00D7";
+
     //some useful non-terminals
     protected static String pStart = "^";
     protected static String end = "$";
@@ -231,14 +233,16 @@ public abstract class NonViralNameParserImplRegExBase  {
     //cultivars and hybrids
     protected static String cultivar = oWs + "'..+'"; //Achtung mit Hochkomma in AuthorNamen
     protected static String cultivarMarker = oWs + "(cv.|')";
-    protected static String hybrid = oWs + "((x|X)" + oWs + "|notho)";//= ( x )|( X )|( notho)
+    protected static String hybridPart = "((x|X)" + oWs + "|"+hybridSign+"|notho)";
+    protected static String hybridFull = "(" +oWs +"|"+ pStart +")" + hybridPart;
+    
     
     //  Name String
-    protected static String genusOrSupraGenus = capitalEpiWord;
+    protected static String genusOrSupraGenus = "("+hybridFull+")?" + capitalEpiWord;
     protected static String infraGenus = capitalEpiWord + oWs + InfraGenusMarker + oWs + capitalEpiWord;
     protected static String aggrOrGroup = capitalEpiWord + oWs + nonCapitalEpiWord + oWs + aggrOrGroupMarker;
-    protected static String species = capitalEpiWord + oWs +  nonCapitalEpiWord;
-    protected static String infraSpecies = capitalEpiWord + oWs +  nonCapitalEpiWord + oWs + infraSpeciesMarker + oWs + nonCapitalEpiWord;
+    protected static String species = genusOrSupraGenus + oWs + "("+hybridPart+")?" + nonCapitalEpiWord;
+    protected static String infraSpecies = species + oWs + infraSpeciesMarker + oWs + "("+hybridPart+")?" + nonCapitalEpiWord;
     protected static String oldInfraSpecies = capitalEpiWord + oWs +  nonCapitalEpiWord + oWs + oldInfraSpeciesMarker + oWs + nonCapitalEpiWord;
     protected static String autonym = capitalEpiWord + oWs + "(" + nonCapitalEpiWord +")" + oWs + fullBotanicAuthorString +  oWs + infraSpeciesMarker + oWs + "\\1";  //2-nd word and last word are the same 
     //autonym pattern used within anyBotanicalFullName pattern
@@ -257,7 +261,7 @@ public abstract class NonViralNameParserImplRegExBase  {
     protected static Pattern teamSplitterPattern = Pattern.compile(teamSplitter);
     protected static Pattern cultivarPattern = Pattern.compile(cultivar);
     protected static Pattern cultivarMarkerPattern = Pattern.compile(cultivarMarker);
-    protected static Pattern hybridPattern = Pattern.compile(hybrid); 
+    protected static Pattern hybridPattern = Pattern.compile(hybridFull); 
     
     protected static Pattern genusOrSupraGenusPattern = Pattern.compile(pStart + genusOrSupraGenus + facultFullAuthorString2 + end);
     protected static Pattern infraGenusPattern = Pattern.compile(pStart + infraGenus + facultFullAuthorString2 + end);
diff --git a/cdmlib-model/src/test/java/eu/etaxonomy/cdm/strategy/cache/name/NonViralNameDefaultCacheStrategyTest.java b/cdmlib-model/src/test/java/eu/etaxonomy/cdm/strategy/cache/name/NonViralNameDefaultCacheStrategyTest.java
index 5009a73d9a..e91b45e983 100644
--- a/cdmlib-model/src/test/java/eu/etaxonomy/cdm/strategy/cache/name/NonViralNameDefaultCacheStrategyTest.java
+++ b/cdmlib-model/src/test/java/eu/etaxonomy/cdm/strategy/cache/name/NonViralNameDefaultCacheStrategyTest.java
@@ -14,7 +14,7 @@ import static org.junit.Assert.assertNull;
 
 import java.util.List;
 
-import junit.framework.Assert;
+import org.junit.Assert;
 
 import org.apache.log4j.Logger;
 import org.junit.Before;
@@ -201,6 +201,42 @@ public class NonViralNameDefaultCacheStrategyTest {
 		assertEquals(atomizedTitleCache, speciesName.getFullTitleCache());	
 	}
 	
+	/**
+	 * Test method for {@link eu.etaxonomy.cdm.strategy.cache.name.NonViralNameDefaultCacheStrategy#getAuthorshipCache(eu.etaxonomy.cdm.model.name.NonViralName)}.
+	 */
+	@Test
+	public void testHybridNames() {
+		this.speciesName.setCombinationAuthorTeam(author);
+		Assert.assertEquals(author.getNomenclaturalTitle(), speciesName.getAuthorshipCache());
+		Assert.assertEquals("Should be Abies alba L.", "Abies alba L.", speciesName.getTitleCache());
+		
+		speciesName.setBinomHybrid(true);
+		//TODO
+//		speciesName.setTitleCache(null, false);
+//		speciesName.setNameCache(null, false);
+		Assert.assertEquals("Should be Abies �alba L.", "Abies �alba L.", speciesName.getTitleCache());
+		speciesName.setMonomHybrid(true);
+		speciesName.setTitleCache(null, false);
+		speciesName.setNameCache(null, false);
+		Assert.assertEquals("Should be '�Abies �alba L.'", "�Abies �alba L.", speciesName.getTitleCache());
+		
+		Assert.assertEquals("Should be 'Genus'", "Genus", genusName.getTitleCache());
+		genusName.setMonomHybrid(true);
+		genusName.setTitleCache(null, false);
+		genusName.setNameCache(null, false);
+		Assert.assertEquals("Should be '�Genus'", "�Genus", genusName.getTitleCache());
+	
+		Assert.assertEquals("Should be 'Abies alba subsp. beta'", subSpeciesNameString, subSpeciesName.getTitleCache());
+		subSpeciesName.setTrinomHybrid(true);
+		subSpeciesName.setTitleCache(null, false);
+		subSpeciesName.setNameCache(null, false);
+		Assert.assertEquals("Should be 'Abies alba subsp. �beta'", "Abies alba subsp. �beta", subSpeciesName.getTitleCache());
+		subSpeciesName.setMonomHybrid(true);
+		subSpeciesName.setTitleCache(null, false);
+		subSpeciesName.setNameCache(null, false);
+		Assert.assertEquals("Should be '�Abies alba subsp. �beta'", "�Abies alba subsp. �beta", subSpeciesName.getTitleCache());
+	}
+	
 	@Test
 	public void testCacheListener() {
 		Reference ref = ReferenceFactory.newGeneric();
diff --git a/cdmlib-model/src/test/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplTest.java b/cdmlib-model/src/test/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplTest.java
index 3044cf488f..3882b2d414 100644
--- a/cdmlib-model/src/test/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplTest.java
+++ b/cdmlib-model/src/test/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplTest.java
@@ -87,12 +87,6 @@ public class NonViralNameParserImplTest {
 		termInitializer.initialize();
 	}
 
-	/**
-	 * @throws java.lang.Exception
-	 */
-	@AfterClass
-	public static void tearDownAfterClass() throws Exception {
-	}
 
 	/**
 	 * @throws java.lang.Exception
@@ -103,12 +97,6 @@ public class NonViralNameParserImplTest {
 		botanicCode = NomenclaturalCode.ICBN;
 	}
 
-	/**
-	 * @throws java.lang.Exception
-	 */
-	@After
-	public void tearDown() throws Exception {
-	}
 
 /*************** TEST *********************************************/
 	
@@ -286,6 +274,43 @@ public class NonViralNameParserImplTest {
 		assertEquals("Basionym author should have 3 authors", 3, ((Team)authorname.getExBasionymAuthorTeam()).getTeamMembers().size());
 	}
 	
+	/**
+	 * Test method for {@link eu.etaxonomy.cdm.strategy.parser.NonViralNameParserImpl#parseFullName(java.lang.String, eu.etaxonomy.cdm.model.name.Rank)}.
+	 */
+	@Test
+	public final void testHybrids() {
+		try {
+			Method parseMethod = parser.getClass().getDeclaredMethod("parseFullName", String.class, NomenclaturalCode.class, Rank.class);
+			testName_StringNomcodeRank(parseMethod);
+		} catch (Exception e) {
+			e.printStackTrace();
+			assertTrue(false);
+		}
+		
+		//Species hybrid
+//		NonViralName nameTeam1 = parser.parseFullName("Aegilops �insulae-cypri H. Scholz");
+		NonViralName name1 = parser.parseFullName("Aegilops �insulae Scholz", botanicCode, null);
+		assertTrue("Name must have binom hybrid bit set", name1.isBinomHybrid());
+		assertFalse("Name must not have monom hybrid bit set", name1.isMonomHybrid());
+		assertFalse("Name must not have trinom hybrid bit set", name1.isTrinomHybrid());
+		assertEquals("Species epithet must be 'insulae'", "insulae", name1.getSpecificEpithet());
+		
+		//Uninomial hybrid
+		name1 = parser.parseFullName("x Aegilops Scholz", botanicCode, null);
+		assertTrue("Name must have monom hybrid bit set", name1.isMonomHybrid());
+		assertFalse("Name must not have binom hybrid bit set", name1.isBinomHybrid());
+		assertFalse("Name must not have trinom hybrid bit set", name1.isTrinomHybrid());
+		assertEquals("Uninomial must be 'Aegilops'", "Aegilops", name1.getGenusOrUninomial());
+
+		//Species hybrid
+		name1 = parser.parseFullName("Aegilops insulae subsp. X abies Scholz", botanicCode, null);
+		assertFalse("Name must not have monom hybrid bit set", name1.isMonomHybrid());
+		assertFalse("Name must not have binom hybrid bit set", name1.isBinomHybrid());
+		assertTrue("Name must have trinom hybrid bit set", name1.isTrinomHybrid());
+		assertEquals("Infraspecific epithet must be 'abies'", "abies", name1.getInfraSpecificEpithet());
+
+	}
+	
 	private void testName_StringNomcodeRank(Method parseMethod) 
 			throws InvocationTargetException, IllegalAccessException  {
 		NonViralName name1 = (NonViralName)parseMethod.invoke(parser, strNameAbies1, null, Rank.SPECIES());
@@ -747,6 +772,8 @@ public class NonViralNameParserImplTest {
 
 	}
 	
+	
+	
 	/**
 	 * @param testParsable
 	 * @param icbn
