/*
 * #%L
 * ELK Reasoner
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2013 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%
 */
package org.semanticweb.elk.reasoner.stages;

import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;

import org.semanticweb.elk.owl.interfaces.ElkClass;
import org.semanticweb.elk.owl.interfaces.ElkNamedIndividual;
import org.semanticweb.elk.owl.interfaces.ElkObject;
import org.semanticweb.elk.reasoner.indexing.classes.DifferentialIndex;
import org.semanticweb.elk.reasoner.indexing.classes.OntologyIndexDummyChangeListener;
import org.semanticweb.elk.reasoner.indexing.conversion.ElkPolarityExpressionConverter;
import org.semanticweb.elk.reasoner.indexing.conversion.ElkPolarityExpressionConverterImpl;
import org.semanticweb.elk.reasoner.indexing.model.IndexedContextRoot;
import org.semanticweb.elk.reasoner.indexing.model.IndexedIndividual;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableIndexedClassExpression;
import org.semanticweb.elk.reasoner.indexing.model.OntologyIndex;
import org.semanticweb.elk.reasoner.saturation.SaturationState;
import org.semanticweb.elk.reasoner.saturation.SaturationStateDummyChangeListener;
import org.semanticweb.elk.reasoner.saturation.context.Context;
import org.semanticweb.elk.reasoner.taxonomy.ConcurrentInstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.DummyInstanceTaxonomyListener;
import org.semanticweb.elk.reasoner.taxonomy.DummyNodeStoreListener;
import org.semanticweb.elk.reasoner.taxonomy.ElkIndividualKeyProvider;
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.NodeStore;
import org.semanticweb.elk.reasoner.taxonomy.model.UpdateableInstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.UpdateableTaxonomy;
import org.semanticweb.elk.util.collections.Operations;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Stores information about the state of the instance taxonomy
 *
 * @author Pavel Klinov
 *
 *         pavel.klinov@uni-ulm.de
 * @author Peter Skocovsky
 */
public class InstanceTaxonomyState {

	private static final Logger LOGGER_ = LoggerFactory
			.getLogger(InstanceTaxonomyState.class);

	private UpdateableInstanceTaxonomy<ElkClass, ElkNamedIndividual> taxonomy_ = null;

	/**
	 * Contains at least all individuals that are in ontology, but either were
	 * removed from taxonomy or their type nodes in taxonomy were removed since
	 * the last completed construction of the taxonomy.
	 */
	private final Queue<IndexedIndividual> toAdd_ = new ConcurrentLinkedQueue<IndexedIndividual>();

	/**
	 * Contains at least all individuals that are in taxonomy, but either were
	 * removed from ontology or their context became not saturated since the
	 * last completed construction of the taxonomy.
	 */
	private final Queue<IndexedIndividual> toRemove_ = new ConcurrentLinkedQueue<IndexedIndividual>();

	private final OntologyIndex ontologyIndex_;

	private final SaturationState<?> saturationState_;

	private final ElkPolarityExpressionConverter converter_;

	public <C extends Context> InstanceTaxonomyState(
			final SaturationState<C> saturationState,
			final DifferentialIndex ontologyIndex,
			final ElkObject.Factory elkFactory) {

		this.ontologyIndex_ = ontologyIndex;
		this.saturationState_ = saturationState;
		this.converter_ = new ElkPolarityExpressionConverterImpl(elkFactory,
				ontologyIndex);

		ontologyIndex.addListener(new OntologyIndexDummyChangeListener() {

			@Override
			public void individualAddition(final IndexedIndividual ind) {
				toAdd_.add(ind);
			}

			@Override
			public void individualRemoval(final IndexedIndividual ind) {
				toRemove_.add(ind);
			}

		});

		saturationState
				.addListener(new SaturationStateDummyChangeListener<C>() {

					@Override
					public void contextMarkNonSaturated(final C context) {
						final IndexedContextRoot root = context.getRoot();
						if (root instanceof IndexedIndividual) {
							final IndexedIndividual ind = (IndexedIndividual) root;
							toRemove_.add(ind);
						}
					}

				});

	}

	private final NodeStore.Listener<ElkNamedIndividual> nodeStoreListener_ = new DummyNodeStoreListener<ElkNamedIndividual>() {

		@Override
		public void memberForNodeDisappeared(final ElkNamedIndividual member,
				final Node<ElkNamedIndividual> node) {
			addToAdd(member);
		}

	};

	private final InstanceTaxonomy.Listener<ElkClass, ElkNamedIndividual> taxonomyListener_ = new DummyInstanceTaxonomyListener<ElkClass, ElkNamedIndividual>() {

		@Override
		public void directTypeNodesDisappeared(
				final InstanceNode<ElkClass, ElkNamedIndividual> instanceNode) {
			for (final ElkNamedIndividual elkIndividual : instanceNode) {
				addToAdd(elkIndividual);
			}
		};

	};

	public UpdateableInstanceTaxonomy<ElkClass, ElkNamedIndividual> getTaxonomy() {
		return taxonomy_;
	}

	private void addToAdd(final ElkNamedIndividual elkIndividual) {
		ModifiableIndexedClassExpression converted = elkIndividual
				.accept(converter_);
		if (converted != null && (converted instanceof IndexedIndividual)) {
			final IndexedIndividual ind = (IndexedIndividual) converted;
			toAdd_.add(ind);
		}
	}

	/**
	 * Prunes {@link #toAdd_}.
	 *

	 * {@code taxonomy_} must not be {@code null}!
	 *
	 * @return The resulting size of {@link #toAdd_}.
	 */
	private int pruneToAdd() {
		final Iterator<IndexedIndividual> iter = toAdd_.iterator();
		int size = 0;
		while (iter.hasNext()) {
			final IndexedIndividual ind = iter.next();
			/* @formatter:off
			 *
			 * Should be pruned when:
			 * it is not in ontology, or
			 * it has types in taxonomy.
			 *
			 * @formatter:on
			 */
			if (!ind.occurs()) {
				iter.remove();
				continue;
			}
			// else
			final Context context = saturationState_.getContext(ind);
			if (context == null || !context.isInitialized()
					|| !context.isSaturated()) {
				// it is not saturated.
				size++;
				continue;
			}
			// else
			final InstanceNode<ElkClass, ElkNamedIndividual> node = taxonomy_
					.getInstanceNode(ind.getElkEntity());
			if (node == null) {
				// it is not in taxonomy
				size++;
				continue;
			}
			// else
			if (!node.getDirectTypeNodes().isEmpty()) {
				iter.remove();
				continue;
			}
			// else
			size++;
		}
		return size;
	}

	/**
	 * Returns collection that contains at least all individuals that are in
	 * ontology, but either are removed from taxonomy or their type nodes in
	 * taxonomy were removed.
	 *
	 * @return collection that contains at least all individuals that are in
	 *         ontology, but either are removed from taxonomy or their type
	 *         nodes in taxonomy were removed.
	 */
	Collection<IndexedIndividual> getToAdd() {
		if (taxonomy_ == null) {
			// No individual can be pruned.
			return toAdd_;
		}
		// else
		final int size = pruneToAdd();
		/*
		 * since getting the size of the queue is a linear operation, use the
		 * computed size
		 */
		return Operations.getCollection(toAdd_, size);
	}

	/**
	 * Prunes {@link #toRemove_}.
	 *

	 * {@code taxonomy_} must not be {@code null}!
	 *
	 * @return The resulting size of {@link #toRemove_}.
	 */
	private int pruneToRemove() {
		final Iterator<IndexedIndividual> iter = toRemove_.iterator();
		int size = 0;
		while (iter.hasNext()) {
			final IndexedIndividual cls = iter.next();
			/* @formatter:off
			 *
			 * Should be pruned when
			 * it is not in taxonomy.
			 *
			 * @formatter:on
			 */
			final InstanceNode<ElkClass, ElkNamedIndividual> node = taxonomy_
					.getInstanceNode(cls.getElkEntity());
			if (node == null) {
				iter.remove();
				continue;
			}
			// else
			size++;
		}
		return size;
	}

	/**
	 * Returns collection that contains at least all individuals that are in
	 * taxonomy, but either are removed from ontology or their context became
	 * not saturated.
	 *
	 * @return collection that contains at least all individuals that are in
	 *         taxonomy, but either are removed from ontology or their context
	 *         became not saturated.
	 */
	Collection<IndexedIndividual> getToRemove() {
		if (taxonomy_ == null) {// TODO: Never set taxonomy_ to null !!!
			// no individuals are in taxonomy
			toRemove_.clear();
			return Collections.emptyList();
		}
		// else
		final int size = pruneToRemove();
		/*
		 * since getting the size of the queue is a linear operation, use the
		 * computed size
		 */
		return Operations.getCollection(toRemove_, size);
	}

	private void resetTaxonomy(
			final UpdateableTaxonomy<ElkClass> classTaxonomy) {
		LOGGER_.trace("Reset instance taxonomy");

		if (taxonomy_ != null) {
			taxonomy_.removeInstanceListener(nodeStoreListener_);
			taxonomy_.removeInstanceListener(taxonomyListener_);
		}
		taxonomy_ = new ConcurrentInstanceTaxonomy(classTaxonomy,
				ElkIndividualKeyProvider.INSTANCE);
		taxonomy_.addInstanceListener(nodeStoreListener_);
		taxonomy_.addInstanceListener(taxonomyListener_);

		// All individuals need to be added to the taxonomy
		toRemove_.clear();
		toAdd_.clear();
		toAdd_.addAll(ontologyIndex_.getIndividuals());

	}

	/**
	 * Notifies this {@link InstanceTaxonomyState} that the taxonomy has just
	 * been completely construction.
	 */
	void taxonomyComplete() {
		// Clear pending individuals.
		toRemove_.clear();
		toAdd_.clear();
	}

	private final ClassTaxonomyState.Listener CLASS_TAXONOMY_STATE_LISTENER = new ClassTaxonomyState.Listener() {

		@Override
		public void taxonomyReset(
				final UpdateableTaxonomy<ElkClass> oldTaxonomy,
				final UpdateableTaxonomy<ElkClass> newTaxonomy) {
			resetTaxonomy(newTaxonomy);
		}

	};

	public ClassTaxonomyState.Listener getClassTaxonomyStateListener() {
		return CLASS_TAXONOMY_STATE_LISTENER;
	}

}


/*
 * #%L
 * elk-reasoner
 *
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2011 Oxford University Computing Laboratory
 * %%
 * 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%
 */
package org.semanticweb.elk.reasoner.taxonomy;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.semanticweb.elk.owl.interfaces.ElkClass;
import org.semanticweb.elk.owl.interfaces.ElkEntity;
import org.semanticweb.elk.owl.interfaces.ElkNamedIndividual;
import org.semanticweb.elk.owl.predefined.PredefinedElkClassFactory;
import org.semanticweb.elk.reasoner.taxonomy.impl.AbstractInstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.impl.ConcurrentNodeStore;
import org.semanticweb.elk.reasoner.taxonomy.impl.IndividualNode;
import org.semanticweb.elk.reasoner.taxonomy.impl.UpdateableInstanceNode;
import org.semanticweb.elk.reasoner.taxonomy.impl.UpdateableNodeStore;
import org.semanticweb.elk.reasoner.taxonomy.impl.UpdateableTypeNode;
import org.semanticweb.elk.reasoner.taxonomy.model.ComparatorKeyProvider;
import org.semanticweb.elk.reasoner.taxonomy.model.GenericInstanceNode;
import org.semanticweb.elk.reasoner.taxonomy.model.GenericTypeNode;
import org.semanticweb.elk.reasoner.taxonomy.model.InstanceNode;
import org.semanticweb.elk.reasoner.taxonomy.model.InstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.NodeFactory;
import org.semanticweb.elk.reasoner.taxonomy.model.NodeStore;
import org.semanticweb.elk.reasoner.taxonomy.model.NonBottomTaxonomyNode;
import org.semanticweb.elk.reasoner.taxonomy.model.NonBottomTypeNode;
import org.semanticweb.elk.reasoner.taxonomy.model.Taxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.TaxonomyNode;
import org.semanticweb.elk.reasoner.taxonomy.model.TypeNode;
import org.semanticweb.elk.reasoner.taxonomy.model.UpdateableInstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.UpdateableTaxonomy;
import org.semanticweb.elk.util.collections.Operations;
import org.semanticweb.elk.util.collections.Operations.FunctorEx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Instance taxonomy that is suitable for concurrent processing. Taxonomy
 * objects are only constructed for consistent ontologies, and some consequences
 * of this are hardcoded here.
 * <p>
 * This class wraps an instance of {@link UpdateableTaxonomy} and lazily
 * generates wrappers for its nodes to store direct instances.
 *
 * @author Yevgeny Kazakov
 * @author Frantisek Simancik
 * @author Markus Kroetzsch
 * @author Pavel Klinov
 * @author Peter Skocovsky
 */
