/** * Copyright 2005 JBoss Inc * * 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.reteoo; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import org.drools.base.DroolsQuery; import org.drools.common.BaseNode; import org.drools.common.InternalFactHandle; import org.drools.common.InternalWorkingMemory; import org.drools.common.PropagationContextImpl; import org.drools.common.RuleBasePartitionId; import org.drools.reteoo.builder.BuildContext; import org.drools.spi.PropagationContext; /** * All asserting Facts must propagated into the right <code>ObjectSink</code> side of a BetaNode, if this is the first Pattern * then there are no BetaNodes to propagate to. <code>LeftInputAdapter</code> is used to adapt an ObjectSink propagation into a * <code>TupleSource</code> which propagates a <code>ReteTuple</code> suitable fot the right <code>ReteTuple</code> side * of a <code>BetaNode</code>. * * @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a> * @author <a href="mailto:bob@werken.com">Bob McWhirter</a> * */ public class LeftInputAdapterNode extends LeftTupleSource implements ObjectSinkNode { /** * */ private static final long serialVersionUID = 510l; private ObjectSource objectSource; private ObjectSinkNode previousRightTupleSinkNode; private ObjectSinkNode nextRightTupleSinkNode; private boolean leftTupleMemoryEnabled; public LeftInputAdapterNode() { } /** * Constructus a LeftInputAdapterNode with a unique id that receives <code>FactHandle</code> from a * parent <code>ObjectSource</code> and adds it to a given pattern in the resulting Tuples. * * @param id * The unique id of this node in the current Rete network * @param source * The parent node, where Facts are propagated from */ public LeftInputAdapterNode(final int id, final ObjectSource source, final BuildContext context) { super( id, context.getPartitionId(), context.getRuleBase().getConfiguration().isMultithreadEvaluation() ); this.objectSource = source; this.leftTupleMemoryEnabled = context.isTupleMemoryEnabled(); } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal( in ); objectSource = (ObjectSource) in.readObject(); previousRightTupleSinkNode = (ObjectSinkNode) in.readObject(); nextRightTupleSinkNode = (ObjectSinkNode) in.readObject(); leftTupleMemoryEnabled = in.readBoolean(); } public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal( out ); out.writeObject( objectSource ); out.writeObject( previousRightTupleSinkNode ); out.writeObject( nextRightTupleSinkNode ); out.writeBoolean( leftTupleMemoryEnabled ); } /* (non-Javadoc) * @see org.drools.reteoo.BaseNode#attach() */ public void attach() { this.objectSource.addObjectSink( this ); } public void attach(final InternalWorkingMemory[] workingMemories) { attach(); for ( int i = 0, length = workingMemories.length; i < length; i++ ) { final InternalWorkingMemory workingMemory = workingMemories[i]; final PropagationContext propagationContext = new PropagationContextImpl( workingMemory.getNextPropagationIdCounter(), PropagationContext.RULE_ADDITION, null, null, null ); this.objectSource.updateSink( this, propagationContext, workingMemory ); } } public void networkUpdated() { this.objectSource.networkUpdated(); } /** * Takes the asserted <code>FactHandleImpl</code> received from the <code>ObjectSource</code> and puts it * in a new <code>ReteTuple</code> before propagating to the <code>TupleSinks</code> * * @param factHandle * The asserted <code>FactHandle/code>. * @param context * The <code>PropagationContext</code> of the <code>WorkingMemory<code> action. * @param workingMemory * the <code>WorkingMemory</code> session. */ public void assertObject(final InternalFactHandle factHandle, final PropagationContext context, final InternalWorkingMemory workingMemory) { boolean useLeftMemory = true; if ( !this.leftTupleMemoryEnabled ) { // This is a hack, to not add closed DroolsQuery objects Object object = ((InternalFactHandle)context.getFactHandle()).getObject(); if ( object instanceof DroolsQuery && !((DroolsQuery)object).isOpen() ) { useLeftMemory = false; } } if ( !workingMemory.isSequential() ) { this.sink.createAndPropagateAssertLeftTuple( factHandle, context, workingMemory, useLeftMemory ); } else { workingMemory.addLIANodePropagation( new LIANodePropagation( this, factHandle, context ) ); } } public void modifyObject(InternalFactHandle factHandle, final ModifyPreviousTuples modifyPreviousTuples, PropagationContext context, InternalWorkingMemory workingMemory) { this.sink.propagateModifyObject( factHandle, modifyPreviousTuples, context, workingMemory ); } public void updateSink(final LeftTupleSink sink, final PropagationContext context, final InternalWorkingMemory workingMemory) { final RightTupleSinkAdapter adapter = new RightTupleSinkAdapter( sink, true ); this.objectSource.updateSink( adapter, context, workingMemory ); } protected void doRemove(final RuleRemovalContext context, final ReteooBuilder builder, final BaseNode node, final InternalWorkingMemory[] workingMemories) { if ( !node.isInUse() ) { removeTupleSink( (LeftTupleSink) node ); } this.objectSource.remove( context, builder, this, workingMemories ); } /** * Returns the next node * @return * The next ObjectSinkNode */ public ObjectSinkNode getNextObjectSinkNode() { return this.nextRightTupleSinkNode; } /** * Sets the next node * @param next * The next ObjectSinkNode */ public void setNextObjectSinkNode(final ObjectSinkNode next) { this.nextRightTupleSinkNode = next; } /** * Returns the previous node * @return * The previous ObjectSinkNode */ public ObjectSinkNode getPreviousObjectSinkNode() { return this.previousRightTupleSinkNode; } /** * Sets the previous node * @param previous * The previous ObjectSinkNode */ public void setPreviousObjectSinkNode(final ObjectSinkNode previous) { this.previousRightTupleSinkNode = previous; } public int hashCode() { return this.objectSource.hashCode(); } public boolean equals(final Object object) { if ( object == this ) { return true; } if ( object == null || !(object instanceof LeftInputAdapterNode) ) { return false; } final LeftInputAdapterNode other = (LeftInputAdapterNode) object; return this.objectSource.equals( other.objectSource ); } /** * Used with the updateSink method, so that the parent ObjectSource * can update the TupleSink * @author mproctor * */ private static class RightTupleSinkAdapter implements ObjectSink { private LeftTupleSink sink; private boolean leftTupleMemoryEnabled; public RightTupleSinkAdapter(final LeftTupleSink sink, boolean leftTupleMemoryEnabled) { this.sink = sink; this.leftTupleMemoryEnabled = leftTupleMemoryEnabled; } public void assertObject(final InternalFactHandle factHandle, final PropagationContext context, final InternalWorkingMemory workingMemory) { final LeftTuple tuple = new LeftTuple( factHandle, this.sink, this.leftTupleMemoryEnabled ); this.sink.assertLeftTuple( tuple, context, workingMemory ); } public void modifyObject(InternalFactHandle factHandle, ModifyPreviousTuples modifyPreviousTuples, PropagationContext context, InternalWorkingMemory workingMemory) { throw new UnsupportedOperationException( "ObjectSinkAdapter onlys supports assertObject method calls" ); } public int getId() { return 0; } public RuleBasePartitionId getPartitionId() { return sink.getPartitionId(); } public void writeExternal(ObjectOutput out) throws IOException { // this is a short living adapter class used only during an update operation, and // as so, no need for serialization code } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // this is a short living adapter class used only during an update operation, and // as so, no need for serialization code } } }