/*
 * #%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 2, 2011
 */
package org.semanticweb.elk.util.hashing;

import java.util.Iterator;
import java.util.List;

/**
 * Class used to generate strong hash codes for newly created objects, and for
 * combining multiple hash codes into new ones in various ways.
 *
 * @author Yevgeny Kazakov
 * @author Markus Kroetzsch
 * @author Frantisek Simancik
 *
 */
public class HashGenerator {

	/**
	 * The counter incremented with each generated hash code which will be used
	 * for generating a hash code by generateNextHashCode().
	 */
	private static int counter = 0;

	/**
	 * Generate a new "hash code" integer. Consecutive calls to this method
	 * create a sequence of integers with near-uniform distribution. These codes
	 * may be used as hash codes for objects that are managed in such a way that
	 * no two objects of different content can occur. Different hash codes then
	 * guarantee that the objects are not equal, even if the hash code does is
	 * not based on the hashed object at all. Obviously, hash codes of this form
	 * are not stable over multiple runs of the program, and can therefore not
	 * be used for comparing objects across runs (e.g. in testing). However,
	 * they can be useful in hash-based data structures.
	 *
	 * @return the generated hash code
	 */
	public static int generateNextHashCode() {
		// we use a strategy from the java.util.HashMap
		int h = ++counter;
		h ^= (h >>> 20) ^ (h >>> 12);
		return h ^ (h >>> 7) ^ (h >>> 4);
	}

	/**
	 * Compute the combined hash code of several objects using their
	 * {@link #hashCode()} functions. The combined hash code depends on the
	 * order in which the objects are listed.
	 *
	 * The current implementation is based on the Jenkins One-at-a-Time hash,
	 * see http://www.burtleburtle.net/bob/hash/doobs.html and also
	 * http://en.wikipedia.org/wiki/Jenkins_hash_function.
	 *
	 * @param objects
	 *            the list of objects for which to compute the combined hash
	 *            code
	 * @return the combined hash code
	 */
	public static int combinedHashCode(Object... objects) {
		int result = 0;
		for (Object obj : objects) {
			int h = obj.hashCode();
			result += h;
			result += (h << 10);
			result ^= (h >> 6);
		}
		result += (result << 3);
		result ^= (result >> 11);
		result += (result << 15);

		return result;
	}

	/**
	 * Compute the combined hash code of several objects using their
	 * {@link #hashCode()} functions. The combined hash code depends on the
	 * order in which the objects are listed.
	 *
	 * The current implementation is based on the Jenkins One-at-a-Time hash,
	 * see http://www.burtleburtle.net/bob/hash/doobs.html and also
	 * http://en.wikipedia.org/wiki/Jenkins_hash_function.
	 *
	 * @param objects
	 *            the list of objects for which to compute the combined hash
	 *            code
	 * @return the combined hash code
	 */
	public static int combinedHashCode(Iterable<?> objects) {
		int result = 0;
		for (Object obj : objects) {
			int h = obj.hashCode();
			result += h;
			result += (h << 10);
			result ^= (h >> 6);
		}
		result += (result << 3);
		result ^= (result >> 11);
		result += (result << 15);

		return result;
	}

	/**
	 * Combine many hash codes with an associative commutative hash function.
	 * Associativity ensures that the result of this functions can be further
	 * combined with other hash codes for getting the same result as if all hash
	 * codes had been combined in one step.
	 *
	 * If finalize is false, then the method as such represents an associative
	 * commutative hash, i.e. the return value can be combined with other
	 * set-based hash codes in any order without making a difference. If
	 * finalize is true, then the method combines its arguments with a
	 * commutative hash, but shuffles the overall result. The method as such
	 * then is neither an associative nor a commutative operation. This mode of
	 * operation should be used whenever no further elements are to be added to
	 * the set of hashes.
	 *
	 * @param finalize
	 * @param hashes
	 * @return the combined hash code
	 */
	public static int combineMultisetHash(boolean finalize, int... hashes) {
		int hash = 0;
		for (int h : hashes) {
			hash = hash + h;
		}
		if (finalize) {
			hash = combineListHash(hash);
		}
		return hash;
	}

	/**
	 * Combine the hash codes of a collection of objects with an associative
	 * commutative hash function. Associativity ensures that the result of this
	 * functions can be further combined with other hash codes for getting the
	 * same result as if all hash codes had been combined in one step.
	 *
	 * If finalize is false, then the method as such represents an associative
	 * commutative hash, i.e. the return value can be combined with other
	 * set-based hash codes in any order without making a difference. If
	 * finalize is true, then the method combines its arguments with a
	 * commutative hash, but shuffles the overall result. The method as such
	 * then is neither an associative nor a commutative operation. This mode of
	 * operation should be used whenever no further elements are to be added to
	 * the set of hashes.
	 *
	 * @param finalize
	 * @param hashObjects
	 * @param hasher
	 * @return the combined hash code
	 */
	public static <T> int combineMultisetHash(boolean finalize,
			Iterable<? extends T> hashObjects, Hasher<T> hasher) {
		return combineMultisetHash(finalize, hashObjects.iterator(), hasher);
	}

	/**
	 * Combine the hash codes of a collection of objects with an associative
	 * commutative hash function. Associativity ensures that the result of this
	 * functions can be further combined with other hash codes for getting the
	 * same result as if all hash codes had been combined in one step.
	 *
	 * If finalize is false, then the method as such represents an associative
	 * commutative hash, i.e. the return value can be combined with other
	 * set-based hash codes in any order without making a difference. If
	 * finalize is true, then the method combines its arguments with a
	 * commutative hash, but shuffles the overall result. The method as such
	 * then is neither an associative nor a commutative operation. This mode of
	 * operation should be used whenever no further elements are to be added to
	 * the set of hashes.
	 *
	 * @param <T>
	 *
	 * @param finalize
	 * @param hashObjectIterator
	 * @param hasher
	 * @return the combined hash code
	 */
	public static <T> int combineMultisetHash(boolean finalize,
			Iterator<? extends T> hashObjectIterator, Hasher<T> hasher) {
		int hash = 0;
		while (hashObjectIterator.hasNext()) {
			hash += hasher.hash(hashObjectIterator.next());
		}
		if (finalize) {
			hash = combineListHash(hash);
		}
		return hash;
	}

	/**
	 * Combine many hash codes into one in a way that depends on their order.
	 *
	 * The current implementation is based on the Jenkins One-at-a-Time hash,
	 * see http://www.burtleburtle.net/bob/hash/doobs.html and also
	 * http://en.wikipedia.org/wiki/Jenkins_hash_function.
	 *
	 * @param hashes
	 * @return the combined hash code
	 */
	public static int combineListHash(int... hashes) {
		int hash = 0;
		for (int h : hashes) {
			hash += h;
			hash += (hash << 10);
			hash ^= (hash >> 6);
		}
		hash += (hash << 3);
		hash ^= (hash >> 11);
		hash += (hash << 15);

		return hash;
	}

	/**
	 * Combine the hash codes of a collection of objects into one in a way that
	 * depends on their order.
	 *
	 * The current implementation is based on the Jenkins One-at-a-Time hash,
	 * see http://www.burtleburtle.net/bob/hash/doobs.html and also
	 * http://en.wikipedia.org/wiki/Jenkins_hash_function.
	 *
	 * @param hashObjects
	 * @param hasher
	 * @return the combined hash code
	 */
	public static <T> int combineListHash(List<? extends T> hashObjects,
			Hasher<T> hasher) {
		return combineListHash(hashObjects.iterator(), hasher);
	}

	/**
	 * Combine the hash codes of a collection of objects into one in a way that
	 * depends on their order.
	 *
	 * The current implementation is based on the Jenkins One-at-a-Time hash,
	 * see http://www.burtleburtle.net/bob/hash/doobs.html and also
	 * http://en.wikipedia.org/wiki/Jenkins_hash_function.
	 *
	 * @param hashObjectIterator
	 * @param hasher
	 * @return the combined hash code
	 */
	public static <T> int combineListHash(
			Iterator<? extends T> hashObjectIterator, Hasher<T> hasher) {
		int hash = 0;
		while (hashObjectIterator.hasNext()) {
			hash += hasher.hash(hashObjectIterator.next());
			hash += (hash << 10);
			hash ^= (hash >> 6);
		}
		hash += (hash << 3);
		hash ^= (hash >> 11);
		hash += (hash << 15);
		return hash;
	}

}


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

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.Writer;
import java.net.URL;
import java.security.CodeSource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Stack;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;

/**
 * @author Pavel Klinov
 *
 *         pavel.klinov@uni-ulm.de
 * @author Peter Skocovsky
 */
public class IOUtils {

	private static final int BUFFER_SIZE = 1024 * 2;

	public static void closeQuietly(InputStream stream) {
		if (stream != null) {
			try {
				stream.close();
			} catch (IOException e) {
			}
		}
	}

	public static void closeQuietly(Writer writer) {
		if (writer != null) {
			try {
				writer.close();
			} catch (IOException e) {
			}
		}
	}

	public static void closeQuietly(OutputStream stream) {
		if (stream != null) {
			try {
				stream.close();
			} catch (IOException e) {
			}
		}
	}

	/**
	 * Copies bytes from the input stream to the output stream
	 *
	 * @param input
	 * @param output
	 *
	 * @return The number of bytes copied
	 * @throws IOException
	 */
	public static int copy(InputStream input, OutputStream output)
			throws IOException {
		byte[] buffer = new byte[BUFFER_SIZE];

		BufferedInputStream in = new BufferedInputStream(input, BUFFER_SIZE);
		BufferedOutputStream out = new BufferedOutputStream(output, BUFFER_SIZE);
		int count = 0, n = 0;

		try {
			while ((n = in.read(buffer, 0, BUFFER_SIZE)) != -1) {
				out.write(buffer, 0, n);
				count += n;
			}
			out.flush();
		} finally {
			IOUtils.closeQuietly(in);
			IOUtils.closeQuietly(out);
		}

		return count;
	}

	private static void collectResourceNamesFromDir(final File file,
			final String prefix, final String extension,
			final Collection<String> result) {

		final String fileName = file.getName();

		if (file.isDirectory()) {
			final File[] innerFiles = file.listFiles();
			if (innerFiles == null) {
				throw new RuntimeException("Error listing directory " + file);
			}
			// else
			for (final File innerFile : innerFiles) {
				collectResourceNamesFromDir(innerFile,
						prefix + fileName + File.separator, extension, result);
			}
			return;
		}
		// else file is not a directory

		if (fileName.endsWith("." + extension)) {
			result.add(prefix + fileName);
		}

	}

	/**
	 * @param dir
	 * @param extension
	 * @return A list of paths relative to {@code dir} of the files from
	 *         {@code dir} with the provided filename extension. The directory
	 *         is traversed recursively.
	 */
	public static List<String> getResourceNamesFromDir(File dir,
			String extension) {
		List<String> testResources = new ArrayList<String>();
		collectResourceNamesFromDir(dir, "", extension, testResources);
		return testResources;
	}

	/**
	 * Retrieves a set of resource names from a JAR file (the code source for
	 * the given Java class)
	 *
	 * @param path
	 *
	 * @param extension
	 * @param clazz
	 * @return the list of resource names from a JAR file
	 * @throws IOException
	 */
	public static List<String> getResourceNamesFromJAR(String path,
			String extension, Class<?> clazz) throws IOException {
		CodeSource src = clazz.getProtectionDomain().getCodeSource();
		List<String> testResources = new ArrayList<String>();
		ZipInputStream zip = null;

		if (src != null) {
			URL jar = src.getLocation();
			ZipEntry ze = null;

			try {
				zip = new ZipInputStream(jar.openStream());

				while ((ze = zip.getNextEntry()) != null) {
					String entryName = ze.getName();

					if (entryName.startsWith(path)
							&& entryName.endsWith("." + extension)) {
						testResources.add(entryName);
					}
				}
			} finally {
				closeQuietly(zip);
			}
		} else {
			throw new IOException("Unable to get code source for "
					+ clazz.getSimpleName());
		}

		return testResources;
	}

