/**
* Copyright 2010 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.util.Arrays;
import org.drools.common.InternalFactHandle;
import org.drools.core.util.Entry;
import org.drools.core.util.LeftTupleList;
import org.drools.rule.Declaration;
import org.drools.spi.Tuple;
public class LeftTuple
implements
Tuple,
Entry {
private static final long serialVersionUID = 510l;
private int index;
private InternalFactHandle handle;
private LeftTuple parent;
private Object object;
private RightTuple blocker;
private LeftTuple blockedPrevious;
private LeftTuple blockedNext;
// left and right tuples in parent
private LeftTuple leftParent;
private LeftTuple leftParentPrevious;
private LeftTuple leftParentNext;
private RightTuple rightParent;
private LeftTuple rightParentPrevious;
private LeftTuple rightParentNext;
// node memory
private LeftTupleList memory;
private Entry next;
private Entry previous;
// children
public LeftTuple firstChild;
public LeftTuple lastChild;
private LeftTupleSink sink;
public LeftTuple() {
// constructor needed for serialisation
}
// ------------------------------------------------------------
// Constructors
// ------------------------------------------------------------
public LeftTuple(final InternalFactHandle factHandle,
LeftTupleSink sink,
boolean leftTupleMemoryEnabled) {
this.handle = factHandle;
if ( leftTupleMemoryEnabled ) {
LeftTuple first = handle.getLastLeftTuple();
if ( first == null ) {
// node other LeftTuples, just add.
handle.setFirstLeftTuple( this );
handle.setLastLeftTuple( this );
} else {
this.leftParentPrevious = handle.getLastLeftTuple();
this.leftParentPrevious.leftParentNext = this;
handle.setLastLeftTuple( this );
}
}
this.sink = sink;
}
public LeftTuple(final LeftTuple leftTuple,
LeftTupleSink sink,
boolean leftTupleMemoryEnabled) {
this.index = leftTuple.index;
this.parent = leftTuple.parent;
this.handle = leftTuple.handle;
if ( leftTupleMemoryEnabled ) {
this.leftParent = leftTuple;
if ( leftTuple.lastChild != null ) {
this.leftParentPrevious = leftTuple.lastChild;
this.leftParentPrevious.leftParentNext = this;
} else {
leftTuple.firstChild = this;
}
leftTuple.lastChild = this;
}
this.sink = sink;
}
public LeftTuple(final LeftTuple leftTuple,
final RightTuple rightTuple,
final LeftTupleSink sink,
final boolean leftTupleMemoryEnabled) {
this( leftTuple,
rightTuple,
null,
null,
sink,
leftTupleMemoryEnabled );
}
public LeftTuple(final LeftTuple leftTuple,
final RightTuple rightTuple,
final LeftTuple currentLeftChild,
final LeftTuple currentRightChild,
final LeftTupleSink sink,
final boolean leftTupleMemoryEnabled) {
this.handle = rightTuple.getFactHandle();
this.index = leftTuple.index + 1;
this.parent = leftTuple;
if ( leftTupleMemoryEnabled ) {
this.leftParent = leftTuple;
this.rightParent = rightTuple;
if( currentLeftChild == null ) {
// insert at the end of the list
if ( leftTuple.lastChild != null ) {
this.leftParentPrevious = leftTuple.lastChild;
this.leftParentPrevious.leftParentNext = this;
} else {
leftTuple.firstChild = this;
}
leftTuple.lastChild = this;
} else {
// insert before current child
this.leftParentNext = currentLeftChild;
this.leftParentPrevious = currentLeftChild.leftParentPrevious;
currentLeftChild.leftParentPrevious = this;
if( this.leftParentPrevious == null ) {
this.leftParent.firstChild = this;
} else {
this.leftParentPrevious.leftParentNext = this;
}
}
if( currentRightChild == null ) {
// insert at the end of the list
if ( rightTuple.lastChild != null ) {
this.rightParentPrevious = rightTuple.lastChild;
this.rightParentPrevious.rightParentNext = this;
} else {
rightTuple.firstChild = this;
}
rightTuple.lastChild = this;
} else {
// insert before current child
this.rightParentNext = currentRightChild;
this.rightParentPrevious = currentRightChild.rightParentPrevious;
currentRightChild.rightParentPrevious = this;
if( this.rightParentPrevious == null ) {
this.rightParent.firstChild = this;
} else {
this.rightParentPrevious.rightParentNext = this;
}
}
}
this.sink = sink;
}
public void reAdd() {
LeftTuple first = handle.getLastLeftTuple();
if ( first == null ) {
// node other LeftTuples, just add.
handle.setFirstLeftTuple( this );
handle.setLastLeftTuple( this );
} else {
handle.getLastLeftTuple().leftParentNext = this;
this.leftParentPrevious = handle.getLastLeftTuple();
handle.setLastLeftTuple( this );
}
}
public void reAddLeft() {
// The parent can never be the FactHandle (root LeftTuple) as that is handled by reAdd()
// make sure we aren't already at the end
if ( this.leftParentNext != null ) {
if ( this.leftParentPrevious != null ) {
// remove the current LeftTuple from the middle of the chain
this.leftParentPrevious.leftParentNext = this.leftParentNext;
this.leftParentNext.leftParentPrevious = this.leftParentPrevious;
} else {
if( this.leftParent.firstChild == this ) {
// remove the current LeftTuple from start start of the chain
this.leftParent.firstChild = this.leftParentNext;
}
this.leftParentNext.leftParentPrevious = null;
}
// re-add to end
this.leftParentPrevious = this.leftParent.lastChild;
this.leftParentPrevious.leftParentNext = this;
this.leftParent.lastChild = this;
this.leftParentNext = null;
}
}
public void reAddRight() {
// make sure we aren't already at the end
if ( this.rightParentNext != null ) {
if ( this.rightParentPrevious != null ) {
// remove the current LeftTuple from the middle of the chain
this.rightParentPrevious.rightParentNext = this.rightParentNext;
this.rightParentNext.rightParentPrevious = this.rightParentPrevious;
} else {
if( this.rightParent.firstChild == this ) {
// remove the current LeftTuple from the start of the chain
this.rightParent.firstChild = this.rightParentNext;
}
this.rightParentNext.rightParentPrevious = null;
}
// re-add to end
this.rightParentPrevious = this.rightParent.lastChild;
this.rightParentPrevious.rightParentNext = this;
this.rightParent.lastChild = this;
this.rightParentNext = null;
}
}
public void unlinkFromLeftParent() {
LeftTuple previousParent = this.leftParentPrevious;
LeftTuple nextParent = this.leftParentNext;
if ( previousParent != null && nextParent != null ) {
//remove from middle
this.leftParentPrevious.leftParentNext = nextParent;
this.leftParentNext.leftParentPrevious = previousParent;
} else if ( nextParent != null ) {
//remove from first
if ( this.leftParent != null ) {
this.leftParent.firstChild = nextParent;
} else {
// This is relevant to the root node and only happens at rule removal time
this.handle.setFirstLeftTuple( nextParent );
}
nextParent.leftParentPrevious = null;
} else if ( previousParent != null ) {
//remove from end
if ( this.leftParent != null ) {
this.leftParent.lastChild = previousParent;
} else {
// relevant to the root node, as here the parent is the FactHandle, only happens at rule removal time
this.handle.setLastLeftTuple( previousParent );
}
previousParent.leftParentNext = null;
} else {
// single remaining item, no previous or next
if( leftParent != null ) {
this.leftParent.firstChild = null;
this.leftParent.lastChild = null;
} else {
// it is a root tuple - only happens during rule removal
this.handle.setFirstLeftTuple( null );
this.handle.setLastLeftTuple( null );
}
}
this.leftParent = null;
this.leftParentPrevious = null;
this.leftParentNext = null;
//
this.blocker = null;
}
public void unlinkFromRightParent() {
if ( this.rightParent == null ) {
// no right parent;
return;
}
LeftTuple previousParent = this.rightParentPrevious;
LeftTuple nextParent = this.rightParentNext;
if ( previousParent != null && nextParent != null ) {
// remove from middle
this.rightParentPrevious.rightParentNext = this.rightParentNext;
this.rightParentNext.rightParentPrevious = this.rightParentPrevious;
} else if ( nextParent != null ) {
// remove from the start
this.rightParent.firstChild = nextParent;
nextParent.rightParentPrevious = null;
} else if ( previousParent != null ) {
// remove from end
this.rightParent.lastChild = previousParent;
previousParent.rightParentNext = null;
} else {
// single remaining item, no previous or next
this.rightParent.firstChild = null;
this.rightParent.lastChild = null;
}
this.blocker = null;
this.rightParent = null;
this.rightParentPrevious = null;
this.rightParentNext = null;
}
public int getIndex() {
return this.index;
}
public LeftTupleSink getLeftTupleSink() {
return sink;
}
/* Had to add the set method because sink adapters must override
* the tuple sink set when the tuple was created.
*/
public void setLeftTupleSink( LeftTupleSink sink ) {
this.sink = sink;
}
public LeftTuple getLeftParent() {
return leftParent;
}
public void setLeftParent(LeftTuple leftParent) {
this.leftParent = leftParent;
}
public LeftTuple getLeftParentPrevious() {
return leftParentPrevious;
}
public void setLeftParentPrevious(LeftTuple leftParentLeft) {
this.leftParentPrevious = leftParentLeft;
}
public LeftTuple getLeftParentNext() {
return leftParentNext;
}
public void setLeftParentNext(LeftTuple leftParentright) {
this.leftParentNext = leftParentright;
}
public RightTuple getRightParent() {
return rightParent;
}
public void setRightParent(RightTuple rightParent) {
this.rightParent = rightParent;
}
public LeftTuple getRightParentPrevious() {
return rightParentPrevious;
}
public void setRightParentPrevious(LeftTuple rightParentLeft) {
this.rightParentPrevious = rightParentLeft;
}
public LeftTuple getRightParentNext() {
return rightParentNext;
}
public void setRightParentNext(LeftTuple rightParentRight) {
this.rightParentNext = rightParentRight;
}
// public void setBetaChildren(LeftTuple leftTuple) {
// this.firstChild = leftTuple;
// }
//
// public LeftTuple getBetaChildren() {
// return this.firstChild;
// }
public InternalFactHandle get(final int index) {
LeftTuple entry = this;
while ( entry.index != index ) {
entry = entry.parent;
}
return entry.handle;
}
public LeftTupleList getMemory() {
return this.memory;
}
public void setMemory(LeftTupleList memory) {
this.memory = memory;
}
public Entry getPrevious() {
return previous;
}
public void setPrevious(Entry previous) {
this.previous = previous;
}
public void setNext(final Entry next) {
this.next = next;
}
public Entry getNext() {
return this.next;
}
public InternalFactHandle getLastHandle() {
return this.handle;
}
public InternalFactHandle get(final Declaration declaration) {
return get( declaration.getPattern().getOffset() );
}
/**
* Returns the fact handles in reverse order
*/
public InternalFactHandle[] getFactHandles() {
InternalFactHandle[] handles = new InternalFactHandle[this.index + 1];
LeftTuple entry = this;
int i = 0;
while ( entry != null ) {
handles[i++] = entry.handle;
entry = entry.parent;
}
return handles;
}
public InternalFactHandle[] toFactHandles() {
InternalFactHandle[] handles = new InternalFactHandle[this.index + 1];
LeftTuple entry = this;
while ( entry != null ) {
handles[entry.index] = entry.handle;
entry = entry.parent;
}
return handles;
}
public void setBlocker(RightTuple blocker) {
this.blocker = blocker;
}
public RightTuple getBlocker() {
return this.blocker;
}
public LeftTuple getBlockedPrevious() {
return this.blockedPrevious;
}
public void setBlockedPrevious(LeftTuple blockerPrevious) {
this.blockedPrevious = blockerPrevious;
}
public LeftTuple getBlockedNext() {
return this.blockedNext;
}
public void setBlockedNext(LeftTuple blockerNext) {
this.blockedNext = blockerNext;
}
public Object getObject() {
return this.object;
}
public void setObject(final Object object) {
this.object = object;
}
// public int hashCode() {
// return this.hashCode;
// }
public String toString() {
final StringBuilder buffer = new StringBuilder();
LeftTuple entry = this;
while ( entry != null ) {
//buffer.append( entry.handle );
buffer.append(entry.handle).append("\n");
entry = entry.parent;
}
return buffer.toString();
}
public int hashCode() {
return this.handle.hashCode();
}
/**
* We use this equals method to avoid the cast
* @param tuple
* @return
*/
public boolean equals(final LeftTuple other) {
// we know the object is never null and always of the type LeftTuple
if ( other == this ) {
return true;
} else if( other == null ) {
return false;
}
// A LeftTuple is only the same if it has the same hashCode, factId and parent
if ( this.hashCode() != other.hashCode() ) {
return false;
}
if ( this.handle != other.handle ) {
return false;
}
// if ( this.sink.getId() != other.sink.getId() ) {
// return false;
// }
//
// if ( this.index != other.index ) {
// return false;
// }
if ( this.parent == null ) {
return (other.parent == null);
} else {
return this.parent.equals( other.parent );
}
}
public boolean equals(final Object object) {
// we know the object is never null and always of the type ReteTuple
return equals( (LeftTuple) object );
}
public int size() {
return this.index + 1;
}
/**
* Returns the ReteTuple that contains the "elements"
* first elements in this tuple.
*
* Use carefully as no cloning is made during this process.
*
* This method is used by TupleStartEqualsConstraint when
* joining a subnetwork tuple into the main network tuple;
*
* @param elements the number of elements to return, starting from
* the begining of the tuple
*
* @return a ReteTuple containing the "elements" first elements
* of this tuple or null if "elements" is greater than size;
*/
public LeftTuple getSubTuple(final int elements) {
LeftTuple entry = this;
if ( elements < this.size() ) {
final int lastindex = elements - 1;
while ( entry.index != lastindex ) {
entry = entry.parent;
}
}
return entry;
}
public Object[] toObjectArray() {
Object[] objects = new Object[this.index + 1];
LeftTuple entry = this;
while ( entry != null ) {
Object object = entry.getLastHandle().getObject();
objects[entry.index] = object;
entry = entry.parent;
}
return objects;
}
public LeftTuple getParent() {
return parent;
}
public String toTupleTree(int indent) {
StringBuilder buf = new StringBuilder();
char[] spaces = new char[indent];
Arrays.fill( spaces, ' ' );
String istr = new String( spaces );
buf.append( istr );
buf.append( this.toExternalString() );
buf.append( "\n" );
for( LeftTuple leftTuple = this.firstChild; leftTuple != null; leftTuple = leftTuple.getLeftParentNext() ) {
buf.append( leftTuple.toTupleTree( indent+4 ) );
}
return buf.toString();
}
private String toExternalString() {
StringBuilder builder = new StringBuilder();
builder.append( String.format( "%08X", System.identityHashCode( this ) ) ).append( ":" );
int[] ids = new int[this.index+1];
LeftTuple entry = this;
while( entry != null ) {
ids[entry.index] = entry.getLastHandle().getId();
entry = entry.parent;
}
builder.append( Arrays.toString( ids ) )
.append( " activation=" )
.append( this.object != null ? this.object : "null" )
.append( " sink=" )
.append( this.sink.getClass().getSimpleName() )
.append( "(" ).append( sink.getId() ).append( ")" );
return builder.toString();
}
}