/** * Check to see if we have found a solution to the problem. TODO: we could throw an exception to * simulate a non-local exit here, since we've assumed that P is the unity set. * * @param uCls * @param vCls * @param index */ protected static void checkSolution(OntClass uCls, OntClass vCls, LCAIndex index) { DisjointSet vSet = index.getSet(vCls); DisjointSet uSet = index.getSet(uCls); if (vSet != null && vSet.isBlack() && !vSet.used() && uSet != null && uSet.isBlack() && !uSet.used()) { vSet.setUsed(); uSet.setUsed(); // log.debug( "Found LCA: u = " + uCls + ", v = " + vCls ); OntClass lca = (OntClass) vSet.find().getAncestor().getNode(); // log.debug( "Found LCA: lca = " + lca ); index.setLCA(uCls, vCls, lca); } }
/** * 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; }