	public static void traverseDirectoryTree(final File root,
			final PathVisitor visitor) {
		traverseDirectoryTree(root, "", visitor);
	}

	private static void traverseDirectoryTree(final File file,
			final String relativePath, final PathVisitor visitor) {

		final String path = relativePath + file.getName();

		visitor.visitBefore(path);

		if (file.isDirectory()) {
			final File[] innerFiles = file.listFiles();
			if (innerFiles == null) {
				throw new RuntimeException("Error listing directory " + file);
			}
			// else
			for (final File innerFile : innerFiles) {
				traverseDirectoryTree(innerFile, path + File.separator,
						visitor);
			}
		}

		visitor.visitAfter(path);

	}

	public static void traverseJarContentTree(final String rootPath,
			final Class<?> srcClass, final PathVisitor visitor)
			throws IOException {

		// Make sure that root is a directory (by appending '/').
		final String root = rootPath.endsWith("/") ? rootPath : rootPath + "/";

		final CodeSource src = srcClass.getProtectionDomain().getCodeSource();
		if (src == null) {
			throw new IOException("Unable to get code source for "
					+ srcClass.getSimpleName());
		}

		// Collect zip entries starting with rootPath.
		final List<String> paths = new ArrayList<String>();
		boolean haveRootPath = false;
		ZipInputStream zip = null;
		try {
			zip = new ZipInputStream(src.getLocation().openStream());

			ZipEntry ze = null;
			while ((ze = zip.getNextEntry()) != null) {
				final String entryName = ze.getName();
				if (entryName.startsWith(root)) {
					paths.add(entryName);
					if (entryName.length() == root.length()) {
						haveRootPath = true;
					}
				}
			}
		} finally {
			closeQuietly(zip);
		}

		// Simulate recursive traversal.
		/*
		 * If nodes in a file tree are the file paths, then any predecessor of a
		 * node is its prefix. We iterate over such nodes in the alphabetical
		 * order and remember the current branch in a stack. When the top of the
		 * stack is a prefix of the current node, we are descending in the tree
		 * (pushing the current node). When it is not, we are ascending by
		 * popping until the top is a prefix again, which is the previous case.
		 */
		if (!haveRootPath) {
			// So that there is only one root.
			paths.add(root);
		}
		Collections.sort(paths);

		final Stack<String> currentBranch = new Stack<String>();

		for (final String path : paths) {
			final String[] pathComponents = path.split("/");

			if (currentBranch.isEmpty()) {
				// The first element.
				visitor.visitBefore(path);
				currentBranch.push(path);
				continue;
			}
			// Else, not the first element.

			String top = currentBranch.peek();
			String[] topComponents = top.split("/");
			if (startsWith(pathComponents, topComponents)) {
				// Descending.
				visitor.visitBefore(path);
				currentBranch.push(path);
				continue;
			}
			// else, ascending.

			do {
				currentBranch.pop();
				visitor.visitAfter(top);
				/*
				 * The stack shouldn't be empty, because the root is prefix of
				 * every node and it is on the bottom of the stack.
				 */
				top = currentBranch.peek();
				topComponents = top.split("/");
			} while (!startsWith(pathComponents, topComponents));

			// Descending again.
			visitor.visitBefore(path);
			currentBranch.push(path);

		}

		// Ascend back to the root.
		while (!currentBranch.isEmpty()) {
			final String top = currentBranch.pop();
			visitor.visitAfter(top);
		}

	}

	private static <E> boolean startsWith(final E[] array, final E[] prefix) {
		if (prefix.length > array.length) {
			return false;
		}
		// else
		for (int i = 0; i < prefix.length; i++) {
			if (prefix[i] == null ? array[i] != null
					: !prefix[i].equals(array[i])) {
				return false;
			}
		}
		return true;
	}

	public static interface PathVisitor {

		void visitBefore(String path);

		void visitAfter(String path);

	}

}


/**
 *
 */
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.IndexedObjectSomeValuesFrom;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.ClassConclusion;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.ForwardLink;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.SubClassInclusionDecomposed;
import org.semanticweb.elk.reasoner.tracing.Conclusion;
import org.semanticweb.elk.reasoner.tracing.Conclusion.Factory;

// TODO: split on two inferences
/**
 * A {@link ClassInference} producing a {@link ForwardLink} from a
 * {@link SubClassInclusionDecomposed} with
 * {@link SubClassInclusionDecomposed#getSubsumer()} of the type
 * {@link IndexedObjectSomeValuesFrom}:

 *
 *

 *    [C] ⊑ -∃R.D
 * ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯
 *  [C] ⊑ <∃R>.(D ⊓ ∃R-)
 *

 *
 * The parameters can be obtained as follows:

 *
 * C = {@link #getOrigin()} = {@link #getDestination()}

 * ∃R.D = {@link #getDecomposedExistential()} (from which R and D can be
 * obtained)

 *
 * @author "Yevgeny Kazakov"
 */
public class ForwardLinkOfObjectSomeValuesFrom extends AbstractClassInference
		implements ForwardLinkInference {

	private final IndexedObjectSomeValuesFrom existential_;

	public ForwardLinkOfObjectSomeValuesFrom(IndexedContextRoot inferenceRoot,
			IndexedObjectSomeValuesFrom subsumer) {
		super(inferenceRoot);
		existential_ = subsumer;
	}

	public IndexedObjectSomeValuesFrom getDecomposedExistential() {
		return this.existential_;
	}

	public SubClassInclusionDecomposed getPremise(
			SubClassInclusionDecomposed.Factory factory) {
		return factory.getSubClassInclusionDecomposed(getOrigin(),
				existential_);
	}

	/**
	 * @param factory
	 *            the factory for creating conclusions
	 *
	 * @return the conclusion produced by this inference
	 */
	public ForwardLink getConclusion(ClassConclusion.Factory factory) {
		return factory.getForwardLink(getDestination(),
				existential_.getProperty(),
				IndexedObjectSomeValuesFrom.Helper.getTarget(existential_));
	}

	@Override
	public IndexedContextRoot getOrigin() {
		return getDestination();
	}

	@Override
	public int getPremiseCount() {
		return 1;
	}

	@Override
	public Conclusion getPremise(int index, Factory factory) {
		switch (index) {
		case 0:
			return getPremise(factory);
		default:
			return failGetPremise(index);
		}
	}

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

	@Override
	public final <O> O accept(ForwardLinkInference.Visitor<O> visitor) {
		return visitor.visit(this);
	}

	/**
	 * Visitor pattern for instances
	 *
	 * @author Yevgeny Kazakov
	 *
	 * @param <O>
	 *            the type of the output
	 */
	public static interface Visitor<O> {

		public O visit(ForwardLinkOfObjectSomeValuesFrom inference);

	}

}


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

import java.util.Collection;
import java.util.Iterator;

import org.semanticweb.elk.exceptions.ElkRuntimeException;
import org.semanticweb.elk.util.concurrent.computation.ConcurrentExecutor;
import org.semanticweb.elk.util.concurrent.computation.ConcurrentComputationWithInputs;
import org.semanticweb.elk.util.concurrent.computation.InputProcessorFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * A {@link ConcurrentComputationWithInputs} used for executing of reasoner
 * stages
 *
 * @author "Yevgeny Kazakov"
 *
 * @param <I>
 *            the input that can be processed by the computation
 * @param <F>
 *            the type of the factory for the input processors
 */
public class ReasonerComputationWithInputs<I, F extends InputProcessorFactory<I, ?>>
		extends ConcurrentComputationWithInputs<I, F> {

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

	/**
	 * the progress monitor used to report the progress of this computation
	 */
	protected final ProgressMonitor progressMonitor;
	/**
	 * the inputs to be processed
	 */
	protected final Iterator<? extends I> todo;
	/**
	 * the final value of the progress monitor: the total number of inputs
	 */
	private final int maxProgress;
	/**
	 * the current value of the progress monitors: the current input
	 */
	private int progress;
	/**
	 * next input to be submitted
	 */
	I nextInput;

	public ReasonerComputationWithInputs(Collection<? extends I> inputs,
			F inputProcessorFactory, ConcurrentExecutor executor,
			int maxWorkers, ProgressMonitor progressMonitor) {
		super(inputProcessorFactory, executor, maxWorkers);
		this.progressMonitor = progressMonitor;
		this.todo = inputs.iterator();
		this.maxProgress = inputs.size();
		this.progress = 0;
		this.nextInput = null;
	}

	/**
	 * Process the given input concurrently using the provided input processor.
	 * If the process has been interrupted, this method can be called again to
	 * continue the computation.
	 */
	public void process() {

		if (!start()) {
			String message = "Could not start workers required for reasoner computation!";
			LOGGER_.error(message);
			throw new ElkRuntimeException(message);
		}

		try {
			// submit the leftover from the previous run
			if (nextInput != null) {
				if (!processNextInput())
					return;
			}
			// repeatedly submit the next inputs from todo
			while (todo.hasNext()) {
				nextInput = todo.next();
				if (!processNextInput())
					return;
			}
			finish();
		} catch (InterruptedException e) {
			// restore interrupt status
			Thread.currentThread().interrupt();
			throw new ElkRuntimeException(
					"Reasoner computation interrupted externally!");
		}
	}

	private boolean processNextInput() throws InterruptedException {
		if (!submit(nextInput)) {
			waitWorkers();
			return false;
		}
		nextInput = null;
		if (isInterrupted()) {
			waitWorkers();
			return false;
		}
		progressMonitor.report(++progress, maxProgress);
		return true;
	}
}


