ref #5983 and ref #6410 parsing of hybrid formulas with missing name parts
authorAndreas Müller <a.mueller@bgbm.org>
Fri, 17 Feb 2017 15:09:08 +0000 (16:09 +0100)
committerAndreas Müller <a.mueller@bgbm.org>
Fri, 17 Feb 2017 15:09:39 +0000 (16:09 +0100)
cdmlib-commons/src/main/java/eu/etaxonomy/cdm/common/CdmUtils.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImpl.java
cdmlib-model/src/main/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplRegExBase.java
cdmlib-model/src/test/java/eu/etaxonomy/cdm/strategy/parser/NonViralNameParserImplTest.java

index 12af663fb5889cca52c070d1faec9cfe13e8fd29..2dbda3dd4c80890c05e572a96b46d77dc54294db 100644 (file)
@@ -240,7 +240,7 @@ public class CdmUtils {
      * @param seperator
      * @return String
      */
-    static public String concat(CharSequence separator, String[] strings){
+    static public String concat(CharSequence separator, String... strings){
         String result = "";
         boolean allNull = true;
         for (String string : strings){
index c9b2882eec682ea1d388792d2e7f7858bde4c3d6..4427f6c027b0c38452444057ed1278580e7a5b26 100644 (file)
@@ -909,8 +909,8 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
                        }
                     //hybrid formula
                         else if (hybridFormulaPattern.matcher(fullNameString).matches()){
-                                Set<HybridRelationship> existingRelations = new HashSet<HybridRelationship>();
-                                Set<HybridRelationship> notToBeDeleted = new HashSet<HybridRelationship>();
+                                Set<HybridRelationship> existingRelations = new HashSet<>();
+                                Set<HybridRelationship> notToBeDeleted = new HashSet<>();
 
                                 for ( HybridRelationship rel : nameToBeFilled.getHybridChildRelations()){
                                     existingRelations.add(rel);
@@ -928,10 +928,15 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
                                                 secondNameString += " " + str;
                                         }
                                 }
+                                firstNameString = firstNameString.trim();
+                                secondNameString = secondNameString.trim();
                                 nameToBeFilled.setHybridFormula(true);
                                 NomenclaturalCode code = nameToBeFilled.getNomenclaturalCode();
-                                INonViralName firstName = this.parseFullName(firstNameString.trim(), code, rank);
-                                INonViralName secondName = this.parseFullName(secondNameString.trim(), code, rank);
+                                INonViralName firstName = this.parseFullName(firstNameString, code, rank);
+                                if (secondNameString.matches(abbrevHybridSecondPart)){
+                                    secondNameString = extendSecondHybridPart(firstName, secondNameString);
+                                }
+                                INonViralName secondName = this.parseFullName(secondNameString, code, rank);
                                 HybridRelationship firstRel = nameToBeFilled.addHybridParent(firstName, HybridRelationshipType.FIRST_PARENT(), null);
                                 HybridRelationship second = nameToBeFilled.addHybridParent(secondName, HybridRelationshipType.SECOND_PARENT(), null);
                                 checkRelationExist(firstRel, existingRelations, notToBeDeleted);
@@ -941,7 +946,7 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
                                 Rank firstRank = firstName.getRank();
                                 Rank secondRank = secondName.getRank();
 
-                                if (firstRank == null || firstRank.isHigher(secondRank)){
+                                if (firstRank == null || (secondRank != null && firstRank.isHigher(secondRank))){
                                         newRank = secondRank;
                                 }else{
                                         newRank = firstRank;
@@ -996,6 +1001,27 @@ public class NonViralNameParserImpl extends NonViralNameParserImplRegExBase impl
        }
 
        /**
+     * @param firstName
+     * @param secondNameString
+     * @return
+     */
+    private String extendSecondHybridPart(INonViralName firstName, String secondNameString) {
+        //first letter of genus given
+        if (secondNameString.matches("^" + abbrevHybridGenus + ".*")){
+            if (StringUtils.isNotBlank(firstName.getGenusOrUninomial())){
+                if (secondNameString.substring(0,1).equals(firstName.getGenusOrUninomial().substring(0, 1))){
+                    secondNameString = secondNameString.replaceAll("^" + abbrevHybridGenus, firstName.getGenusOrUninomial() + " ");
+                }
+            }
+        }else if (secondNameString.matches(abbrevHybridSecondPartOnlyInfraSpecies)){
+            secondNameString = CdmUtils.concat(" " , firstName.getGenusOrUninomial(), firstName.getSpecificEpithet(), secondNameString);
+        }else if (true){  //there will be further alternatives in future maybe
+            secondNameString = CdmUtils.concat(" " , firstName.getGenusOrUninomial(), secondNameString);
+        }
+        return secondNameString;
+    }
+
+    /**
      * Checks if a hybrid relation exists in the Set of existing relations
      * and <BR>
      *  if it does not adds it to relations not to be deleted <BR>
index 7a2ccf7f9151b03f0f4947c5d9686ebb4690cc4d..e87b019c03cd56086e86ac8535e033d6162e1684 100644 (file)
@@ -87,7 +87,8 @@ public abstract class NonViralNameParserImplRegExBase  {
     //marker
     protected static String InfraGenusMarker = "(n|notho)?(subgen\\.|subg\\.|sect\\.|subsect\\.|ser\\.|subser\\.|t\\.infgen\\.|\\[unranked\\])";
     protected static String aggrOrGroupMarker = "(aggr\\.|agg\\.|group)";
-    protected static String infraSpeciesMarker = "(n|notho)?(subsp\\.|convar\\.|var\\.|subvar\\.|f\\.|subf\\.|f\\.\\ssp\\.|f\\.spec\\.|f\\.sp\\.|\\[unranked\\]|tax\\." + fWs + "infrasp\\.)";
+    protected static String infraSpeciesMarkerNoNotho = "(subsp\\.|convar\\.|var\\.|subvar\\.|f\\.|subf\\.|f\\.\\ssp\\.|f\\.spec\\.|f\\.sp\\.|\\[unranked\\]|tax\\." + fWs + "infrasp\\.)";
+    protected static String infraSpeciesMarker = "(n|notho)?" + infraSpeciesMarkerNoNotho;
     protected static String oldInfraSpeciesMarker = "(prol\\.|proles|race|taxon|sublusus)";
 
 
@@ -287,7 +288,13 @@ public abstract class NonViralNameParserImplRegExBase  {
     protected static String anyBotanicFullName = "(" + autonym2 + "|" + anyBotanicName + oWs + fullBotanicAuthorString + ")"  ;
     protected static String anyZooFullName = anyZooName + oWs + fullZooAuthorString ;
     protected static String anyFullName = "(" + anyBotanicFullName + "|" + anyZooFullName + ")";
-    protected static String hybridFullName = "(" + anyFullName  + "|" +  anyBotanicName + "|" + anyZooName + ")" + hybridFormularSeparator + "(" + anyFullName  + "|" +  anyBotanicName + "|" + anyZooName + ")";
+    protected static String abbrevHybridGenus = "([A-Z](\\.\\s*|\\s+))";
+    protected static String abbrevHybridSecondPartWithSpecies = abbrevHybridGenus + "?" + nonCapitalEpiWord + "(" + oWs + infraSpeciesMarkerNoNotho + oWs + nonCapitalEpiWord + ")?";  //#5983 first step but still to strict
+    protected static String abbrevHybridSecondPartOnlyInfraSpecies = infraSpeciesMarkerNoNotho + oWs + nonCapitalEpiWord;
+    protected static String abbrevHybridSecondPart = "(" + abbrevHybridSecondPartWithSpecies + "|" + abbrevHybridSecondPartOnlyInfraSpecies + ")";
+
+    protected static String hybridSecondPart = "(" + anyFullName  + "|" +  anyBotanicName + "|" + anyZooName + "|" + abbrevHybridSecondPart + ")";
+    protected static String hybridFullName = "(" + anyFullName  + "|" +  anyBotanicName + "|" + anyZooName + ")" + hybridFormularSeparator + hybridSecondPart ;
 
     //Pattern
     protected static Pattern oWsPattern = Pattern.compile(oWs);
index d869790552d4c01d3ad4f914cfea25502894a3bc..823ab42d70ffa682637a9b965ff696f7d9d87df1 100644 (file)
@@ -673,7 +673,7 @@ public class NonViralNameParserImplTest {
         assertEquals("Title cache must be correct", "Abies alba \u00D7 Pinus bus var. beta", name1.getTitleCache());\r
         assertEquals("Hybrid name must have the lower rank ('variety') as rank", Rank.VARIETY(), name1.getRank());\r
 \r
-        //hybrids with authors\r
+        //hybrids with authors  //happens but questionable\r
         name1 = parser.parseFullName("Abies alba L. \u00D7 Pinus bus Mill.", botanicCode, null);\r
         assertTrue("Name must have hybrid formula bit set", name1.isHybridFormula());\r
         assertEquals("Name must have 2 hybrid parents", 2, name1.getHybridChildRelations().size());\r
@@ -686,8 +686,133 @@ public class NonViralNameParserImplTest {
         assertEquals("Name must have Pinus bus Mill. as second hybrid parent", "Pinus bus Mill.", secondParent.getTitleCache());\r
         assertEquals("Hybrid name must have the lower rank ('species') as rank", Rank.SPECIES(), name1.getRank());\r
 \r
+        //abbreviated genus hybrid formula #6410 / #5983\r
+        String nameStr = "Nepenthes mirabilis \u00D7 N. alata";\r
+        name1 = parser.parseFullName(nameStr, botanicCode, null);\r
+        assertTrue("Name must have hybrid formula bit set", name1.isHybridFormula());\r
+        assertEquals("Name must have 2 hybrid parents", 2, name1.getHybridChildRelations().size());\r
+        //could also be N. or no genus at all, depends on formatter\r
+        assertEquals("Title cache must be correct", "Nepenthes mirabilis \u00D7 Nepenthes alata", name1.getTitleCache());\r
+        orderedRels = name1.getOrderedChildRelationships();\r
+        assertEquals("Name must have 2 hybrid parents in ordered list", 2, orderedRels.size());\r
+        firstParent = orderedRels.get(0).getParentName();\r
+        //to be discussed as usually they should be ordered alphabetically\r
+        assertEquals("Name must have Nepenthes mirabilis as first hybrid parent", "Nepenthes mirabilis", firstParent.getTitleCache());\r
+        secondParent = orderedRels.get(1).getParentName();\r
+        assertEquals("Name must have Nepenthes alata as second hybrid parent", "Nepenthes alata", secondParent.getTitleCache());\r
+        assertEquals("Hybrid name must have the lower rank ('species') as rank", Rank.SPECIES(), name1.getRank());\r
+\r
+        //missing genus hybrid formula #5983\r
+        nameStr = "Nepenthes mirabilis \u00D7 alata";\r
+        name1 = parser.parseFullName(nameStr, botanicCode, null);\r
+        assertTrue("Name must have hybrid formula bit set", name1.isHybridFormula());\r
+        assertEquals("Name must have 2 hybrid parents", 2, name1.getHybridChildRelations().size());\r
+        //could also be N. or no genus at all, depends on formatter\r
+        assertEquals("Title cache must be correct", "Nepenthes mirabilis \u00D7 Nepenthes alata", name1.getTitleCache());\r
+        orderedRels = name1.getOrderedChildRelationships();\r
+        assertEquals("Name must have 2 hybrid parents in ordered list", 2, orderedRels.size());\r
+        firstParent = orderedRels.get(0).getParentName();\r
+        //to be discussed as usually they should be ordered alphabetically\r
+        assertEquals("Name must have Nepenthes mirabilis as first hybrid parent", "Nepenthes mirabilis", firstParent.getTitleCache());\r
+        secondParent = orderedRels.get(1).getParentName();\r
+        assertEquals("Name must have Nepenthes alata as second hybrid parent", "Nepenthes alata", secondParent.getTitleCache());\r
+        assertEquals("Hybrid name must have the lower rank ('species') as rank", Rank.SPECIES(), name1.getRank());\r
+\r
+        //#5983 subsp. with species and missing genus\r
+        nameStr = "Orchis coriophora subsp. fragrans \u00D7 sancta";\r
+        name1 = parser.parseFullName(nameStr, botanicCode, null);\r
+        assertTrue("Name must have hybrid formula bit set", name1.isHybridFormula());\r
+        assertEquals("Name must have 2 hybrid parents", 2, name1.getHybridChildRelations().size());\r
+        //could also be N. or no genus at all, depends on formatter\r
+        assertEquals("Title cache must be correct", "Orchis coriophora subsp. fragrans \u00D7 Orchis sancta", name1.getTitleCache());\r
+        orderedRels = name1.getOrderedChildRelationships();\r
+        assertEquals("Name must have 2 hybrid parents in ordered list", 2, orderedRels.size());\r
+        firstParent = orderedRels.get(0).getParentName();\r
+        assertEquals("Name must have Orchis coriophora subsp. fragrans as first hybrid parent", "Orchis coriophora subsp. fragrans", firstParent.getTitleCache());\r
+        secondParent = orderedRels.get(1).getParentName();\r
+        assertEquals("Name must have Orchis sancta as second hybrid parent", "Orchis sancta", secondParent.getTitleCache());\r
+        assertEquals("Hybrid name must have the lower rank ('subspecies') as rank", Rank.SUBSPECIES(), name1.getRank());\r
+\r
+        //2 subspecies with missing genus part #5983\r
+        nameStr = "Orchis morio subsp. syriaca \u00D7 papilionacea subsp. schirvanica";\r
+        name1 = parser.parseFullName(nameStr, botanicCode, null);\r
+        assertTrue("Name must have hybrid formula bit set", name1.isHybridFormula());\r
+        assertEquals("Name must have 2 hybrid parents", 2, name1.getHybridChildRelations().size());\r
+        //could also be N. or no genus at all, depends on formatter\r
+        assertEquals("Title cache must be correct", "Orchis morio subsp. syriaca \u00D7 Orchis papilionacea subsp. schirvanica", name1.getTitleCache());\r
+        orderedRels = name1.getOrderedChildRelationships();\r
+        assertEquals("Name must have 2 hybrid parents in ordered list", 2, orderedRels.size());\r
+        firstParent = orderedRels.get(0).getParentName();\r
+        assertEquals("Name must have Orchis morio subsp. syriaca as first hybrid parent", "Orchis morio subsp. syriaca", firstParent.getTitleCache());\r
+        secondParent = orderedRels.get(1).getParentName();\r
+        assertEquals("Name must have Orchis papilionacea subsp. schirvanica as second hybrid parent", "Orchis papilionacea subsp. schirvanica", secondParent.getTitleCache());\r
+        assertEquals("Hybrid name must have the lower rank ('subspecies') as rank", Rank.SUBSPECIES(), name1.getRank());\r
+\r
+        //subspecies and variety with missing genus part\r
+        nameStr = "Orchis morio subsp. syriaca \u00D7 papilionacea var. schirvanica";\r
+        name1 = parser.parseFullName(nameStr, botanicCode, null);\r
+        assertTrue("Name must have hybrid formula bit set", name1.isHybridFormula());\r
+        assertEquals("Name must have 2 hybrid parents", 2, name1.getHybridChildRelations().size());\r
+        //could also be N. or no genus at all, depends on formatter\r
+        assertEquals("Title cache must be correct", "Orchis morio subsp. syriaca \u00D7 Orchis papilionacea var. schirvanica", name1.getTitleCache());\r
+        orderedRels = name1.getOrderedChildRelationships();\r
+        assertEquals("Name must have 2 hybrid parents in ordered list", 2, orderedRels.size());\r
+        firstParent = orderedRels.get(0).getParentName();\r
+        assertEquals("Name must have Orchis morio subsp. syriaca as first hybrid parent", "Orchis morio subsp. syriaca", firstParent.getTitleCache());\r
+        secondParent = orderedRels.get(1).getParentName();\r
+        assertEquals("Name must have Orchis papilionacea var. schirvanica as second hybrid parent", "Orchis papilionacea var. schirvanica", secondParent.getTitleCache());\r
+        assertEquals("Hybrid name must have the lower rank ('variety') as rank", Rank.VARIETY(), name1.getRank());\r
+\r
+        //2 subspecies with missing genus and species part #5983\r
+        nameStr = "Orchis morio subsp. syriaca \u00D7 subsp. schirvanica";\r
+        name1 = parser.parseFullName(nameStr, botanicCode, null);\r
+        assertTrue("Name must have hybrid formula bit set", name1.isHybridFormula());\r
+        assertEquals("Name must have 2 hybrid parents", 2, name1.getHybridChildRelations().size());\r
+        //could also be N. or no genus at all, depends on formatter\r
+        assertEquals("Title cache must be correct", "Orchis morio subsp. syriaca \u00D7 Orchis morio subsp. schirvanica", name1.getTitleCache());\r
+        orderedRels = name1.getOrderedChildRelationships();\r
+        assertEquals("Name must have 2 hybrid parents in ordered list", 2, orderedRels.size());\r
+        firstParent = orderedRels.get(0).getParentName();\r
+        assertEquals("Name must have Orchis morio subsp. syriaca as first hybrid parent", "Orchis morio subsp. syriaca", firstParent.getTitleCache());\r
+        secondParent = orderedRels.get(1).getParentName();\r
+        assertEquals("Name must have Orchis morio subsp. schirvanica as second hybrid parent", "Orchis morio subsp. schirvanica", secondParent.getTitleCache());\r
+        assertEquals("Hybrid name must have the lower rank ('subspecies') as rank", Rank.SUBSPECIES(), name1.getRank());\r
+\r
+        //subspecies and variety with missing genus and species part #5983\r
+        nameStr = "Orchis morio subsp. syriaca \u00D7 var. schirvanica";\r
+        name1 = parser.parseFullName(nameStr, botanicCode, null);\r
+        assertTrue("Name must have hybrid formula bit set", name1.isHybridFormula());\r
+        assertEquals("Name must have 2 hybrid parents", 2, name1.getHybridChildRelations().size());\r
+        //could also be N. or no genus at all, depends on formatter\r
+        assertEquals("Title cache must be correct", "Orchis morio subsp. syriaca \u00D7 Orchis morio var. schirvanica", name1.getTitleCache());\r
+        orderedRels = name1.getOrderedChildRelationships();\r
+        assertEquals("Name must have 2 hybrid parents in ordered list", 2, orderedRels.size());\r
+        firstParent = orderedRels.get(0).getParentName();\r
+        assertEquals("Name must have Orchis morio subsp. syriaca as first hybrid parent", "Orchis morio subsp. syriaca", firstParent.getTitleCache());\r
+        secondParent = orderedRels.get(1).getParentName();\r
+        assertEquals("Name must have Orchis morio subsp. schirvanica as second hybrid parent", "Orchis morio var. schirvanica", secondParent.getTitleCache());\r
+        assertEquals("Hybrid name must have the lower rank ('variety') as rank", Rank.VARIETY(), name1.getRank());\r
+\r
+\r
     }\r
 \r
+//    @Test\r
+//    public final void testTemp(){\r
+////        String nalata = "N. alata";\r
+////        if (! nalata.matches(NonViralNameParserImplRegExBase.abbrevHybridSecondPart)){\r
+////            throw new RuntimeException();\r
+////        }\r
+//\r
+//        //abbreviated hybrid formula #6410\r
+//        String nameStr = "Orchis morio subsp. syriaca \u00D7 papilionacea subsp. schirvanica";\r
+//        INonViralName name1 = parser.parseFullName(nameStr, botanicCode, null);\r
+//        assertTrue("Name must have hybrid formula bit set", name1.isHybridFormula());\r
+//        assertEquals("Name must have 2 hybrid parents", 2, name1.getHybridChildRelations().size());\r
+//        //could also be N. or no genus at all, depends on formatter\r
+//        assertEquals("Title cache must be correct", "Orchis morio subsp. syriaca \u00D7 Orchis papilionacea subsp. schirvanica", name1.getTitleCache());\r
+//    }\r
+\r
+\r
     @Test\r
     public final void testHybridsRemoval(){\r
         //if the parser input already has hybridrelationships they need to be removed\r