/** * Partition the members of an iterator into two lists, according to whether they are named or * anonymous classes * * @param i An iterator to partition * @param named A list of named classes * @param anon A list of anonymous classes */ protected static void partitionByNamed( Iterator<? extends OntClass> i, List<OntClass> named, List<OntClass> anon) { while (i.hasNext()) { OntClass c = i.next(); boolean ignore = false; // duplicate check: we ignore this class if we've already got it if (named.contains(c)) { ignore = true; } // subsumption check: c must have only anon classes or Thing // as super-classes to still qualify as a root class Resource thing = c.getProfile().THING(); for (Iterator<OntClass> j = c.listSuperClasses(); !ignore && j.hasNext(); ) { OntClass sup = j.next(); if (!((thing != null && sup.equals(thing)) || sup.isAnon() || sup.equals(c))) { ignore = true; } } if (!ignore) { // place the class in the appropriate partition (c.isAnon() ? anon : named).add(c); } } }
/** * Answer a list of the named hierarchy roots of a given {@link OntModel}. This will be similar to * the results of {@link OntModel#listHierarchyRootClasses()}, with the added constraint that * every member of the returned iterator will be a named class, not an anonymous class expression. * The named root classes are calculated from the root classes, by recursively replacing every * anonymous class with its direct sub-classes. Thus it can be seen that the values in the list * consists of the shallowest fringe of named classes in the hierarchy. * * @param m An ontology model * @return A list of classes whose members are the named root classes of the class hierarchy in * <code>m</code> */ public static List<OntClass> namedHierarchyRoots(OntModel m) { List<OntClass> nhr = new ArrayList<OntClass>(); // named roots List<OntClass> ahr = new ArrayList<OntClass>(); // anon roots // do the initial partition of the root classes partitionByNamed(m.listHierarchyRootClasses(), nhr, ahr); // now push the fringe down until we have only named classes while (!ahr.isEmpty()) { OntClass c = ahr.remove(0); partitionByNamed(c.listSubClasses(true), nhr, ahr); } return nhr; }
/** * Answer the lowest common ancestor of two classes, assuming that the given class is the root * concept to start searching from. See {@link #getLCA(OntModel, OntClass, OntClass)} for details. * * @param m The ontology model being queried to find the LCA, which should conform to the reasoner * capabilities described above * @param root The root concept, which will be the starting point for the algorithm * @param u An ontology class * @param v An ontology class * @return The LCA of <code>u</code> and <code>v</code> * @exception JenaException if the language profile of the given model does not define a top * concept (e.g. <code>owl:Thing</code>) */ public static OntClass getLCA(OntModel m, OntClass root, OntClass u, OntClass v) { // check some common cases first if (u.equals(root) || v.equals(root)) { return root; } if (u.hasSubClass(v)) { return u; } if (v.hasSubClass(u)) { return v; } // not a common case, so apply Tarjan's LCA algorithm LCAIndex index = new LCAIndex(); lca(root, u, v, index); return (OntClass) index.getLCA(u, v); }
/** * Compute the LCA disjoint set at <code>cls</code>, noting that we are searching for the LCA of * <code>uCls</code> and <code>vCls</code>. * * @param cls The class we are testing (this is 'u' in the Wiki article) * @param uCls One of the two classes we are searching for the LCA of. We have simplified the set * P of pairs to the unity set {uCls,vCls} * @param vCls One of the two classes we are searching for the LCA of. We have simplified the set * P of pairs to the unity set {uCls,vCls} * @param index A data structure mapping resources to disjoint sets (since we can't side-effect * Jena resources), and which is used to record the LCA pairs */ protected static DisjointSet lca(OntClass cls, OntClass uCls, OntClass vCls, LCAIndex index) { // log.debug( "Entering lca(), cls = " + cls ); DisjointSet clsSet = index.getSet(cls); if (clsSet.isBlack()) { // already visited return clsSet; } // not visited yet clsSet.setAncestor(clsSet); // for each child of cls for (Iterator<OntClass> i = cls.listSubClasses(true); i.hasNext(); ) { OntClass child = i.next(); if (child.equals(cls) || child.equals(cls.getProfile().NOTHING())) { // we ignore the reflexive case and bottom continue; } // compute the LCA of the sub-tree DisjointSet v = lca(child, uCls, vCls, index); // union the two disjoint sets together clsSet.union(v); // propagate the distinguished member clsSet.find().setAncestor(clsSet); } // this node is done clsSet.setBlack(); // are we inspecting one of the elements we're interested in? if (cls.equals(uCls)) { checkSolution(uCls, vCls, index); } else if (cls.equals(vCls)) { checkSolution(vCls, uCls, index); } return clsSet; }