/*-
 * #%L
 * ELK Reasoner Core
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2017 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.completeness;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.semanticweb.elk.reasoner.indexing.model.OccurrenceStore;
import org.semanticweb.elk.util.logging.LogLevel;
import org.semanticweb.elk.util.logging.LoggerWrap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;

public class IncompletenessManager {

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

	private static final Marker MARKER_ = MarkerFactory
			.getMarker("Incompleteness");

	private final IncompletenessMonitor incompletenessDueToStatedAxiomsMonitor_;

	/**
	 * @param occurrencesInStatedAxiomsStore
	 *            The store of occurrences in the loaded ontology.
	 */
	public IncompletenessManager(
			final OccurrenceStore occurrencesInStatedAxiomsStore) {
		this.incompletenessDueToStatedAxiomsMonitor_ = new DelegatingIncompletenessMonitor(
				new IncompletenessDueToNegativeOccurrenceOfObjectComplementOfMonitor(
						occurrencesInStatedAxiomsStore, LogLevel.INFO,
						IncompletenessDueToInStatedAxiomsMessageProvider.INSTANCE),
				new IncompletenessDueToNegativeOccurrenceOfTopObjectPropertyMonitor(
						occurrencesInStatedAxiomsStore, LogLevel.INFO,
						IncompletenessDueToInStatedAxiomsMessageProvider.INSTANCE),
				new IncompletenessDueToOccurrenceOfDataHasValueMonitor(
						occurrencesInStatedAxiomsStore, LogLevel.INFO,
						IncompletenessDueToInStatedAxiomsMessageProvider.INSTANCE),
				new IncompletenessDueToOccurrenceOfDisjointUnionMonitor(
						occurrencesInStatedAxiomsStore, LogLevel.INFO,
						IncompletenessDueToInStatedAxiomsMessageProvider.INSTANCE),
				new IncompletenessDueToOccurrenceOfNominalMonitor(
						occurrencesInStatedAxiomsStore, LogLevel.INFO,
						IncompletenessDueToInStatedAxiomsMessageProvider.INSTANCE),
				new IncompletenessDueToOccurrenceOfUnsupportedExpressionMonitor(
						occurrencesInStatedAxiomsStore, LogLevel.INFO,
						IncompletenessDueToInStatedAxiomsMessageProvider.INSTANCE),
				new IncompletenessDueToPositiveOccurrenceOfBottomObjectPropertyMonitor(
						occurrencesInStatedAxiomsStore, LogLevel.INFO,
						IncompletenessDueToInStatedAxiomsMessageProvider.INSTANCE),
				new IncompletenessDueToPositiveOccurrenceOfObjectUnionOfMonitor(
						occurrencesInStatedAxiomsStore, LogLevel.INFO,
						IncompletenessDueToInStatedAxiomsMessageProvider.INSTANCE),
				new IncompletenessDueToRangeAndPropertyAssertionOccurrencesInStatedAxiomsMonitor(
						occurrencesInStatedAxiomsStore, LogLevel.INFO));
	}

	/**
	 * @return The {@link IncompletenessMonitor} that monitors incompleteness
	 *         that follows from the loaded ontology.
	 */
	public IncompletenessMonitor getIncompletenessDueToStatedAxiomsMonitor() {
		return incompletenessDueToStatedAxiomsMonitor_;
	}

	/**
	 * @param occurrencesInQuery
	 *            Occurrences in a query that is being checked for completeness.
	 * @param messageProvider
	 * @return The {@link IncompletenessMonitor} that monitors incompleteness
	 *         that follows from the provided occurrences in a class expression
	 *         or an entailment query.
	 */
	private IncompletenessMonitor getIncompletenessDueToOccurrencesInQueryMonitor(
			final OccurrenceStore occurrencesInQuery,
			final IncompletenessMessageProvider messageProvider) {
		return new DelegatingIncompletenessMonitor(
				new IncompletenessDueToNegativeOccurrenceOfObjectComplementOfMonitor(
						occurrencesInQuery, LogLevel.INFO, messageProvider),
				new IncompletenessDueToNegativeOccurrenceOfTopObjectPropertyMonitor(
						occurrencesInQuery, LogLevel.INFO, messageProvider),
				new IncompletenessDueToOccurrenceOfDataHasValueMonitor(
						occurrencesInQuery, LogLevel.INFO, messageProvider),
				new IncompletenessDueToOccurrenceOfNominalMonitor(
						occurrencesInQuery, LogLevel.INFO, messageProvider),
				new IncompletenessDueToOccurrenceOfUnsupportedExpressionMonitor(
						occurrencesInQuery, LogLevel.INFO, messageProvider),
				new IncompletenessDueToPositiveOccurrenceOfBottomObjectPropertyMonitor(
						occurrencesInQuery, LogLevel.INFO, messageProvider),
				new IncompletenessDueToPositiveOccurrenceOfObjectUnionOfMonitor(
						occurrencesInQuery, LogLevel.INFO, messageProvider));
	}

	/**
	 * Combines the provided partial incompleteness monitors into the top-level
	 * monitor for reasoning tasks.
	 *
	 * @param additionalMonitors
	 *            The partial monitors.
	 * @return The top-level monitor for reasoning tasks.
	 */
	public IncompletenessMonitor getReasonerIncompletenessMonitor(
			final IncompletenessMonitor... additionalMonitors) {
		final List<IncompletenessMonitor> monitors = new ArrayList<IncompletenessMonitor>(
				additionalMonitors.length + 1);
		monitors.add(getIncompletenessDueToStatedAxiomsMonitor());
		monitors.addAll(Arrays.asList(additionalMonitors));
		return new DelegatingIncompletenessMonitor(monitors) {
			@Override
			public boolean logNewIncompletenessReasons(final Logger logger) {
				final boolean result = super.logNewIncompletenessReasons(
						logger);
				if (result) {
					LoggerWrap.log(logger, LogLevel.WARN, MARKER_,
							"Reasoning may be incomplete! See log level INFO for more details.");
				}
				return result;
			}
		};
	}

	public IncompletenessMonitor getIncompletenessMonitorForClassification() {
		return getReasonerIncompletenessMonitor();
	}

	/**
	 * @param occurrencesInQuery
	 *            Occurrences in a class expression query that is being checked
	 *            for completeness.
	 * @return
	 */
	public IncompletenessMonitor getIncompletenessMonitorForClassExpressionQuery(
			final OccurrenceStore occurrencesInQuery) {
		return getReasonerIncompletenessMonitor(
				getIncompletenessDueToOccurrencesInQueryMonitor(
						occurrencesInQuery,
						IncompletenessDueToOccurrencesInClassExpressionQueryMessageProvider.INSTANCE));
	}

	/**
	 * @param occurrencesInQuery
	 *            Occurrences in a entailment query that is being checked for
	 *            completeness.
	 * @return
	 */
	public IncompletenessMonitor getIncompletenessMonitorForEntailmentQuery(
			final OccurrenceStore occurrencesInQuery) {
		return getReasonerIncompletenessMonitor(
				getIncompletenessDueToOccurrencesInQueryMonitor(
						occurrencesInQuery,
						IncompletenessDueToOccurrencesInEntailmentQueryMessageProvider.INSTANCE));
	}

	public void log(final IncompletenessMonitor monitor) {
		monitor.logNewIncompletenessReasons(LOGGER_);
	}

}


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

/*
 * #%L
 * ELK Reasoner
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2016 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.owl.interfaces.ElkClass;
import org.semanticweb.elk.owl.predefined.PredefinedElkIris;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClass;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClassExpressionList;
import org.semanticweb.elk.reasoner.indexing.model.IndexedComplexPropertyChain;
import org.semanticweb.elk.reasoner.indexing.model.IndexedDataHasValue;
import org.semanticweb.elk.reasoner.indexing.model.IndexedDeclarationAxiom;
import org.semanticweb.elk.reasoner.indexing.model.IndexedEquivalentClassesAxiom;
import org.semanticweb.elk.reasoner.indexing.model.IndexedDisjointClassesAxiom;
import org.semanticweb.elk.reasoner.indexing.model.IndexedIndividual;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObject;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectComplementOf;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectHasSelf;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectIntersectionOf;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectProperty;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectPropertyRangeAxiom;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectSomeValuesFrom;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectUnionOf;
import org.semanticweb.elk.reasoner.indexing.model.IndexedRangeFiller;
import org.semanticweb.elk.reasoner.indexing.model.IndexedSubClassOfAxiom;
import org.semanticweb.elk.reasoner.indexing.model.IndexedSubObjectPropertyOfAxiom;

public class IndexedObjectPrinter implements IndexedObject.Visitor<String> {

	private static IndexedObjectPrinter INSTANCE_ = new IndexedObjectPrinter();

	static IndexedObject.Visitor<String> getPrinterVisitor() {
		return INSTANCE_;
	}

	public static String toString(IndexedObject element) {
		return element.accept(INSTANCE_);
	}

	private IndexedObjectPrinter() {

	}

	// TODO: parentheses and precedence of symbols

	@Override
	public String visit(IndexedClass element) {
		ElkClass entity = element.getElkEntity();
		if (entity.getIri().equals(PredefinedElkIris.OWL_THING)) {
			return "⊤";
		}
		// else
		if (entity.getIri().equals(PredefinedElkIris.OWL_NOTHING)) {
			return "⊥";
		}
		// else
		return entity.toString();
	}

	@Override
	public String visit(IndexedClassExpressionList element) {
		return element.getElements().toString();
	}

	@Override
	public String visit(IndexedComplexPropertyChain element) {
		return element.getFirstProperty() + "∘" + element.getSuffixChain();
	}

	@Override
	public String visit(IndexedDataHasValue element) {
		return "∃" + element.getRelation() + '.' + element.getFiller();
	}

	@Override
	public String visit(IndexedDeclarationAxiom axiom) {
		return axiom.getEntity().getElkEntity().getEntityType() + "("
				+ axiom.getEntity() + ")";
	}

	@Override
	public String visit(IndexedEquivalentClassesAxiom axiom) {
		return axiom.getFirstMember() + " = " + axiom.getSecondMember();
	}

	@Override
	public String visit(IndexedDisjointClassesAxiom axiom) {
		return "Disjoint(" + axiom.getMembers() + ")";
	}

	@Override
	public String visit(IndexedIndividual element) {
		return "{" + element.getElkEntity() + "}";
	}

	@Override
	public String visit(IndexedObjectComplementOf element) {
		return "¬" + element.getNegated();
	}

	@Override
	public String visit(IndexedObjectHasSelf element) {
		return "∃" + element.getProperty() + ".Self";
	}

	@Override
	public String visit(IndexedObjectIntersectionOf element) {
		return element.getFirstConjunct() + " ⊓ " + element.getSecondConjunct();
	}

	@Override
	public String visit(IndexedObjectProperty element) {
		return element.getElkEntity().getIri().toString();
	}

	@Override
	public String visit(IndexedObjectPropertyRangeAxiom axiom) {
		return "Range(" + axiom.getProperty() + ' ' + axiom.getRange() + ')';
	}

	@Override
	public String visit(IndexedObjectSomeValuesFrom element) {
		return "∃" + element.getProperty() + '.' + element.getFiller();
	}

	@Override
	public String visit(IndexedObjectUnionOf element) {
		return "ObjectUnionOf(" + element.getDisjuncts() + ')';
	}

	@Override
	public String visit(IndexedRangeFiller element) {
		return element.getFiller() + " ⊓ " + "∃" + element.getProperty() + "-"
				+ ".⊤";
	}

	@Override
	public String visit(IndexedSubClassOfAxiom axiom) {
		return axiom.getSubClass() + " ⊑ " + axiom.getSuperClass();
	}

	@Override
	public String visit(IndexedSubObjectPropertyOfAxiom axiom) {
		return axiom.getSubPropertyChain() + " ⊑ " + axiom.getSuperProperty();
	}

}


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

import java.util.Collection;
import java.util.Map;
import java.util.Set;

import org.semanticweb.elk.owl.interfaces.ElkAxiom;
import org.semanticweb.elk.owl.interfaces.ElkClass;
import org.semanticweb.elk.owl.interfaces.ElkNamedIndividual;
import org.semanticweb.elk.owl.interfaces.ElkObjectProperty;
import org.semanticweb.elk.owl.predefined.PredefinedElkEntityFactory;
import org.semanticweb.elk.reasoner.indexing.conversion.ElkUnexpectedIndexingException;
import org.semanticweb.elk.reasoner.indexing.model.CachedIndexedObject;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClass;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClassExpression;
import org.semanticweb.elk.reasoner.indexing.model.IndexedEntity;
import org.semanticweb.elk.reasoner.indexing.model.IndexedIndividual;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectProperty;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableIndexedClass;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableIndexedClassExpression;
import org.semanticweb.elk.reasoner.saturation.rules.Rule;
import org.semanticweb.elk.reasoner.saturation.rules.contextinit.ChainableContextInitRule;
import org.semanticweb.elk.reasoner.saturation.rules.subsumers.ChainableSubsumerRule;
import org.semanticweb.elk.util.collections.ArrayHashMap;
import org.semanticweb.elk.util.collections.ArrayHashSet;
import org.semanticweb.elk.util.collections.Operations;
import org.semanticweb.elk.util.collections.chains.AbstractChain;
import org.semanticweb.elk.util.collections.chains.Chain;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * An object representing incremental changes in the index. The changes are
 * stored in two maps: additions and deletions. The map for additions assigns to
 * every {@link IndexedClassExpression} new rules to be added to the index;
 * likewise, the map of deletions assigns to every
 * {@link IndexedClassExpression} a new rules to be removed from the index.
 *
 * @author "Yevgeny Kazakov"
 * @author Pavel Klinov
 *
 */
