Merge branch 'develop' into features/JAL-3010ontologyFeatureSettings
[jalview.git] / src / jalview / ext / so / SequenceOntology.java
index 0d631e6..8a3805d 100644 (file)
@@ -20,6 +20,7 @@
  */
 package jalview.ext.so;
 
+import jalview.datamodel.ontology.OntologyBase;
 import jalview.io.gff.SequenceOntologyI;
 
 import java.io.BufferedInputStream;
@@ -34,6 +35,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.NoSuchElementException;
+import java.util.Set;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipInputStream;
 
@@ -48,7 +50,8 @@ import org.biojava.nbio.ontology.utils.Annotation;
  * A wrapper class that parses the Sequence Ontology and exposes useful access
  * methods. This version uses the BioJava parser.
  */
-public class SequenceOntology implements SequenceOntologyI
+public class SequenceOntology extends OntologyBase
+        implements SequenceOntologyI
 {
   /*
    * the parsed Ontology data as modelled by BioJava
@@ -82,10 +85,10 @@ public class SequenceOntology implements SequenceOntologyI
    */
   public SequenceOntology()
   {
-    termsFound = new ArrayList<String>();
-    termsNotFound = new ArrayList<String>();
-    termsByDescription = new HashMap<String, Term>();
-    termIsA = new HashMap<Term, List<Term>>();
+    termsFound = new ArrayList<>();
+    termsNotFound = new ArrayList<>();
+    termsByDescription = new HashMap<>();
+    termIsA = new HashMap<>();
 
     loadOntologyZipFile("so-xp-simple.obo");
   }
@@ -404,7 +407,7 @@ public class SequenceOntology implements SequenceOntologyI
    */
   protected synchronized void findParents(Term childTerm)
   {
-    List<Term> result = new ArrayList<Term>();
+    List<Term> result = new ArrayList<>();
     for (Triple triple : ontology.getTriples(childTerm, null, isA))
     {
       Term parent = triple.getObject();
@@ -471,4 +474,82 @@ public class SequenceOntology implements SequenceOntologyI
       return termsNotFound;
     }
   }
+
+  /**
+   * {@inheritDoc}
+   * 
+   * @throws IllegalStateException
+   *           if a loop is detected in the ontology
+   */
+  @Override
+  public List<String> getRootParents(final String term)
+  {
+    /*
+     * check in cache first
+     */
+    if (rootParents.containsKey(term))
+    {
+      return rootParents.get(term);
+    }
+    Term t = getTerm(term);
+    if (t == null)
+    {
+      return null;
+    }
+
+    /*
+     * todo: check for loops using 'seen', allowing for alternate paths e.g.
+     * stop_gained isA feature_truncation isA feature_variant
+     * " isA nonsynonymous_variant ... isA geneVariant isA feature_variant 
+     */
+    List<Term> seen = new ArrayList<>();
+    List<Term> top = new ArrayList<>();
+    List<Term> query = new ArrayList<>();
+    query.add(t);
+
+    while (!query.isEmpty())
+    {
+      List<Term> nextQuery = new ArrayList<>();
+      for (Term q : query)
+      {
+        Set<Triple> parents = ontology.getTriples(q, null, isA);
+        if (parents.isEmpty())
+        {
+          /*
+           * q has no parents so is a top level term
+           */
+          top.add(q);
+        }
+        else
+        {
+          /*
+           * search all parent terms
+           */
+          for (Triple triple : parents)
+          {
+            Term parent = triple.getObject();
+            nextQuery.add(parent);
+          }
+        }
+      }
+      query = nextQuery;
+    }
+
+    List<String> result = new ArrayList<>();
+    for (Term found : top)
+    {
+      String desc = found.getDescription();
+      if (!result.contains(desc))
+      {
+        result.add(desc);
+      }
+    }
+
+    /*
+     * save result in cache
+     */
+    rootParents.put(term, result);
+
+    return result;
+  }
 }