/*
* Copyright 2015 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.
*
* 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 java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import org.drools.core.RuleBaseConfiguration;
import org.drools.core.common.InternalFactHandle;
import org.drools.core.common.InternalWorkingMemory;
import org.drools.core.common.Memory;
import org.drools.core.common.MemoryFactory;
import org.drools.core.common.UpdateContext;
import org.drools.core.reteoo.builder.BuildContext;
import org.drools.core.spi.PropagationContext;
import org.drools.core.util.AbstractBaseLinkedListNode;
/**
* Node which allows to follow different paths in the Rete-OO network,
* based on the result of a boolean <code>Test</code>.
*/
public class ConditionalBranchNode extends LeftTupleSource implements LeftTupleSinkNode, MemoryFactory<ConditionalBranchNode.ConditionalBranchMemory> {
protected ConditionalBranchEvaluator branchEvaluator;
protected boolean tupleMemoryEnabled;
private LeftTupleSinkNode previousTupleSinkNode;
private LeftTupleSinkNode nextTupleSinkNode;
public ConditionalBranchNode() { }
public ConditionalBranchNode( int id,
LeftTupleSource tupleSource,
ConditionalBranchEvaluator branchEvaluator,
BuildContext context ) {
super(id, context);
setLeftTupleSource( tupleSource );
this.tupleMemoryEnabled = context.isTupleMemoryEnabled();
this.branchEvaluator = branchEvaluator;
initMasks(context, tupleSource);
hashcode = calculateHashCode();
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
super.readExternal(in);
tupleMemoryEnabled = in.readBoolean();
branchEvaluator = (ConditionalBranchEvaluator) in.readObject();
}
@Override
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal(out);
out.writeBoolean(tupleMemoryEnabled);
out.writeObject(branchEvaluator);
}
public ConditionalBranchEvaluator getBranchEvaluator() {
return branchEvaluator;
}
public void attach( BuildContext context ) {
getLeftTupleSource().addTupleSink(this, context);
}
public void networkUpdated(UpdateContext updateContext) {
getLeftTupleSource().networkUpdated(updateContext);
}
/**
* Produce a debug string.
*
* @return The debug string.
*/
public String toString() {
return "[ConditionalBranchNode: cond=" + this.branchEvaluator + "]";
}
private int calculateHashCode() {
return getLeftTupleSource().hashCode() ^ this.branchEvaluator.hashCode();
}
@Override
public boolean equals(Object object) {
return this == object ||
( internalEquals( object ) && getLeftTupleSource().thisNodeEquals( ((ConditionalBranchNode)object).getLeftTupleSource() ) );
}
@Override
protected boolean internalEquals( Object object ) {
if ( object == null || !(object instanceof ConditionalBranchNode) || this.hashCode() != object.hashCode() ) {
return false;
}
return this.branchEvaluator.equals( ((ConditionalBranchNode)object).branchEvaluator );
}
public ConditionalBranchMemory createMemory(final RuleBaseConfiguration config, InternalWorkingMemory wm) {
return new ConditionalBranchMemory( branchEvaluator.createContext() );
}
@Override
public LeftTuple createPeer(LeftTuple original) {
EvalNodeLeftTuple peer = new EvalNodeLeftTuple();
peer.initPeer( (BaseLeftTuple) original, this );
original.setPeer( peer );
return peer;
}
public boolean isLeftTupleMemoryEnabled() {
return tupleMemoryEnabled;
}
public void setLeftTupleMemoryEnabled(boolean tupleMemoryEnabled) {
this.tupleMemoryEnabled = tupleMemoryEnabled;
}
/**
* Returns the next node
* @return
* The next TupleSinkNode
*/
public LeftTupleSinkNode getNextLeftTupleSinkNode() {
return this.nextTupleSinkNode;
}
/**
* Sets the next node
* @param next
* The next TupleSinkNode
*/
public void setNextLeftTupleSinkNode(final LeftTupleSinkNode next) {
this.nextTupleSinkNode = next;
}
/**
* Returns the previous node
* @return
* The previous TupleSinkNode
*/
public LeftTupleSinkNode getPreviousLeftTupleSinkNode() {
return this.previousTupleSinkNode;
}
/**
* Sets the previous node
* @param previous
* The previous TupleSinkNode
*/
public void setPreviousLeftTupleSinkNode(final LeftTupleSinkNode previous) {
this.previousTupleSinkNode = previous;
}
public short getType() {
return NodeTypeEnums.ConditionalBranchNode;
}
public LeftTuple createLeftTuple(InternalFactHandle factHandle,
Sink sink,
boolean leftTupleMemoryEnabled) {
return new EvalNodeLeftTuple(factHandle, sink, leftTupleMemoryEnabled );
}
public LeftTuple createLeftTuple(final InternalFactHandle factHandle,
final LeftTuple leftTuple,
final Sink sink) {
return new EvalNodeLeftTuple(factHandle,leftTuple, sink );
}
public LeftTuple createLeftTuple(LeftTuple leftTuple,
Sink sink,
PropagationContext pctx, boolean leftTupleMemoryEnabled) {
return new EvalNodeLeftTuple(leftTuple,sink, pctx, leftTupleMemoryEnabled );
}
public LeftTuple createLeftTuple(LeftTuple leftTuple,
RightTuple rightTuple,
Sink sink) {
return new EvalNodeLeftTuple(leftTuple, rightTuple, sink );
}
public LeftTuple createLeftTuple(LeftTuple leftTuple,
RightTuple rightTuple,
LeftTuple currentLeftChild,
LeftTuple currentRightChild,
Sink sink,
boolean leftTupleMemoryEnabled) {
return new EvalNodeLeftTuple(leftTuple, rightTuple, currentLeftChild, currentRightChild, sink, leftTupleMemoryEnabled );
}
public static class ConditionalBranchMemory extends AbstractBaseLinkedListNode<Memory>
implements
Externalizable,
Memory {
private static final long serialVersionUID = 510l;
public Object context;
private SegmentMemory segmentMemory;
public ConditionalBranchMemory() {
}
public ConditionalBranchMemory(final Object context) {
this.context = context;
}
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
context = in.readObject();
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject( context );
}
public short getNodeType() {
return NodeTypeEnums.ConditionalBranchNode;
}
public void setSegmentMemory(SegmentMemory segmentMemory) {
this.segmentMemory = segmentMemory;
}
public SegmentMemory getSegmentMemory() {
return segmentMemory;
}
public void reset() { }
}
@Override
public ObjectTypeNode getObjectTypeNode() {
return getLeftTupleSource().getObjectTypeNode();
}
@Override
public void assertLeftTuple(LeftTuple leftTuple, PropagationContext context, InternalWorkingMemory workingMemory) {
throw new UnsupportedOperationException();
}
@Override
public void retractLeftTuple(LeftTuple leftTuple, PropagationContext context, InternalWorkingMemory workingMemory) {
throw new UnsupportedOperationException();
}
protected boolean doRemove(final RuleRemovalContext context,
final ReteooBuilder builder,
final InternalWorkingMemory[] workingMemories) {
if ( !this.isInUse() ) {
getLeftTupleSource().removeTupleSink( this );
return true;
} else {
throw new RuntimeException("ConditionalBranchNode cannot be shared");
}
}
@Override
protected void initDeclaredMask(BuildContext context, LeftTupleSource leftInput) {
// See LeftTupleSource.initDeclaredMask() should result for the ConditionalBranch to result in ALLSET:
// at the moment if pattern is null (e.g. for eval node) we cannot calculate the mask, so we leave it to 0
//
// In other words, a conditional branch is analogous to an eval() call - mask ALL SET
// To achieve the result, we highjack the call:
super.initDeclaredMask(null, null);
}
}