public class DifferentialIndex extends DirectIndex {

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

	/**
	 * if {@code true} all changes will be applied incrementally; otherwise
	 * changes are applied directly
	 */
	boolean incrementalMode = false;

	/**
	 * added and removed {@link ElkClass}es during the last incremental session
	 */
	private Set<ElkClass> addedClasses_, removedClasses_;

	/**
	 * added and removed {@link ElkNamedIndividual}s during the last incremental
	 * session
	 */
	private Set<ElkNamedIndividual> addedIndividuals_, removedIndividuals_;

	/**
	 * added and removed {@link ElkObjectProperty}ies during the last
	 * incremental session
	 */
	private Set<ElkObjectProperty> addedObjectProperties_,
			removedObjectProperties_;

	private final IndexedEntity.Visitor<Void> entityInsertionListener_ = new EntityInsertionListener(),
			entityDeletionListener_ = new EntityDeletionListener();

	/**
	 * Pending deletions of {@link CachedIndexedObject}s
	 */
	private Set<CachedIndexedObject<?>> todoDeletions_;

	/**
	 * The added and removed initialization {@link Rule}s
	 */
	private ChainableContextInitRule addedContextInitRules_,
			removedContextInitRules_;

	/**
	 * The maps of added and removed {@link Rule}s for index class expressions;
	 */
	private Map<ModifiableIndexedClassExpression, ChainableSubsumerRule> addedContextRuleHeadByClassExpressions_,
			removedContextRuleHeadByClassExpressions_;

	private Map<ModifiableIndexedClass, ModifiableIndexedClassExpression> addedDefinitions_,
			removedDefinitions_;

	private Map<ModifiableIndexedClass, ElkAxiom> addedDefinitionReasons_,
			removedDefinitionReasons_;

	public DifferentialIndex(final PredefinedElkEntityFactory elkFactory) {
		super(elkFactory);
		init();
	}

	/**
	 * Initializes all data structures
	 */
	void init() {
		initClassChanges();
		initIndividualChanges();
		initObjectPropertyChanges();
		initAdditions();
		initDeletions();
	}

	public void initClassChanges() {
		this.addedClasses_ = new ArrayHashSet<ElkClass>(32);
		this.removedClasses_ = new ArrayHashSet<ElkClass>(32);
	}

	public void initIndividualChanges() {
		this.addedIndividuals_ = new ArrayHashSet<ElkNamedIndividual>(32);
		this.removedIndividuals_ = new ArrayHashSet<ElkNamedIndividual>(32);
	}

	public void initObjectPropertyChanges() {
		this.addedObjectProperties_ = new ArrayHashSet<ElkObjectProperty>(32);
		this.removedObjectProperties_ = new ArrayHashSet<ElkObjectProperty>(32);
	}

	public void initAdditions() {
		this.addedContextInitRules_ = null;
		this.addedContextRuleHeadByClassExpressions_ = new ArrayHashMap<ModifiableIndexedClassExpression, ChainableSubsumerRule>(
				32);
		this.addedDefinitions_ = new ArrayHashMap<ModifiableIndexedClass, ModifiableIndexedClassExpression>(
				32);
		this.addedDefinitionReasons_ = new ArrayHashMap<ModifiableIndexedClass, ElkAxiom>(
				32);
	}

	public void initDeletions() {
		this.removedContextInitRules_ = null;
		this.todoDeletions_ = new ArrayHashSet<CachedIndexedObject<?>>(1024);
		this.removedContextRuleHeadByClassExpressions_ = new ArrayHashMap<ModifiableIndexedClassExpression, ChainableSubsumerRule>(
				32);
		this.removedDefinitions_ = new ArrayHashMap<ModifiableIndexedClass, ModifiableIndexedClassExpression>(
				32);
		this.removedDefinitionReasons_ = new ArrayHashMap<ModifiableIndexedClass, ElkAxiom>(
				32);
	}

	/* read-only methods */

	// nothing so far

	/* read-write methods */

	@Override
	public void add(CachedIndexedObject<?> input) {
		if (!incrementalMode) {
			super.add(input);
			return;
		}
		// else incrementalMode
		LOGGER_.trace("{}: to add", input);
		if (input instanceof IndexedEntity)
			((IndexedEntity) input).accept(entityInsertionListener_);
		if (todoDeletions_.remove(input))
			return;
		// else
		super.add(input);
	}

	@Override
	public void remove(CachedIndexedObject<?> input) {
		if (!incrementalMode) {
			super.remove(input);
			return;
		}
		// else incrementalMode
		LOGGER_.trace("{}: to remove", input);
		if (input instanceof IndexedEntity)
			((IndexedEntity) input).accept(entityDeletionListener_);
		todoDeletions_.add(input);
	}

	private class EntityInsertionListener implements IndexedEntity.Visitor<Void> {

		@Override
		public Void visit(IndexedClass element) {
			ElkClass entity = element.getElkEntity();
			if (!removedClasses_.remove(entity))
				addedClasses_.add(entity);
			return null;
		}

		@Override
		public Void visit(IndexedIndividual element) {
			ElkNamedIndividual entity = element.getElkEntity();
			if (!removedIndividuals_.remove(entity))
				addedIndividuals_.add(entity);
			return null;
		}

		@Override
		public Void visit(IndexedObjectProperty element) {
			ElkObjectProperty entity = element.getElkEntity();
			if (!removedObjectProperties_.remove(entity))
				addedObjectProperties_.add(entity);
			return null;
		}

	}

	private class EntityDeletionListener implements IndexedEntity.Visitor<Void> {

		@Override
		public Void visit(IndexedClass element) {
			ElkClass entity = element.getElkEntity();
			if (!addedClasses_.remove(entity))
				removedClasses_.add(entity);
			return null;
		}

		@Override
		public Void visit(IndexedIndividual element) {
			ElkNamedIndividual entity = element.getElkEntity();
			if (!addedIndividuals_.remove(entity))
				removedIndividuals_.add(entity);
			return null;
		}

		@Override
		public Void visit(IndexedObjectProperty element) {
			ElkObjectProperty entity = element.getElkEntity();
			if (!addedObjectProperties_.remove(entity))
				removedObjectProperties_.add(entity);
			return null;
		}
	}

	@Override
	public boolean add(ModifiableIndexedClassExpression target,
			ChainableSubsumerRule newRule) {
		if (!incrementalMode) {
			return super.add(target, newRule);
		}
		// else incrementalMode
		if (newRule.removeFrom(getRemovedContextRuleChain(target))) {
			if (newRule.addTo(target.getCompositionRuleChain()))
				return true;
			// else revert
			newRule.addTo(getRemovedContextRuleChain(target));
		}
		// if above fails
		return newRule.addTo(getAddedContextRuleChain(target));
	}

	@Override
	public boolean remove(ModifiableIndexedClassExpression target,
			ChainableSubsumerRule oldRule) {
		if (!incrementalMode) {
			return super.remove(target, oldRule);
		}
		// else incrementalMode
		if (oldRule.removeFrom(getAddedContextRuleChain(target)))
			return true;
		// else
		if (oldRule.addTo(getRemovedContextRuleChain(target))) {
			if (oldRule.removeFrom(target.getCompositionRuleChain()))
				return true;
			// else revert
			oldRule.removeFrom(getRemovedContextRuleChain(target));
		}
		return false;
	}

	@Override
	public boolean tryAddDefinition(ModifiableIndexedClass target,
			ModifiableIndexedClassExpression definition, ElkAxiom reason) {
		if (!incrementalMode)
			return super.tryAddDefinition(target, definition, reason);
		// for incremental mode:
		IndexedClassExpression removedDefintion = removedDefinitions_
				.get(target);
		ElkAxiom removedDefinitionReason = removedDefinitionReasons_
				.get(target);
		if (target.getDefinition() != removedDefintion
				|| addedDefinitions_.get(target) != null) {
			// the existing definition was not removed or some definition has
			// been added
			return false;
		}
		// else removing
		if (removedDefintion == definition
				&& removedDefinitionReason.equals(reason)) {
			removedDefinitions_.remove(target);
			removedDefinitionReasons_.remove(target);
			target.setDefinition(definition, reason);
		} else {
			addedDefinitions_.put(target, definition);
			addedDefinitionReasons_.put(target, reason);
		}
		return true;
	}

	@Override
	public boolean tryRemoveDefinition(ModifiableIndexedClass target,
			ModifiableIndexedClassExpression definition, ElkAxiom reason) {
		if (!incrementalMode)
			return super.tryRemoveDefinition(target, definition, reason);
		// for incremental mode:
		IndexedClassExpression addedDefinition = addedDefinitions_.get(target);
		ElkAxiom addedDefinitionReason = addedDefinitionReasons_.get(target);
		if (addedDefinition == definition
				&& addedDefinitionReason.equals(reason)) {
			addedDefinitions_.remove(target);
			addedDefinitionReasons_.remove(target);
			return true;
		}
		// else
		if (addedDefinition != null || target.getDefinition() != definition
				|| !target.getDefinitionReason().equals(reason))
			return false;
		// else
		target.removeDefinition();
		removedDefinitions_.put(target, definition);
		removedDefinitionReasons_.put(target, reason);
		return true;
	}

	@Override
	public boolean addContextInitRule(ChainableContextInitRule newRule) {
		if (!incrementalMode) {
			return super.addContextInitRule(newRule);
		}
		// else incrementalMode
		if (newRule.removeFrom(getRemovedContextInitRuleChain())) {
			if (newRule.addTo(getContextInitRuleChain()))
				return true;
			// else revert
			newRule.addTo(getRemovedContextInitRuleChain());
		}
		// if above fails
		return newRule.addTo(getAddedContextInitRuleChain());
	}

	@Override
	public boolean removeContextInitRule(ChainableContextInitRule oldRule) {
		if (!incrementalMode) {
			return super.removeContextInitRule(oldRule);
		}
		// else incrementalMode
		if (oldRule.removeFrom(getAddedContextInitRuleChain()))
			return true;
		// else
		if (oldRule.addTo(getRemovedContextInitRuleChain())) {
			if (oldRule.removeFrom(getContextInitRuleChain()))
				return true;
			// else revert
			oldRule.removeFrom(getRemovedContextInitRuleChain());
		}
		return false;
	}

	/* incremental-specific methods */

	/**
	 * @return the context initialization rules added during the last
	 *         incremental session
	 */
	public ChainableContextInitRule getAddedContextInitRules() {
		return addedContextInitRules_;
	}

	/**
	 * @return the context initialization rules removed during the last
	 *         incremental session
	 */
	public ChainableContextInitRule getRemovedContextInitRules() {
		return removedContextInitRules_;
	}

	/**
	 * @return the map from indexed class expressions to the corresponding
	 *         objects containing index additions for these class expressions
	 */
	public Map<? extends IndexedClassExpression, ChainableSubsumerRule> getAddedContextRulesByClassExpressions() {
		return this.addedContextRuleHeadByClassExpressions_;
	}

