+
+ /**
+ * {@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;
+ }