/* Copyright 2008, 2009, 2010 by the Oxford University Computing Laboratory
This file is part of HermiT.
HermiT is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
HermiT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with HermiT. If not, see <http://www.gnu.org/licenses/>.
*/
package org.semanticweb.HermiT.blocking;
import java.io.Serializable;
import java.util.List;
import org.semanticweb.HermiT.model.AtomicRole;
import org.semanticweb.HermiT.model.Concept;
import org.semanticweb.HermiT.model.DLClause;
import org.semanticweb.HermiT.model.DLOntology;
import org.semanticweb.HermiT.model.DataRange;
import org.semanticweb.HermiT.model.Variable;
import org.semanticweb.HermiT.tableau.DLClauseEvaluator;
import org.semanticweb.HermiT.tableau.Node;
import org.semanticweb.HermiT.tableau.Tableau;
public class AnywhereBlocking implements BlockingStrategy,Serializable {
private static final long serialVersionUID=-2959900333817197464L;
protected final DirectBlockingChecker m_directBlockingChecker;
protected final BlockersCache m_currentBlockersCache;
protected final BlockingSignatureCache m_blockingSignatureCache;
protected Tableau m_tableau;
protected boolean m_useBlockingSignatureCache;
protected Node m_firstChangedNode;
public AnywhereBlocking(DirectBlockingChecker directBlockingChecker,BlockingSignatureCache blockingSignatureCache) {
m_directBlockingChecker=directBlockingChecker;
m_currentBlockersCache=new BlockersCache(m_directBlockingChecker);
m_blockingSignatureCache=blockingSignatureCache;
}
public void initialize(Tableau tableau) {
m_tableau=tableau;
m_directBlockingChecker.initialize(tableau);
updateBlockingSignatureCacheUsage();
}
public void additionalDLOntologySet(DLOntology additionalDLOntology) {
updateBlockingSignatureCacheUsage();
}
public void additionalDLOntologyCleared() {
updateBlockingSignatureCacheUsage();
}
protected void updateBlockingSignatureCacheUsage() {
m_useBlockingSignatureCache=(m_tableau.getAdditionalHyperresolutionManager()==null);
}
public void clear() {
m_currentBlockersCache.clear();
m_firstChangedNode=null;
m_directBlockingChecker.clear();
}
public void computeBlocking(boolean finalChance) {
if (m_firstChangedNode!=null) {
Node node=m_firstChangedNode;
while (node!=null) {
m_currentBlockersCache.removeNode(node);
node=node.getNextTableauNode();
}
node=m_firstChangedNode;
boolean checkBlockingSignatureCache=(m_useBlockingSignatureCache && m_blockingSignatureCache!=null && !m_blockingSignatureCache.isEmpty());
while (node!=null) {
if (node.isActive() && (m_directBlockingChecker.canBeBlocked(node) || m_directBlockingChecker.canBeBlocker(node))) {
if (m_directBlockingChecker.hasBlockingInfoChanged(node) || !node.isDirectlyBlocked() || node.getBlocker().getNodeID()>=m_firstChangedNode.getNodeID()) {
Node parent=node.getParent();
if (parent==null)
node.setBlocked(null,false);
else if (parent.isBlocked())
node.setBlocked(parent,false);
else if (checkBlockingSignatureCache) {
if (m_blockingSignatureCache.containsSignature(node))
node.setBlocked(Node.SIGNATURE_CACHE_BLOCKER,true);
else {
Node blocker=m_currentBlockersCache.getBlocker(node);
node.setBlocked(blocker,blocker!=null);
}
}
else {
Node blocker=m_currentBlockersCache.getBlocker(node);
node.setBlocked(blocker,blocker!=null);
}
if (!node.isBlocked() && m_directBlockingChecker.canBeBlocker(node))
m_currentBlockersCache.addNode(node);
}
m_directBlockingChecker.clearBlockingInfoChanged(node);
}
node=node.getNextTableauNode();
}
m_firstChangedNode=null;
}
}
public boolean isPermanentAssertion(Concept concept,Node node) {
return true;
}
public boolean isPermanentAssertion(DataRange range,Node node) {
return true;
}
public void assertionAdded(Concept concept,Node node,boolean isCore) {
updateNodeChange(m_directBlockingChecker.assertionAdded(concept,node,isCore));
}
public void assertionCoreSet(Concept concept,Node node) {
}
public void assertionRemoved(Concept concept,Node node,boolean isCore) {
updateNodeChange(m_directBlockingChecker.assertionRemoved(concept,node,isCore));
}
public void assertionAdded(DataRange range,Node node,boolean isCore) {
m_directBlockingChecker.assertionAdded(range,node,isCore);
}
public void assertionCoreSet(DataRange range,Node node) {
}
public void assertionRemoved(DataRange range,Node node,boolean isCore) {
m_directBlockingChecker.assertionRemoved(range,node,isCore);
}
public void assertionAdded(AtomicRole atomicRole,Node nodeFrom,Node nodeTo,boolean isCore) {
updateNodeChange(m_directBlockingChecker.assertionAdded(atomicRole,nodeFrom,nodeTo,isCore));
}
public void nodesMerged(Node mergeFrom,Node mergeInto) {
updateNodeChange(m_directBlockingChecker.nodesMerged(mergeFrom,mergeInto));
}
public void nodesUnmerged(Node mergeFrom,Node mergeInto) {
updateNodeChange(m_directBlockingChecker.nodesUnmerged(mergeFrom,mergeInto));
}
public void assertionCoreSet(AtomicRole atomicRole,Node nodeFrom,Node nodeTo) {
}
public void assertionRemoved(AtomicRole atomicRole,Node nodeFrom,Node nodeTo,boolean isCore) {
updateNodeChange(m_directBlockingChecker.assertionRemoved(atomicRole,nodeFrom,nodeTo,isCore));
}
public void nodeStatusChanged(Node node) {
updateNodeChange(node);
}
protected final void updateNodeChange(Node node) {
if (node!=null && (m_firstChangedNode==null || node.getNodeID()<m_firstChangedNode.getNodeID()))
m_firstChangedNode=node;
}
public void nodeInitialized(Node node) {
m_directBlockingChecker.nodeInitialized(node);
}
public void nodeDestroyed(Node node) {
m_currentBlockersCache.removeNode(node);
m_directBlockingChecker.nodeDestroyed(node);
if (m_firstChangedNode!=null && m_firstChangedNode.getNodeID()>=node.getNodeID())
m_firstChangedNode=null;
}
public void modelFound() {
if (m_useBlockingSignatureCache && m_blockingSignatureCache!=null) {
// Since we've found a model, we know what is blocked and what is not, so we don't need to update the blocking status.
assert m_firstChangedNode==null;
Node node=m_tableau.getFirstTableauNode();
while (node!=null) {
if (node.isActive() && !node.isBlocked() && m_directBlockingChecker.canBeBlocker(node))
m_blockingSignatureCache.addNode(node);
node=node.getNextTableauNode();
}
}
}
public boolean isExact() {
return true;
}
public void dlClauseBodyCompiled(List<DLClauseEvaluator.Worker> workers,DLClause dlClause,List<Variable> variables,Object[] valuesBuffer,boolean[] coreVariables) {
for (int i=0;i<coreVariables.length;i++) {
coreVariables[i]=true;
}
}
}
class BlockersCache implements Serializable {
private static final long serialVersionUID=-7692825443489644667L;
protected Tableau m_tableau;
protected final DirectBlockingChecker m_directBlockingChecker;
protected CacheEntry[] m_buckets;
protected int m_numberOfElements;
protected int m_threshold;
protected CacheEntry m_emptyEntries;
public BlockersCache(DirectBlockingChecker directBlockingChecker) {
m_directBlockingChecker=directBlockingChecker;
clear();
}
public boolean isEmpty() {
return m_numberOfElements==0;
}
public void clear() {
m_buckets=new CacheEntry[1024];
m_threshold=(int)(m_buckets.length*0.75);
m_numberOfElements=0;
m_emptyEntries=null;
}
public void removeNode(Node node) {
// Check addNode() for an explanation of why we associate the entry with the node.
BlockersCache.CacheEntry removeEntry=(BlockersCache.CacheEntry)node.getBlockingCargo();
if (removeEntry!=null) {
int bucketIndex=getIndexFor(removeEntry.m_hashCode,m_buckets.length);
CacheEntry lastEntry=null;
CacheEntry entry=m_buckets[bucketIndex];
while (entry!=null) {
if (entry==removeEntry) {
if (lastEntry==null)
m_buckets[bucketIndex]=entry.m_nextEntry;
else
lastEntry.m_nextEntry=entry.m_nextEntry;
entry.m_nextEntry=m_emptyEntries;
entry.m_node=null;
entry.m_hashCode=0;
m_emptyEntries=entry;
m_numberOfElements--;
node.setBlockingCargo(null);
return;
}
lastEntry=entry;
entry=entry.m_nextEntry;
}
throw new IllegalStateException("Internal error: entry not in cache!");
}
}
public void addNode(Node node) {
int hashCode=m_directBlockingChecker.blockingHashCode(node);
int bucketIndex=getIndexFor(hashCode,m_buckets.length);
CacheEntry entry=m_buckets[bucketIndex];
while (entry!=null) {
if (hashCode==entry.m_hashCode && m_directBlockingChecker.isBlockedBy(entry.m_node,node))
throw new IllegalStateException("Internal error: node already in the cache!");
entry=entry.m_nextEntry;
}
if (m_emptyEntries==null)
entry=new CacheEntry();
else {
entry=m_emptyEntries;
m_emptyEntries=m_emptyEntries.m_nextEntry;
}
entry.initialize(node,hashCode,m_buckets[bucketIndex]);
m_buckets[bucketIndex]=entry;
// When a node is added to the cache, we record with the node the entry.
// This is used to remove nodes from the cache. Note that changes to a node
// can affect its label. Therefore, we CANNOT remove a node by taking its present
// blocking hash-code, as this can be different from the hash-code used at the
// time the node has been added to the cache.
node.setBlockingCargo(entry);
m_numberOfElements++;
if (m_numberOfElements>=m_threshold)
resize(m_buckets.length*2);
}
protected void resize(int newCapacity) {
CacheEntry[] newBuckets=new CacheEntry[newCapacity];
for (int i=0;i<m_buckets.length;i++) {
CacheEntry entry=m_buckets[i];
while (entry!=null) {
CacheEntry nextEntry=entry.m_nextEntry;
int newIndex=getIndexFor(entry.m_hashCode,newCapacity);
entry.m_nextEntry=newBuckets[newIndex];
newBuckets[newIndex]=entry;
entry=nextEntry;
}
}
m_buckets=newBuckets;
m_threshold=(int)(newCapacity*0.75);
}
public Node getBlocker(Node node) {
if (m_directBlockingChecker.canBeBlocked(node)) {
int hashCode=m_directBlockingChecker.blockingHashCode(node);
int bucketIndex=getIndexFor(hashCode,m_buckets.length);
CacheEntry entry=m_buckets[bucketIndex];
while (entry!=null) {
if (hashCode==entry.m_hashCode && m_directBlockingChecker.isBlockedBy(entry.m_node,node))
return entry.m_node;
entry=entry.m_nextEntry;
}
}
return null;
}
protected static int getIndexFor(int hashCode,int tableLength) {
hashCode+=~(hashCode << 9);
hashCode^=(hashCode >>> 14);
hashCode+=(hashCode << 4);
hashCode^=(hashCode >>> 10);
return hashCode & (tableLength-1);
}
public static class CacheEntry implements Serializable {
private static final long serialVersionUID=-7047487963170250200L;
protected Node m_node;
protected int m_hashCode;
protected CacheEntry m_nextEntry;
public void initialize(Node node,int hashCode,CacheEntry nextEntry) {
m_node=node;
m_hashCode=hashCode;
m_nextEntry=nextEntry;
}
}
}