All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.semanticweb.elk.reasoner.taxonomy.MockInstanceTaxonomy Maven / Gradle / Ivy

There is a newer version: 0.4.3
Show newest version
/**
 * 
 */
package org.semanticweb.elk.reasoner.taxonomy;
/*
 * #%L
 * ELK Reasoner
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2012 Department of Computer Science, University of Oxford
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */

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.SortedSet;
import java.util.TreeSet;

import org.semanticweb.elk.owl.interfaces.ElkObject;
import org.semanticweb.elk.owl.printers.OwlFunctionalStylePrinter;
import org.semanticweb.elk.reasoner.taxonomy.DepthFirstSearch.Direction;
import org.semanticweb.elk.reasoner.taxonomy.model.InstanceNode;
import org.semanticweb.elk.reasoner.taxonomy.model.InstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.Node;
import org.semanticweb.elk.reasoner.taxonomy.model.TaxonomyNode;
import org.semanticweb.elk.reasoner.taxonomy.model.TypeNode;
import org.semanticweb.elk.util.collections.ArrayHashSet;
import org.semanticweb.elk.util.collections.Operations;
import org.semanticweb.elk.util.collections.Operations.Condition;

/**
 * @author Pavel Klinov
 * 
 *         [email protected]
 */
public class MockInstanceTaxonomy implements InstanceTaxonomy {
	
	//Top should be made final, the only problem is inconsistency (when it holds a reference to Bot)
	protected ExtremeNode top;
	protected final MockBottomNode bottom;
	protected final Map, Set>> parentMap = new HashMap, Set>>();
	protected final Map, Set>> childrenMap = new HashMap, Set>>();
	protected final Map> typeIndex = new HashMap>();
	protected final Map instanceIndex = new HashMap();
	protected final Map>> instanceTypeMap = new HashMap>>();
	
	private int nodesWithoutParent = 0;
	private int nodesWithoutChildren = 0;
	private int instancesWithoutType = 0;
	
	protected final Comparator typeComparator;
	protected final Comparator instanceComparator;

	MockInstanceTaxonomy(T top, T bottom, Comparator tCmp, Comparator iCmp) {
		this.typeComparator = tCmp;
		this.instanceComparator = iCmp;		
		this.top = new MockTopNode(top);
		this.bottom = new MockBottomNode(bottom);
		
		initTypeNode(this.top);
		initTypeNode(this.bottom);
	}
		
	private void initTypeNode(TypeNode typeNode) {
		for (T member : typeNode.getMembers()) {
			typeIndex.put(member, typeNode);
		}
		
		parentMap.put(typeNode, new HashSet>());
		childrenMap.put(typeNode, new HashSet>());
	}

	@Override
	public TaxonomyNode getNode(T elkObject) {
		return getTypeNode(elkObject);
	}	
	
	@Override
	public Set> getNodes() {
		return getTypeNodes();
	}

	@Override
	public ExtremeNode getTopNode() {
		return top;
	}

	@Override
	public MockBottomNode getBottomNode() {
		return bottom;
	}

	
	@Override
	public MockTypeNode getTypeNode(T elkObject) {
		return (MockTypeNode) typeIndex.get(elkObject);
	}

	@Override
	public Set> getTypeNodes() {
		return parentMap.keySet();
	}

	@Override
	public MockInstanceNode getInstanceNode(I elkObject) {
		return instanceIndex.get(elkObject);
	}

	@Override
	public Set> getInstanceNodes() {
		return instanceTypeMap.keySet();
	}	
	
	protected MockTypeNode makeInconsistent() {
		parentMap.clear();
		childrenMap.clear();
		instanceIndex.clear();
		typeIndex.clear();
		instanceTypeMap.clear();
		bottom.addMembers(top.getMembers());
		top = bottom;
		//init the only node in the taxonomy
		initTypeNode(bottom);
		
		return bottom;
	}
	
	public boolean isConsistent() {
		return top != bottom;
	}
	