public class ConcurrentInstanceTaxonomy
		extends AbstractInstanceTaxonomy<ElkClass, ElkNamedIndividual>
		implements UpdateableInstanceTaxonomy<ElkClass, ElkNamedIndividual> {

	// logger for events
	private static final Logger LOGGER_ = LoggerFactory
			.getLogger(ConcurrentInstanceTaxonomy.class);

	/**
	 * The store for instance nodes of this taxonomy.
	 */
	private final UpdateableNodeStore<ElkNamedIndividual, IndividualNode.Projection<ElkClass, ElkNamedIndividual>> individualNodeStore_;

	/**
	 * The wrapped class taxonomy
	 */
	private final UpdateableTaxonomy<ElkClass> classTaxonomy_;

	/**
	 * Map from wrapped nodes to their wrappers.
	 */
	private final ConcurrentMap<TaxonomyNode<ElkClass>, UpdateableTypeNodeWrapper> wrapperMap_;

	/** The listeners notified about the changes to instance taxonomy. */
	protected final List<InstanceTaxonomy.Listener<ElkClass, ElkNamedIndividual>> instanceListeners_;

	public ConcurrentInstanceTaxonomy(PredefinedElkClassFactory elkFactory,
			final ComparatorKeyProvider<ElkEntity> classKeyProvider,
			final ComparatorKeyProvider<ElkEntity> individualKeyProvider) {
		this(new ConcurrentClassTaxonomy(elkFactory, classKeyProvider),
				individualKeyProvider);
	}

	public ConcurrentInstanceTaxonomy(
			final UpdateableTaxonomy<ElkClass> classTaxonomy,
			final ComparatorKeyProvider<ElkEntity> individualKeyProvider) {
		this.individualNodeStore_ = new ConcurrentNodeStore<ElkNamedIndividual, IndividualNode.Projection<ElkClass, ElkNamedIndividual>>(
				individualKeyProvider);
		this.classTaxonomy_ = classTaxonomy;
		this.wrapperMap_ = new ConcurrentHashMap<TaxonomyNode<ElkClass>, UpdateableTypeNodeWrapper>();
		this.instanceListeners_ = new ArrayList<InstanceTaxonomy.Listener<ElkClass, ElkNamedIndividual>>();
	}

	@Override
	public ComparatorKeyProvider<? super ElkClass> getKeyProvider() {
		return classTaxonomy_.getKeyProvider();
	}

	@Override
	public ComparatorKeyProvider<? super ElkNamedIndividual> getInstanceKeyProvider() {
		return individualNodeStore_.getKeyProvider();
	}

	@Override
	public TypeNode<ElkClass, ElkNamedIndividual> getNode(ElkClass elkClass) {
		return functor_.apply(classTaxonomy_.getNode(elkClass));
	}

	/**
	 * Obtain a {@link TypeNode} object for a given {@link ElkClass}, or
	 * {@code null} if none assigned.
	 *
	 * @param individual
	 * @return instance node object for elkClass, possibly still incomplete
	 */
	@Override
	public IndividualNode.Projection<ElkClass, ElkNamedIndividual> getInstanceNode(
			final ElkNamedIndividual individual) {
		return individualNodeStore_.getNode(individual);
	}

	@Override
	public Set<? extends TypeNode<ElkClass, ElkNamedIndividual>> getNodes() {
		return Operations.map(classTaxonomy_.getNodes(), functor_);
	}

	@Override
	public Set<? extends InstanceNode<ElkClass, ElkNamedIndividual>> getInstanceNodes() {
		return individualNodeStore_.getNodes();
	}

	@Override
	public NonBottomTypeNode<ElkClass, ElkNamedIndividual> getNonBottomNode(
			final ElkClass elkEntity) {
		return getCreateUpdateableTypeNode(
				classTaxonomy_.getNonBottomNode(elkEntity));
	}

	@Override
	public Set<? extends NonBottomTypeNode<ElkClass, ElkNamedIndividual>> getNonBottomNodes() {
		return Operations.map(classTaxonomy_.getNonBottomNodes(),
				nonBottomFunctor_);
	}

	/**
	 * Node factory creating instance nodes of this taxonomy.
	 */
	private final NodeFactory<ElkNamedIndividual, IndividualNode.Projection<ElkClass, ElkNamedIndividual>> INSTANCE_NODE_FACTORY = new NodeFactory<ElkNamedIndividual, IndividualNode.Projection<ElkClass, ElkNamedIndividual>>() {

		@Override
		public IndividualNode.Projection<ElkClass, ElkNamedIndividual> createNode(
				final Iterable<? extends ElkNamedIndividual> members,
				final int size) {
			return new IndividualNode.Projection<ElkClass, ElkNamedIndividual>(
					ConcurrentInstanceTaxonomy.this, members, size);
		}

	};

	@Override
	public InstanceNode<ElkClass, ElkNamedIndividual> getCreateInstanceNode(
			final Collection<? extends ElkNamedIndividual> instances) {
		return individualNodeStore_.getCreateNode(instances, instances.size(),
				INSTANCE_NODE_FACTORY);
	};

	@Override
	public boolean setCreateDirectTypes(
			final InstanceNode<ElkClass, ElkNamedIndividual> instanceNode,
			final Iterable<? extends java.util.Collection<? extends ElkClass>> typeSets) {

		if (!(instanceNode instanceof IndividualNode)) {
			throw new IllegalArgumentException(
					"The sub-node must belong to this taxonomy: "
							+ instanceNode);
		}
		final IndividualNode.Projection<ElkClass, ElkNamedIndividual> node = (IndividualNode.Projection<ElkClass, ElkNamedIndividual>) instanceNode;
		if (node.getTaxonomy() != this) {
			throw new IllegalArgumentException(
					"The sub-node must belong to this taxonomy: "
							+ instanceNode);
		}

		// TODO: establish consistency by adding default type to the nodes.

		boolean isTypeSets = true;

		for (final Collection<? extends ElkClass> superMembers : typeSets) {
			final UpdateableTypeNode.Projection<ElkClass, ElkNamedIndividual> superNode = getCreateUpdateableTypeNode(
					classTaxonomy_.getCreateNode(superMembers));
			isTypeSets = false;
			addDirectType(superNode, node);
		}

		if (node.trySetAllParentsAssigned(true)) {
			if (!isTypeSets) {
				fireDirectTypeAssignment(instanceNode,
						instanceNode.getDirectTypeNodes());
			}
			return true;
		} else {
			return false;
		}
	};

	private static void addDirectType(
			final UpdateableTypeNode.Projection<ElkClass, ElkNamedIndividual> typeNode,
			final UpdateableInstanceNode.Projection<ElkClass, ElkNamedIndividual> instanceNode) {
		instanceNode.addDirectTypeNode(typeNode);
		typeNode.addDirectInstanceNode(instanceNode);
	}

	@Override
	public boolean removeDirectTypes(
			final InstanceNode<ElkClass, ElkNamedIndividual> instanceNode) {

		if (!(instanceNode instanceof IndividualNode)) {
			throw new IllegalArgumentException(
					"The sub-node must belong to this taxonomy: "
							+ instanceNode);
		}
		final IndividualNode.Projection<ElkClass, ElkNamedIndividual> node = (IndividualNode.Projection<ElkClass, ElkNamedIndividual>) instanceNode;
		if (node.getTaxonomy() != this) {
			throw new IllegalArgumentException(
					"The sub-node must belong to this taxonomy: "
							+ instanceNode);
		}

		if (!node.trySetAllParentsAssigned(false)) {
			return false;
		}

		final List<UpdateableTypeNode.Projection<ElkClass, ElkNamedIndividual>> directTypes = new ArrayList<UpdateableTypeNode.Projection<ElkClass, ElkNamedIndividual>>();

		synchronized (node) {
			directTypes.addAll(node.getDirectNonBottomTypeNodes());
			for (UpdateableTypeNode.Projection<ElkClass, ElkNamedIndividual> typeNode : directTypes) {
				node.removeDirectTypeNode(typeNode);
			}
		}
		// detaching the removed instance node from all its direct types
		for (UpdateableTypeNode.Projection<ElkClass, ElkNamedIndividual> typeNode : directTypes) {
			synchronized (typeNode) {
				typeNode.removeDirectInstanceNode(node);
			}
		}

		fireDirectTypeRemoval(instanceNode, directTypes);

		return true;
	}

	@Override
	public boolean removeInstanceNode(final ElkNamedIndividual instance) {
		if (individualNodeStore_.removeNode(instance)) {
			LOGGER_.trace("removed instance node with member: {}", instance);
			return true;
		} else {
			return false;
		}
	}

	@Override
	public NonBottomTypeNode<ElkClass, ElkNamedIndividual> getCreateNode(
			final Collection<? extends ElkClass> members) {
		return getCreateUpdateableTypeNode(
				classTaxonomy_.getCreateNode(members));
	}

	@Override
	public boolean setCreateDirectSupernodes(
			final NonBottomTaxonomyNode<ElkClass> subNode,
			final Iterable<? extends Collection<? extends ElkClass>> superMemberSets) {
		if (!(subNode instanceof UpdateableTypeNodeWrapper)) {
			throw new IllegalArgumentException(
					"The sub-node must belong to this taxonomy: " + subNode);
		}
		final UpdateableTypeNodeWrapper node = (UpdateableTypeNodeWrapper) subNode;
		return classTaxonomy_.setCreateDirectSupernodes(node.getNode(),
				superMemberSets);
	}

	@Override
	public TypeNode<ElkClass, ElkNamedIndividual> getTopNode() {
		return functor_.apply(classTaxonomy_.getTopNode());
	}

	@Override
	public TypeNode<ElkClass, ElkNamedIndividual> getBottomNode() {
		return bottomNodeWrapper_;
	}

	@Override
	public boolean removeDirectSupernodes(
			final NonBottomTaxonomyNode<ElkClass> subNode) {
		if (!(subNode instanceof UpdateableTypeNodeWrapper)) {
			throw new IllegalArgumentException(
					"The sub-node must belong to this taxonomy: " + subNode);
		}
		final UpdateableTypeNodeWrapper node = (UpdateableTypeNodeWrapper) subNode;
		return classTaxonomy_.removeDirectSupernodes(node.getNode());
	}

	@Override
	public boolean removeNode(final ElkClass member) {
		final TaxonomyNode<ElkClass> node = classTaxonomy_.getNode(member);
		if (node == null) {
			return false;
		}
		UpdateableTypeNodeWrapper wrapper = wrapperMap_.get(node);

		if (wrapper != null && wrapperMap_.remove(node, wrapper)) {

			// TODO: maybe this can be removed
			for (UpdateableInstanceNode.Projection<ElkClass, ElkNamedIndividual> instanceNode : wrapper
					.getDirectInstanceNodes()) {
				synchronized (instanceNode) {
					instanceNode.removeDirectTypeNode(wrapper);
				}
			}
		}

		return classTaxonomy_.removeNode(member);
	}

	@Override
	public boolean addToBottomNode(final ElkClass member) {
		return classTaxonomy_.addToBottomNode(member);
	}

	@Override
	public boolean removeFromBottomNode(final ElkClass member) {
		return classTaxonomy_.removeFromBottomNode(member);
	}

	private UpdateableTypeNodeWrapper getCreateUpdateableTypeNode(
			NonBottomTaxonomyNode<ElkClass> taxNode) {
		if (taxNode == null) {
			return null;
		}

		synchronized (taxNode) {
			UpdateableTypeNodeWrapper wrapper = wrapperMap_.get(taxNode);

			if (wrapper == null) {
				wrapper = new UpdateableTypeNodeWrapper(taxNode);
				wrapperMap_.put(taxNode, wrapper);
			}

			return wrapper;
		}
	}

	/**
	 * Transforms updateable taxonomy nodes into updateable type nodes
	 */
	private final FunctorEx<NonBottomTaxonomyNode<ElkClass>, UpdateableTypeNodeWrapper> nonBottomFunctor_ = new FunctorEx<NonBottomTaxonomyNode<ElkClass>, UpdateableTypeNodeWrapper>() {

		@Override
		public UpdateableTypeNodeWrapper apply(
				final NonBottomTaxonomyNode<ElkClass> node) {
			return getCreateUpdateableTypeNode(node);
		}

		@Override
		public NonBottomTaxonomyNode<ElkClass> deapply(final Object element) {
			if (element instanceof UpdateableTypeNodeWrapper) {
				return ((UpdateableTypeNodeWrapper) element).getNode();
			} else {
				return null;
			}
		}

	};

	private final FunctorEx<TaxonomyNode<ElkClass>, TypeNodeWrapper> functor_ = new FunctorEx<TaxonomyNode<ElkClass>, TypeNodeWrapper>() {

		@Override
		public TypeNodeWrapper apply(final TaxonomyNode<ElkClass> node) {
			if (node == null) {
				return null;
			} else if (node instanceof NonBottomTaxonomyNode) {
				return nonBottomFunctor_
						.apply((NonBottomTaxonomyNode<ElkClass>) node);
			} else {
				return bottomNodeWrapper_;
			}
		}

		@Override
		public TaxonomyNode<ElkClass> deapply(Object element) {
			if (element instanceof TypeNodeWrapper) {
				return ((TypeNodeWrapper) element).getNode();
			} else {
				return null;
			}
		}

	};

	/**
	 *
	 * @author Pavel Klinov
	 *
	 *         pavel.klinov@uni-ulm.de
	 * @author Peter Skocovsky
	 */
	private abstract class TypeNodeWrapper implements
			GenericTypeNode.Projection<ElkClass, ElkNamedIndividual> {

		public abstract TaxonomyNode<ElkClass> getNode();

		@Override
		public ComparatorKeyProvider<? super ElkClass> getKeyProvider() {
			return getNode().getKeyProvider();
		}

		@Override
		public boolean contains(ElkClass member) {
			return getNode().contains(member);
		}

		@Override
		public int size() {
			return getNode().size();
		}

		@Override
		public ElkClass getCanonicalMember() {
			return getNode().getCanonicalMember();
		}

		@Override
		public Iterator<ElkClass> iterator() {
			return getNode().iterator();
		}

		@Override
		public Taxonomy<ElkClass> getTaxonomy() {
			return getNode().getTaxonomy();
		}

		@Override
		public Set<TypeNodeWrapper> getDirectSuperNodes() {
			return Operations.map(getNode().getDirectSuperNodes(), functor_);
		}

		@Override
		public Set<TypeNodeWrapper> getAllSuperNodes() {
			return Operations.map(getNode().getAllSuperNodes(), functor_);
		}

		@Override
		public Set<TypeNodeWrapper> getDirectSubNodes() {
			return Operations.map(getNode().getDirectSubNodes(), functor_);
		}

		@Override
		public Set<TypeNodeWrapper> getAllSubNodes() {
			return Operations.map(getNode().getAllSubNodes(), functor_);
		}

	}

	private final TypeNodeWrapper bottomNodeWrapper_ = new TypeNodeWrapper() {

		@Override
		public TaxonomyNode<ElkClass> getNode() {
			return classTaxonomy_.getBottomNode();
		}

		@Override
		public Set<? extends GenericInstanceNode.Projection<ElkClass, ElkNamedIndividual>> getDirectInstanceNodes() {
			return Collections.emptySet();
		}

		@Override
		public Set<? extends GenericInstanceNode.Projection<ElkClass, ElkNamedIndividual>> getAllInstanceNodes() {
			return Collections.emptySet();
		}

	};

	/**
	 *
	 * @author Pavel Klinov
	 *
	 *         pavel.klinov@uni-ulm.de
	 * @author Peter Skocovsky
	 */
	private class UpdateableTypeNodeWrapper extends TypeNodeWrapper implements
			UpdateableTypeNode.Projection<ElkClass, ElkNamedIndividual> {

		/**
		 * The wrapped node.
		 */
		protected final NonBottomTaxonomyNode<ElkClass> classNode_;

		/**
		 * ElkNamedIndividual nodes whose members are instances of the members
		 * of this node.
		 */
		private final Set<UpdateableInstanceNode.Projection<ElkClass, ElkNamedIndividual>> directInstanceNodes_;

		UpdateableTypeNodeWrapper(final NonBottomTaxonomyNode<ElkClass> node) {
			this.classNode_ = node;
			this.directInstanceNodes_ = Collections.newSetFromMap(
					new ConcurrentHashMap<UpdateableInstanceNode.Projection<ElkClass, ElkNamedIndividual>, Boolean>());
		}

		public NonBottomTaxonomyNode<ElkClass> getNode() {
			return classNode_;
		}

		@Override
		public Set<? extends UpdateableInstanceNode.Projection<ElkClass, ElkNamedIndividual>> getDirectInstanceNodes() {
			return Collections.unmodifiableSet(directInstanceNodes_);
		}

		@Override
		public Set<? extends GenericInstanceNode.Projection<ElkClass, ElkNamedIndividual>> getAllInstanceNodes() {
			return TaxonomyNodeUtils.getAllInstanceNodes(this);
		}

		@Override
		public Set<? extends UpdateableTypeNode.Projection<ElkClass, ElkNamedIndividual>> getDirectNonBottomSuperNodes() {
			return Operations.map(getNode().getDirectNonBottomSuperNodes(),
					nonBottomFunctor_);
		}

		@Override
		public Set<? extends UpdateableTypeNode.Projection<ElkClass, ElkNamedIndividual>> getDirectNonBottomSubNodes() {
			return Operations.map(getNode().getDirectNonBottomSubNodes(),
					nonBottomFunctor_);
		}

		@Override
		public void addDirectInstanceNode(
				UpdateableInstanceNode.Projection<ElkClass, ElkNamedIndividual> instanceNode) {
			LOGGER_.trace("{}: new direct instance-node {}", classNode_,
					instanceNode);

			directInstanceNodes_.add(instanceNode);
		}

		/*
		 * This method is not thread safe
		 */
		@Override
		public void removeDirectInstanceNode(
				UpdateableInstanceNode.Projection<ElkClass, ElkNamedIndividual> instanceNode) {
			LOGGER_.trace("{}: direct instance node removed {}", classNode_,
					instanceNode);

			directInstanceNodes_.remove(instanceNode);
		}

		@Override
		public String toString() {
			return classNode_.toString();
		}

		@Override
		public int hashCode() {
			return classNode_.hashCode();
		}

		@Override
		public boolean equals(Object obj) {
			if (obj instanceof UpdateableTypeNodeWrapper) {
				return classNode_ == ((UpdateableTypeNodeWrapper) obj).classNode_;
			}

			return false;
		}

	}

	@Override
	public boolean addListener(final Taxonomy.Listener<ElkClass> listener) {
		return classTaxonomy_.addListener(listener);
	}

	@Override
	public boolean removeListener(final Taxonomy.Listener<ElkClass> listener) {
		return classTaxonomy_.removeListener(listener);
	}

	@Override
	public boolean addListener(final NodeStore.Listener<ElkClass> listener) {
		return classTaxonomy_.addListener(listener);
	}

	@Override
	public boolean removeListener(final NodeStore.Listener<ElkClass> listener) {
		return classTaxonomy_.removeListener(listener);
	}

	@Override
	public boolean addInstanceListener(
			final NodeStore.Listener<ElkNamedIndividual> listener) {
		return individualNodeStore_.addListener(listener);
	}

	@Override
	public boolean removeInstanceListener(
			final NodeStore.Listener<ElkNamedIndividual> listener) {
		return individualNodeStore_.removeListener(listener);
	}

	@Override
	public boolean addInstanceListener(
			final InstanceTaxonomy.Listener<ElkClass, ElkNamedIndividual> listener) {
		return instanceListeners_.add(listener);
	}

	@Override
	public boolean removeInstanceListener(
			final InstanceTaxonomy.Listener<ElkClass, ElkNamedIndividual> listener) {
		return instanceListeners_.remove(listener);
	}

	protected void fireDirectTypeAssignment(
			final InstanceNode<ElkClass, ElkNamedIndividual> instanceNode,
			final Collection<? extends TypeNode<ElkClass, ElkNamedIndividual>> typeNodes) {
		for (final InstanceTaxonomy.Listener<ElkClass, ElkNamedIndividual> listener : instanceListeners_) {
			listener.directTypeNodesAppeared(instanceNode);
			for (final TypeNode<ElkClass, ElkNamedIndividual> typeNode : typeNodes) {
				listener.directInstanceNodesAppeared(typeNode);
			}
		}
	}

	protected void fireDirectTypeRemoval(
			final InstanceNode<ElkClass, ElkNamedIndividual> instanceNode,
			final Collection<? extends TypeNode<ElkClass, ElkNamedIndividual>> typeNodes) {
		for (final InstanceTaxonomy.Listener<ElkClass, ElkNamedIndividual> listener : instanceListeners_) {
			listener.directTypeNodesDisappeared(instanceNode);
			for (final TypeNode<ElkClass, ElkNamedIndividual> typeNode : typeNodes) {
				listener.directInstanceNodesDisappeared(typeNode);
			}
		}
	}

}


