/*
* Copyright 2005 Red Hat, Inc. and/or its affiliates.
*
* 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.
*/
package org.drools.core.common;
import org.drools.core.RuleBaseConfiguration.AssertBehaviour;
import org.drools.core.beliefsystem.BeliefSet;
import org.drools.core.beliefsystem.BeliefSystem;
import org.drools.core.beliefsystem.simple.SimpleMode;
import org.drools.core.definitions.rule.impl.RuleImpl;
import org.drools.core.impl.StatefulKnowledgeSessionImpl;
import org.drools.core.reteoo.ObjectTypeConf;
import org.drools.core.spi.Activation;
import org.drools.core.spi.PropagationContext;
import org.drools.core.util.ObjectHashMap;
import org.kie.api.runtime.rule.FactHandle;
import org.kie.internal.runtime.beliefs.Mode;
import java.util.Iterator;
import static org.drools.core.common.ClassAwareObjectStore.getActualClass;
/**
* The Truth Maintenance System is responsible for tracking two things. Firstly
* It maintains a Map to track the classes with the same Equality, using the
* EqualityKey. The EqualityKey has an internal data structure which references
* all the handles which are equal. Secondly It maintains another map tracking
* the justifications for logically asserted facts.
*/
public class TruthMaintenanceSystem {
private InternalWorkingMemoryEntryPoint ep;
private ObjectTypeConfigurationRegistry typeConfReg;
private ObjectHashMap equalityKeyMap;
private BeliefSystem defaultBeliefSystem;
private AssertBehaviour assertBehaviour;
public TruthMaintenanceSystem() {}
public TruthMaintenanceSystem(StatefulKnowledgeSessionImpl wm,
InternalWorkingMemoryEntryPoint ep) {
this.ep = ep;
assertBehaviour = ep.getKnowledgeBase().getConfiguration().getAssertBehaviour();
typeConfReg = ep.getObjectTypeConfigurationRegistry();
this.equalityKeyMap = new ObjectHashMap();
this.equalityKeyMap.setComparator( EqualityKeyComparator.getInstance() );
defaultBeliefSystem = wm.getKnowledgeBase().getConfiguration().getComponentFactory().getBeliefSystemFactory().createBeliefSystem(wm.getSessionConfiguration().getBeliefSystemType(), ep, this);
}
public ObjectHashMap getEqualityKeyMap() {
return this.equalityKeyMap;
}
public Object put(final EqualityKey key) {
return this.equalityKeyMap.put( key,
key,
false );
}
public InternalFactHandle insert(Object object,
Object tmsValue,
RuleImpl rule,
Activation activation) {
ObjectTypeConf typeConf = typeConfReg.getObjectTypeConf( ep.getEntryPoint(), object );
if ( !typeConf.isTMSEnabled()) {
enableTMS(object, typeConf);
}
// get the key for other "equal" objects, returns null if none exist
EqualityKey key = get(object);
InternalFactHandle fh = null;
if ( key == null ) {
// no EqualityKey exits, so we construct one. We know it can only be justified.
fh = ep.getHandleFactory().newFactHandle(object, typeConf, ep.getInternalWorkingMemory(), ep );
key = new EqualityKey( fh, EqualityKey.JUSTIFIED );
fh.setEqualityKey( key );
put(key);
} else {
fh = key.getLogicalFactHandle();
if ( fh == null ) {
// The EqualityKey exists, but this is the first logical object in the key.
fh = ep.getHandleFactory().newFactHandle(object, typeConf, ep.getInternalWorkingMemory(), ep );
key.setLogicalFactHandle( fh );
fh.setEqualityKey( key );
}
}
// Any logical propagations are handled via the TMS.addLogicalDependency
fh = addLogicalDependency(fh,
object,
tmsValue,
activation,
activation.getPropagationContext(),
rule,
typeConf);
return fh;
}
public void delete(FactHandle fh) {
if ( fh == null ) {
return;
}
InternalFactHandle ifh = (InternalFactHandle) fh;
// This will clear out the logical entries for the FH. However the FH and EqualityKey remain, if it's stated
// Update the equality key, which maintains a list of stated FactHandles
final EqualityKey key = ifh.getEqualityKey();
if ( key.getLogicalFactHandle() != fh ) {
throw new IllegalArgumentException( "The FactHandle did not originate from TMS : " + fh);
}
InternalWorkingMemory wm = ep.getInternalWorkingMemory();
final PropagationContext propagationContext = ep.getPctxFactory().createPropagationContext( wm.getNextPropagationIdCounter(), PropagationContext.Type.DELETION,
null, null, ifh, ep.getEntryPoint());
TruthMaintenanceSystemHelper.removeLogicalDependencies( ifh, propagationContext );
}
public EqualityKey get(final EqualityKey key) {
return (EqualityKey) this.equalityKeyMap.get( key );
}
public EqualityKey get(final Object object) {
EqualityKey key = (EqualityKey) this.equalityKeyMap.get( object );
if ( key == null && assertBehaviour == AssertBehaviour.EQUALITY ) {
// Edge case: another object X, equivalent (equals+hashcode) to "object" Y
// has been previously stated. However, if X is a subclass of Y, TMS
// may have not been enabled yet, and key would be null.
InternalFactHandle fh = ep.getObjectStore().getHandleForObject(object);
if ( fh != null ) {
key = fh.getEqualityKey();
if ( key == null ) {
// we use the FH's Object here, not the inserted object
ObjectTypeConf typeC = this.typeConfReg.getObjectTypeConf( ep.getEntryPoint(), fh.getObject() );
enableTMS( fh.getObject(), typeC );
key = fh.getEqualityKey();
}
}
}
return key;
}
public EqualityKey remove(final EqualityKey key) {
return (EqualityKey) this.equalityKeyMap.remove( key );
}
/**
* Adds a justification for the FactHandle to the justifiedMap.
*
* @param handle
* @param activation
* @param context
* @param rule
* @param typeConf
*/
public void readLogicalDependency(final InternalFactHandle handle,
final Object object,
final Object value,
final Activation activation,
final PropagationContext context,
final RuleImpl rule,
final ObjectTypeConf typeConf) {
addLogicalDependency( handle, object, value, activation, context, rule, typeConf, true );
}
public InternalFactHandle addLogicalDependency(final InternalFactHandle handle,
final Object object,
final Object value,
final Activation activation,
final PropagationContext context,
final RuleImpl rule,
final ObjectTypeConf typeConf) {
return addLogicalDependency( handle, object, value, activation, context, rule, typeConf, false );
}
public InternalFactHandle addLogicalDependency(final InternalFactHandle handle,
final Object object,
final Object value,
final Activation activation,
final PropagationContext context,
final RuleImpl rule,
final ObjectTypeConf typeConf,
final boolean read) {
BeliefSystem beliefSystem = defaultBeliefSystem;
if ( value != null && value instanceof Mode & !( value instanceof SimpleMode ) ) {
Mode mode = (Mode) value;
beliefSystem = (BeliefSystem) mode.getBeliefSystem();
}
BeliefSet beliefSet = handle.getEqualityKey().getBeliefSet();
if ( beliefSet == null ) {
if ( context.getType() == PropagationContext.Type.MODIFICATION ) {
// if this was a update, chances are its trying to retract a logical assertion
}
beliefSet = beliefSystem.newBeliefSet( handle );
handle.getEqualityKey().setBeliefSet( beliefSet );
}
final LogicalDependency node = beliefSystem.newLogicalDependency( activation, beliefSet, object, value );
activation.getRule().setHasLogicalDependency( true );
activation.addLogicalDependency( node );
if ( read ) {
// used when deserialising
beliefSystem.read( node, beliefSet, context, typeConf );
} else {
beliefSet = beliefSystem.insert( node, beliefSet, context, typeConf );
}
return beliefSet.getFactHandle();
}
public void clear() {
this.equalityKeyMap.clear();
}
public BeliefSystem getBeliefSystem() {
return defaultBeliefSystem;
}
/**
* TMS will be automatically enabled when the first logical insert happens.
*
* We will take all the already asserted objects of the same type and initialize
* the equality map.
*
* @param object the logically inserted object.
* @param conf the type's configuration.
*/
private void enableTMS(Object object, ObjectTypeConf conf) {
Iterator<InternalFactHandle> it = ((ClassAwareObjectStore) ep.getObjectStore()).iterateFactHandles(getActualClass(object));
while (it.hasNext()) {
InternalFactHandle handle = it.next();
if (handle != null && handle.getEqualityKey() == null) {
EqualityKey key = new EqualityKey(handle);
handle.setEqualityKey(key);
key.setStatus(EqualityKey.STATED);
put(key);
}
}
// Enable TMS for this type.
conf.enableTMS();
}
}