/*
* 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 java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.Collection;
import java.util.List;
import org.drools.core.base.ClassObjectType;
import org.drools.core.common.BaseNode;
import org.drools.core.common.RuleBasePartitionId;
import org.drools.core.reteoo.builder.BuildContext;
import org.drools.core.rule.Pattern;
import org.drools.core.spi.ClassWireable;
import org.drools.core.spi.ObjectType;
import org.drools.core.util.bitmask.AllSetBitMask;
import org.drools.core.util.bitmask.BitMask;
import org.drools.core.util.bitmask.EmptyBitMask;
import static org.drools.core.reteoo.PropertySpecificUtil.*;
/**
* A source of <code>ReteTuple</code> s for a <code>TupleSink</code>.
*
* <p>
* Nodes that propagate <code>Tuples</code> extend this class.
* </p>
*
* @see LeftTupleSource
* @see LeftTuple
*/
public abstract class LeftTupleSource extends BaseNode
implements
LeftTupleNode,
Externalizable {
private BitMask leftDeclaredMask = EmptyBitMask.get();
private BitMask leftInferredMask = EmptyBitMask.get();
private BitMask leftNegativeMask = EmptyBitMask.get();
/** The left input <code>TupleSource</code>. */
protected LeftTupleSource leftInput;
// ------------------------------------------------------------
// Instance members
// ------------------------------------------------------------
/** The destination for <code>Tuples</code>. */
protected LeftTupleSinkPropagator sink;
private transient ObjectTypeNode.Id leftInputOtnId;
private int positionInPath;
// ------------------------------------------------------------
// Constructors
// ------------------------------------------------------------
public LeftTupleSource() {
}
/**
* Single parameter constructor that specifies the unique id of the node.
*
* @param id
*/
protected LeftTupleSource(int id, BuildContext context) {
super(id,
context != null ? context.getPartitionId() : RuleBasePartitionId.MAIN_PARTITION,
context != null && context.getKnowledgeBase().getConfiguration().isMultithreadEvaluation());
this.sink = EmptyLeftTupleSinkAdapter.getInstance();
initMemoryId( context );
}
// ------------------------------------------------------------
// Instance methods
// ------------------------------------------------------------
public void readExternal(ObjectInput in) throws IOException,
ClassNotFoundException {
super.readExternal( in );
sink = (LeftTupleSinkPropagator) in.readObject();
leftDeclaredMask = (BitMask) in.readObject();
leftInferredMask = (BitMask) in.readObject();
leftNegativeMask = (BitMask) in.readObject();
positionInPath = in.readInt();
}
public void writeExternal(ObjectOutput out) throws IOException {
super.writeExternal( out );
out.writeObject( sink );
out.writeObject(leftDeclaredMask);
out.writeObject(leftInferredMask);
out.writeObject(leftNegativeMask);
out.writeInt( positionInPath );
}
public int getPositionInPath() {
return positionInPath;
}
public abstract short getType();
public abstract LeftTuple createPeer(LeftTuple original);
public void addTupleSink(final LeftTupleSink tupleSink) {
addTupleSink(tupleSink, null);
}
public LeftTupleSource getLeftTupleSource() {
return leftInput;
}
public final void setLeftTupleSource(LeftTupleSource leftInput) {
this.leftInput = leftInput;
positionInPath = leftInput.getPositionInPath() + 1;
}
/**
* Adds the <code>TupleSink</code> so that it may receive
* <code>Tuples</code> propagated from this <code>TupleSource</code>.
*
* @param tupleSink
* The <code>TupleSink</code> to receive propagated
* <code>Tuples</code>.
*/
public void addTupleSink(final LeftTupleSink tupleSink, final BuildContext context) {
this.sink = addTupleSink(this.sink, tupleSink, context);
}
protected LeftTupleSinkPropagator addTupleSink(final LeftTupleSinkPropagator sinkPropagator, final LeftTupleSink tupleSink, final BuildContext context) {
if ( sinkPropagator instanceof EmptyLeftTupleSinkAdapter ) {
// otherwise, we use the lighter synchronous propagator
return new SingleLeftTupleSinkAdapter( this.getPartitionId(), tupleSink );
}
if ( sinkPropagator instanceof SingleLeftTupleSinkAdapter ) {
CompositeLeftTupleSinkAdapter sinkAdapter = new CompositeLeftTupleSinkAdapter( this.getPartitionId() );
sinkAdapter.addTupleSink( sinkPropagator.getSinks()[0] );
sinkAdapter.addTupleSink( tupleSink );
return sinkAdapter;
}
((CompositeLeftTupleSinkAdapter) sinkPropagator).addTupleSink( tupleSink );
return sinkPropagator;
}
/**
* Removes the <code>TupleSink</code>
*
* @param tupleSink
* The <code>TupleSink</code> to remove
*/
public void removeTupleSink(final LeftTupleSink tupleSink) {
if ( this.sink instanceof EmptyLeftTupleSinkAdapter ) {
throw new IllegalArgumentException( "Cannot remove a sink, when the list of sinks is null" );
}
if ( this.sink instanceof SingleLeftTupleSinkAdapter ) {
this.sink = EmptyLeftTupleSinkAdapter.getInstance();
} else {
final CompositeLeftTupleSinkAdapter sinkAdapter = (CompositeLeftTupleSinkAdapter) this.sink;
sinkAdapter.removeTupleSink( tupleSink );
if ( sinkAdapter.size() == 1 ) {
this.sink = new SingleLeftTupleSinkAdapter( this.getPartitionId(), sinkAdapter.getSinks()[0] );
}
}
}
public LeftTupleSinkPropagator getSinkPropagator() {
return this.sink;
}
public void setSourcePartitionId(BuildContext context, RuleBasePartitionId partitionId) {
setSourcePartitionId(leftInput, context, partitionId);
}
protected void setSourcePartitionId(BaseNode source, BuildContext context, RuleBasePartitionId partitionId) {
if (this.partitionId == partitionId) {
return;
}
this.partitionId = partitionId;
if (source.getPartitionId() == RuleBasePartitionId.MAIN_PARTITION) {
setPartitionIdWithSinks( partitionId );
} else {
source.setPartitionId( context, partitionId );
}
}
public final void setPartitionIdWithSinks( RuleBasePartitionId partitionId ) {
this.partitionId = partitionId;
for (LeftTupleSink sink : getSinkPropagator().getSinks()) {
sink.setPartitionIdWithSinks( partitionId );
}
}
public boolean isInUse() {
return this.sink.size() > 0;
}
protected final void initMasks(BuildContext context,
LeftTupleSource leftInput) {
initDeclaredMask( context, leftInput );
initInferredMask( leftInput );
}
protected void initDeclaredMask(BuildContext context,
LeftTupleSource leftInput) {
if ( context == null || context.getLastBuiltPatterns() == null ) {
// only happens during unit tests
leftDeclaredMask = AllSetBitMask.get();
return;
}
if ( leftInput.getType() != NodeTypeEnums.LeftInputAdapterNode) {
// BetaNode's not after LIANode are not relevant for left mask property specific, so don't block anything.
leftDeclaredMask = AllSetBitMask.get();
return;
}
Pattern pattern = context.getLastBuiltPatterns()[1]; // left input pattern
ObjectType objectType = pattern == null || this.getType() == NodeTypeEnums.AccumulateNode ?
((LeftInputAdapterNode)leftInput).getParentObjectSource().getObjectTypeNode().getObjectType() :
pattern.getObjectType();
if ( !(objectType instanceof ClassObjectType) ) {
// Only ClassObjectType can use property specific
leftDeclaredMask = AllSetBitMask.get();
return;
}
Class objectClass = ((ClassWireable) objectType).getClassType();
// if pattern is null (e.g. for eval or query nodes) we cannot calculate the mask, so we set it all
if ( pattern != null && isPropertyReactive(context, objectClass) ) {
Collection<String> leftListenedProperties = pattern.getListenedProperties();
List<String> settableProperties = getAccessibleProperties( context.getKnowledgeBase(), objectClass );
leftDeclaredMask = calculatePositiveMask( leftListenedProperties, settableProperties );
leftNegativeMask = calculateNegativeMask( leftListenedProperties, settableProperties );
setLeftListenedProperties(leftListenedProperties);
} else {
// if property specific is not on, then accept all modification propagations
leftDeclaredMask = AllSetBitMask.get();
}
}
protected void setLeftListenedProperties(Collection<String> leftListenedProperties) { }
protected void initInferredMask(LeftTupleSource leftInput) {
LeftTupleSource unwrappedLeft = unwrapLeftInput(leftInput);
if ( unwrappedLeft.getType() == NodeTypeEnums.LeftInputAdapterNode && ((LeftInputAdapterNode)unwrappedLeft).getParentObjectSource().getType() == NodeTypeEnums.AlphaNode ) {
AlphaNode alphaNode = (AlphaNode) ((LeftInputAdapterNode)unwrappedLeft).getParentObjectSource();
leftInferredMask = alphaNode.updateMask( leftDeclaredMask );
} else {
leftInferredMask = leftDeclaredMask;
}
leftInferredMask = leftInferredMask.resetAll(leftNegativeMask);
}
private LeftTupleSource unwrapLeftInput(LeftTupleSource leftInput) {
if (leftInput.getType() == NodeTypeEnums.FromNode || leftInput.getType() == NodeTypeEnums.ReactiveFromNode) {
return leftInput.getLeftTupleSource();
}
return leftInput;
}
public BitMask getLeftDeclaredMask() {
return leftDeclaredMask;
}
public BitMask getLeftInferredMask() {
return leftInferredMask;
}
protected void setLeftInferredMask(BitMask leftInferredMask) {
this.leftInferredMask = leftInferredMask;
}
public BitMask getLeftNegativeMask() {
return leftNegativeMask;
}
public ObjectTypeNode.Id getLeftInputOtnId() {
return leftInputOtnId;
}
public void setLeftInputOtnId(ObjectTypeNode.Id leftInputOtnId) {
this.leftInputOtnId = leftInputOtnId;
}
public ObjectType getObjectType() {
ObjectTypeNode objectTypeNode = getObjectTypeNode();
return objectTypeNode != null ? objectTypeNode.getObjectType() : null;
}
public abstract boolean isLeftTupleMemoryEnabled();
}