/* 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; } } }