/** * 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.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import org.drools.RuleBaseConfiguration; import org.drools.common.InternalFactHandle; import org.drools.common.InternalWorkingMemory; import org.drools.common.NodeMemory; import org.drools.common.PropagationContextImpl; import org.drools.common.RuleBasePartitionId; import org.drools.reteoo.builder.BuildContext; import org.drools.rule.ContextEntry; import org.drools.spi.AlphaNodeFieldConstraint; import org.drools.spi.PropagationContext; /** * <code>AlphaNodes</code> are nodes in the <code>Rete</code> network used * to apply <code>FieldConstraint<.code>s on asserted fact * objects where the <code>FieldConstraint</code>s have no dependencies on any other of the facts in the current <code>Rule</code>. * * @see AlphaNodeFieldConstraint * * @author <a href="mailto:mark.proctor@jboss.com">Mark Proctor</a> * @author <a href="mailto:bob@werken.com">Bob McWhirter</a> * */ public class AlphaNode extends ObjectSource implements ObjectSinkNode, NodeMemory { /** * */ private static final long serialVersionUID = 510l; /** The <code>FieldConstraint</code> */ private AlphaNodeFieldConstraint constraint; private ObjectSinkNode previousRightTupleSinkNode; private ObjectSinkNode nextRightTupleSinkNode; public AlphaNode() { } /** * Construct an <code>AlphaNode</code> with a unique id using the provided * <code>FieldConstraint</code> and the given <code>ObjectSource</code>. * Set the boolean flag to true if the node is supposed to have local * memory, or false otherwise. Memory is optional for <code>AlphaNode</code>s * and is only of benefic when adding additional <code>Rule</code>s at runtime. * * @param id Node's ID * @param constraint Node's constraints * @param objectSource Node's object source */ public AlphaNode(final int id, final AlphaNodeFieldConstraint constraint, final ObjectSource objectSource, final BuildContext context) { super( id, context.getPartitionId(), context.getRuleBase().getConfiguration().isMultithreadEvaluation(), objectSource, context.getRuleBase().getConfiguration().getAlphaNodeHashingThreshold() ); this.constraint = constraint; } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { super.readExternal( in ); constraint = (AlphaNodeFieldConstraint) in.readObject(); previousRightTupleSinkNode = (ObjectSinkNode) in.readObject(); nextRightTupleSinkNode = (ObjectSinkNode) in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { super.writeExternal( out ); out.writeObject( constraint ); out.writeObject( previousRightTupleSinkNode ); out.writeObject( nextRightTupleSinkNode ); } /** * Retruns the <code>FieldConstraint</code> * * @return <code>FieldConstraint</code> */ public AlphaNodeFieldConstraint getConstraint() { return this.constraint; } /* * (non-Javadoc) * * @see org.drools.reteoo.BaseNode#attach() */ public void attach() { this.source.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.source.updateSink( this, propagationContext, workingMemory ); } } public void assertObject(final InternalFactHandle factHandle, final PropagationContext context, final InternalWorkingMemory workingMemory) { final AlphaMemory memory = (AlphaMemory) workingMemory.getNodeMemory( this ); if ( this.constraint.isAllowed( factHandle, workingMemory, memory.context ) ) { this.sink.propagateAssertObject( factHandle, context, workingMemory ); } } public void modifyObject(final InternalFactHandle factHandle, final ModifyPreviousTuples modifyPreviousTuples, final PropagationContext context, final InternalWorkingMemory workingMemory) { final AlphaMemory memory = (AlphaMemory) workingMemory.getNodeMemory( this ); if ( this.constraint.isAllowed( factHandle, workingMemory, memory.context ) ) { this.sink.propagateModifyObject( factHandle, modifyPreviousTuples, context, workingMemory ); } } public void updateSink(final ObjectSink sink, final PropagationContext context, final InternalWorkingMemory workingMemory) { final AlphaMemory memory = (AlphaMemory) workingMemory.getNodeMemory( this ); // get the objects from the parent ObjectSinkUpdateAdapter adapter = new ObjectSinkUpdateAdapter( sink, this.constraint, memory.context ); this.source.updateSink( adapter, context, workingMemory ); } /** * Creates a HashSet for the AlphaNode's memory. */ public Object createMemory(final RuleBaseConfiguration config) { AlphaMemory memory = new AlphaMemory(); memory.context = this.constraint.createContextEntry(); return memory; } public String toString() { return "[AlphaNode(" + this.id + ") constraint=" + this.constraint + "]"; } public int hashCode() { return this.source.hashCode() * 17 + ((this.constraint != null) ? this.constraint.hashCode() : 0); } /* * (non-Javadoc) * * @see java.lang.Object#equals(java.lang.Object) */ public boolean equals(final Object object) { if ( this == object ) { return true; } if ( object == null || !(object instanceof AlphaNode) ) { return false; } final AlphaNode other = (AlphaNode) object; return this.source.equals( other.source ) && this.constraint.equals( other.constraint ); } /** * 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 static class AlphaMemory implements Externalizable { private static final long serialVersionUID = 510l; public ContextEntry context; public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { context = (ContextEntry) in.readObject(); } public void writeExternal(ObjectOutput out) throws IOException { out.writeObject( context ); } } /** * Used with the updateSink method, so that the parent ObjectSource * can update the TupleSink * @author mproctor * */ private static class ObjectSinkUpdateAdapter implements ObjectSink { private final ObjectSink sink; private final AlphaNodeFieldConstraint constraint; private final ContextEntry contextEntry; public ObjectSinkUpdateAdapter(final ObjectSink sink, final AlphaNodeFieldConstraint constraint, final ContextEntry contextEntry) { this.sink = sink; this.constraint = constraint; this.contextEntry = contextEntry; } public void assertObject(final InternalFactHandle handle, final PropagationContext propagationContext, final InternalWorkingMemory workingMemory) { if ( this.constraint.isAllowed( handle, workingMemory, this.contextEntry ) ) { this.sink.assertObject( handle, propagationContext, workingMemory ); } } public int getId() { return 0; } public RuleBasePartitionId getPartitionId() { return this.sink.getPartitionId(); } public void writeExternal(ObjectOutput out) throws IOException { // this is a short living adapter class, so no need for serialization } public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { // this is a short living adapter class, so no need for serialization } public void modifyObject(final InternalFactHandle factHandle, final ModifyPreviousTuples modifyPreviousTuples, final PropagationContext context, final InternalWorkingMemory workingMemory) { throw new UnsupportedOperationException( "This method should NEVER EVER be called" ); } } }