	@SuppressWarnings("unchecked")
	public MutableTypeNode getCreateTypeNode(Collection types) {
		// check for inconsistency
		if (types.contains(top.getCanonicalMember())
				&& types.contains(bottom.getCanonicalMember())) {
			return makeInconsistent();
		} else {
			// nodes for some types may already exist
			MutableTypeNode node = null;
			ExtremeNode extreme = null;

			for (T type : types) {
				if (type.equals(top.getCanonicalMember()) || type.equals(bottom.getCanonicalMember())) {
					//this is an unchecked cast. could be avoided at the expense of another LoC but i'm gonna punt on that
					extreme = (ExtremeNode)getTypeNode(type);
				}
				else {
					MockTypeNode typeNode = getTypeNode(type);
					// raise an error since this class doesn't support node merging
					assert node == null || node == typeNode;

					node = typeNode;
				}
			}

			if (node == null) {
				if (extreme != null) {
					extreme.addMembers(types);
					node = extreme;
				}
				else {
					node = new MockTypeNode(types);
					initTypeNode(node);
					nodesWithoutParent++;
					nodesWithoutChildren++;
				}
			} else {
				if (extreme != null) {
					extreme.merge(node);
				}
				else {
					node.addMembers(types);
				}
			}

			return node;
		}
	}
	

	public MockInstanceNode getCreateInstanceNode(Collection instances, Collection> types) {
		MockInstanceNode node = instanceIndex.get(instances.iterator().next());
		
		if (node == null) {
			node = new MockInstanceNode(instances, types);
			
			for (I instance : instances) {
				instanceIndex.put(instance, node);			
			}
		}
		
		return node;
	}	
	
	protected void remove(TypeNode node) {
		//clean-up all indices
		for (TypeNode subNode : node.getDirectSubNodes()) {
			parentMap.get(subNode).remove(node);
		}
		
		for (TypeNode supNode : node.getDirectSuperNodes()) {
			childrenMap.get(supNode).remove(node);
		}
		
		for (InstanceNode instanceNode : node.getDirectInstanceNodes()) {
			instanceTypeMap.get(instanceNode).remove(node);
		}
		//update counters
		if (parentMap.get(node).isEmpty()) {
			nodesWithoutParent--;
		}
		
		if (childrenMap.get(node).isEmpty()) {
			nodesWithoutChildren--;
		}
		//the final cleanup
		parentMap.remove(node);
		childrenMap.remove(node);
		instanceTypeMap.remove(node);
	}	
	
	/**
	 * 
	 * @author Pavel Klinov
	 *
	 * [email protected]
	 */
	interface MutableTypeNode extends TypeNode {
		void addDirectInstance(InstanceNode instNode);
		void addDirectParent(TypeNode parent);
		void addMembers(Collection members);
	}
	
	
	/**
	 * 
	 * @author Pavel Klinov
	 *
	 * [email protected]
	 */
	protected abstract class MockNode implements Node {

		final SortedSet members;
		
		MockNode(Collection members, Comparator cmp) {
			this.members = new TreeSet(cmp);
			this.members.addAll(members);
		}
		
		@Override
		public Set getMembers() {
			return members;
		}

		@Override
		public O getCanonicalMember() {
			return members.isEmpty() ? null : members.iterator().next();
		}
		
		protected abstract void addMembers(Collection members);

		@Override
		public String toString() {
			StringBuilder builder = new StringBuilder();
			
			for (O member : members) {
				builder.append(OwlFunctionalStylePrinter.toString(member) + ",");
			}
			
			return builder.toString();
		}
	}
	
	/**
	 * 
	 * @author Pavel Klinov
	 *
	 * [email protected]
	 */
	protected class MockTypeNode extends MockNode implements MutableTypeNode {
		
		final Set> instances = new ArrayHashSet>();

		MockTypeNode(Collection members) {
			super(members, typeComparator);
		}

		@Override
		public Set> getDirectInstanceNodes() {
			return instances;
		}

		@Override
		public Set> getAllInstanceNodes() {
			Set> ancestors = getAllSubNodes();
			Set> result = new ArrayHashSet>();
			
			result.addAll(instances);
			
			for (TypeNode ancestor : ancestors) {
				result.addAll(ancestor.getDirectInstanceNodes());
			}
			
			return result;
		}

		@Override
		public Set> getDirectSuperNodes() {
			Set> sup = MockInstanceTaxonomy.this.parentMap.get(this);
			
			return sup.isEmpty() ? Collections.>singleton(getTopNode()) : Collections.unmodifiableSet(sup);
		}

		@Override
		public Set> getAllSuperNodes() {
			Set> sups = new HashSet>();

			computeSuccessors(this, sups, Direction.UP);
			
			if (sups.size() > 2) {
				sups.remove(getTopNode());
				sups.remove(getBottomNode());
			}

			return sups;
		}