	/**
	 * @return the map from indexed class expressions to the corresponding
	 *         objects containing index deletions for these class expressions
	 */
	public Map<? extends IndexedClassExpression, ChainableSubsumerRule> getRemovedContextRulesByClassExpressions() {
		return this.removedContextRuleHeadByClassExpressions_;
	}

	/**
	 * @return the added definitions for {@link IndexedClass}es
	 */
	public Map<? extends IndexedClass, ? extends IndexedClassExpression> getAddedDefinitions() {
		return this.addedDefinitions_;
	}

	/**
	 * @return the {@link ElkAxiom}s from which the added definitions for the
	 *         corresponding {@link IndexedClass}es originate
	 */
	public Map<? extends IndexedClass, ? extends ElkAxiom> getAddedDefinitionReasons() {
		return this.addedDefinitionReasons_;
	}

	/**
	 * @return the removed definitions for {@link IndexedClass}es
	 */
	public Map<? extends IndexedClass, ? extends IndexedClassExpression> getRemovedDefinitions() {
		return this.removedDefinitions_;
	}

	/**
	 * @return the {@link ElkAxiom}s from which the removed definitions for the
	 *         corresponding {@link IndexedClass}es originate
	 */
	public Map<? extends IndexedClass, ? extends ElkAxiom> getRemovedDefinitionReasons() {
		return this.removedDefinitionReasons_;
	}

	/**
	 * @return the {@link ElkClass} added during the last incremental session
	 */
	public Collection<ElkClass> getAddedClasses() {
		return this.addedClasses_;
	}

	/**
	 * @return the collection of named individuals added to the signature
	 */
	public Collection<ElkNamedIndividual> getAddedIndividuals() {
		return this.addedIndividuals_;
	}

	/**
	 * @return the {@link IndexedClassExpression}s removed during the last
	 *         incremental session
	 */
	public Iterable<? extends IndexedClassExpression> getRemovedClassExpressions() {
		return Operations.filter(todoDeletions_, IndexedClassExpression.class);
	}

	/**
	 * Removes the deleted rules from this {@link DifferentialIndex}; these
	 * rules should be already applied in the main index during their
	 * registration
	 */
	public void clearDeletedRules() {
		for (CachedIndexedObject<?> deletion : todoDeletions_) {
			LOGGER_.trace("{}: comitting removal", deletion);
			super.remove(deletion);
		}
		initDeletions();
	}

	/**
	 * Commits the added rules to the main index and removes them from this
	 * {@link DifferentialIndex}.
	 */
	public void commitAddedRules() {
		// commit changes in the context initialization rules
		ChainableContextInitRule nextContextInitRule;
		Chain<ChainableContextInitRule> contextInitRuleChain;

		nextContextInitRule = addedContextInitRules_;
		contextInitRuleChain = getContextInitRuleChain();
		while (nextContextInitRule != null) {
			nextContextInitRule.addTo(contextInitRuleChain);
			nextContextInitRule = nextContextInitRule.next();
		}

		// commit changes in rules for IndexedClassExpression
		ChainableSubsumerRule nextClassExpressionRule;
		Chain<ChainableSubsumerRule> classExpressionRuleChain;
		for (ModifiableIndexedClassExpression target : addedContextRuleHeadByClassExpressions_
				.keySet()) {
			LOGGER_.trace("{}: committing context rule additions", target);

			nextClassExpressionRule = addedContextRuleHeadByClassExpressions_
					.get(target);
			classExpressionRuleChain = target.getCompositionRuleChain();
			while (nextClassExpressionRule != null) {
				nextClassExpressionRule.addTo(classExpressionRuleChain);
				nextClassExpressionRule = nextClassExpressionRule.next();
			}
		}
		for (ModifiableIndexedClass target : addedDefinitions_.keySet()) {
			ModifiableIndexedClassExpression definition = addedDefinitions_
					.get(target);
			ElkAxiom reason = addedDefinitionReasons_.get(target);
			LOGGER_.trace("{}: committing definition addition {}", target,
					definition);
			if (!target.setDefinition(definition, reason))
				throw new ElkUnexpectedIndexingException(target);
		}
		initAdditions();
	}

	/**
	 * @return {@code true} if there are no uncommitted changes in this
	 *         {@link DifferentialIndex}
	 */
	public boolean isFullyCommitted() {
		return addedContextInitRules_ == null
				&& removedContextInitRules_ == null
				&& (addedContextRuleHeadByClassExpressions_ == null || addedContextRuleHeadByClassExpressions_
						.isEmpty())
				&& (removedContextRuleHeadByClassExpressions_ == null || removedContextRuleHeadByClassExpressions_
						.isEmpty());
	}

	/**
	 * Sets the incremental mode for this {@code DifferentialIndex}.
	 *
	 * @param incremental
	 *            if {@code true}, deletions and additions to this indexed are
	 *            stored separately; if {@code false} all changes are
	 *            immediately applied to the index.
	 */
	public void setIncrementalMode(boolean incremental) {
		if (this.incrementalMode == incremental)
			// already set
			return;
		LOGGER_.trace("set incremental mode: " + incremental);
		this.incrementalMode = incremental;
		if (!incremental) {
			clearDeletedRules();
			commitAddedRules();
			initClassChanges();
			initIndividualChanges();
		}
	}

	/**
	 * @return the current value of the incremental mode for this
	 *         {@code DifferentialIndex}
	 * @see #setIncrementalMode(boolean)
	 */
	public boolean isIncrementalMode() {
		return incrementalMode;
	}

	/**
	 * @return the chain of added context initialization rules suitable for
	 *         modifications (addition or deletions) of rules
	 */
	private Chain<ChainableContextInitRule> getAddedContextInitRuleChain() {
		return new AbstractChain<ChainableContextInitRule>() {

			@Override
			public ChainableContextInitRule next() {
				return addedContextInitRules_;
			}

			@Override
			public void setNext(ChainableContextInitRule tail) {
				addedContextInitRules_ = tail;
			}
		};
	}

	/**
	 * @return the chain of removed context initialization rules suitable for
	 *         modifications (addition or deletions) of rules
	 */
	private Chain<ChainableContextInitRule> getRemovedContextInitRuleChain() {
		return new AbstractChain<ChainableContextInitRule>() {

			@Override
			public ChainableContextInitRule next() {
				return removedContextInitRules_;
			}

			@Override
			public void setNext(ChainableContextInitRule tail) {
				removedContextInitRules_ = tail;
			}
		};
	}

	/**
	 * @param target
	 *            the {@link IndexedClassExpression} for which to return the
	 *            chain of added context rules
	 *
	 * @return the chain of added context rules for the given
	 *         {@link IndexedClassExpression} that is suitable for modifications
	 *         (addition or deletions) of rules
	 */
	private Chain<ChainableSubsumerRule> getAddedContextRuleChain(
			final ModifiableIndexedClassExpression target) {
		return AbstractChain.getMapBackedChain(
				addedContextRuleHeadByClassExpressions_, target);
	}

	/**
	 * @param target
	 *            the {@link IndexedClassExpression} for which to return the
	 *            chain of removed context rules
	 *
	 * @return the chain of removed context rules for the given
	 *         {@link IndexedClassExpression} that is suitable for modifications
	 *         (addition or deletions) of rules
	 */
	private Chain<ChainableSubsumerRule> getRemovedContextRuleChain(
			final ModifiableIndexedClassExpression target) {
		return AbstractChain.getMapBackedChain(
				removedContextRuleHeadByClassExpressions_, target);
	}
}


package org.semanticweb.elk.reasoner.saturation.rules.subsumers;

/*
 * #%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.IndexedClassExpression;
import org.semanticweb.elk.reasoner.indexing.model.IndexedObjectIntersectionOf;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableIndexedObjectIntersectionOf;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableOntologyIndex;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.SubClassInclusion;
import org.semanticweb.elk.reasoner.saturation.context.Context;
import org.semanticweb.elk.reasoner.saturation.context.ContextPremises;
import org.semanticweb.elk.reasoner.saturation.rules.ClassInferenceProducer;
import org.semanticweb.elk.util.collections.chains.Chain;
import org.semanticweb.elk.util.collections.chains.Matcher;
import org.semanticweb.elk.util.collections.chains.ReferenceFactory;
import org.semanticweb.elk.util.collections.chains.SimpleTypeBasedMatcher;

/**
 * A {@link ChainableSubsumerRule} that produces {@link SubClassInclusion} for an
 * {@link IndexedObjectIntersectionOf} when processing its second conjunct
 * {@link IndexedClassExpression} and when the first conjunct is contained in
 * the {@link Context}
 *
 * @author "Yevgeny Kazakov"
 *
 * @see ObjectIntersectionFromSecondConjunctRule
 */
public class ObjectIntersectionFromFirstConjunctRule extends
		AbstractObjectIntersectionFromConjunctRule {

	public static final String NAME = "ObjectIntersectionOf From 1st Conjunct";

	ObjectIntersectionFromFirstConjunctRule(ChainableSubsumerRule tail) {
		super(tail);
	}

	ObjectIntersectionFromFirstConjunctRule(IndexedClassExpression conjunct,
			IndexedObjectIntersectionOf conjunction) {
		super(conjunct, conjunction);
	}

	@Override
	public String toString() {
		return NAME;
	}

	/**
	 * Add {@link ObjectIntersectionFromFirstConjunctRule}s for the given
	 * {@link ModifiableIndexedObjectIntersectionOf} in the given
	 * {@link ModifiableOntologyIndex}
	 *
	 * @param conjunction
	 * @param index
	 */
	public static boolean addRulesFor(
			ModifiableIndexedObjectIntersectionOf conjunction,
			ModifiableOntologyIndex index) {
		return index.add(
				conjunction.getSecondConjunct(),
				new ObjectIntersectionFromFirstConjunctRule(conjunction
						.getFirstConjunct(), conjunction));
	}

	/**
	 * Removes {@link ObjectIntersectionFromFirstConjunctRule}s for the given
	 * {@link ModifiableIndexedObjectIntersectionOf} in the given
	 * {@link ModifiableOntologyIndex}
	 *
	 * @param conjunction
	 * @param index
	 */
	public static boolean removeRulesFor(
			ModifiableIndexedObjectIntersectionOf conjunction,
			ModifiableOntologyIndex index) {
		return index.remove(
				conjunction.getSecondConjunct(),
				new ObjectIntersectionFromFirstConjunctRule(conjunction
						.getFirstConjunct(), conjunction));
	}

	@Override
	public boolean addTo(Chain<ChainableSubsumerRule> ruleChain) {
		if (isEmpty())
			return true;
		return addTo(ruleChain.getCreate(MATCHER_, FACTORY_));
	}

	@Override
	public boolean removeFrom(Chain<ChainableSubsumerRule> ruleChain) {
		if (isEmpty())
			return true;
		AbstractObjectIntersectionFromConjunctRule rule = ruleChain
				.find(MATCHER_);
		if (rule == null)
			return false;
		boolean success = removeFrom(rule);
		if (success && rule.isEmpty()) {
			ruleChain.remove(MATCHER_);
		}
		return success;
	}

	@Override
	public void accept(LinkedSubsumerRuleVisitor<?> visitor,
			IndexedClassExpression premise, ContextPremises premises,
			ClassInferenceProducer producer) {
		visitor.visit(this, premise, premises, producer);
	}

	private static final Matcher<ChainableSubsumerRule, ObjectIntersectionFromFirstConjunctRule> MATCHER_ = new SimpleTypeBasedMatcher<ChainableSubsumerRule, ObjectIntersectionFromFirstConjunctRule>(
			ObjectIntersectionFromFirstConjunctRule.class);

	private static final ReferenceFactory<ChainableSubsumerRule, ObjectIntersectionFromFirstConjunctRule> FACTORY_ = new ReferenceFactory<ChainableSubsumerRule, ObjectIntersectionFromFirstConjunctRule>() {
		@Override
		public ObjectIntersectionFromFirstConjunctRule create(
				ChainableSubsumerRule tail) {
			return new ObjectIntersectionFromFirstConjunctRule(tail);
		}
	};

}


