/* 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.io.PrintWriter; import java.io.StringWriter; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.TreeSet; public class Hierarchy<E> { protected final HierarchyNode<E> m_topNode; protected final HierarchyNode<E> m_bottomNode; protected final Map<E,HierarchyNode<E>> m_nodesByElements; public Hierarchy(HierarchyNode<E> topNode,HierarchyNode<E> bottomNode) { m_topNode=topNode; m_bottomNode=bottomNode; m_nodesByElements=new HashMap<E,HierarchyNode<E>>(); for (E element : m_topNode.m_equivalentElements) m_nodesByElements.put(element,m_topNode); for (E element : m_bottomNode.m_equivalentElements) m_nodesByElements.put(element,m_bottomNode); } public HierarchyNode<E> getTopNode() { return m_topNode; } public HierarchyNode<E> getBottomNode() { return m_bottomNode; } public boolean isEmpty() { return m_nodesByElements.size()==2 && m_topNode.m_equivalentElements.size()==1 && m_bottomNode.m_equivalentElements.size()==1; } public HierarchyNode<E> getNodeForElement(E element) { return m_nodesByElements.get(element); } public Collection<HierarchyNode<E>> getAllNodes() { return Collections.unmodifiableCollection(m_nodesByElements.values()); } public Set<HierarchyNode<E>> getAllNodesSet() { return Collections.unmodifiableSet(new HashSet<HierarchyNode<E>>(m_nodesByElements.values())); } public Set<E> getAllElements() { return Collections.unmodifiableSet(m_nodesByElements.keySet()); } public int getDepth() { HierarchyDepthFinder<E> depthFinder=new HierarchyDepthFinder<E>(m_bottomNode); traverseDepthFirst(depthFinder); return depthFinder.depth; } protected final class HierarchyDepthFinder<T> implements Hierarchy.HierarchyNodeVisitor<T> { protected final HierarchyNode<T> m_bottomNode; protected int depth=0; public HierarchyDepthFinder(HierarchyNode<T> bottomNode) { m_bottomNode=bottomNode; } public boolean redirect(HierarchyNode<T>[] nodes) { return true; } public void visit(int level,HierarchyNode<T> node,HierarchyNode<T> parentNode,boolean firstVisit) { if (node.equals(m_bottomNode)&&level>depth) depth=level; } } public <T> Hierarchy<T> transform(Transformer<? super E,T> transformer,Comparator<T> comparator) { HierarchyNodeComparator<T> newNodeComparator=new HierarchyNodeComparator<T>(comparator); Map<HierarchyNode<E>,HierarchyNode<T>> oldToNew=new HashMap<HierarchyNode<E>,HierarchyNode<T>>(); for (HierarchyNode<E> oldNode : m_nodesByElements.values()) { Set<T> newEquivalentElements; Set<HierarchyNode<T>> newParentNodes; Set<HierarchyNode<T>> newChildNodes; if (comparator==null) { newEquivalentElements=new HashSet<T>(); newParentNodes=new HashSet<HierarchyNode<T>>(); newChildNodes=new HashSet<HierarchyNode<T>>(); } else { newEquivalentElements=new TreeSet<T>(comparator); newParentNodes=new TreeSet<HierarchyNode<T>>(newNodeComparator); newChildNodes=new TreeSet<HierarchyNode<T>>(newNodeComparator); } for (E oldElement : oldNode.m_equivalentElements) { T newElement=transformer.transform(oldElement); newEquivalentElements.add(newElement); } T newRepresentative=transformer.determineRepresentative(oldNode.m_representative,newEquivalentElements); HierarchyNode<T> newNode=new HierarchyNode<T>(newRepresentative,newEquivalentElements,newParentNodes,newChildNodes); oldToNew.put(oldNode,newNode); } for (HierarchyNode<E> oldParentNode : m_nodesByElements.values()) { HierarchyNode<T> newParentNode=oldToNew.get(oldParentNode); for (HierarchyNode<E> oldChildNode : oldParentNode.m_childNodes) { HierarchyNode<T> newChildNode=oldToNew.get(oldChildNode); newParentNode.m_childNodes.add(newChildNode); newChildNode.m_parentNodes.add(newParentNode); } } HierarchyNode<T> newTopNode=oldToNew.get(m_topNode); HierarchyNode<T> newBottomNode=oldToNew.get(m_bottomNode); Hierarchy<T> newHierarchy=new Hierarchy<T>(newTopNode,newBottomNode); for (HierarchyNode<T> newNode : oldToNew.values()) for (T newElement : newNode.m_equivalentElements) newHierarchy.m_nodesByElements.put(newElement,newNode); return newHierarchy; } @SuppressWarnings("unchecked") public void traverseDepthFirst(HierarchyNodeVisitor<E> visitor) { HierarchyNode<E>[] redirectBuffer=new HierarchyNode[2]; Set<HierarchyNode<E>> visited=new HashSet<HierarchyNode<E>>(); traverseDepthFirst(visitor,0,m_topNode,null,visited,redirectBuffer); } protected void traverseDepthFirst(HierarchyNodeVisitor<E> visitor,int level,HierarchyNode<E> node,HierarchyNode<E> parentNode,Set<HierarchyNode<E>> visited,HierarchyNode<E>[] redirectBuffer) { redirectBuffer[0]=node; redirectBuffer[1]=parentNode; if (visitor.redirect(redirectBuffer)) { node=redirectBuffer[0]; parentNode=redirectBuffer[1]; boolean firstVisit=visited.add(node); visitor.visit(level,node,parentNode,firstVisit); if (firstVisit) for (HierarchyNode<E> childNode : node.m_childNodes) traverseDepthFirst(visitor,level+1,childNode,node,visited,redirectBuffer); } } public String toString() { StringWriter buffer=new StringWriter(); final PrintWriter output=new PrintWriter(buffer); traverseDepthFirst(new HierarchyNodeVisitor<E>() { public boolean redirect(HierarchyNode<E>[] nodes) { return true; } public void visit(int level,HierarchyNode<E> node,HierarchyNode<E> parentNode,boolean firstVisit) { if (!node.equals(m_bottomNode)) printNode(level,node,parentNode,firstVisit); } public void printNode(int level,HierarchyNode<E> node,HierarchyNode<E> parentNode,boolean firstVisit) { Set<E> equivalences=node.getEquivalentElements(); boolean printSubClasOf=(parentNode!=null); boolean printEquivalences=firstVisit && equivalences.size()>1; if (printSubClasOf || printEquivalences) { for (int i=4*level;i>0;--i) output.print(' '); output.print(node.getRepresentative().toString()); if (printEquivalences) { output.print('['); boolean first=true; for (E element : equivalences) { if (!node.getRepresentative().equals(element)) { if (first) first=false; else output.print(' '); output.print(element); } } output.print(']'); } if (printSubClasOf) { output.print(" -> "); output.print(parentNode.getRepresentative().toString()); } output.println(); } } }); output.flush(); return buffer.toString(); } public static <T> Hierarchy<T> emptyHierarchy(Collection<T> elements,T topElement,T bottomElement) { HierarchyNode<T> topBottomNode=new HierarchyNode<T>(topElement); topBottomNode.m_equivalentElements.add(topElement); topBottomNode.m_equivalentElements.add(bottomElement); topBottomNode.m_equivalentElements.addAll(elements); return new Hierarchy<T>(topBottomNode,topBottomNode); } public static <T> Hierarchy<T> trivialHierarchy(T topElement,T bottomElement) { HierarchyNode<T> topNode=new HierarchyNode<T>(topElement); topNode.m_equivalentElements.add(topElement); HierarchyNode<T> bottomNode=new HierarchyNode<T>(bottomElement); bottomNode.m_equivalentElements.add(bottomElement); topNode.m_childNodes.add(bottomNode); bottomNode.m_parentNodes.add(topNode); return new Hierarchy<T>(topNode,bottomNode); } protected static interface HierarchyNodeVisitor<E> { boolean redirect(HierarchyNode<E>[] nodes); void visit(int level,HierarchyNode<E> node,HierarchyNode<E> parentNode,boolean firstVisit); } public static interface Transformer<E,T> { T transform(E element); T determineRepresentative(E oldRepresentative,Set<T> newEquivalentElements); } protected static class HierarchyNodeComparator<E> implements Comparator<HierarchyNode<E>> { protected final Comparator<E> m_elementComparator; public HierarchyNodeComparator(Comparator<E> elementComparator) { m_elementComparator=elementComparator; } public int compare(HierarchyNode<E> n1,HierarchyNode<E> n2) { return m_elementComparator.compare(n1.m_representative,n2.m_representative); } } }