package org.semanticweb.elk.reasoner.saturation.inferences;

/*
 * #%L
 * ELK Reasoner
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2015 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 org.semanticweb.elk.reasoner.indexing.model.IndexedContextRoot;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectProperty;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.BackwardLink;

abstract class AbstractBackwardLinkInference extends AbstractClassInference
		implements BackwardLinkInference {

	private final IndexedObjectProperty conclusionRelation_;

	private final IndexedContextRoot conclusionSource_;

	public AbstractBackwardLinkInference(IndexedContextRoot destination,
			IndexedObjectProperty relation, IndexedContextRoot source) {
		super(destination);
		this.conclusionRelation_ = relation;
		this.conclusionSource_ = source;
	}

	public IndexedObjectProperty getConclusionRelation() {
		return conclusionRelation_;
	}

	public IndexedContextRoot getConclusionSource() {
		return conclusionSource_;
	}

	@Override
	public IndexedContextRoot getTraceRoot() {
		return conclusionSource_;
	}

	/**
	 * @param factory
	 *            the factory for creating conclusions
	 *
	 * @return the conclusion produced by this inference
	 */
	public BackwardLink getConclusion(BackwardLink.Factory factory) {
		return factory.getBackwardLink(getDestination(), conclusionRelation_,
				conclusionSource_);
	}

	@Override
	public final <O> O accept(ClassInference.Visitor<O> visitor) {
		return accept((BackwardLinkInference.Visitor<O>) visitor);
	}

}