/*
 * #%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%
 */
package org.semanticweb.elk.reasoner.indexing.classes;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;

import org.semanticweb.elk.owl.interfaces.ElkAxiom;
import org.semanticweb.elk.owl.interfaces.ElkObjectProperty;
import org.semanticweb.elk.reasoner.indexing.model.CachedIndexedObjectProperty;
import org.semanticweb.elk.reasoner.indexing.model.CachedIndexedPropertyChain;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClassExpression;
import org.semanticweb.elk.reasoner.indexing.model.IndexedComplexPropertyChain;
import org.semanticweb.elk.reasoner.indexing.model.IndexedEntity;
import org.semanticweb.elk.reasoner.indexing.model.IndexedPropertyChain;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableOntologyIndex;
import org.semanticweb.elk.reasoner.indexing.model.OccurrenceIncrement;

/**
 * Implements {@link CachedIndexedObjectProperty}
 *
 * @author "Yevgeny Kazakov"
 *
 */
class CachedIndexedObjectPropertyImpl
		extends
		CachedIndexedPropertyChainImpl<CachedIndexedObjectProperty, CachedIndexedObjectProperty>
		implements CachedIndexedObjectProperty {

	private final ElkObjectProperty property_;

	/**
	 * Collections of all binary role chains in which this
	 * {@link IndexedPropertyChain} occurs on the left.
	 */
	private Collection<IndexedComplexPropertyChain> leftChains_;

	/**
	 * The {@link IndexedPropertyChain} that are subsumed by this
	 * {@link IndexedComplexPropertyChain} according to axioms
	 */
	private ArrayList<IndexedPropertyChain> toldSubChains_;

	/**
	 * The corresponding {@link ElkAxiom}s that resulted for the sub property
	 * chains
	 */
	private ArrayList<ElkAxiom> toldSubChainsReasons_;

	/**
	 * The {@link IndexedClassExpression} from range axioms with this property
	 */
	private ArrayList<IndexedClassExpression> toldRanges_;

	/**
	 * The corresponding {@link ElkAxiom}s that resulted for the ranges
	 */
	private ArrayList<ElkAxiom> toldRangesReasons_;

	CachedIndexedObjectPropertyImpl(ElkObjectProperty entity) {
		super(CachedIndexedObjectProperty.Helper.structuralHashCode(entity));
		this.property_ = entity;
	}

	@Override
	public final ElkObjectProperty getElkEntity() {
		return property_;
	}

	@Override
	public final ArrayList<IndexedPropertyChain> getToldSubChains() {
		if (toldSubChains_ == null)
			return emptyArrayList();
		// else
		return toldSubChains_;
	}

	@Override
	public final ArrayList<ElkAxiom> getToldSubChainsReasons() {
		if (toldSubChainsReasons_ == null)
			return emptyArrayList();
		// else
		return toldSubChainsReasons_;
	}

	@Override
	public final ArrayList<IndexedClassExpression> getToldRanges() {
		if (toldRanges_ == null)
			return emptyArrayList();
		// else
		return toldRanges_;
	}

	@Override
	public final ArrayList<ElkAxiom> getToldRangesReasons() {
		if (toldRangesReasons_ == null)
			return emptyArrayList();
		// else
		return toldRangesReasons_;
	}

	@Override
	public final Collection<IndexedComplexPropertyChain> getLeftChains() {
		if (leftChains_ == null)
			return Collections.emptySet();
		// else
		return Collections.unmodifiableCollection(leftChains_);
	}

	@Override
	public final CachedIndexedObjectProperty structuralEquals(Object other) {
		return CachedIndexedObjectProperty.Helper.structuralEquals(this, other);
	}

	@Override
	public final boolean addLeftChain(IndexedComplexPropertyChain chain) {
		if (leftChains_ == null)
			leftChains_ = new ArrayList<IndexedComplexPropertyChain>(1);
		return leftChains_.add(chain);
	}

	@Override
	public final boolean removeLeftChain(IndexedComplexPropertyChain chain) {
		boolean success = false;
		if (leftChains_ != null) {
			success = leftChains_.remove(chain);
			if (leftChains_.isEmpty())
				leftChains_ = null;
		}
		return success;
	}

	@Override
	public final boolean addToldSubPropertyChain(IndexedPropertyChain subChain,
			ElkAxiom reason) {
		if (toldSubChains_ == null) {
			toldSubChains_ = new ArrayList<IndexedPropertyChain>(1);
		}
		if (toldSubChainsReasons_ == null) {
			toldSubChainsReasons_ = new ArrayList<ElkAxiom>(1);
		}
		toldSubChains_.add(subChain);
		toldSubChainsReasons_.add(reason);
		return true;
	}

	@Override
	public final boolean removeToldSubPropertyChain(
			IndexedPropertyChain subChain, ElkAxiom reason) {
		int i = indexOf(subChain, reason);
		if (i < 0)
			return false;
		// else success
		toldSubChains_.remove(i);
		toldSubChainsReasons_.remove(i);
		if (toldSubChains_.isEmpty())
			toldSubChains_ = null;
		if (toldSubChainsReasons_.isEmpty())
			toldSubChainsReasons_ = null;
		return true;
	}

	@Override
	public final boolean addToldRange(IndexedClassExpression range,
			ElkAxiom reason) {
		if (toldRanges_ == null) {
			toldRanges_ = new ArrayList<IndexedClassExpression>(1);
		}
		if (toldRangesReasons_ == null) {
			toldRangesReasons_ = new ArrayList<ElkAxiom>(1);
		}
		toldRanges_.add(range);
		toldRangesReasons_.add(reason);
		return true;
	}

	@Override
	public final boolean removeToldRange(IndexedClassExpression range,
			ElkAxiom reason) {
		int i = indexOf(range, reason);
		if (i < 0)
			return false;
		// else success
		toldRanges_.remove(i);
		toldRangesReasons_.remove(i);
		if (toldRanges_.isEmpty())
			toldRanges_ = null;
		if (toldRangesReasons_.isEmpty())
			toldRangesReasons_ = null;
		return true;
	}

	// TODO: create a generic method for this operation (used in other places)
	private int indexOf(IndexedPropertyChain subChain, ElkAxiom reason) {
		for (int i = 0; i < toldSubChains_.size(); i++) {
			if (toldSubChains_.get(i).equals(subChain)
					&& toldSubChainsReasons_.get(i).equals(reason))
				return i;
		}
		// else not found
		return -1;
	}

	private int indexOf(IndexedClassExpression range, ElkAxiom reason) {
		for (int i = 0; i < toldRanges_.size(); i++) {
			if (toldRanges_.get(i).equals(range)
					&& toldRangesReasons_.get(i).equals(reason))
				return i;
		}
		// else not found
		return -1;
	}

	@Override
	public boolean updateOccurrenceNumbers(ModifiableOntologyIndex index,
			OccurrenceIncrement increment) {
		totalOccurrenceNo += increment.totalIncrement;
		return true;
	}

	@Override
	public final <O> O accept(IndexedEntity.Visitor<O> visitor) {
		return visitor.visit(this);
	}

	@Override
	public final <O> O accept(IndexedPropertyChain.Visitor<O> visitor) {
		return visitor.visit(this);
	}

	@Override
	public CachedIndexedObjectProperty accept(
			CachedIndexedPropertyChain.Filter filter) {
		return filter.filter(this);
	}

}


/*
 * #%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%
 */
package org.semanticweb.elk.reasoner.saturation.inferences;

import org.semanticweb.elk.reasoner.indexing.model.IndexedClassExpressionList;
import org.semanticweb.elk.reasoner.indexing.model.IndexedContextRoot;
import org.semanticweb.elk.reasoner.saturation.conclusions.model.DisjointSubsumer;

abstract class AbstractDisjointSubsumerInference extends AbstractClassInference
		implements DisjointSubsumerInference {

	private final IndexedClassExpressionList disjointExpressions_;

	private final int position_;

	public AbstractDisjointSubsumerInference(IndexedContextRoot destination,
			IndexedClassExpressionList disjointExpressions, int position) {
		super(destination);
		this.disjointExpressions_ = disjointExpressions;
		this.position_ = position;
	}

	public IndexedClassExpressionList getDisjointExpressions() {
		return disjointExpressions_;
	}

	public int getPosition() {
		return position_;
	}

	/**
	 * @param factory
	 *            the factory for creating conclusions
	 *
	 * @return the conclusion produced by this inference
	 */
	public final DisjointSubsumer getConclusion(
			DisjointSubsumer.Factory factory) {
		return factory.getDisjointSubsumer(getDestination(),
				getDisjointExpressions(), getPosition());
	}

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

}


/*-
 * #%L
 * ELK Reasoner Core
 * $Id:$
 * $HeadURL:$
 * %%
 * Copyright (C) 2011 - 2016 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.query;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import org.semanticweb.elk.owl.interfaces.ElkAxiom;
import org.semanticweb.elk.owl.interfaces.ElkClassAssertionAxiom;
import org.semanticweb.elk.owl.interfaces.ElkClassExpression;
import org.semanticweb.elk.owl.interfaces.ElkDifferentIndividualsAxiom;
import org.semanticweb.elk.owl.interfaces.ElkDisjointClassesAxiom;
import org.semanticweb.elk.owl.interfaces.ElkEquivalentClassesAxiom;
import org.semanticweb.elk.owl.interfaces.ElkIndividual;
import org.semanticweb.elk.owl.interfaces.ElkObject;
import org.semanticweb.elk.owl.interfaces.ElkObjectPropertyAssertionAxiom;
import org.semanticweb.elk.owl.interfaces.ElkObjectPropertyDomainAxiom;
import org.semanticweb.elk.owl.interfaces.ElkSameIndividualAxiom;
import org.semanticweb.elk.owl.interfaces.ElkSubClassOfAxiom;
import org.semanticweb.elk.owl.predefined.ElkPolarity;
import org.semanticweb.elk.owl.visitors.DummyElkAxiomVisitor;
import org.semanticweb.elk.reasoner.entailments.impl.ClassAssertionAxiomEntailmentImpl;
import org.semanticweb.elk.reasoner.entailments.impl.DifferentIndividualsAxiomEntailmentImpl;
import org.semanticweb.elk.reasoner.entailments.impl.DisjointClassesAxiomEntailmentImpl;
import org.semanticweb.elk.reasoner.entailments.impl.EquivalentClassesAxiomEntailmentImpl;
import org.semanticweb.elk.reasoner.entailments.impl.ObjectPropertyAssertionAxiomEntailmentImpl;
import org.semanticweb.elk.reasoner.entailments.impl.ObjectPropertyDomainAxiomEntailmentImpl;
import org.semanticweb.elk.reasoner.entailments.impl.SameIndividualAxiomEntailmentImpl;
import org.semanticweb.elk.reasoner.entailments.impl.SubClassOfAxiomEntailmentImpl;
import org.semanticweb.elk.reasoner.entailments.model.Entailment;
import org.semanticweb.elk.reasoner.indexing.classes.ModifiableIndexedObjectBaseFactory;
import org.semanticweb.elk.reasoner.indexing.classes.UpdatingModifiableIndexedObjectFactory;
import org.semanticweb.elk.reasoner.indexing.conversion.ElkIndexingUnsupportedException;
import org.semanticweb.elk.reasoner.indexing.conversion.ElkPolarityExpressionConverter;
import org.semanticweb.elk.reasoner.indexing.conversion.ElkPolarityExpressionConverterImpl;
import org.semanticweb.elk.reasoner.indexing.model.IndexedClassExpression;
import org.semanticweb.elk.reasoner.indexing.model.IndexedIndividual;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableIndexedObject;
import org.semanticweb.elk.reasoner.indexing.model.ModifiableOntologyIndex;
import org.semanticweb.elk.reasoner.indexing.model.OccurrenceIncrement;

/**
 * Indexes axiom entailment queries. All supported axiom entailment queries
 * should be indexed by this converter, so it also serves the purpose of list of
 * supported queries ({@link #isEntailmentCheckingSupported(Class)}).
 *
 * @author Peter Skocovsky
 */
