Repository: ontodev/robot
Pull request #569 (2019-09-13 16:33:00)

Fix method for axiom annotations from templates
If you have two axiom annotations next to each other:

| A prop:0 | >A prop:1 | >A prop:2 |
| --- | --- | --- |
| val 0 | val 1 | val 2 |

"val 2" will become a nested axiom annotation on the "val 1" axiom annotation, which is an annotation on "val 0". Instead, these should both be axiom annotations on the "val 0" annotation.

This is only an issue when you use two different `>` columns on one axiom.

The original template test still passes fine, but I can update the template to test different `>` columns if you'd like.


Patch:
diff --git a/robot-core/src/main/java/org/obolibrary/robot/ReasonOperation.java b/robot-core/src/main/java/org/obolibrary/robot/ReasonOperation.java
index 9bddb942..a6e572e1 100644
--- a/robot-core/src/main/java/org/obolibrary/robot/ReasonOperation.java
+++ b/robot-core/src/main/java/org/obolibrary/robot/ReasonOperation.java
@@ -496,60 +496,16 @@ public class ReasonOperation {
       throws InvalidReferenceException {
     Set<InvalidReferenceViolation> referenceViolations =
         InvalidReferenceChecker.getInvalidReferenceViolations(ontology, false);
-    Set<InvalidReferenceViolation> filteredViolations = new HashSet();
-
     if (referenceViolations.size() > 0) {
-      for (InvalidReferenceViolation v : referenceViolations) {
-        // Don't log errors for:
-        // - annotations
-        // - subclass of ObsoleteClass
-        // - subproperty of ObsoleteProperty
-        // - rdf:type owl:Thing
-        if (v.getAxiom() instanceof OWLAnnotationAxiom) {
-          continue;
-        }
-        if (v.getAxiom() instanceof OWLSubClassOfAxiom) {
-          OWLSubClassOfAxiom sub = (OWLSubClassOfAxiom) v.getAxiom();
-          OWLClassExpression sup = sub.getSuperClass();
-          if (!sup.isAnonymous()
-              && sup.asOWLClass()
-                  .getIRI()
-                  .toString()
-                  .equals("http://www.geneontology.org/formats/oboInOwl#ObsoleteClass")) {
-            continue;
-          }
-        }
-        if (v.getAxiom() instanceof OWLSubObjectPropertyOfAxiom) {
-          OWLSubObjectPropertyOfAxiom sub = (OWLSubObjectPropertyOfAxiom) v.getAxiom();
-          OWLObjectPropertyExpression sup = sub.getSuperProperty();
-          if (!sup.isAnonymous()
-              && sup.asOWLObjectProperty()
-                  .getIRI()
-                  .toString()
-                  .equals("http://www.geneontology.org/formats/oboInOwl#ObsoleteProperty")) {
-            continue;
-          }
-        }
-        if (v.getAxiom() instanceof OWLClassAssertionAxiom) {
-          OWLClassAssertionAxiom sub = (OWLClassAssertionAxiom) v.getAxiom();
-          if (sub.getClassExpression().isOWLThing()) {
-            continue;
-          }
-        }
-
-        filteredViolations.add(v);
-      }
-    }
-
-    if (filteredViolations.size() > 0) {
       logger.error(
           "Reference violations found: "
-              + filteredViolations.size()
+              + referenceViolations.size()
               + " - reasoning may be incomplete");
 
       int maxDanglings = 10;
       int danglings = 0;
-      for (InvalidReferenceViolation v : filteredViolations) {
+      for (InvalidReferenceViolation v : referenceViolations) {
+
         if (v.getCategory().equals(InvalidReferenceViolation.Category.DANGLING)
             && danglings < maxDanglings) {
           logger.error("Reference violation: " + v);
diff --git a/robot-core/src/main/java/org/obolibrary/robot/Template.java b/robot-core/src/main/java/org/obolibrary/robot/Template.java
index e2597bc8..2a57a772 100644
--- a/robot-core/src/main/java/org/obolibrary/robot/Template.java
+++ b/robot-core/src/main/java/org/obolibrary/robot/Template.java
@@ -1,6 +1,7 @@
 package org.obolibrary.robot;
 
 import java.util.*;
+import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 import javax.annotation.Nonnull;
 import org.obolibrary.robot.exceptions.ColumnException;
@@ -1984,6 +1985,86 @@ public class Template {
     return fixedAnnotations;
   }
 
+  /**
+   * Given a row as a list of strings and a column number, determine if the next column contains a
+   * one or more axiom annotations. If so, return the axiom annotation or annotations as a set of
+   * OWLAnnotations.
+   *
+   * @param row list of strings
+   * @param column column number
+   * @return set of OWLAnnotations, maybe empty
+   * @throws Exception on issue getting the OWLAnnotations
+   */
+  private Set<OWLAnnotation> maybeGetAxiomAnnotations(List<String> row, int column)
+      throws Exception {
+    Map<Integer, List<Integer>> annotationsPlusAnnotations = new HashMap<>();
+    int lastAnnotation = -1;
+    while (column < row.size() - 1) {
+      column++;
+      String template = templates.get(column);
+      Matcher m = Pattern.compile("^>.*").matcher(template);
+      if (m.matches()) {
+
+        m = Pattern.compile("^>[^>].*").matcher(template);
+        if (m.matches()) {
+          // This is an annotation on the original axiom
+          lastAnnotation = column;
+          annotationsPlusAnnotations.put(column, new ArrayList<>());
+        }
+
+        m = Pattern.compile("^>>.*").matcher(template);
+        if (m.matches()) {
+          // This is an annotation on the last axiom annotation
+          // Update the map
+          List<Integer> cols = annotationsPlusAnnotations.get(lastAnnotation);
+          cols.add(column);
+          annotationsPlusAnnotations.put(lastAnnotation, cols);
+        }
+      } else {
+        // We are done getting the annotations
+        break;
+      }
+    }
+
+    Set<OWLAnnotation> annotations = new HashSet<>();
+    for (Map.Entry<Integer, List<Integer>> annPlusAnns : annotationsPlusAnnotations.entrySet()) {
+      int annColumn = annPlusAnns.getKey();
+      List<Integer> moreAnns = annPlusAnns.getValue();
+
+      String template = templates.get(annColumn);
+      String value = row.get(annColumn).trim();
+      if (value.isEmpty()) {
+        continue;
+      }
+      Set<OWLAnnotation> firstAnnotations =
+          TemplateHelper.getAnnotations(name, checker, template, value, rowNum, annColumn);
+
+      if (moreAnns.isEmpty()) {
+        annotations.addAll(firstAnnotations);
+        continue;
+      }
+
+      Set<OWLAnnotation> moreAnnotations = new HashSet<>();
+      for (int m : moreAnns) {
+        template = templates.get(m);
+        while (template.startsWith(">")) {
+          template = template.substring(1);
+        }
+        value = row.get(m).trim();
+        if (value.isEmpty()) {
+          continue;
+        }
+        moreAnnotations.addAll(
+            TemplateHelper.getAnnotations(name, checker, template, value, rowNum, annColumn));
+      }
+
+      for (OWLAnnotation f : firstAnnotations) {
+        annotations.add(f.getAnnotatedAnnotation(moreAnnotations));
+      }
+    }
+    return annotations;
+  }
+
   /**
    * Given a row as a list of strings, the template string, and the number of the next column, maybe
    * get axiom annotations on existing axiom annotations.
@@ -2039,35 +2120,6 @@ public class Template {
     return new HashSet<>();
   }
 
-  /**
-   * Given a row as a list of strings and a column number, determine if the next column contains a
-   * one or more axiom annotations. If so, return the axiom annotation or annotations as a set of
-   * OWLAnnotations.
-   *
-   * @param row list of strings
-   * @param column column number
-   * @return set of OWLAnnotations, maybe empty
-   * @throws Exception on issue getting the OWLAnnotations
-   */
-  private Set<OWLAnnotation> maybeGetAxiomAnnotations(List<String> row, int column)
-      throws Exception {
-    // Look at the template string of the next column
-    String nextTemplate;
-    try {
-      nextTemplate = templates.get(column + 1);
-    } catch (IndexOutOfBoundsException e) {
-      return new HashSet<>();
-    }
-
-    // If the next template string is not null, not empty, and it starts with >
-    // Get the axiom annotations from the row
-    if (!nextTemplate.trim().isEmpty() && (nextTemplate.startsWith(">"))) {
-      return getAxiomAnnotations(row, nextTemplate, column + 1);
-    } else {
-      return new HashSet<>();
-    }
-  }
-
   /**
    * Given a property string (label or CURIE) and the column of that template string, determine if
    * this is RDFS label and if so, set the label column.


Test patch:
diff --git a/robot-core/src/test/resources/legacy-template.csv b/robot-core/src/test/resources/legacy-template.csv
index 8928cec0..a244b8cc 100644
--- a/robot-core/src/test/resources/legacy-template.csv
+++ b/robot-core/src/test/resources/legacy-template.csv
@@ -1,5 +1,5 @@
-Label,Label Comment,Nested Comment,ID,Synonyms,Type,Parent,Parts,Parts Annotation
-A rdfs:label,>AL rdfs:comment@en,>>A rdfs:comment,ID,A IAO:0000118 SPLIT=|,CLASS_TYPE,C %,C part_of some %,>C rdfs:comment
+Label,Label Comment,Nested Comment,See Also,ID,Synonyms,Type,Parent,Parts,Parts Annotation
+A rdfs:label,>AL rdfs:comment@en,>>A rdfs:comment,>AI rdfs:seeAlso,ID,A IAO:0000118 SPLIT=|,CLASS_TYPE,C %,C part_of some %,>C rdfs:comment
 skip this row
-test 3,test 3 comment,test 3 comment comment,GO:1234,synonym 1|synonym 2,subclass,test2,test one,test one comment
-test 4,test 4 comment,,GO:1235,,equivalent,test one,(test2 and 'test 3'),test2 and test 3 comment
+test 3,test 3 comment,test 3 comment comment,http://robot.obolibrary.org/,GO:1234,synonym 1|synonym 2,subclass,test2,test one,test one comment
+test 4,test 4 comment,,,GO:1235,,equivalent,test one,(test2 and 'test 3'),test2 and test 3 comment
diff --git a/robot-core/src/test/resources/template.csv b/robot-core/src/test/resources/template.csv
index c5d30670..07ea632e 100644
--- a/robot-core/src/test/resources/template.csv
+++ b/robot-core/src/test/resources/template.csv
@@ -1,5 +1,5 @@
-Label,Label Comment,Nested Comment,ID,Synonyms,Parent,Parts,Parts Annotation,Equivalent,Eq Annotation
-A rdfs:label,>AL rdfs:comment@en,>>A rdfs:comment,ID,A IAO:0000118 SPLIT=|,SC %,SC part_of some %,>A rdfs:comment,EC %,>A rdfs:comment
+Label,Label Comment,Nested Comment,See Also,ID,Synonyms,Parent,Parts,Parts Annotation,Equivalent,Eq Annotation
+A rdfs:label,>AL rdfs:comment@en,>>A rdfs:comment,>AI rdfs:seeAlso,ID,A IAO:0000118 SPLIT=|,SC %,SC part_of some %,>A rdfs:comment,EC %,>A rdfs:comment
 skip this row
-test 3,test 3 comment,test 3 comment comment,GO:1234,synonym 1|synonym 2,test2,test one,test one comment,,
-test 4,test 4 comment,,GO:1235,,,,,'test one' and (part_of some (test2 and 'test 3')),test2 and test 3 comment
+test 3,test 3 comment,test 3 comment comment,http://robot.obolibrary.org/,GO:1234,synonym 1|synonym 2,test2,test one,test one comment,,
+test 4,test 4 comment,,,GO:1235,,,,,'test one' and (part_of some (test2 and 'test 3')),test2 and test 3 comment
\ No newline at end of file
diff --git a/robot-core/src/test/resources/template.owl b/robot-core/src/test/resources/template.owl
index 9e720b42..6822c092 100644
--- a/robot-core/src/test/resources/template.owl
+++ b/robot-core/src/test/resources/template.owl
@@ -8,10 +8,10 @@
      xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
      xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
     <Ontology rdf:about="http://test.com/template.owl"/>
-    
 
 
-    <!-- 
+
+    <!--
     ///////////////////////////////////////////////////////////////////////////////////////
     //
     // Classes
@@ -19,7 +19,7 @@
     ///////////////////////////////////////////////////////////////////////////////////////
      -->
 
-    
+
 
 
     <!-- http://purl.obolibrary.org/obo/GO_1234 -->
@@ -54,13 +54,14 @@
                 <annotatedProperty rdf:resource="http://www.w3.org/2000/01/rdf-schema#label"/>
                 <annotatedTarget rdf:datatype="http://www.w3.org/2001/XMLSchema#string">test 3</annotatedTarget>
                 <rdfs:comment xml:lang="en">test 3 comment</rdfs:comment>
+                <rdfs:seeAlso rdf:resource="http://robot.obolibrary.org/"/>
             </Axiom>
         </annotatedSource>
         <annotatedProperty rdf:resource="http://www.w3.org/2000/01/rdf-schema#comment"/>
         <annotatedTarget xml:lang="en">test 3 comment</annotatedTarget>
         <rdfs:comment rdf:datatype="http://www.w3.org/2001/XMLSchema#string">test 3 comment comment</rdfs:comment>
     </Annotation>
-    
+
 
 
     <!-- http://purl.obolibrary.org/obo/GO_1235 -->