/*
 * #%L
 * elk-reasoner
 *
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2011 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%
 */
/**
 * @author Yevgeny Kazakov, Jun 28, 2011
 */
package org.semanticweb.elk.owlapi;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.semanticweb.elk.exceptions.ElkException;
import org.semanticweb.elk.exceptions.ElkRuntimeException;
import org.semanticweb.elk.owl.interfaces.ElkAxiom;
import org.semanticweb.elk.owl.interfaces.ElkClass;
import org.semanticweb.elk.owl.interfaces.ElkObject;
import org.semanticweb.elk.owl.interfaces.ElkObjectProperty;
import org.semanticweb.elk.owlapi.wrapper.OwlConverter;
import org.semanticweb.elk.reasoner.DummyProgressMonitor;
import org.semanticweb.elk.reasoner.ElkUnsupportedReasoningTaskException;
import org.semanticweb.elk.reasoner.ProgressMonitor;
import org.semanticweb.elk.reasoner.Reasoner;
import org.semanticweb.elk.reasoner.ReasonerFactory;
import org.semanticweb.elk.reasoner.config.ReasonerConfiguration;
import org.semanticweb.elk.reasoner.query.ElkQueryException;
import org.semanticweb.elk.reasoner.query.EntailmentQueryConverter;
import org.semanticweb.elk.reasoner.query.EntailmentQueryResult;
import org.semanticweb.elk.reasoner.query.ProperEntailmentQueryResult;
import org.semanticweb.elk.reasoner.query.UnsupportedIndexingEntailmentQueryResult;
import org.semanticweb.elk.reasoner.query.UnsupportedQueryTypeEntailmentQueryResult;
import org.semanticweb.elk.reasoner.stages.ElkInterruptedException;
import org.semanticweb.elk.util.logging.LogLevel;
import org.semanticweb.elk.util.logging.LoggerWrap;
import org.semanticweb.owlapi.model.AxiomType;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataPropertyExpression;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyChange;
import org.semanticweb.owlapi.model.OWLOntologyChangeListener;
import org.semanticweb.owlapi.model.OWLOntologyChangeProgressListener;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.reasoner.AxiomNotInProfileException;
import org.semanticweb.owlapi.reasoner.BufferingMode;
import org.semanticweb.owlapi.reasoner.ClassExpressionNotInProfileException;
import org.semanticweb.owlapi.reasoner.FreshEntitiesException;
import org.semanticweb.owlapi.reasoner.FreshEntityPolicy;
import org.semanticweb.owlapi.reasoner.InconsistentOntologyException;
import org.semanticweb.owlapi.reasoner.IndividualNodeSetPolicy;
import org.semanticweb.owlapi.reasoner.InferenceType;
import org.semanticweb.owlapi.reasoner.Node;
import org.semanticweb.owlapi.reasoner.NodeSet;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.reasoner.ReasonerInternalException;
import org.semanticweb.owlapi.reasoner.ReasonerInterruptedException;
import org.semanticweb.owlapi.reasoner.TimeOutException;
import org.semanticweb.owlapi.reasoner.UnsupportedEntailmentTypeException;
import org.semanticweb.owlapi.reasoner.impl.OWLNamedIndividualNode;
import org.semanticweb.owlapi.util.Version;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

/**
 * {@link OWLReasoner} interface implementation for ELK {@link Reasoner}
 *
 * @author Yevgeny Kazakov
 * @author Markus Kroetzsch
 * @author Peter Skocovsky
 */
public class ElkReasoner implements OWLReasoner {
	// logger for this class
	private static final Logger LOGGER_ = LoggerFactory
			.getLogger(ElkReasoner.class);
	private static final Marker MARKER_UNSUPPORTED_METHOD_ = MarkerFactory
			.getMarker("owlapi.unsupportedMethod");

	// OWL API related objects
	private final OWLOntology owlOntology_;
	private final OWLOntologyManager owlOntologymanager_;
	/**
	 * ELK progress monitor to display progress of main reasoning tasks, e.g.,
	 * classification
	 */
	private final ProgressMonitor mainProgressMonitor_;
	/**
	 * ELK progress monitor to display progress of other reasoning tasks, e.g.,
	 * satisfiability checking
	 */
	private final ProgressMonitor secondaryProgressMonitor_;
	/**
	 * {@code true} iff the buffering mode for reasoner is
	 * {@link BufferingMode#BUFFERING}
	 */
	private final boolean isBufferingMode_;
	/** listener to implement addition and removal of axioms */
	private final OntologyChangeListener ontologyChangeListener_;
	/** listener to keep track of when changes are applied to the ontology */
	private final OntologyChangeProgressListener ontologyChangeProgressListener_;
	/** ELK object factory used to create any ElkObjects */
	private final ElkObject.Factory objectFactory_;
	/** Converter from OWL API to ELK OWL */
	private final OwlConverter owlConverter_;
	/** Converter from ELK OWL to OWL API */
	private final ElkConverter elkConverter_;
	/** this object is used to load pending changes */
	private volatile OwlChangesLoaderFactory bufferedChangesLoader_;
	/** configurations required for ELK reasoner */
	private ReasonerConfiguration config_;
	private final boolean isAllowFreshEntities;
	/** the ELK reasoner instance used for reasoning */
	private Reasoner reasoner_;
	/** listeners that monitor changes in some of the methods */
	private final List<ChangeListener> changeListeners_ = new ArrayList<ChangeListener>();

	/**
	 * {@code true} if the ontology should be loaded before any changes are
	 * applied to the ontology (used only for buffering mode)
	 */
	private boolean loadBeforeChanges_ = true;
	/**
	 * {@code true} if it is required to reload the whole ontology next time the
	 * changes should be flushed
	 */
	private boolean ontologyReloadRequired_;

	ElkReasoner(OWLOntology ontology, boolean isBufferingMode,
			ElkReasonerConfiguration elkConfig,
			final Reasoner internalReasoner) {
		this.owlOntology_ = ontology;
		this.owlOntologymanager_ = ontology.getOWLOntologyManager();
		this.mainProgressMonitor_ = elkConfig.getProgressMonitor() == null
				? new DummyProgressMonitor()
				: new ElkReasonerProgressMonitor(
						elkConfig.getProgressMonitor());
		this.secondaryProgressMonitor_ = new DummyProgressMonitor();
		this.isBufferingMode_ = isBufferingMode;
		this.ontologyChangeListener_ = new OntologyChangeListener();
		this.owlOntologymanager_
				.addOntologyChangeListener(ontologyChangeListener_);
		this.ontologyChangeProgressListener_ = new OntologyChangeProgressListener();
		this.owlOntologymanager_.addOntologyChangeProgessListener(
				ontologyChangeProgressListener_);
		this.objectFactory_ = internalReasoner.getElkFactory();
		this.owlConverter_ = OwlConverter.getInstance();
		this.elkConverter_ = ElkConverter.getInstance();

		this.config_ = elkConfig.getElkConfiguration();
		this.isAllowFreshEntities = elkConfig
				.getFreshEntityPolicy() == FreshEntityPolicy.ALLOW;

		initReasoner(internalReasoner);
		this.bufferedChangesLoader_ = new OwlChangesLoaderFactory(
				this.mainProgressMonitor_);
		if (!isBufferingMode_) {
			// register the change loader only in non-buffering mode;
			// in buffering mode the loader is registered only when
			// changes are flushed
			reasoner_.registerAxiomLoader(bufferedChangesLoader_);
		}
		this.ontologyReloadRequired_ = false;
	}

	ElkReasoner(OWLOntology ontology, boolean isBufferingMode,
			ElkReasonerConfiguration elkConfig) {
		this(ontology, isBufferingMode, elkConfig, new ReasonerFactory()
				.createReasoner(elkConfig.getElkConfiguration()));
	}

	OWLOntology getOWLOntology() {
		return owlOntology_;
	}

	ElkObject.Factory getElkObjectFactory() {
		return objectFactory_;
	}

	private void initReasoner(final Reasoner reasoner) {
		this.reasoner_ = reasoner;
		this.reasoner_.registerAxiomLoader(new OwlOntologyLoader.Factory(
				owlOntology_, this.mainProgressMonitor_));
		this.reasoner_.setAllowFreshEntities(isAllowFreshEntities);
		// use the secondary progress monitor by default, when necessary, we
		// switch to the primary progress monitor; this is to avoid bugs with
		// progress monitors in Protege
		this.reasoner_.setProgressMonitor(this.secondaryProgressMonitor_);
	}

	/**
	 * @return the ELK reasoner used internally in this OWL API wrapper.
	 */
	public Reasoner getInternalReasoner() {
		return reasoner_;
	}

	public ReasonerConfiguration getConfigurationOptions() {
		return config_;
	}

	public void setConfigurationOptions(ReasonerConfiguration config) {
		this.config_ = config;
		reasoner_.setConfigurationOptions(config);
	}

	/**
	 * Logs a warning message for unsupported OWL API method
	 *
	 * @param method
	 *            the method which is not supported
	 * @return the {@link UnsupportedOperationException} saying that this method
	 *         is not supported
	 */
	private static UnsupportedOperationException unsupportedOwlApiMethod(
			String method) {
		String message = "OWL API reasoner method is not implemented: " + method
				+ ".";
		/*
		 * TODO: The method String can be used to create more specific message
		 * types, but with the current large amount of unsupported methods and
		 * non-persistent settings for ignoring them, we better use only one
		 * message type to make it easier to ignore them.
		 */
		LoggerWrap.log(LOGGER_, LogLevel.WARN, MARKER_UNSUPPORTED_METHOD_,
				message);

		return new UnsupportedOperationException(message);
	}

	/**
	 * Logs a warning message for unsupported case of an OWL API method
	 *
	 * @param method
	 *            the method which some case is not supported
	 * @param reason
	 *            the reason why the case is not supported
	 * @return the {@link UnsupportedOperationException} saying that this method
	 *         is not supported
	 */
	private static UnsupportedOperationException unsupportedOwlApiMethod(
			String method, String reason) {
		String message = "OWL API reasoner method is not fully implemented: "
				+ method + ": " + reason;
		LoggerWrap.log(LOGGER_, LogLevel.WARN, MARKER_UNSUPPORTED_METHOD_,
				message);

		return new UnsupportedOperationException(message);
	}

	private Node<OWLClass> getClassNode(ElkClass elkClass)
			throws FreshEntitiesException, InconsistentOntologyException,
			ElkException {
		try {
			return elkConverter_
					.convertClassNode(reasoner_.getEquivalentClasses(elkClass));
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		}
	}

	private Node<OWLObjectPropertyExpression> getObjectPropertyNode(
			final ElkObjectProperty elkClass) throws FreshEntitiesException,
			InconsistentOntologyException, ElkException {
		try {
			return elkConverter_.convertObjectPropertyNode(
					reasoner_.getObjectPropertyNode(elkClass));
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		}
	}

	/**
	 *
	 * @throws ReasonerInterruptedException
	 *             if the reasoner is in the interrupted state, throws
	 */
	private void checkInterrupted() throws ReasonerInterruptedException {
		try {
			reasoner_.checkInterrupt();
		} catch (final ElkInterruptedException e) {
			throw new ReasonerInterruptedException(e);
		}
	}

	/* Methods required by the OWLReasoner interface */