public class EntailmentQueryConverter extends
		DummyElkAxiomVisitor<IndexedEntailmentQuery<? extends Entailment>> {

	private final ElkObject.Factory elkFactory_;
	private final ElkPolarityExpressionConverter positiveConverter_;
	private final ElkPolarityExpressionConverter negativeConverter_;

	/**
	 * @param elkFactory
	 *            {@link ElkObject} factory.
	 * @param index
	 *            Indexed queries are inserted into/deleted from this index.
	 * @param increment
	 *            How should occurrence counts of indexed object change.
	 */
	public EntailmentQueryConverter(final ElkObject.Factory elkFactory,
			final ModifiableOntologyIndex index, final int increment) {
		this.elkFactory_ = elkFactory;
		final ModifiableIndexedObjectBaseFactory baseFactory = new ModifiableIndexedObjectBaseFactory();
		final ModifiableIndexedObject.Factory positiveFactory = new UpdatingModifiableIndexedObjectFactory(
				baseFactory, index,
				OccurrenceIncrement.getPositiveIncrement(increment));
		final ModifiableIndexedObject.Factory negativeFactory = new UpdatingModifiableIndexedObjectFactory(
				baseFactory, index,
				OccurrenceIncrement.getNegativeIncrement(increment));
		this.positiveConverter_ = new ElkPolarityExpressionConverterImpl(
				ElkPolarity.POSITIVE, elkFactory, positiveFactory,
				negativeFactory, index);
		this.negativeConverter_ = positiveConverter_
				.getComplementaryConverter();
	}

	@Override
	protected IndexedEntailmentQuery<? extends Entailment> defaultVisit(
			final ElkAxiom axiom) {
		throw new ElkIndexingUnsupportedException(axiom);
	}

	@Override
	public ClassAssertionEntailmentQuery visit(
			final ElkClassAssertionAxiom axiom) {
		final IndexedIndividual individual = axiom.getIndividual()
				.accept(positiveConverter_);
		final IndexedClassExpression classExpression = axiom
				.getClassExpression().accept(negativeConverter_);

		return new ClassAssertionEntailmentQuery(
				new ClassAssertionAxiomEntailmentImpl(axiom), individual,
				classExpression);
	}

	@Override
	public DifferentIndividualsEntailmentQuery visit(
			final ElkDifferentIndividualsAxiom axiom) {

		final List<? extends ElkIndividual> individuals = axiom
				.getIndividuals();
		final List<ElkClassExpression> nominals = new ArrayList<ElkClassExpression>(
				individuals.size());
		for (final ElkIndividual individual : individuals) {
			nominals.add(elkFactory_.getObjectOneOf(individual));
		}
		final DisjointClassesEntailmentQuery disjointness = visit(
				elkFactory_.getDisjointClassesAxiom(nominals));

		return new DifferentIndividualsEntailmentQuery(
				new DifferentIndividualsAxiomEntailmentImpl(axiom),
				disjointness);
	}

	@Override
	public DisjointClassesEntailmentQuery visit(
			final ElkDisjointClassesAxiom axiom) {

		final List<? extends ElkClassExpression> disjoint = axiom
				.getClassExpressions();
		final int size = disjoint.size();
		final List<SubClassOfEntailmentQuery> premises = new ArrayList<SubClassOfEntailmentQuery>(
				size);

		for (int first = 0; first < size - 1; first++) {
			for (int second = first + 1; second < size; second++) {

				final SubClassOfEntailmentQuery subsumption = visit(elkFactory_
						.getSubClassOfAxiom(elkFactory_.getObjectIntersectionOf(
								disjoint.get(first), disjoint.get(second)),
								elkFactory_.getOwlNothing()));

				premises.add(subsumption);

			}
		}

		return new DisjointClassesEntailmentQuery(
				new DisjointClassesAxiomEntailmentImpl(axiom), premises);
	}

	@Override
	public EquivalentClassesEntailmentQuery visit(
			final ElkEquivalentClassesAxiom axiom) {
		final List<? extends ElkClassExpression> elkClassExpressions = axiom
				.getClassExpressions();

		final List<SubClassOfEntailmentQuery> subsumptionCycle = new ArrayList<SubClassOfEntailmentQuery>(
				elkClassExpressions.size());

		ElkClassExpression elkSubclass = elkClassExpressions
				.get(elkClassExpressions.size() - 1);
		for (int i = 0; i < elkClassExpressions.size(); i++) {
			final ElkClassExpression elkSuperclass = elkClassExpressions.get(i);

			final SubClassOfEntailmentQuery subsumption = visit(
					elkFactory_.getSubClassOfAxiom(elkSubclass, elkSuperclass));

			subsumptionCycle.add(subsumption);

			elkSubclass = elkSuperclass;
		}

		return new EquivalentClassesEntailmentQuery(
				new EquivalentClassesAxiomEntailmentImpl(axiom),
				subsumptionCycle);
	}

	@Override
	public ObjectPropertyAssertionEntailmentQuery visit(
			final ElkObjectPropertyAssertionAxiom axiom) {

		final IndexedIndividual subject = axiom.getSubject()
				.accept(positiveConverter_);
		final IndexedClassExpression ovjectExistential = elkFactory_
				.getObjectSomeValuesFrom(axiom.getProperty(),
						elkFactory_.getObjectOneOf(axiom.getObject()))
				.accept(negativeConverter_);

		return new ObjectPropertyAssertionEntailmentQuery(
				new ObjectPropertyAssertionAxiomEntailmentImpl(axiom), subject,
				ovjectExistential);
	}

	@Override
	public ObjectPropertyDomainEntailmentQuery visit(
			final ElkObjectPropertyDomainAxiom axiom) {

		final IndexedClassExpression existential = elkFactory_
				.getObjectSomeValuesFrom(axiom.getProperty(),
						elkFactory_.getOwlThing())
				.accept(positiveConverter_);
		final IndexedClassExpression domain = axiom.getDomain()
				.accept(negativeConverter_);

		return new ObjectPropertyDomainEntailmentQuery(
				new ObjectPropertyDomainAxiomEntailmentImpl(axiom), existential,
				domain);
	}

	@Override
	public SameIndividualEntailmentQuery visit(
			final ElkSameIndividualAxiom axiom) {

		final List<? extends ElkIndividual> individuals = axiom
				.getIndividuals();
		final List<ElkClassExpression> nominals = new ArrayList<ElkClassExpression>(
				individuals.size());
		for (final ElkIndividual individual : individuals) {
			nominals.add(elkFactory_.getObjectOneOf(individual));
		}
		final EquivalentClassesEntailmentQuery equivalence = visit(
				elkFactory_.getEquivalentClassesAxiom(nominals));

		return new SameIndividualEntailmentQuery(
				new SameIndividualAxiomEntailmentImpl(axiom), equivalence);
	}

	@Override
	public SubClassOfEntailmentQuery visit(final ElkSubClassOfAxiom axiom) {
		final IndexedClassExpression subClass = axiom.getSubClassExpression()
				.accept(positiveConverter_);
		final IndexedClassExpression superClass = axiom
				.getSuperClassExpression().accept(negativeConverter_);

		return new SubClassOfEntailmentQuery(
				new SubClassOfAxiomEntailmentImpl(axiom), subClass, superClass);
	}

	/**
	 * One-parameter, public, non-static methods called "visit" and declared in
	 * this class enumerate subclasses of {@link ElkAxiom} (parameter) for which
	 * entailment queries are supported. This method returns {@code true} iff
	 * the subclass of {@link ElkAxiom} specified as the parameter is a
	 * parameter type of some of these methods, e.g., whether entailment query
	 * of such an {@link ElkAxiom} is supported.
	 *
	 * @param axiomClass
	 * @return
	 */
	public static boolean isEntailmentCheckingSupported(
			final Class<? extends ElkAxiom> axiomClass) {

		for (final Method declaredMethod : EntailmentQueryConverter.class
				.getDeclaredMethods()) {

			final int mod = declaredMethod.getModifiers();
			final Class<?>[] parameterTypes = declaredMethod
					.getParameterTypes();
			if ("visit".equals(declaredMethod.getName())
					&& Modifier.isPublic(mod) && !Modifier.isStatic(mod)
					&& parameterTypes.length == 1
					&& parameterTypes[0].isAssignableFrom(axiomClass)) {
				// There is a declared visit method that accepts axiomClass
				return true;
			}

		}

		return false;
	}

}


/*
 * #%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%
 */
package org.semanticweb.elk.reasoner.taxonomy;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;

import org.semanticweb.elk.owl.interfaces.ElkAxiom;
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.interfaces.ElkObject;
import org.semanticweb.elk.owl.interfaces.ElkObjectProperty;
import org.semanticweb.elk.owl.managers.ElkObjectEntityRecyclingFactory;
import org.semanticweb.elk.owl.printers.OwlFunctionalStylePrinter;
import org.semanticweb.elk.owl.visitors.AbstractElkEntityVisitor;
import org.semanticweb.elk.owl.visitors.ElkEntityVisitor;
import org.semanticweb.elk.reasoner.taxonomy.hashing.InstanceTaxonomyHasher;
import org.semanticweb.elk.reasoner.taxonomy.hashing.TaxonomyHasher;
import org.semanticweb.elk.reasoner.taxonomy.model.InstanceNode;
import org.semanticweb.elk.reasoner.taxonomy.model.InstanceTaxonomy;
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.util.collections.Operations;

/**
 * Class of static helper functions for printing and hashing a taxonomy. It is
 * primarily intended to be used for controlling the output of classification.
 *
 * @author Markus Kroetzsch
 * @author Peter Skocovsky
 */
public class TaxonomyPrinter {

	/**
	 * Convenience method for printing a {@link Taxonomy} to a file at the given
	 * location.
	 *
	 * @see #dumpTaxomomy(Taxonomy, Writer, boolean)
	 *
	 * @param taxonomy
	 * @param fileName
	 * @param addHash
	 *            if true, a hash string will be added at the end of the output
	 *            using comment syntax of OWL 2 Functional Style
	 * @throws IOException
	 *             If an I/O error occurs
	 */
	public static void dumpTaxomomyToFile(
			final Taxonomy<? extends ElkEntity> taxonomy, final String fileName,
			final boolean addHash) throws IOException {
		final FileWriter fstream = new FileWriter(fileName);
		final BufferedWriter writer = new BufferedWriter(fstream);
		try {
			dumpTaxomomy(taxonomy, writer, addHash);
		} finally {
			writer.close();
		}
	}