		@Override
		public Set> getDirectSubNodes() {
			Set> sub = MockInstanceTaxonomy.this.childrenMap.get(this);
			
			return sub.isEmpty() ? Collections.>singleton(getBottomNode()) : Collections.unmodifiableSet(sub);
		}

		@Override
		public Set> getAllSubNodes() {
			Set> subs = new HashSet>();

			computeSuccessors(this, subs, Direction.DOWN);
			
			if (subs.size() > 2) {
				subs.remove(getTopNode());
				subs.remove(getBottomNode());
			}

			return subs;
		}

		
		@Override
		public MockInstanceTaxonomy getTaxonomy() {
			return MockInstanceTaxonomy.this;
		}
		
		@Override
		public void addDirectParent(TypeNode parent) {
			assert parent.getTaxonomy() == getTaxonomy();

			if (this != getBottomNode() && parent != getTopNode()) {
				if (parent == getBottomNode()) {
					getBottomNode().merge(this);
				} else {
					if (childrenMap.get(parent).isEmpty()) {
						nodesWithoutChildren--;
					}

					if (parentMap.get(this).isEmpty()) {
						nodesWithoutParent--;
					}

					parentMap.get(this).add(parent);
					childrenMap.get(parent).add(this);
				}
			}
		}
		
		@Override
		public void addDirectInstance(InstanceNode instance) {
			assert instance.getTaxonomy() == getTaxonomy();
			assert this != getBottomNode();
			
			if (this != getTopNode()) { 
				if (instances.isEmpty()) {
					instancesWithoutType--;
				}
				
				MockInstanceTaxonomy.this.instanceTypeMap.get(instance).add(this);
				instances.add(instance);
			}
		}
		
		private void computeSuccessors(TypeNode node, Set> successors, Direction dir) {

			for (TypeNode succ : dir == Direction.UP ? node.getDirectSuperNodes() : node.getDirectSubNodes()) {
				successors.add(succ);
				computeSuccessors(succ, successors, dir);
			}
		}

		@Override
		public void addMembers(Collection members) {
			for (T member : members) {
				this.members.add(member);
				MockInstanceTaxonomy.this.typeIndex.put(member, this);
			}
		}
	}
	
	/**
	 * Only Top and Bot implement this since they're the only ones supporting the merge op so far
	 * 
	 * @author Pavel Klinov
	 *
	 * [email protected]
	 */
	interface ExtremeNode extends MutableTypeNode {
		void merge(TypeNode node);
	}
	
	/**
	 * 
	 * @author Pavel Klinov
	 *
	 * [email protected]
	 */
	class MockTopNode extends MockTypeNode implements ExtremeNode {

		MockTopNode(T top) {
			super(Collections.singleton(top));
		}

		@Override
		public void merge(TypeNode node) {
			// Merge that node into top
			// It's possible to have a generic "merge" method but it's
			// non-trivial
			addMembers(node.getMembers());
			// No need to do anything with instances
			remove(node);
		}
		
		@Override
		public void addDirectParent(TypeNode node) {
			if (node == getBottomNode()) {
				// this is the special case of an unsatisfiable taxonomy
				makeInconsistent();
			} else {
				merge(node);
			}
		}

		@Override
		public Set> getDirectInstanceNodes() {
			// all instances that don't have a direct type
			return 	Operations.filter(MockInstanceTaxonomy.this.instanceTypeMap.keySet(),
					new Condition>() {
				@Override
				public boolean holds(InstanceNode node) {
					return MockInstanceTaxonomy.this.instanceTypeMap.get(node).isEmpty();
				}
			}, instancesWithoutType);
		}

		@Override
		public Set> getAllInstanceNodes() {
			// all instances in the taxonomy
			return MockInstanceTaxonomy.this.instanceTypeMap.keySet();
		}

		@Override
		public Set> getDirectSuperNodes() {
			return Collections.emptySet();
		}

		@Override
		public Set> getAllSuperNodes() {
			return Collections.emptySet();
		}