	@Override
	public void dispose() {
		LOGGER_.trace("dispose()");

		owlOntologymanager_
				.removeOntologyChangeListener(ontologyChangeListener_);
		owlOntologymanager_.removeOntologyChangeProgessListener(
				ontologyChangeProgressListener_);
		try {
			for (;;) {
				try {
					if (!reasoner_.shutdown())
						throw new ReasonerInternalException(
								"Failed to shut down ELK!");
					break;
				} catch (InterruptedException e) {
					continue;
				}
			}
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public void flush() {
		LOGGER_.trace("flush()");

		try {
			if (ontologyReloadRequired_) {
				/**
				 * re-creates a new instance of reasoner for the parameters,
				 * since a reasoner can do initial load only once
				 */
				initReasoner(new ReasonerFactory().createReasoner(reasoner_,
						objectFactory_, config_));
				bufferedChangesLoader_ = new OwlChangesLoaderFactory(
						this.secondaryProgressMonitor_);
				ontologyReloadRequired_ = false;
			} else if (bufferedChangesLoader_.isLoadingFinished()) {
				// no changes
				return;
			} else if (isBufferingMode_) {
				// in buffering mode, new changes need to be buffered
				// separately in order not to mix them with the flushed
				// changes that now need to be loaded
				reasoner_.registerAxiomLoader(bufferedChangesLoader_);
				bufferedChangesLoader_ = new OwlChangesLoaderFactory(
						this.secondaryProgressMonitor_);
			}
			// notify about the changes
			for (ChangeListener listener : changeListeners_) {
				listener.ontologyChanged();
			}
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public Node<OWLClass> getBottomClassNode() {
		LOGGER_.trace("getBottomClassNode()");

		checkInterrupted();

		try {
			return getClassNode(objectFactory_.getOwlNothing());
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod("getBottomClassNode()",
					e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public Node<OWLDataProperty> getBottomDataPropertyNode() {
		LOGGER_.trace("getBottomDataPropertyNode()");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod("getBottomDataPropertyNode()");
	}

	@Override
	public Node<OWLObjectPropertyExpression> getBottomObjectPropertyNode() {
		LOGGER_.trace("getBottomObjectPropertyNode()");

		checkInterrupted();

		try {
			return getObjectPropertyNode(
					objectFactory_.getOwlBottomObjectProperty());
		} catch (final ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod("getBottomObjectPropertyNode()",
					e.getMessage());
		} catch (final ElkException e) {
			throw elkConverter_.convert(e);
		} catch (final ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public BufferingMode getBufferingMode() {
		LOGGER_.trace("getBufferingMode()");

		return isBufferingMode_ ? BufferingMode.BUFFERING
				: BufferingMode.NON_BUFFERING;
	}

	@Override
	public NodeSet<OWLClass> getDataPropertyDomains(OWLDataProperty arg0,
			boolean arg1)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getDataPropertyDomains(OWLDataProperty, boolean)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getDataPropertyDomains(OWLDataProperty, boolean)");
	}

	@Override
	public Set<OWLLiteral> getDataPropertyValues(OWLNamedIndividual arg0,
			OWLDataProperty arg1)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace(
				"getDataPropertyValues(OWLNamedIndividual, OWLDataProperty)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getDataPropertyValues(OWLNamedIndividual, OWLDataProperty)");
	}

	@Override
	public NodeSet<OWLNamedIndividual> getDifferentIndividuals(
			OWLNamedIndividual arg0)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getDifferentIndividuals(OWLNamedIndividual)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getDifferentIndividuals(OWLNamedIndividual)");
	}

	@Override
	public NodeSet<OWLClass> getDisjointClasses(OWLClassExpression arg0)
			throws ReasonerInterruptedException, TimeOutException,
			FreshEntitiesException, InconsistentOntologyException {
		LOGGER_.trace("getDisjointClasses(OWLClassExpression)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod("getDisjointClasses(OWLClassExpression)");
	}

	@Override
	public NodeSet<OWLDataProperty> getDisjointDataProperties(
			OWLDataPropertyExpression arg0)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getDisjointDataProperties(OWLDataPropertyExpression)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getDisjointDataProperties(OWLDataPropertyExpression)");
	}

	@Override
	public NodeSet<OWLObjectPropertyExpression> getDisjointObjectProperties(
			OWLObjectPropertyExpression arg0)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace(
				"getDisjointObjectProperties(OWLObjectPropertyExpression)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getDisjointObjectProperties(OWLObjectPropertyExpression)");
	}

	@Override
	public Node<OWLClass> getEquivalentClasses(OWLClassExpression ce)
			throws InconsistentOntologyException,
			ClassExpressionNotInProfileException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getEquivalentClasses(OWLClassExpression)");

		checkInterrupted();
		try {
			return elkConverter_.convertClassNode(
					reasoner_.getEquivalentClasses(owlConverter_.convert(ce)));
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod(
					"getEquivalentClasses(OWLClassExpression)", e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public Node<OWLDataProperty> getEquivalentDataProperties(
			OWLDataProperty arg0)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getEquivalentDataProperties(OWLDataProperty)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getEquivalentDataProperties(OWLDataProperty)");
	}

	@Override
	public Node<OWLObjectPropertyExpression> getEquivalentObjectProperties(
			final OWLObjectPropertyExpression pe)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace(
				"getEquivalentObjectProperties(OWLObjectPropertyExpression)");

		checkInterrupted();
		if (pe instanceof OWLObjectProperty) {
			try {
				return elkConverter_.convertObjectPropertyNode(
						reasoner_.getObjectPropertyNode(
								owlConverter_.convert((OWLObjectProperty) pe)));
			} catch (final ElkUnsupportedReasoningTaskException e) {
				throw unsupportedOwlApiMethod(
						"getEquivalentObjectProperties(OWLObjectPropertyExpression)",
						e.getMessage());
			} catch (final ElkException e) {
				throw elkConverter_.convert(e);
			} catch (final ElkRuntimeException e) {
				throw elkConverter_.convert(e);
			}
		}
		throw unsupportedOwlApiMethod(
				"getEquivalentObjectProperties(OWLObjectPropertyExpression)");
	}

	@Override
	public FreshEntityPolicy getFreshEntityPolicy() {
		LOGGER_.trace("getFreshEntityPolicy()");

		return reasoner_.getAllowFreshEntities() ? FreshEntityPolicy.ALLOW
				: FreshEntityPolicy.DISALLOW;
	}

	@Override
	public IndividualNodeSetPolicy getIndividualNodeSetPolicy() {
		LOGGER_.trace("getIndividualNodeSetPolicy()");

		return IndividualNodeSetPolicy.BY_NAME;
	}

	@Override
	public NodeSet<OWLNamedIndividual> getInstances(OWLClassExpression ce,
			boolean direct) throws InconsistentOntologyException,
			ClassExpressionNotInProfileException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getInstances(OWLClassExpression, boolean)");

		checkInterrupted();
		try {
			return elkConverter_.convertIndividualNodes(
					reasoner_.getInstances(owlConverter_.convert(ce), direct));
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod(
					"getInstances(OWLClassExpression, boolean)",
					e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public Node<OWLObjectPropertyExpression> getInverseObjectProperties(
			OWLObjectPropertyExpression arg0)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace(
				"getInverseObjectProperties(OWLObjectPropertyExpression)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getInverseObjectProperties(OWLObjectPropertyExpression)");
	}

	@Override
	public NodeSet<OWLClass> getObjectPropertyDomains(
			OWLObjectPropertyExpression arg0, boolean arg1)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace(
				"getObjectPropertyDomains(OWLObjectPropertyExpression, boolean)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getObjectPropertyDomains(OWLObjectPropertyExpression, boolean)");
	}

	@Override
	public NodeSet<OWLClass> getObjectPropertyRanges(
			OWLObjectPropertyExpression arg0, boolean arg1)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace(
				"getObjectPropertyRanges(OWLObjectPropertyExpression, boolean)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getObjectPropertyRanges(OWLObjectPropertyExpression, boolean)");
	}

	@Override
	public NodeSet<OWLNamedIndividual> getObjectPropertyValues(
			OWLNamedIndividual arg0, OWLObjectPropertyExpression arg1)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace(
				"getObjectPropertyValues(OWLNamedIndividual, OWLObjectPropertyExpression)");

		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getObjectPropertyValues(OWLNamedIndividual, OWLObjectPropertyExpression)");
	}

	@Override
	public Set<OWLAxiom> getPendingAxiomAdditions() {
		LOGGER_.trace("getPendingAxiomAdditions()");
		return bufferedChangesLoader_.getPendingAxiomAdditions();
	}

	@Override
	public Set<OWLAxiom> getPendingAxiomRemovals() {
		LOGGER_.trace("getPendingAxiomRemovals()");
		return bufferedChangesLoader_.getPendingAxiomRemovals();
	}

	@Override
	public List<OWLOntologyChange> getPendingChanges() {
		LOGGER_.trace("getPendingChanges()");
		return bufferedChangesLoader_.getPendingChanges();
	}

	@Override
	public Set<InferenceType> getPrecomputableInferenceTypes() {
		LOGGER_.trace("getPrecomputableInferenceTypes()");
		return new HashSet<InferenceType>(Arrays.asList(
				InferenceType.CLASS_ASSERTIONS, InferenceType.CLASS_HIERARCHY,
				InferenceType.OBJECT_PROPERTY_HIERARCHY));
	}

	@Override
	public String getReasonerName() {
		LOGGER_.trace("getReasonerName()");
		return ElkReasoner.class.getPackage().getImplementationTitle();
	}

	@Override
	public Version getReasonerVersion() {
		LOGGER_.trace("getReasonerVersion()");
		String versionString = ElkReasoner.class.getPackage()
				.getImplementationVersion();
		String[] splitted;
		int filled = 0;
		int version[] = new int[4];
		if (versionString != null) {
			splitted = versionString.replaceAll("[^\\d.]", "").split("\\.");
			while (filled < splitted.length && filled < version.length) {
				String part = splitted[filled];
				if (part.length() > 8) {
					part = part.substring(0, 8);
				}
				version[filled] = Integer.parseInt(part);
				filled++;
			}
		}
		while (filled < version.length) {
			version[filled] = 0;
			filled++;
		}
		return new Version(version[0], version[1], version[2], version[3]);
	}

	@Override
	public OWLOntology getRootOntology() {
		LOGGER_.trace("getRootOntology()");
		return owlOntology_;
	}

	@Override
	public Node<OWLNamedIndividual> getSameIndividuals(OWLNamedIndividual arg0)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getSameIndividuals(OWLNamedIndividual)");
		checkInterrupted();
		// TODO This needs to be updated when we support nominals
		return new OWLNamedIndividualNode(arg0);
	}

	@Override
	public NodeSet<OWLClass> getSubClasses(OWLClassExpression ce,
			boolean direct)
			throws ReasonerInterruptedException, TimeOutException,
			FreshEntitiesException, InconsistentOntologyException,
			ClassExpressionNotInProfileException {
		LOGGER_.trace("getSubClasses(OWLClassExpression, boolean)");
		checkInterrupted();
		try {
			return elkConverter_.convertClassNodes(
					reasoner_.getSubClasses(owlConverter_.convert(ce), direct));
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod(
					"getSubClasses(OWLClassExpression, boolean)",
					e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public NodeSet<OWLDataProperty> getSubDataProperties(OWLDataProperty arg0,
			boolean arg1)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getSubDataProperties(OWLDataProperty, boolean)");
		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getSubDataProperties(OWLDataProperty, boolean)");
	}

	@Override
	public NodeSet<OWLObjectPropertyExpression> getSubObjectProperties(
			final OWLObjectPropertyExpression pe, final boolean direct)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace(
				"getSubObjectProperties(OWLObjectPropertyExpression, boolean)");

		checkInterrupted();

		if (pe instanceof OWLObjectProperty) {
			try {
				return elkConverter_.convertObjectPropertyNodes(
						reasoner_.getSubObjectProperties(
								owlConverter_.convert((OWLObjectProperty) pe),
								direct));
			} catch (final ElkUnsupportedReasoningTaskException e) {
				throw unsupportedOwlApiMethod(
						"getSubObjectProperties(OWLObjectPropertyExpression, boolean)",
						e.getMessage());
			} catch (final ElkException e) {
				throw elkConverter_.convert(e);
			} catch (final ElkRuntimeException e) {
				throw elkConverter_.convert(e);
			}
		}
		throw unsupportedOwlApiMethod(
				"getSubObjectProperties(OWLObjectPropertyExpression, boolean)");
	}

	@Override
	public NodeSet<OWLClass> getSuperClasses(OWLClassExpression ce,
			boolean direct) throws InconsistentOntologyException,
			ClassExpressionNotInProfileException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getSuperClasses(OWLClassExpression, boolean)");
		checkInterrupted();
		try {
			return elkConverter_.convertClassNodes(reasoner_
					.getSuperClasses(owlConverter_.convert(ce), direct));
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod(
					"getSuperClasses(OWLClassExpression, boolean)",
					e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public NodeSet<OWLDataProperty> getSuperDataProperties(OWLDataProperty arg0,
			boolean arg1)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getSuperDataProperties(OWLDataProperty, boolean)");
		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod(
				"getSuperDataProperties(OWLDataProperty, boolean)");
	}

	@Override
	public NodeSet<OWLObjectPropertyExpression> getSuperObjectProperties(
			final OWLObjectPropertyExpression pe, final boolean direct)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace(
				"getSuperObjectProperties(OWLObjectPropertyExpression, boolean)");

		checkInterrupted();

		if (pe instanceof OWLObjectProperty) {
			try {
				return elkConverter_.convertObjectPropertyNodes(
						reasoner_.getSuperObjectProperties(
								owlConverter_.convert((OWLObjectProperty) pe),
								direct));
			} catch (final ElkUnsupportedReasoningTaskException e) {
				throw unsupportedOwlApiMethod(
						"getSuperObjectProperties(OWLObjectPropertyExpression, boolean)",
						e.getMessage());
			} catch (final ElkException e) {
				throw elkConverter_.convert(e);
			} catch (final ElkRuntimeException e) {
				throw elkConverter_.convert(e);
			}
		}
		throw unsupportedOwlApiMethod(
				"getSuperObjectProperties(OWLObjectPropertyExpression, boolean)");
	}

	@Override
	public long getTimeOut() {
		LOGGER_.trace("getTimeOut()");
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public Node<OWLClass> getTopClassNode() {
		LOGGER_.trace("getTopClassNode()");
		checkInterrupted();
		try {
			return getClassNode(objectFactory_.getOwlThing());
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod("getTopClassNode()", e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public Node<OWLDataProperty> getTopDataPropertyNode() {
		LOGGER_.trace("getTopDataPropertyNode()");
		checkInterrupted();
		// TODO Provide implementation
		throw unsupportedOwlApiMethod("getTopDataPropertyNode()");
	}

	@Override
	public Node<OWLObjectPropertyExpression> getTopObjectPropertyNode() {
		LOGGER_.trace("getTopObjectPropertyNode()");

		checkInterrupted();

		try {
			return getObjectPropertyNode(
					objectFactory_.getOwlTopObjectProperty());
		} catch (final ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod("getTopObjectPropertyNode()",
					e.getMessage());
		} catch (final ElkException e) {
			throw elkConverter_.convert(e);
		} catch (final ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public NodeSet<OWLClass> getTypes(OWLNamedIndividual ind, boolean direct)
			throws InconsistentOntologyException, FreshEntitiesException,
			ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("getTypes(OWLNamedIndividual, boolean)");
		checkInterrupted();
		try {
			return elkConverter_.convertClassNodes(
					reasoner_.getTypes(owlConverter_.convert(ind), direct));
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod(
					"getTypes(OWLNamedIndividual, boolean)", e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public Node<OWLClass> getUnsatisfiableClasses()
			throws ReasonerInterruptedException, TimeOutException,
			InconsistentOntologyException {
		LOGGER_.trace("getUnsatisfiableClasses()");
		checkInterrupted();
		try {
			return getClassNode(objectFactory_.getOwlNothing());
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod("getUnsatisfiableClasses()",
					e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}

	}

	@Override
	public void interrupt() {
		LOGGER_.trace("interrupt()");
		reasoner_.interrupt();
	}

	@Override
	public boolean isConsistent()
			throws ReasonerInterruptedException, TimeOutException {
		LOGGER_.trace("isConsistent()");
		try {
			return !reasoner_.isInconsistent();
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod("isConsistent()", e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	private final EntailmentQueryResult.Visitor<Boolean, ElkQueryException> ENTAILMENT_QUERY_RESULT_CONVERTER = new EntailmentQueryResult.Visitor<Boolean, ElkQueryException>() {

		@Override
		public Boolean visit(final ProperEntailmentQueryResult properResult)
				throws ElkQueryException {
			try {
				return properResult.isEntailed();
			} finally {
				properResult.unlock();
			}
		}

		@Override
		public Boolean visit(
				final UnsupportedIndexingEntailmentQueryResult unsupportedIndexing) {
			/*
			 * Returning an incomplete result. The warning should be logged
			 * during loading.
			 */
			return false;
		}

		@Override
		public Boolean visit(
				final UnsupportedQueryTypeEntailmentQueryResult unsupportedQueryType) {
			throw new UnsupportedEntailmentTypeException(
					elkConverter_.convert(unsupportedQueryType.getQuery()));
		}

	};

	@Override
	public boolean isEntailed(final OWLAxiom owlAxiom)
			throws ReasonerInterruptedException,
			UnsupportedEntailmentTypeException, TimeOutException,
			AxiomNotInProfileException, FreshEntitiesException,
			InconsistentOntologyException {
		LOGGER_.trace("isEntailed(OWLAxiom)");
		checkInterrupted();
		try {

			final ElkAxiom elkAxiom = owlConverter_.convert(owlAxiom);
			final EntailmentQueryResult result = reasoner_.isEntailed(elkAxiom);
			return result.accept(ENTAILMENT_QUERY_RESULT_CONVERTER);

		} catch (final ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod("isEntailed(OWLAxiom)",
					e.getMessage());
		} catch (final ElkException e) {
			throw elkConverter_.convert(e);
		} catch (final ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public boolean isEntailed(final Set<? extends OWLAxiom> owlAxioms)
			throws ReasonerInterruptedException,
			UnsupportedEntailmentTypeException, TimeOutException,
			AxiomNotInProfileException, FreshEntitiesException,
			InconsistentOntologyException {
		LOGGER_.trace("isEntailed(Set<? extends OWLAxiom>)");
		checkInterrupted();
		try {

			final Map<ElkAxiom, EntailmentQueryResult> results = reasoner_
					.isEntailed(owlConverter_.convertAxiomSet(owlAxioms));
			for (final EntailmentQueryResult result : results.values()) {
				if (!result.accept(ENTAILMENT_QUERY_RESULT_CONVERTER)) {
					return false;
				}
			}
			return true;

		} catch (final ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod("isEntailed(Set<? extends OWLAxiom>)",
					e.getMessage());
		} catch (final ElkException e) {
			throw elkConverter_.convert(e);
		} catch (final ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public boolean isEntailmentCheckingSupported(final AxiomType<?> axiomType) {
		Class<? extends ElkObject> elkAxiomClass = OwlConverter
				.convertType(axiomType.getActualClass());
		if (elkAxiomClass == null
				|| !ElkAxiom.class.isAssignableFrom(elkAxiomClass)) {
			// not supported
			return false;
		}
		// else
		return EntailmentQueryConverter.isEntailmentCheckingSupported(
				elkAxiomClass.asSubclass(ElkAxiom.class));
	}

	@Override
	public boolean isPrecomputed(InferenceType inferenceType) {
		LOGGER_.trace("isPrecomputed(InferenceType)");
		if (inferenceType.equals(InferenceType.CLASS_HIERARCHY))
			return reasoner_.doneTaxonomy();
		if (inferenceType.equals(InferenceType.CLASS_ASSERTIONS))
			return reasoner_.doneInstanceTaxonomy();
		if (inferenceType.equals(InferenceType.OBJECT_PROPERTY_HIERARCHY)) {
			return reasoner_.doneObjectPropertyTaxonomy();
		}

		return false;
	}

	@Override
	public boolean isSatisfiable(OWLClassExpression classExpression)
			throws ReasonerInterruptedException, TimeOutException,
			ClassExpressionNotInProfileException, FreshEntitiesException,
			InconsistentOntologyException {
		LOGGER_.trace("isSatisfiable(OWLClassExpression)");
		checkInterrupted();
		try {
			return reasoner_
					.isSatisfiable(owlConverter_.convert(classExpression));
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod("isSatisfiable(classExpression)",
					e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		}
	}

	@Override
	public void precomputeInferences(InferenceType... inferenceTypes)
			throws ReasonerInterruptedException, TimeOutException,
			InconsistentOntologyException {
		LOGGER_.trace("precomputeInferences(InferenceType...)");
		checkInterrupted();
		// we use the main progress monitor only here
		this.reasoner_.setProgressMonitor(this.mainProgressMonitor_);
		try {
			for (InferenceType inferenceType : inferenceTypes) {
				if (inferenceType.equals(InferenceType.CLASS_HIERARCHY))
					reasoner_.getTaxonomy();
				else if (inferenceType.equals(InferenceType.CLASS_ASSERTIONS))
					reasoner_.getInstanceTaxonomy();
				else if (inferenceType
						.equals(InferenceType.OBJECT_PROPERTY_HIERARCHY)) {
					reasoner_.getObjectPropertyTaxonomy();
				}
			}
		} catch (ElkUnsupportedReasoningTaskException e) {
			throw unsupportedOwlApiMethod(
					"precomputeInferences(inferenceTypes)", e.getMessage());
		} catch (ElkException e) {
			throw elkConverter_.convert(e);
		} catch (ElkRuntimeException e) {
			throw elkConverter_.convert(e);
		} finally {
			this.reasoner_.setProgressMonitor(this.secondaryProgressMonitor_);
		}

	}

	public void addListener(ChangeListener listener) {
		changeListeners_.add(listener);
	}

	public void removeListener(ChangeListener listener) {
		changeListeners_.remove(listener);
	}

	protected class OntologyChangeListener
			implements OWLOntologyChangeListener {
		@Override
		public void ontologiesChanged(List<? extends OWLOntologyChange> changes) {
			Set<OWLOntology> importClosure = null;
			for (OWLOntologyChange change : changes) {
				OWLOntology changedOntology = change.getOntology();
				if (!changedOntology.equals(owlOntology_)) {
					if (importClosure == null) {
						importClosure = owlOntology_.getImportsClosure();
					}
					if (!importClosure.contains(changedOntology)) {
						LOGGER_.trace(
								"Ignoring the change not applicable to the current ontology: {}"
										+ change);
						continue;
					}
				}

				if (!change.isAxiomChange()) {
					LOGGER_.trace(
							"Non-axiom change: {}\n The ontology will be reloaded.",
							change);
					// cannot handle non-axiom changes incrementally
					ontologyReloadRequired_ = true;
				} else {
					bufferedChangesLoader_.registerChange(change);
				}
			}
			if (!isBufferingMode_)
				flush();
		}

	}

	private class OntologyChangeProgressListener
			implements OWLOntologyChangeProgressListener {

		private static final long serialVersionUID = -609834181047406971L;

		@Override
		public void begin(int size) {
			if (isBufferingMode_ && loadBeforeChanges_) {
				try {
					LOGGER_.trace("force initial loading");
					reasoner_.ensureLoading();
					loadBeforeChanges_ = false;
				} catch (ElkException e) {
					throw elkConverter_.convert(e);
				}
			}
		}

		@Override
		public void appliedChange(OWLOntologyChange change) {
			// nothing to do

		}

		@Override
		public void end() {
			// nothing to do
		}
	}

	/**
	 * A listener to monitor changes for {@link ElkReasoner}
	 *
	 * @author Yevgeny Kazakov
	 *
	 */
	public static interface ChangeListener {

		/**
		 * Called when the ontology maintained by the reasoner has changed and
		 * the changes were made visible to the reasoner (e.g., by calling
		 * {@link OWLReasoner#flush()}). This signals that the logical
		 * entailments produced by the reasoner may change. Note that if the
		 * reasoner buffering mode is {@link BufferingMode.NON_BUFFERING} then
		 * this method is called only after {@link OWLReasoner#flush()} is
		 * called.
		 */
		void ontologyChanged();

	}

}


package org.semanticweb.elk.util.collections;
/*
 * #%L
 * ELK Utilities Collections
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2014 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%
 */

/**
 * A collection of utilities for creating collections (e.g., sets or maps)
 * backed by arrays with linear probing as method for collision resolution: see
 * [1] p.526.
 *
 * [1] Donald E. Knuth, The Art of Computer Programming, Volume 3, Sorting and
 * Searching, Second Edition
 *
 * @author Yevgeny Kazakov
 *
 */
class LinearProbing {

	/**
	 * The default initial capacity - MUST be a power of two.
	 */
	static final int DEFAULT_INITIAL_CAPACITY = 16;

	/**
	 * The maximum capacity, used if a higher value is implicitly specified by
	 * either of the constructors with arguments. MUST be a power of two <=
	 * 1<<30.
	 */
	static final int MAXIMUM_CAPACITY = 1 << 30;

	/**
	 * Computes the initial capacity of the table for storing elements. The
	 * capacity is the largest power of two that does not exceed the given value
	 * or {@link #MAXIMUM_CAPACITY}.
	 *
	 * @param capacity
	 *            the input value used as a guideline
	 * @return the largest power of two that does not exceed the given value or
	 *         {@link #MAXIMUM_CAPACITY}
	 */
	static int getInitialCapacity(int capacity) {
		if (capacity < 0)
			throw new IllegalArgumentException("Illegal Capacity: " + capacity);
		if (capacity > LinearProbing.MAXIMUM_CAPACITY)
			capacity = LinearProbing.MAXIMUM_CAPACITY;
		// Find a power of 2 >= initialCapacity
		int result = 1;
		while (result < capacity)
			result <<= 1;
		return result;
	}

	/**
	 * Computes a maximum size of the table for a given capacity after which the
	 * table should be expanded.
	 *
	 * @param capacity
	 *            the capacity of the table.
	 * @return maximum size of the table for a given capacity after which to
	 *         stretch the table.
	 */
	static int getUpperSize(int capacity) {
		if (capacity > 64)
			return (capacity >> 1) + (capacity >> 2); // max 75% filled
		// else
		return capacity;
	}

	/**
	 * Computes a minimum size of the table for a given capacity after which to
	 * shrink the table.
	 *
	 * @param capacity
	 *            the capacity of the table.
	 * @return minimum size of the table for a given capacity after which to
	 *         shrink the table
	 */
	static int getLowerSize(int capacity) {
		return capacity >> 2;
	}

	/**
	 * Computes the index position for the object relative to the given length
	 * of the table. This is the position starting from which the object should
	 * be searched for using linear probing.
	 *
	 * @param o
	 *            the object for which to compute the position
	 * @param length
	 *            the length of the table in which the objects are located
	 * @return the first position in the table from which the object should be
	 *         searched
	 */
	private static int getIndex(Object o, int length) {
		return o.hashCode() & (length - 1);
	}

	static <E> int getPosition(E[] d, Object o) {
		int i = getIndex(o, d.length);
		for (;;) {
			Object probe = d[i];
			if (probe == null || o.equals(probe))
				return i;
			if (++i == d.length)
				i = 0;
		}
	}

	/**
	 * Removes the element at the given position of the table shifting, if
	 * necessary, other elements so that all elements can be found by linear
	 * probing.
	 *
	 * @param d
	 *            the array of the elements
	 * @param pos
	 *            the position of data at which to delete the element
	 */
	static <E> void remove(E[] d, int pos) {
		for (;;) {
			int next = getMovedPosition(d, pos);
			E moved = d[pos] = d[next];
			if (moved == null)
				return;
			// else
			pos = next;
		}
	}

	/**
	 * Removes the element at the given position of the table and the
	 * corresponding value at the same position, shifting, if necessary, other
	 * elements and values so that all elements can be found by linear probing.
	 *
	 * @param d
	 *            the array of the elements
	 * @param pos
	 *            the position of data at which to delete the element
	 */
	static <K, V> void remove(K[] k, V[] v, int pos) {
		for (;;) {
			int next = getMovedPosition(k, pos);
			K moved = k[pos] = k[next];
			v[pos] = v[next];
			if (moved == null)
				return;
			// else
			pos = next;
		}
	}

	/**
	 * Finds the position of the next element starting from the given position
	 * that would not be found by linear probing if the element at the given
	 * position are deleted. This should be the element whose index is smaller
	 * than this position.
	 *
	 * @param d
	 * @param del
	 * @return
	 */
	static <E> int getMovedPosition(E[] d, int del) {
		int j = del;
		for (;;) {
			if (++j == d.length)
				j = 0;
			// invariant: interval ]del, j] contains only non-null elements
			// whose index is in ]del, j]
			E test = d[j];
			if (test == null)
				return j;
			int k = getIndex(test, d.length);
			// check if k is in ]del, j] (this interval can wrap over)
			if ((del < j) ? (del < k) && (k <= j) : (del < k) || (k <= j))
				// the test element should not be shifted
				continue;
			// else it should be shifted
			return j;
		}

	}

	/**
	 * Tests if the set represented by given data array contains a given object.
	 *
	 * @param d
	 *            the elements representing the set
	 * @param o
	 *            the object to be tested on inclusion in the set
	 * @return {@code true} if the object occurs in the array and {@code false}
	 *         otherwise
	 */
	static <E> boolean contains(E[] d, Object o) {
		int pos = getPosition(d, o);
		if (d[pos] == null)
			return false;
		// else
		return true;
	}

	/**
	 * Adds the element to the set represented by given data array, if it did
	 * not contain there already.
	 *
	 * @param d
	 *            the elements of the set
	 * @param e
	 *            the element to be added to the set
	 * @return {@code true} if the set has changed (the element is added),
	 *         {@code false} otherwise
	 */
	static <E> boolean add(E[] d, E e) {
		int pos = getPosition(d, e);
		if (d[pos] == null) {
			d[pos] = e;
			return true;
		}
		// else the element is already there
		return false;
	}

	/**
	 * Removes the element from the set represented by given data array, if it
	 * occurs there.
	 *
	 * @param d
	 *            the elements of the set
	 * @param e
	 *            the element to be removed from the array
	 * @return {@code true} if the set has changed (the element is added),
	 *         {@code false} otherwise
	 */
	static <E> boolean remove(E[] d, Object o) {
		int pos = getPosition(d, o);
		if (d[pos] == null)
			return false;
		// else
		remove(d, pos);
		return true;
	}
}


package org.semanticweb.elk.reasoner.indexing.classes;

/*
 * #%L
 * ELK Reasoner
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2015 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 org.semanticweb.elk.reasoner.indexing.conversion.ElkUnexpectedIndexingException;
import org.semanticweb.elk.reasoner.indexing.model.CachedIndexedComplexClassExpression;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClassExpression;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableOntologyIndex;
import org.semanticweb.elk.reasoner.indexing.model.OccurrenceIncrement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Implements {@link CachedIndexedComplexClassExpression}.
 *
 * @author "Yevgeny Kazakov"
 *
 * @param <T>
 *            the type of objects this object can be structurally equal to
 */
abstract class CachedIndexedComplexClassExpressionImpl<T extends CachedIndexedComplexClassExpression<T>>
		extends
		CachedIndexedClassExpressionImpl<T, CachedIndexedComplexClassExpression<?>>
		implements CachedIndexedComplexClassExpression<T> {

	// logger for events
	private static final Logger LOGGER_ = LoggerFactory
			.getLogger(CachedIndexedComplexClassExpressionImpl.class);

	/**
	 * This counts how often this object occurred positively. Some indexing
	 * operations are only needed when encountering objects positively for the
	 * first time.
	 */
	int positiveOccurrenceNo = 0;

	/**
	 * This counts how often this object occurred negatively. Some indexing
	 * operations are only needed when encountering objects negatively for the
	 * first time.
	 */
	int negativeOccurrenceNo = 0;

	CachedIndexedComplexClassExpressionImpl(int structuralHash) {
		super(structuralHash);
	}

	/**
	 * This method should always return true apart from intermediate steps
	 * during the indexing.
	 *
	 * @return true if the represented class expression occurs in the ontology
	 */
	@Override
	public final boolean occurs() {
		return positiveOccurrenceNo > 0 || negativeOccurrenceNo > 0;
	}

	/**
	 * @return the string representation for the occurrence numbers of this
	 *         {@link IndexedClassExpression}
	 */
	@Override
	public final String printOccurrenceNumbers() {
		return "[pos=" + positiveOccurrenceNo + "; neg="
				+ +negativeOccurrenceNo + "]";
	}

	/**
	 * verifies that occurrence numbers are not negative
	 */
	public final void checkOccurrenceNumbers() {
		if (LOGGER_.isTraceEnabled())
			LOGGER_.trace(toString() + " occurences: "
					+ printOccurrenceNumbers());
		if (positiveOccurrenceNo < 0 || negativeOccurrenceNo < 0)
			throw new ElkUnexpectedIndexingException(toString()
					+ " has a negative occurrence: " + printOccurrenceNumbers());
	}

	final boolean updateAndCheckOccurrenceNumbers(
			ModifiableOntologyIndex index, OccurrenceIncrement increment) {
		if (!updateOccurrenceNumbers(index, increment)) {
			LOGGER_.trace("{}: cannot index!", this);
			return false;
		}
		checkOccurrenceNumbers();
		return true;
	}

}


/*
 * #%L
 * ELK OWL API
 *
 * $Id$
 * $HeadURL$
 * %%
 * Copyright (C) 2011 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%
 */
/**
 * @author Yevgeny Kazakov, Jul 1, 2011
 */
package org.semanticweb.elk.owlapi;

import java.util.HashSet;
import java.util.Set;

import org.semanticweb.elk.exceptions.ElkException;
import org.semanticweb.elk.exceptions.ElkRuntimeException;
import org.semanticweb.elk.owl.interfaces.ElkAnnotationProperty;
import org.semanticweb.elk.owl.interfaces.ElkAnnotationSubject;
import org.semanticweb.elk.owl.interfaces.ElkAnnotationValue;
import org.semanticweb.elk.owl.interfaces.ElkAxiom;
import org.semanticweb.elk.owl.interfaces.ElkClass;
import org.semanticweb.elk.owl.interfaces.ElkClassExpression;
import org.semanticweb.elk.owl.interfaces.ElkDataProperty;
import org.semanticweb.elk.owl.interfaces.ElkDataPropertyExpression;
import org.semanticweb.elk.owl.interfaces.ElkDataRange;
import org.semanticweb.elk.owl.interfaces.ElkDatatype;
import org.semanticweb.elk.owl.interfaces.ElkEntity;
import org.semanticweb.elk.owl.interfaces.ElkFacetRestriction;
import org.semanticweb.elk.owl.interfaces.ElkIndividual;
import org.semanticweb.elk.owl.interfaces.ElkLiteral;
import org.semanticweb.elk.owl.interfaces.ElkNamedIndividual;
import org.semanticweb.elk.owl.interfaces.ElkObjectProperty;
import org.semanticweb.elk.owl.interfaces.ElkObjectPropertyExpression;
import org.semanticweb.elk.owl.iris.ElkIri;
import org.semanticweb.elk.owlapi.wrapper.ElkAnnotationPropertyWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkAxiomWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkClassExpressionWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkClassWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkDataPropertyExpressionWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkDataPropertyWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkDataRangeWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkDatatypeWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkEntityWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkFacetRestrictionWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkIndividualWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkLiteralWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkNamedIndividualWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkObjectPropertyExpressionWrap;
import org.semanticweb.elk.owlapi.wrapper.ElkObjectPropertyWrap;
import org.semanticweb.elk.reasoner.ElkFreshEntitiesException;
import org.semanticweb.elk.reasoner.ElkInconsistentOntologyException;
import org.semanticweb.elk.reasoner.stages.ElkInterruptedException;
import org.semanticweb.elk.reasoner.taxonomy.model.Node;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLAnnotationProperty;
import org.semanticweb.owlapi.model.OWLAnnotationSubject;
import org.semanticweb.owlapi.model.OWLAnnotationValue;
import org.semanticweb.owlapi.model.OWLAxiom;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassExpression;
import org.semanticweb.owlapi.model.OWLDataProperty;
import org.semanticweb.owlapi.model.OWLDataPropertyExpression;
import org.semanticweb.owlapi.model.OWLDataRange;
import org.semanticweb.owlapi.model.OWLDatatype;
import org.semanticweb.owlapi.model.OWLEntity;
import org.semanticweb.owlapi.model.OWLFacetRestriction;
import org.semanticweb.owlapi.model.OWLIndividual;
import org.semanticweb.owlapi.model.OWLLiteral;
import org.semanticweb.owlapi.model.OWLNamedIndividual;
import org.semanticweb.owlapi.model.OWLObjectProperty;
import org.semanticweb.owlapi.model.OWLObjectPropertyExpression;
import org.semanticweb.owlapi.model.OWLRuntimeException;
import org.semanticweb.owlapi.reasoner.FreshEntitiesException;
import org.semanticweb.owlapi.reasoner.InconsistentOntologyException;
import org.semanticweb.owlapi.reasoner.ReasonerInternalException;
import org.semanticweb.owlapi.reasoner.ReasonerInterruptedException;
import org.semanticweb.owlapi.reasoner.impl.OWLClassNode;
import org.semanticweb.owlapi.reasoner.impl.OWLClassNodeSet;
import org.semanticweb.owlapi.reasoner.impl.OWLNamedIndividualNode;
import org.semanticweb.owlapi.reasoner.impl.OWLNamedIndividualNodeSet;
import org.semanticweb.owlapi.reasoner.impl.OWLObjectPropertyNode;
import org.semanticweb.owlapi.reasoner.impl.OWLObjectPropertyNodeSet;

/**
 * Facade class for conversion from ELK objects to OWL API objects.
 *
 * @author Yevgeny Kazakov
 * @author Markus Kroetzsch
 * @author Peter Skocovsky
 */
public class ElkConverter extends AbstractElkObjectConverter {

	private static ElkConverter INSTANCE_ = new ElkConverter();

	public static ElkConverter getInstance() {
		return INSTANCE_;
	}

	private ElkConverter() {
	}

	@Override
	public OWLAnnotationProperty convert(ElkAnnotationProperty input) {
		if (input instanceof ElkAnnotationPropertyWrap<?>) {
			return ((ElkAnnotationPropertyWrap<?>) input).getOwlObject();
		}
		// else
		return visit(input);
	}

	@Override
	public OWLAnnotationSubject convert(ElkAnnotationSubject input) {
		return (OWLAnnotationSubject) input.accept(this);
	}

	@Override
	public OWLAnnotationValue convert(ElkAnnotationValue input) {
		return (OWLAnnotationValue) input.accept(this);
	}

	@Override
	public OWLAxiom convert(ElkAxiom input) {
		if (input instanceof ElkAxiomWrap<?>) {
			return ((ElkAxiomWrap<?>) input).getOwlObject();
		}
		// else
		return (OWLAxiom) input.accept(this);
	}

	@Override
	public OWLClass convert(ElkClass input) {
		if (input instanceof ElkClassWrap<?>) {
			return ((ElkClassWrap<?>) input).getOwlObject();
		}
		// else
		return visit(input);
	}

	@Override
	public OWLClassExpression convert(ElkClassExpression input) {
		if (input instanceof ElkClassExpressionWrap<?>) {
			return ((ElkClassExpressionWrap<?>) input).getOwlObject();
		}
		// else
		return (OWLClassExpression) input.accept(this);
	}

	@Override
	public OWLDataProperty convert(ElkDataProperty input) {
		if (input instanceof ElkDataPropertyWrap<?>) {
			return ((ElkDataPropertyWrap<?>) input).getOwlObject();
		}
		// else
		return visit(input);
	}

	@Override
	public OWLDataPropertyExpression convert(ElkDataPropertyExpression input) {
		if (input instanceof ElkDataPropertyExpressionWrap<?>) {
			return ((ElkDataPropertyExpressionWrap<?>) input).getOwlObject();
		}
		// else
		return (OWLDataPropertyExpression) input.accept(this);
	}

	@Override
	public OWLDataRange convert(ElkDataRange input) {
		if (input instanceof ElkDataRangeWrap<?>) {
			return ((ElkDataRangeWrap<?>) input).getOwlObject();
		}
		// else
		return (OWLDataRange) input.accept(this);
	}

	@Override
	public OWLDatatype convert(ElkDatatype input) {
		if (input instanceof ElkDatatypeWrap<?>) {
			return ((ElkDatatypeWrap<?>) input).getOwlObject();
		}
		// else
		return visit(input);
	}

	@Override
	public OWLEntity convert(ElkEntity input) {
		if (input instanceof ElkEntityWrap<?>) {
			return ((ElkEntityWrap<?>) input).getOwlObject();
		}
		// else
		return (OWLEntity) input.accept(this);
	}

	// TODO: perhaps convert using some visitor
	public OWLRuntimeException convert(ElkException e) {
		if (e instanceof ElkFreshEntitiesException)
			return convert((ElkFreshEntitiesException) e);
		else if (e instanceof ElkInconsistentOntologyException)
			return convert((ElkInconsistentOntologyException) e);
		else if (e instanceof ElkInterruptedException)
			return convert((ElkInterruptedException) e);
		else
			return new OWLRuntimeException(e);
	}

	@Override
	public OWLFacetRestriction convert(ElkFacetRestriction input) {
		if (input instanceof ElkFacetRestrictionWrap<?>) {
			return ((ElkFacetRestrictionWrap<?>) input).getOwlObject();
		}
		// else
		return visit(input);
	}

	public FreshEntitiesException convert(ElkFreshEntitiesException e) {
		HashSet<OWLEntity> owlEntities = new HashSet<OWLEntity>();
		for (ElkEntity elkEntity : e.getEntities()) {
			owlEntities.add(convert(elkEntity));
		}
		return new FreshEntitiesException(owlEntities);
	}

	public InconsistentOntologyException convert(
			ElkInconsistentOntologyException e) {
		return new InconsistentOntologyException();
	}

	@Override
	public OWLIndividual convert(ElkIndividual input) {
		if (input instanceof ElkIndividualWrap<?>) {
			return ((ElkIndividualWrap<?>) input).getOwlObject();
		}
		// else
		return (OWLIndividual) input.accept(this);
	}

	public ReasonerInterruptedException convert(ElkInterruptedException e) {
		return new ReasonerInterruptedException((ElkInterruptedException) e);
	}

	@Override
	public IRI convert(ElkIri input) {
		return (IRI) input.accept(this);
	}

	@Override
	public OWLLiteral convert(ElkLiteral input) {
		if (input instanceof ElkLiteralWrap<?>) {
			return ((ElkLiteralWrap<?>) input).getOwlObject();
		}
		// else
		return visit(input);
	}

	@Override
	public OWLNamedIndividual convert(ElkNamedIndividual input) {
		if (input instanceof ElkNamedIndividualWrap<?>) {
			return ((ElkNamedIndividualWrap<?>) input).getOwlObject();
		}
		// else
		return visit(input);
	}

	@Override
	public OWLObjectProperty convert(ElkObjectProperty input) {
		if (input instanceof ElkObjectPropertyWrap<?>) {
			return ((ElkObjectPropertyWrap<?>) input).getOwlObject();
		}
		// else
		return visit(input);
	}

	@Override
	public OWLObjectPropertyExpression convert(
			ElkObjectPropertyExpression input) {
		if (input instanceof ElkObjectPropertyExpressionWrap<?>) {
			return ((ElkObjectPropertyExpressionWrap<?>) input).getOwlObject();
		}
		// else
		return (OWLObjectPropertyExpression) input.accept(this);
	}

	public OWLRuntimeException convert(ElkRuntimeException e) {
		return new ReasonerInternalException(e);
	}

	public OWLClassNode convertClassNode(Node<ElkClass> node) {
		Set<OWLClass> owlClasses = new HashSet<OWLClass>();
		for (ElkClass cls : node) {
			owlClasses.add(convert(cls));
		}
		return new OWLClassNode(owlClasses);
	}

	public OWLClassNodeSet convertClassNodes(
			Iterable<? extends Node<ElkClass>> nodes) {
		Set<org.semanticweb.owlapi.reasoner.Node<OWLClass>> owlNodes = new HashSet<org.semanticweb.owlapi.reasoner.Node<OWLClass>>();
		for (Node<ElkClass> node : nodes) {
			owlNodes.add(convertClassNode(node));
		}
		return new OWLClassNodeSet(owlNodes);
	}

	public OWLNamedIndividualNode convertIndividualNode(
			Node<ElkNamedIndividual> node) {
		Set<OWLNamedIndividual> owlIndividuals = new HashSet<OWLNamedIndividual>();
		for (ElkNamedIndividual ind : node) {
			owlIndividuals.add(convert(ind));
		}
		return new OWLNamedIndividualNode(owlIndividuals);
	}

	public OWLNamedIndividualNodeSet convertIndividualNodes(
			Iterable<? extends Node<ElkNamedIndividual>> nodes) {
		Set<org.semanticweb.owlapi.reasoner.Node<OWLNamedIndividual>> owlNodes = new HashSet<org.semanticweb.owlapi.reasoner.Node<OWLNamedIndividual>>();
		for (Node<ElkNamedIndividual> node : nodes) {
			owlNodes.add(convertIndividualNode(node));
		}
		return new OWLNamedIndividualNodeSet(owlNodes);
	}

	public OWLObjectPropertyNode convertObjectPropertyNode(
			final Node<ElkObjectProperty> node) {
		final Set<OWLObjectPropertyExpression> owlObjectProps = new HashSet<OWLObjectPropertyExpression>();
		for (final ElkObjectProperty cls : node) {
			owlObjectProps.add(convert(cls));
		}
		return new OWLObjectPropertyNode(owlObjectProps);
	}

	public OWLObjectPropertyNodeSet convertObjectPropertyNodes(
			final Iterable<? extends Node<ElkObjectProperty>> nodes) {
		Set<org.semanticweb.owlapi.reasoner.Node<OWLObjectPropertyExpression>> owlNodes = new HashSet<org.semanticweb.owlapi.reasoner.Node<OWLObjectPropertyExpression>>();
		for (final Node<ElkObjectProperty> node : nodes) {
			owlNodes.add(convertObjectPropertyNode(node));
		}
		return new OWLObjectPropertyNodeSet(owlNodes);
	}

}