	/**
	 * Print the contents of the given {@link Taxonomy} to the specified Writer.
	 * Expressions are ordered for generating the output, ensuring that the
	 * output is deterministic.
	 *
	 * @param taxonomy
	 * @param writer
	 * @param addHash
	 *            if true, a hash string will be added at the end of the output
	 *            using comment syntax of OWL 2 Functional Style
	 * @throws IOException
	 *             If an I/O error occurs
	 */
	public static void dumpTaxomomy(
			final Taxonomy<? extends ElkEntity> taxonomy, final Writer writer,
			final boolean addHash) throws IOException {
		writer.append("Ontology(\n");
		processTaxomomy(taxonomy, writer);
		writer.append(")\n");

		if (addHash) {
			writer.append("\n# Hash code: " + getHashString(taxonomy) + "\n");
		}
		writer.flush();
	}

	/**
	 * Convenience method for printing an {@link InstanceTaxonomy} to a file at
	 * the given location.
	 *
	 * @see org.semanticweb.elk.reasoner.taxonomy.TaxonomyPrinter#dumpInstanceTaxomomy
	 *
	 * @param taxonomy
	 * @param fileName
	 * @param addHash
	 *            if true, a hash string will be added at the end of the output
	 *            using comment syntax of OWL 2 Functional Style
	 * @throws IOException
	 *             If an I/O error occurs
	 */
	public static void dumpInstanceTaxomomyToFile(
			final InstanceTaxonomy<? extends ElkEntity, ? extends ElkEntity> taxonomy,
			final String fileName, final boolean addHash) throws IOException {
		final FileWriter fstream = new FileWriter(fileName);
		final BufferedWriter writer = new BufferedWriter(fstream);
		try {
			dumpInstanceTaxomomy(taxonomy, writer, addHash);
		} finally {
			writer.close();
		}
	}

	/**
	 * Print the contents of the given {@link InstanceTaxonomy} to the specified
	 * Writer. Expressions are ordered for generating the output, ensuring that
	 * the output is deterministic.
	 *
	 * @param taxonomy
	 * @param writer
	 * @param addHash
	 *            if true, a hash string will be added at the end of the output
	 *            using comment syntax of OWL 2 Functional Style
	 * @throws IOException
	 *             If an I/O error occurs
	 */
	public static void dumpInstanceTaxomomy(
			final InstanceTaxonomy<? extends ElkEntity, ? extends ElkEntity> taxonomy,
			final Writer writer, final boolean addHash) throws IOException {
		writer.write("Ontology(\n");
		processInstanceTaxomomy(taxonomy, writer);
		writer.write(")\n");

		if (addHash) {
			writer.write(
					"\n# Hash code: " + getInstanceHashString(taxonomy) + "\n");
		}
		writer.flush();
	}

	/**
	 * Get a has string for the given {@link Taxonomy}. Besides possible hash
	 * collisions (which have very low probability) the hash string is the same
	 * for two inputs if and only if the inputs describe the same taxonomy. So
	 * it can be used to compare classification results.
	 *
	 * @param taxonomy
	 * @return hash string
	 */
	public static String getHashString(Taxonomy<? extends ElkEntity> taxonomy) {
		return Integer.toHexString(TaxonomyHasher.hash(taxonomy));
	}

	public static String getInstanceHashString(
			InstanceTaxonomy<? extends ElkEntity, ? extends ElkEntity> taxonomy) {
		return Integer.toHexString(InstanceTaxonomyHasher.hash(taxonomy));
	}

	/**
	 * Process a taxonomy and write a normalized serialization.
	 *
	 * @param taxonomy
	 * @param writer
	 * @throws IOException
	 */
	protected static <T extends ElkEntity> void processTaxomomy(
			final Taxonomy<T> taxonomy, final Appendable writer)
					throws IOException {

		final ElkObject.Factory factory = new ElkObjectEntityRecyclingFactory();

		// Declarations.

		final List<T> members = new ArrayList<T>(
				taxonomy.getNodes().size() * 2);

		for (final TaxonomyNode<T> node : taxonomy.getNodes()) {
			for (final T member : node) {
				// TODO: this should check whether IRIs are predefined!
				if (!member.getIri()
						.equals(taxonomy.getTopNode().getCanonicalMember()
								.getIri())
						&& !member.getIri().equals(taxonomy.getBottomNode()
								.getCanonicalMember().getIri())) {
					members.add(member);
				}
			}
		}

		Collections.sort(members, taxonomy.getKeyProvider().getComparator());

		printDeclarations(members, factory, writer);

		// Relations.

		final TreeSet<T> canonicalMembers = new TreeSet<T>(
				taxonomy.getKeyProvider().getComparator());
		for (final TaxonomyNode<T> node : taxonomy.getNodes()) {
			canonicalMembers.add(node.getCanonicalMember());
		}

		for (final T canonicalMember : canonicalMembers) {
			final TaxonomyNode<T> node = taxonomy.getNode(canonicalMember);

			final ArrayList<T> orderedEquivalentMembers = new ArrayList<T>(
					node.size());
			for (final T member : node) {
				orderedEquivalentMembers.add(member);
			}
			Collections.sort(orderedEquivalentMembers,
					taxonomy.getKeyProvider().getComparator());

			final TreeSet<T> orderedSuperMembers = new TreeSet<T>(
					taxonomy.getKeyProvider().getComparator());
			for (final TaxonomyNode<T> superNode : node.getDirectSuperNodes()) {
				orderedSuperMembers.add(superNode.getCanonicalMember());
			}

			printMemberAxioms(canonicalMember, orderedEquivalentMembers,
					orderedSuperMembers, taxonomy, factory, writer);
		}
	}

	protected static <T extends ElkEntity> void printDeclarations(
			final Iterable<T> members, final ElkObject.Factory factory,
			final Appendable writer) throws IOException {
		for (final T member : members) {
			final ElkAxiom axiom = factory.getDeclarationAxiom(member);
			OwlFunctionalStylePrinter.append(writer, axiom, true);
			writer.append('\n');
		}
	}

	/**
	 * Process axioms related to one member of {@link Taxonomy}, where the
	 * relevant related members are given in two ordered collections of
	 * equivalent members and super-members, respectively. The method serializes
	 * the axioms to the Writer.
	 */
	protected static <T extends ElkEntity, I extends ElkEntity> void printMemberAxioms(
			final I member, final List<I> equivalentMembers,
			final SortedSet<T> directSuperMembers, final Taxonomy<T> taxonomy,
			final ElkObject.Factory factory, final Appendable writer)
					throws IOException {

		if (equivalentMembers.size() > 1) {
			final ElkAxiom axiom = member.accept(
					getEquivalentAxiomProvider(equivalentMembers, factory));
			OwlFunctionalStylePrinter.append(writer, axiom, true);
			writer.append('\n');
		}

		// TODO: this should exclude implicit axioms as owl:Thing ⊑ owl:Nothing
		if (!member.equals(taxonomy.getBottomNode().getCanonicalMember())) {
			for (final T superMember : directSuperMembers) {
				if (!superMember
						.equals(taxonomy.getTopNode().getCanonicalMember())) {
					final ElkAxiom axiom = member
							.accept(getSubAxiomProvider(superMember, factory));
					OwlFunctionalStylePrinter.append(writer, axiom, true);
					writer.append('\n');
				}
			}
		}

	}

	private static ElkEntityVisitor<ElkAxiom> getEquivalentAxiomProvider(
			final List<? extends ElkEntity> equivalent,
			final ElkObject.Factory factory) {

		return new AbstractElkEntityVisitor<ElkAxiom>() {

			@Override
			protected ElkAxiom defaultVisit(final ElkEntity entity) {
				return null;
			}

			@Override
			public ElkAxiom visit(final ElkClass cls) {
				return factory.getEquivalentClassesAxiom(
						new ArrayList<ElkClass>(Operations.getCollection(
								Operations.filter(equivalent, ElkClass.class),
								equivalent.size())));
			}

			@Override
			public ElkAxiom visit(final ElkNamedIndividual ind) {
				return factory.getSameIndividualAxiom(
						new ArrayList<ElkNamedIndividual>(
								Operations.getCollection(
										Operations.filter(equivalent,
												ElkNamedIndividual.class),
										equivalent.size())));
			}

			@Override
			public ElkAxiom visit(final ElkObjectProperty prop) {
				return factory.getEquivalentObjectPropertiesAxiom(
						new ArrayList<ElkObjectProperty>(
								Operations.getCollection(
										Operations.filter(equivalent,
												ElkObjectProperty.class),
										equivalent.size())));
			}

		};
	}

	private static ElkEntityVisitor<ElkAxiom> getSubAxiomProvider(
			final ElkEntity superEntity, final ElkObject.Factory factory) {

		return new AbstractElkEntityVisitor<ElkAxiom>() {

			@Override
			protected ElkAxiom defaultVisit(final ElkEntity entity) {
				return null;
			}

			@Override
			public ElkAxiom visit(final ElkClass cls) {
				if (superEntity instanceof ElkClass) {
					return factory.getSubClassOfAxiom(cls,
							(ElkClass) superEntity);
				} else {
					return defaultVisit(cls);
				}
			}

			@Override
			public ElkAxiom visit(final ElkNamedIndividual ind) {
				if (superEntity instanceof ElkClass) {
					return factory.getClassAssertionAxiom(
							(ElkClass) superEntity, ind);
				} else {
					return defaultVisit(ind);
				}
			}

			@Override
			public ElkAxiom visit(final ElkObjectProperty prop) {
				if (superEntity instanceof ElkObjectProperty) {
					return factory.getSubObjectPropertyOfAxiom(prop,
							(ElkObjectProperty) superEntity);
				} else {
					return defaultVisit(prop);
				}
			}

		};
	}

	protected static <T extends ElkEntity, I extends ElkEntity> void processInstanceTaxomomy(
			final InstanceTaxonomy<T, I> taxonomy, final Appendable writer)
					throws IOException {

		final ElkObject.Factory factory = new ElkObjectEntityRecyclingFactory();

		// Declarations.

		final List<I> members = new ArrayList<I>(
				taxonomy.getInstanceNodes().size() * 2);

		for (final InstanceNode<T, I> node : taxonomy.getInstanceNodes()) {
			for (final I member : node) {
				members.add(member);
			}
		}

		Collections.sort(members,
				taxonomy.getInstanceKeyProvider().getComparator());

		printDeclarations(members, factory, writer);

		// TBox.

		processTaxomomy(taxonomy, writer);

		// ABox.

		final TreeSet<I> canonicalIndividuals = new TreeSet<I>(
				taxonomy.getInstanceKeyProvider().getComparator());
		for (final InstanceNode<T, I> node : taxonomy.getInstanceNodes()) {
			canonicalIndividuals.add(node.getCanonicalMember());
		}

		for (final I individual : canonicalIndividuals) {
			final InstanceNode<T, I> node = taxonomy
					.getInstanceNode(individual);

			final ArrayList<I> orderedSameIndividuals = new ArrayList<I>(
					node.size());
			for (final I member : node) {
				orderedSameIndividuals.add(member);
			}
			Collections.sort(orderedSameIndividuals,
					taxonomy.getInstanceKeyProvider().getComparator());

			final TreeSet<T> orderedTypes = new TreeSet<T>(
					taxonomy.getKeyProvider().getComparator());
			for (final TypeNode<T, I> typeNode : node.getDirectTypeNodes()) {
				orderedTypes.add(typeNode.getCanonicalMember());
			}

			printMemberAxioms(individual, orderedSameIndividuals, orderedTypes,
					taxonomy, factory, writer);
		}
	}

}