/* Copyright 2009 by the Oxford University Computing Laboratory This file is part of HermiT. HermiT is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. HermiT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with HermiT. If not, see <http://www.gnu.org/licenses/>. */ package org.semanticweb.HermiT.hierarchy; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.Queue; import java.util.Set; public class HierarchySearch { public static <E> HierarchyNode<E> findPosition(Relation<E> hierarchyRelation,E element,HierarchyNode<E> topNode,HierarchyNode<E> bottomNode) { Set<HierarchyNode<E>> parentNodes=findParents(hierarchyRelation,element,topNode); Set<HierarchyNode<E>> childNodes=findChildren(hierarchyRelation,element,bottomNode,parentNodes); if (parentNodes.equals(childNodes)) { assert parentNodes.size()==1 && childNodes.size()==1; return parentNodes.iterator().next(); } else { Set<E> equivalentElements=new HashSet<E>(); equivalentElements.add(element); return new HierarchyNode<E>(element,equivalentElements,parentNodes,childNodes); } } protected static <E> Set<HierarchyNode<E>> findParents(final Relation<E> hierarchyRelation,final E element,HierarchyNode<E> topNode) { return search( new SearchPredicate<HierarchyNode<E>>() { public Set<HierarchyNode<E>> getSuccessorElements(HierarchyNode<E> u) { return u.m_childNodes; } public Set<HierarchyNode<E>> getPredecessorElements(HierarchyNode<E> u) { return u.m_parentNodes; } public boolean trueOf(HierarchyNode<E> u) { return hierarchyRelation.doesSubsume(u.getRepresentative(),element); } },Collections.singleton(topNode),null); } protected static <E> Set<HierarchyNode<E>> findChildren(final Relation<E> hierarchyRelation,final E element,HierarchyNode<E> bottomNode,Set<HierarchyNode<E>> parentNodes) { if (parentNodes.size()==1 && hierarchyRelation.doesSubsume(element,parentNodes.iterator().next().getRepresentative())) return parentNodes; else { // We now determine the set of nodes that are descendants of each node in parentNodes Iterator<HierarchyNode<E>> parentNodesIterator=parentNodes.iterator(); Set<HierarchyNode<E>> marked=new HashSet<HierarchyNode<E>>(parentNodesIterator.next().getDescendantNodes()); while (parentNodesIterator.hasNext()) { Set<HierarchyNode<E>> freshlyMarked=new HashSet<HierarchyNode<E>>(); Set<HierarchyNode<E>> visited=new HashSet<HierarchyNode<E>>(); Queue<HierarchyNode<E>> toProcess=new LinkedList<HierarchyNode<E>>(); toProcess.add(parentNodesIterator.next()); while (!toProcess.isEmpty()) { HierarchyNode<E> currentNode=toProcess.remove(); for (HierarchyNode<E> childNode : currentNode.m_childNodes) if (marked.contains(childNode)) freshlyMarked.add(childNode); else if (visited.add(childNode)) toProcess.add(childNode); } toProcess.addAll(freshlyMarked); while (!toProcess.isEmpty()) { HierarchyNode<E> currentNode=toProcess.remove(); for (HierarchyNode<E> childNode : currentNode.m_childNodes) if (freshlyMarked.add(childNode)) toProcess.add(childNode); } marked=freshlyMarked; } // Determine the subset of marked that is directly above the bottomNode and that is below the current element. Set<HierarchyNode<E>> aboveBottomNodes=new HashSet<HierarchyNode<E>>(); for (HierarchyNode<E> node : marked) if (node.m_childNodes.contains(bottomNode) && hierarchyRelation.doesSubsume(element,node.getRepresentative())) aboveBottomNodes.add(node); // If this set is empty, then we omit the bottom search phase. if (aboveBottomNodes.isEmpty()) { Set<HierarchyNode<E>> childNodes=new HashSet<HierarchyNode<E>>(); childNodes.add(bottomNode); return childNodes; } else { return search( new SearchPredicate<HierarchyNode<E>>() { public Set<HierarchyNode<E>> getSuccessorElements(HierarchyNode<E> u) { return u.m_parentNodes; } public Set<HierarchyNode<E>> getPredecessorElements(HierarchyNode<E> u) { return u.m_childNodes; } public boolean trueOf(HierarchyNode<E> u) { return hierarchyRelation.doesSubsume(element,u.getRepresentative()); } },aboveBottomNodes,marked); } } } public static <U> Set<U> search(SearchPredicate<U> searchPredicate,Collection<U> startSearch,Set<U> possibilities) { SearchCache<U> cache=new SearchCache<U>(searchPredicate,possibilities); Set<U> result=new HashSet<U>(); Set<U> visited=new HashSet<U>(startSearch); Queue<U> toProcess=new LinkedList<U>(startSearch); while (!toProcess.isEmpty()) { U current=toProcess.remove(); boolean foundSubordinateElement=false; Set<U> subordinateElements=searchPredicate.getSuccessorElements(current); for (U subordinateElement : subordinateElements) if (cache.trueOf(subordinateElement)) { foundSubordinateElement=true; if (visited.add(subordinateElement)) toProcess.add(subordinateElement); } if (!foundSubordinateElement) result.add(current); } return result; } public static interface Relation<U> { boolean doesSubsume(U parent,U child); } public static interface SearchPredicate<U> { Set<U> getSuccessorElements(U u); Set<U> getPredecessorElements(U u); boolean trueOf(U u); } protected static final class SearchCache<U> { protected final SearchPredicate<U> m_searchPredicate; protected final Set<U> m_possibilities; protected final Set<U> m_positives; protected final Set<U> m_negatives; public SearchCache(SearchPredicate<U> f,Set<U> possibilities) { m_searchPredicate=f; m_possibilities=possibilities; m_positives=new HashSet<U>(); m_negatives=new HashSet<U>(); } public boolean trueOf(U element) { if (m_positives.contains(element)) return true; else if (m_negatives.contains(element) || (m_possibilities!=null && !m_possibilities.contains(element))) return false; else { for (U superordinateElement : m_searchPredicate.getPredecessorElements(element)) { if (!trueOf(superordinateElement)) { m_negatives.add(element); return false; } } if (m_searchPredicate.trueOf(element)) { m_positives.add(element); return true; } else { m_negatives.add(element); return false; } } } } }