/*
* 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.reteoo;
import org.drools.core.WorkingMemoryEntryPoint;
import org.drools.core.common.DroolsObjectInputStream;
import org.drools.core.common.InternalFactHandle;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.common.RuleBasePartitionId;
import org.drools.core.common.UpdateContext;
import org.drools.core.impl.InternalKnowledgeBase;
import org.drools.core.reteoo.builder.BuildContext;
import org.drools.core.rule.EntryPointId;
import org.drools.core.spi.ObjectType;
import org.drools.core.spi.PropagationContext;
import org.drools.core.util.bitmask.BitMask;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* The Rete-OO network.
*
* The Rete class is the root <code>Object</code>. All objects are asserted into
* the Rete node where it propagates to all matching ObjectTypeNodes.
*
* The first time an instance of a Class type is asserted it does a full
* iteration of all ObjectTyppeNodes looking for matches, any matches are
* then cached in a HashMap which is used for future assertions.
*
* While Rete extends ObjectSource nad implements ObjectSink it nulls the
* methods attach(), remove() and updateNewNode() as this is the root node
* they are no applicable
*
* @see ObjectTypeNode
*/
public class Rete extends ObjectSource
implements
Externalizable,
ObjectSink {
// ------------------------------------------------------------
// Instance members
// ------------------------------------------------------------
private static final long serialVersionUID = 510l;
private Map<EntryPointId, EntryPointNode> entryPoints;
private transient InternalKnowledgeBase kBase;
public Rete() {
this( null );
}
// ------------------------------------------------------------
// Constructors
// ------------------------------------------------------------
public Rete(InternalKnowledgeBase kBase) {
super( 0, RuleBasePartitionId.MAIN_PARTITION, kBase != null && kBase.getConfiguration().isMultithreadEvaluation() );
this.entryPoints = Collections.synchronizedMap( new HashMap<EntryPointId, EntryPointNode>() );
this.kBase = kBase;
hashcode = calculateHashCode();
}
public short getType() {
return NodeTypeEnums.ReteNode;
}
/**
* This is the entry point into the network for all asserted Facts. Iterates a cache
* of matching <code>ObjectTypdeNode</code>s asserting the Fact. If the cache does not
* exist it first iteraes and builds the cache.
*
* @param factHandle
* The FactHandle of the fact to assert
* @param context
* The <code>PropagationContext</code> of the <code>WorkingMemory</code> action
* @param workingMemory
* The working memory session.
*/
public void assertObject(final InternalFactHandle factHandle,
final PropagationContext context,
final InternalWorkingMemory workingMemory) {
EntryPointId entryPoint = context.getEntryPoint();
EntryPointNode node = this.entryPoints.get( entryPoint );
ObjectTypeConf typeConf = ((WorkingMemoryEntryPoint) workingMemory.getWorkingMemoryEntryPoint( entryPoint.getEntryPointId() )).getObjectTypeConfigurationRegistry().getObjectTypeConf( entryPoint,
factHandle.getObject() );
node.assertObject( factHandle,
context,
typeConf,
workingMemory );
}
/**
* Retract a fact object from this <code>RuleBase</code> and the specified
* <code>WorkingMemory</code>.
*
* @param handle
* The handle of the fact to retract.
* @param workingMemory
* The working memory session.
*/
public void retractObject(final InternalFactHandle handle,
final PropagationContext context,
final InternalWorkingMemory workingMemory) {
EntryPointId entryPoint = context.getEntryPoint();
EntryPointNode node = this.entryPoints.get( entryPoint );
ObjectTypeConf typeConf = ((WorkingMemoryEntryPoint) workingMemory.getWorkingMemoryEntryPoint( entryPoint.getEntryPointId() )).getObjectTypeConfigurationRegistry().getObjectTypeConf( entryPoint,
handle.getObject() );
node.retractObject( handle,
context,
typeConf,
workingMemory );
}
public void modifyObject(final InternalFactHandle factHandle,
final ModifyPreviousTuples modifyPreviousTuples,
final PropagationContext context,
final InternalWorkingMemory workingMemory) {
throw new UnsupportedOperationException();
}
/**
* Adds the <code>ObjectSink</code> so that it may receive
* <code>Objects</code> propagated from this <code>ObjectSource</code>.
*
* @param objectSink
* The <code>ObjectSink</code> to receive propagated
* <code>Objects</code>. Rete only accepts <code>ObjectTypeNode</code>s
* as parameters to this method, though.
*/
public void addObjectSink(final ObjectSink objectSink) {
final EntryPointNode node = (EntryPointNode) objectSink;
entryPoints.put(node.getEntryPoint(), node);
kBase.registerAddedEntryNodeCache(node);
}
public void removeObjectSink(final ObjectSink objectSink) {
final EntryPointNode node = (EntryPointNode) objectSink;
entryPoints.remove(node.getEntryPoint());
kBase.registeRremovedEntryNodeCache(node);
}
public void attach( BuildContext context ) {
throw new UnsupportedOperationException( "cannot call attach() from the root Rete node" );
}
public void networkUpdated(UpdateContext updateContext) {
// nothing to do
}
protected boolean doRemove(final RuleRemovalContext context,
final ReteooBuilder builder,
final InternalWorkingMemory[] workingMemories) {
// for now, we don't remove EntryPointNodes because they might be referenced by external sources
return false;
}
public EntryPointNode getEntryPointNode(final EntryPointId entryPoint) {
return this.entryPoints.get( entryPoint );
}
public List<ObjectTypeNode> getObjectTypeNodes() {
List<ObjectTypeNode> allNodes = new LinkedList<ObjectTypeNode>();
for ( EntryPointNode node : this.entryPoints.values() ) {
allNodes.addAll( node.getObjectTypeNodes().values() );
}
return allNodes;
}
public Map<ObjectType, ObjectTypeNode> getObjectTypeNodes(EntryPointId entryPoint) {
return this.entryPoints.get( entryPoint ).getObjectTypeNodes();
}
@Override
public InternalKnowledgeBase getKnowledgeBase() {
return this.kBase;
}
private int calculateHashCode() {
return this.entryPoints.hashCode();
}
public boolean equals(final Object object) {
return this == object || internalEquals( object );
}
@Override
protected boolean internalEquals( Object object ) {
if ( object == null || !(object instanceof Rete) || this.hashCode() != object.hashCode() ) {
return false;
}
return this.entryPoints.equals( ((Rete)object).entryPoints );
}
public void updateSink(final ObjectSink sink,
final PropagationContext context,
final InternalWorkingMemory workingMemory) {
// nothing to do, since Rete object itself holds no facts to propagate.
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject( entryPoints );
super.writeExternal( out );
}
@SuppressWarnings("unchecked")
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
entryPoints = (Map<EntryPointId, EntryPointNode>) in.readObject();
kBase = ((DroolsObjectInputStream)in).getKnowledgeBase();
for (Map.Entry<EntryPointId, EntryPointNode> entry : entryPoints.entrySet()) {
EntryPointNode node = entry.getValue();
if (node.getEntryPoint() == null) node.setEntryPoint(entry.getKey());
kBase.registerAddedEntryNodeCache(node);
}
super.readExternal( in );
}
public Map<EntryPointId,EntryPointNode> getEntryPointNodes() {
return this.entryPoints;
}
public void byPassModifyToBetaNode(InternalFactHandle factHandle,
ModifyPreviousTuples modifyPreviousTuples,
PropagationContext context,
InternalWorkingMemory workingMemory) {
throw new UnsupportedOperationException( "This should never get called, as the PropertyReactive first happens at the AlphaNode" );
}
@Override
public BitMask calculateDeclaredMask(List<String> settableProperties) {
throw new UnsupportedOperationException();
}
}