org.semanticweb.elk.reasoner.taxonomy.ConcurrentInstanceTaxonomy Maven / Gradle / Ivy
/*
* #%L
* elk-reasoner
*
* $Id$
* $HeadURL$
* %%
* Copyright (C) 2011 Oxford University Computing Laboratory
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
package org.semanticweb.elk.reasoner.taxonomy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.semanticweb.elk.owl.interfaces.ElkClass;
import org.semanticweb.elk.owl.interfaces.ElkEntity;
import org.semanticweb.elk.owl.interfaces.ElkNamedIndividual;
import org.semanticweb.elk.owl.predefined.PredefinedElkClassFactory;
import org.semanticweb.elk.reasoner.taxonomy.impl.AbstractInstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.impl.ConcurrentNodeStore;
import org.semanticweb.elk.reasoner.taxonomy.impl.IndividualNode;
import org.semanticweb.elk.reasoner.taxonomy.impl.UpdateableInstanceNode;
import org.semanticweb.elk.reasoner.taxonomy.impl.UpdateableNodeStore;
import org.semanticweb.elk.reasoner.taxonomy.impl.UpdateableTypeNode;
import org.semanticweb.elk.reasoner.taxonomy.model.ComparatorKeyProvider;
import org.semanticweb.elk.reasoner.taxonomy.model.GenericInstanceNode;
import org.semanticweb.elk.reasoner.taxonomy.model.GenericTypeNode;
import org.semanticweb.elk.reasoner.taxonomy.model.InstanceNode;
import org.semanticweb.elk.reasoner.taxonomy.model.InstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.NodeFactory;
import org.semanticweb.elk.reasoner.taxonomy.model.NodeStore;
import org.semanticweb.elk.reasoner.taxonomy.model.NonBottomTaxonomyNode;
import org.semanticweb.elk.reasoner.taxonomy.model.NonBottomTypeNode;
import org.semanticweb.elk.reasoner.taxonomy.model.Taxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.TaxonomyNode;
import org.semanticweb.elk.reasoner.taxonomy.model.TypeNode;
import org.semanticweb.elk.reasoner.taxonomy.model.UpdateableInstanceTaxonomy;
import org.semanticweb.elk.reasoner.taxonomy.model.UpdateableTaxonomy;
import org.semanticweb.elk.util.collections.Operations;
import org.semanticweb.elk.util.collections.Operations.FunctorEx;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Instance taxonomy that is suitable for concurrent processing. Taxonomy
* objects are only constructed for consistent ontologies, and some consequences
* of this are hardcoded here.
*
* This class wraps an instance of {@link UpdateableTaxonomy} and lazily
* generates wrappers for its nodes to store direct instances.
*
* @author Yevgeny Kazakov
* @author Frantisek Simancik
* @author Markus Kroetzsch
* @author Pavel Klinov
* @author Peter Skocovsky
*/
public class ConcurrentInstanceTaxonomy
extends AbstractInstanceTaxonomy
implements UpdateableInstanceTaxonomy {
// logger for events
private static final Logger LOGGER_ = LoggerFactory
.getLogger(ConcurrentInstanceTaxonomy.class);
/**
* The store for instance nodes of this taxonomy.
*/
private final UpdateableNodeStore> individualNodeStore_;
/**
* The wrapped class taxonomy
*/
private final UpdateableTaxonomy classTaxonomy_;
/**
* Map from wrapped nodes to their wrappers.
*/
private final ConcurrentMap, UpdateableTypeNodeWrapper> wrapperMap_;
/** The listeners notified about the changes to instance taxonomy. */
protected final List> instanceListeners_;
public ConcurrentInstanceTaxonomy(PredefinedElkClassFactory elkFactory,
final ComparatorKeyProvider classKeyProvider,
final ComparatorKeyProvider individualKeyProvider) {
this(new ConcurrentClassTaxonomy(elkFactory, classKeyProvider),
individualKeyProvider);
}
public ConcurrentInstanceTaxonomy(
final UpdateableTaxonomy classTaxonomy,
final ComparatorKeyProvider individualKeyProvider) {
this.individualNodeStore_ = new ConcurrentNodeStore>(
individualKeyProvider);
this.classTaxonomy_ = classTaxonomy;
this.wrapperMap_ = new ConcurrentHashMap, UpdateableTypeNodeWrapper>();
this.instanceListeners_ = new ArrayList>();
}
@Override
public ComparatorKeyProvider super ElkClass> getKeyProvider() {
return classTaxonomy_.getKeyProvider();
}
@Override
public ComparatorKeyProvider super ElkNamedIndividual> getInstanceKeyProvider() {
return individualNodeStore_.getKeyProvider();
}
@Override
public TypeNode getNode(ElkClass elkClass) {
return functor_.apply(classTaxonomy_.getNode(elkClass));
}
/**
* Obtain a {@link TypeNode} object for a given {@link ElkNamedIndividual},
* or {@code null} if none assigned.
*
* @param individual
* the {@link ElkNamedIndividual} for which to retrieve the
* {@link TypeNode}
* @return instance node object for {@link ElkNamedIndividual}, possibly still incomplete
*/
@Override
public IndividualNode.Projection getInstanceNode(
final ElkNamedIndividual individual) {
return individualNodeStore_.getNode(individual);
}
@Override
public Set extends TypeNode> getNodes() {
return Operations.map(classTaxonomy_.getNodes(), functor_);
}
@Override
public Set extends InstanceNode> getInstanceNodes() {
return individualNodeStore_.getNodes();
}
@Override
public NonBottomTypeNode getNonBottomNode(
final ElkClass elkEntity) {
return getCreateUpdateableTypeNode(
classTaxonomy_.getNonBottomNode(elkEntity));
}
@Override
public Set extends NonBottomTypeNode> getNonBottomNodes() {
return Operations.map(classTaxonomy_.getNonBottomNodes(),
nonBottomFunctor_);
}
/**
* Node factory creating instance nodes of this taxonomy.
*/
private final NodeFactory> INSTANCE_NODE_FACTORY = new NodeFactory>() {
@Override
public IndividualNode.Projection createNode(
final Iterable extends ElkNamedIndividual> members,
final int size) {
return new IndividualNode.Projection(
ConcurrentInstanceTaxonomy.this, members, size);
}
};
@Override
public InstanceNode getCreateInstanceNode(
final Collection extends ElkNamedIndividual> instances) {
return individualNodeStore_.getCreateNode(instances, instances.size(),
INSTANCE_NODE_FACTORY);
}
@Override
public boolean setCreateDirectTypes(
final InstanceNode instanceNode,
final Iterable extends java.util.Collection extends ElkClass>> typeSets) {
if (!(instanceNode instanceof IndividualNode)) {
throw new IllegalArgumentException(
"The sub-node must belong to this taxonomy: "
+ instanceNode);
}
final IndividualNode.Projection node = (IndividualNode.Projection) instanceNode;
if (node.getTaxonomy() != this) {
throw new IllegalArgumentException(
"The sub-node must belong to this taxonomy: "
+ instanceNode);
}
// TODO: establish consistency by adding default type to the nodes.
boolean isTypeSets = true;
for (final Collection extends ElkClass> superMembers : typeSets) {
final UpdateableTypeNode.Projection superNode = getCreateUpdateableTypeNode(
classTaxonomy_.getCreateNode(superMembers));
isTypeSets = false;
addDirectType(superNode, node);
}
if (node.trySetAllParentsAssigned(true)) {
if (!isTypeSets) {
fireDirectTypeAssignment(instanceNode,
instanceNode.getDirectTypeNodes());
}
return true;
}
// else
return false;
}
private static void addDirectType(
final UpdateableTypeNode.Projection typeNode,
final UpdateableInstanceNode.Projection instanceNode) {
instanceNode.addDirectTypeNode(typeNode);
typeNode.addDirectInstanceNode(instanceNode);
}
@Override
public boolean removeDirectTypes(
final InstanceNode instanceNode) {
if (!(instanceNode instanceof IndividualNode)) {
throw new IllegalArgumentException(
"The sub-node must belong to this taxonomy: "
+ instanceNode);
}
final IndividualNode.Projection node = (IndividualNode.Projection) instanceNode;
if (node.getTaxonomy() != this) {
throw new IllegalArgumentException(
"The sub-node must belong to this taxonomy: "
+ instanceNode);
}
if (!node.trySetAllParentsAssigned(false)) {
return false;
}
final List> directTypes = new ArrayList>();
synchronized (node) {
directTypes.addAll(node.getDirectNonBottomTypeNodes());
for (UpdateableTypeNode.Projection typeNode : directTypes) {
node.removeDirectTypeNode(typeNode);
}
}
// detaching the removed instance node from all its direct types
for (UpdateableTypeNode.Projection typeNode : directTypes) {
synchronized (typeNode) {
typeNode.removeDirectInstanceNode(node);
}
}
fireDirectTypeRemoval(instanceNode, directTypes);
return true;
}
@Override
public boolean removeInstanceNode(final ElkNamedIndividual instance) {
if (individualNodeStore_.removeNode(instance)) {
LOGGER_.trace("removed instance node with member: {}", instance);
return true;
}
// else
return false;
}
@Override
public NonBottomTypeNode getCreateNode(
final Collection extends ElkClass> members) {
return getCreateUpdateableTypeNode(
classTaxonomy_.getCreateNode(members));
}
@Override
public boolean setCreateDirectSupernodes(
final NonBottomTaxonomyNode subNode,
final Iterable extends Collection extends ElkClass>> superMemberSets) {
if (!(subNode instanceof UpdateableTypeNodeWrapper)) {
throw new IllegalArgumentException(
"The sub-node must belong to this taxonomy: " + subNode);
}
final UpdateableTypeNodeWrapper node = (UpdateableTypeNodeWrapper) subNode;
return classTaxonomy_.setCreateDirectSupernodes(node.getNode(),
superMemberSets);
}
@Override
public TypeNode getTopNode() {
return functor_.apply(classTaxonomy_.getTopNode());
}
@Override
public TypeNode getBottomNode() {
return bottomNodeWrapper_;
}
@Override
public boolean removeDirectSupernodes(
final NonBottomTaxonomyNode subNode) {
if (!(subNode instanceof UpdateableTypeNodeWrapper)) {
throw new IllegalArgumentException(
"The sub-node must belong to this taxonomy: " + subNode);
}
final UpdateableTypeNodeWrapper node = (UpdateableTypeNodeWrapper) subNode;
return classTaxonomy_.removeDirectSupernodes(node.getNode());
}
@Override
public boolean removeNode(final ElkClass member) {
final TaxonomyNode node = classTaxonomy_.getNode(member);
if (node == null) {
return false;
}
UpdateableTypeNodeWrapper wrapper = wrapperMap_.get(node);
if (wrapper != null && wrapperMap_.remove(node, wrapper)) {
// TODO: maybe this can be removed
for (UpdateableInstanceNode.Projection instanceNode : wrapper
.getDirectInstanceNodes()) {
synchronized (instanceNode) {
instanceNode.removeDirectTypeNode(wrapper);
}
}
}
return classTaxonomy_.removeNode(member);
}
@Override
public boolean addToBottomNode(final ElkClass member) {
return classTaxonomy_.addToBottomNode(member);
}
@Override
public boolean removeFromBottomNode(final ElkClass member) {
return classTaxonomy_.removeFromBottomNode(member);
}
private UpdateableTypeNodeWrapper getCreateUpdateableTypeNode(
NonBottomTaxonomyNode taxNode) {
if (taxNode == null) {
return null;
}
synchronized (taxNode) {
UpdateableTypeNodeWrapper wrapper = wrapperMap_.get(taxNode);
if (wrapper == null) {
wrapper = new UpdateableTypeNodeWrapper(taxNode);
wrapperMap_.put(taxNode, wrapper);
}
return wrapper;
}
}
/**
* Transforms updateable taxonomy nodes into updateable type nodes
*/
private final FunctorEx, UpdateableTypeNodeWrapper> nonBottomFunctor_ = new FunctorEx, UpdateableTypeNodeWrapper>() {
@Override
public UpdateableTypeNodeWrapper apply(
final NonBottomTaxonomyNode node) {
return getCreateUpdateableTypeNode(node);
}
@Override
public NonBottomTaxonomyNode deapply(final Object element) {
if (element instanceof UpdateableTypeNodeWrapper) {
return ((UpdateableTypeNodeWrapper) element).getNode();
}
// else
return null;
}
};
private final FunctorEx, TypeNodeWrapper> functor_ = new FunctorEx, TypeNodeWrapper>() {
@Override
public TypeNodeWrapper apply(final TaxonomyNode node) {
if (node == null) {
return null;
} else if (node instanceof NonBottomTaxonomyNode) {
return nonBottomFunctor_
.apply((NonBottomTaxonomyNode) node);
} else {
return bottomNodeWrapper_;
}
}
@Override
public TaxonomyNode deapply(Object element) {
if (element instanceof TypeNodeWrapper) {
return ((TypeNodeWrapper) element).getNode();
}
// else
return null;
}
};
/**
*
* @author Pavel Klinov
*
* [email protected]
* @author Peter Skocovsky
*/
private abstract class TypeNodeWrapper implements
GenericTypeNode.Projection {
public abstract TaxonomyNode getNode();
@Override
public ComparatorKeyProvider super ElkClass> getKeyProvider() {
return getNode().getKeyProvider();
}
@Override
public boolean contains(ElkClass member) {
return getNode().contains(member);
}
@Override
public int size() {
return getNode().size();
}
@Override
public ElkClass getCanonicalMember() {
return getNode().getCanonicalMember();
}
@Override
public Iterator iterator() {
return getNode().iterator();
}
@Override
public Taxonomy getTaxonomy() {
return getNode().getTaxonomy();
}
@Override
public Set getDirectSuperNodes() {
return Operations.map(getNode().getDirectSuperNodes(), functor_);
}
@Override
public Set getAllSuperNodes() {
return Operations.map(getNode().getAllSuperNodes(), functor_);
}
@Override
public Set getDirectSubNodes() {
return Operations.map(getNode().getDirectSubNodes(), functor_);
}
@Override
public Set getAllSubNodes() {
return Operations.map(getNode().getAllSubNodes(), functor_);
}
}
private final TypeNodeWrapper bottomNodeWrapper_ = new TypeNodeWrapper() {
@Override
public TaxonomyNode getNode() {
return classTaxonomy_.getBottomNode();
}
@Override
public Set extends GenericInstanceNode.Projection> getDirectInstanceNodes() {
return Collections.emptySet();
}
@Override
public Set extends GenericInstanceNode.Projection> getAllInstanceNodes() {
return Collections.emptySet();
}
};
/**
*
* @author Pavel Klinov
*
* [email protected]
* @author Peter Skocovsky
*/
private class UpdateableTypeNodeWrapper extends TypeNodeWrapper implements
UpdateableTypeNode.Projection {
/**
* The wrapped node.
*/
protected final NonBottomTaxonomyNode classNode_;
/**
* ElkNamedIndividual nodes whose members are instances of the members
* of this node.
*/
private final Set> directInstanceNodes_;
UpdateableTypeNodeWrapper(final NonBottomTaxonomyNode node) {
this.classNode_ = node;
this.directInstanceNodes_ = Collections.newSetFromMap(
new ConcurrentHashMap, Boolean>());
}
@Override
public NonBottomTaxonomyNode getNode() {
return classNode_;
}
@Override
public Set extends UpdateableInstanceNode.Projection> getDirectInstanceNodes() {
return Collections.unmodifiableSet(directInstanceNodes_);
}
@Override
public Set extends GenericInstanceNode.Projection> getAllInstanceNodes() {
return TaxonomyNodeUtils.getAllInstanceNodes(this);
}
@Override
public Set extends UpdateableTypeNode.Projection> getDirectNonBottomSuperNodes() {
return Operations.map(getNode().getDirectNonBottomSuperNodes(),
nonBottomFunctor_);
}
@Override
public Set extends UpdateableTypeNode.Projection> getDirectNonBottomSubNodes() {
return Operations.map(getNode().getDirectNonBottomSubNodes(),
nonBottomFunctor_);
}
@Override
public void addDirectInstanceNode(
UpdateableInstanceNode.Projection instanceNode) {
LOGGER_.trace("{}: new direct instance-node {}", classNode_,
instanceNode);
directInstanceNodes_.add(instanceNode);
}
/*
* This method is not thread safe
*/
@Override
public void removeDirectInstanceNode(
UpdateableInstanceNode.Projection instanceNode) {
LOGGER_.trace("{}: direct instance node removed {}", classNode_,
instanceNode);
directInstanceNodes_.remove(instanceNode);
}
@Override
public String toString() {
return classNode_.toString();
}
@Override
public int hashCode() {
return classNode_.hashCode();
}
@Override
public boolean equals(Object obj) {
if (obj instanceof UpdateableTypeNodeWrapper) {
return classNode_ == ((UpdateableTypeNodeWrapper) obj).classNode_;
}
return false;
}
}
@Override
public boolean addListener(final Taxonomy.Listener listener) {
return classTaxonomy_.addListener(listener);
}
@Override
public boolean removeListener(final Taxonomy.Listener listener) {
return classTaxonomy_.removeListener(listener);
}
@Override
public boolean addListener(final NodeStore.Listener listener) {
return classTaxonomy_.addListener(listener);
}
@Override
public boolean removeListener(final NodeStore.Listener listener) {
return classTaxonomy_.removeListener(listener);
}
@Override
public boolean addInstanceListener(
final NodeStore.Listener listener) {
return individualNodeStore_.addListener(listener);
}
@Override
public boolean removeInstanceListener(
final NodeStore.Listener listener) {
return individualNodeStore_.removeListener(listener);
}
@Override
public boolean addInstanceListener(
final InstanceTaxonomy.Listener listener) {
return instanceListeners_.add(listener);
}
@Override
public boolean removeInstanceListener(
final InstanceTaxonomy.Listener listener) {
return instanceListeners_.remove(listener);
}
protected void fireDirectTypeAssignment(
final InstanceNode instanceNode,
final Collection extends TypeNode> typeNodes) {
for (final InstanceTaxonomy.Listener listener : instanceListeners_) {
listener.directTypeNodesAppeared(instanceNode);
for (final TypeNode typeNode : typeNodes) {
listener.directInstanceNodesAppeared(typeNode);
}
}
}
protected void fireDirectTypeRemoval(
final InstanceNode instanceNode,
final Collection extends TypeNode> typeNodes) {
for (final InstanceTaxonomy.Listener listener : instanceListeners_) {
listener.directTypeNodesDisappeared(instanceNode);
for (final TypeNode typeNode : typeNodes) {
listener.directInstanceNodesDisappeared(typeNode);
}
}
}
}