		@Override
		public Set> getDirectSubNodes() {
			// all type nodes that have no non-top parent
			final boolean empty = MockInstanceTaxonomy.this.childrenMap.size() == 2;
			
			return Operations.filter(MockInstanceTaxonomy.this.parentMap.keySet(),
					new Condition>() {
				@Override
				public boolean holds(TaxonomyNode node) {
					return node != getTopNode() //it's not Top
							&& MockInstanceTaxonomy.this.parentMap.get(node).isEmpty() // no direct parents
							&& (node != getBottomNode() || empty); //bottom but there're no intermediate nodes
				}
			}, empty ? 1 : nodesWithoutParent);
		}

		@Override
		public Set> getAllSubNodes() {
			// all nodes
			return Operations.filter(MockInstanceTaxonomy.this.parentMap.keySet(),
					new Condition>() {
				@Override
				public boolean holds(TaxonomyNode node) {
					return node != getTopNode();
				}
			}, MockInstanceTaxonomy.this.parentMap.keySet().size() - 1);
		}
	}
	
	/**
	 * 
	 * @author Pavel Klinov
	 *
	 * [email protected]
	 */
	class MockBottomNode extends MockTypeNode implements ExtremeNode {

		MockBottomNode(T bot) {
			super(Collections.singleton(bot));
		}

		@Override
		public Set> getDirectInstanceNodes() {
			return Collections.emptySet();
		}

		@Override
		public Set> getAllInstanceNodes() {
			return Collections.emptySet();
		}

		@Override
		public void merge(TypeNode node) {
			addMembers(node.getMembers());
			
			if (!node.getDirectInstanceNodes().isEmpty()) {
				makeInconsistent();
			}
			else {
				remove(node);
			}
		}		
		
		@Override
		public Set> getDirectSuperNodes() {
			// all type nodes that have no non-bot children
			final boolean empty = MockInstanceTaxonomy.this.childrenMap.size() == 2;
			
			return Operations.filter(
					MockInstanceTaxonomy.this.childrenMap.keySet(),
					new Condition>() {
						@Override
						public boolean holds(TaxonomyNode node) {
							return node != getBottomNode() && //not Bot
									MockInstanceTaxonomy.this.childrenMap.get(node).isEmpty() // no children
									&& (node != getTopNode() || empty); //Top but there're no intermediate nodes;
						}
					}, empty ? 1 : nodesWithoutChildren);
		}

		@Override
		public Set> getAllSuperNodes() {
			return Operations.filter(
					MockInstanceTaxonomy.this.childrenMap.keySet(),
					new Condition>() {
						@Override
						public boolean holds(TaxonomyNode node) {
							return node != getBottomNode();
						}
					}, MockInstanceTaxonomy.this.childrenMap.size() - 1);
		}

		@Override
		public Set> getDirectSubNodes() {
			return Collections.emptySet();
		}

		@Override
		public Set> getAllSubNodes() {
			return Collections.emptySet();
		}

		@Override
		public void addDirectInstance(InstanceNode instance) {
			makeInconsistent();
		}
	}	
	
	
	/**
	 * 
	 * @author Pavel Klinov
	 *
	 * [email protected]
	 */
	protected class MockInstanceNode extends MockNode implements InstanceNode {

		MockInstanceNode(Collection members, Collection> types) {
			super(members, instanceComparator);
			
			instancesWithoutType++;
			MockInstanceTaxonomy.this.instanceTypeMap.put(this, new ArrayHashSet>());

			for (TypeNode type : types) {
				assert type != null;
				assert type.getTaxonomy() == getTaxonomy();
				
				((MockTypeNode)type).addDirectInstance(this);
			}
		}

		@Override
		public Set> getDirectTypeNodes() {
			Set> typeNodes = MockInstanceTaxonomy.this.instanceTypeMap.get(this);
			
			return typeNodes.isEmpty() ? Collections.singleton(getTopNode()) : typeNodes;
		}

		@Override
		public Set> getAllTypeNodes() {
			Set> allTypes = new HashSet>();
			
			for (TypeNode aType : getDirectTypeNodes()) {
				allTypes.addAll(aType.getAllSuperNodes());
				allTypes.add(aType);
			}
			
			if (allTypes.size() > 1) {
				allTypes.remove(getTopNode());
			}
			
			return allTypes;
		}

		@Override
		public InstanceTaxonomy getTaxonomy() {
			return MockInstanceTaxonomy.this;
		}
		
		@Override
		protected void addMembers(Collection members) {
			for (I member : members) {
				this.members.add(member);
				MockInstanceTaxonomy.this.instanceIndex.put(member, this);
			}
		}		
	}
}