[jboss-svn-commits] JBL Code SVN: r31026 - in labs/jbossrules/branches/true_modify_20100104/drools-core/src: test/resources/org/drools/reteoo/test and 1 other directory.
jboss-svn-commits at lists.jboss.org
jboss-svn-commits at lists.jboss.org
Mon Jan 11 21:27:42 EST 2010
Author: tirelli
Date: 2010-01-11 21:27:42 -0500 (Mon, 11 Jan 2010)
New Revision: 31026
Modified:
labs/jbossrules/branches/true_modify_20100104/drools-core/src/main/java/org/drools/reteoo/CollectNode.java
labs/jbossrules/branches/true_modify_20100104/drools-core/src/test/resources/org/drools/reteoo/test/CollectNodeAssertRetractTest.data
Log:
JBRULES-2340: implemeting proper collect node modify method
Modified: labs/jbossrules/branches/true_modify_20100104/drools-core/src/main/java/org/drools/reteoo/CollectNode.java
===================================================================
--- labs/jbossrules/branches/true_modify_20100104/drools-core/src/main/java/org/drools/reteoo/CollectNode.java 2010-01-11 21:58:14 UTC (rev 31025)
+++ labs/jbossrules/branches/true_modify_20100104/drools-core/src/main/java/org/drools/reteoo/CollectNode.java 2010-01-12 02:27:42 UTC (rev 31026)
@@ -40,7 +40,6 @@
import org.drools.spi.BetaNodeFieldConstraint;
import org.drools.spi.PropagationContext;
import org.drools.util.ArrayUtils;
-import org.drools.util.Entry;
import org.drools.util.Iterator;
import org.drools.util.ObjectHashMap.ObjectEntry;
@@ -179,19 +178,9 @@
InternalFactHandle handle = rightTuple.getFactHandle();
if ( this.constraints.isAllowedCachedLeft( memory.betaMemory.getContext(),
handle ) ) {
- if ( this.unwrapRightObject ) {
- handle = ((LeftTuple) handle.getObject()).getLastHandle();
- }
- result.add( handle.getObject() );
-
- // in sequential mode, we don't need to keep record of matched tuples
- if ( this.tupleMemoryEnabled ) {
- // linking left and right by creating a new left tuple
- new LeftTuple( leftTuple,
- rightTuple,
- this,
- this.tupleMemoryEnabled );
- }
+ addMatch( leftTuple,
+ rightTuple,
+ colctx );
}
}
@@ -207,6 +196,9 @@
/**
* @inheritDoc
+ *
+ * When retracting the left tuple, clear all matches from the right tuples
+ * and if previously propagated as an assert, propagate a retract
*/
public void retractLeftTuple(final LeftTuple leftTuple,
final PropagationContext context,
@@ -216,17 +208,9 @@
memory.betaMemory.getLeftTupleMemory().remove( leftTuple );
final CollectContext colctx = (CollectContext) memory.betaMemory.getCreatedHandles().remove( leftTuple );
- LeftTuple child = getFirstMatch( leftTuple,
- colctx );
+ removePreviousMatchesForLeftTuple( leftTuple,
+ colctx );
- // Now, unlink the matches
- while ( child != null ) {
- LeftTuple tmp = child.getLeftParentNext();
- child.unlinkFromLeftParent();
- child.unlinkFromRightParent();
- child = tmp;
- }
-
if ( colctx.propagated ) {
// if tuple was previously propagated, retract it
this.sink.propagateRetractLeftTupleDestroyRightTuple( leftTuple,
@@ -241,7 +225,7 @@
* When a new object is asserted into a CollectNode, do this:
*
* 1. Select all matching tuples from left memory
- * 2. For each matching tuple, call a modify tuple
+ * 2. For each matching tuple, add the new match and evaluate the result constraints
*
*/
public void assertObject(final InternalFactHandle factHandle,
@@ -271,21 +255,19 @@
workingMemory,
factHandle );
- // need to clone the tuples to avoid concurrent modification exceptions
- // @TODO: now that we use linked lists, can we avoid the cloning?
- Entry[] tuples = memory.betaMemory.getLeftTupleMemory().toArray();
- for ( int i = 0; i < tuples.length; i++ ) {
- LeftTuple tuple = (LeftTuple) tuples[i];
+ for ( LeftTuple leftTuple = memory.betaMemory.getLeftTupleMemory().getFirst( rightTuple ); leftTuple != null; leftTuple = (LeftTuple) leftTuple.getNext() ) {
if ( this.constraints.isAllowedCachedRight( memory.betaMemory.getContext(),
- tuple ) ) {
- modifyTuple( ActivitySource.RIGHT,
- true,
- tuple,
- rightTuple,
- null,
- context,
- workingMemory,
- memory );
+ leftTuple ) ) {
+ final CollectContext colctx = (CollectContext) memory.betaMemory.getCreatedHandles().get( leftTuple );
+ addMatch( leftTuple,
+ rightTuple,
+ colctx );
+ evaluateResultConstraints( ActivitySource.RIGHT,
+ leftTuple,
+ context,
+ workingMemory,
+ memory,
+ colctx );
}
}
@@ -314,18 +296,11 @@
workingMemory );
memory.betaMemory.getRightTupleMemory().remove( rightTuple );
- for ( LeftTuple child = rightTuple.firstChild; child != null; ) {
- LeftTuple tmp = child.getRightParentNext();
- this.modifyTuple( ActivitySource.RIGHT,
- false,
- child.getParent(),
- rightTuple,
- child,
- context,
- workingMemory,
- memory );
- child = tmp;
- }
+ removePreviousMatchesForRightTuple( rightTuple,
+ context,
+ workingMemory,
+ memory,
+ rightTuple.firstChild );
if ( context.getType() == PropagationContext.EXPIRATION ) {
((PropagationContextImpl) context).setFactHandle( origin );
@@ -336,76 +311,185 @@
public void modifyLeftTuple(LeftTuple leftTuple,
PropagationContext context,
InternalWorkingMemory workingMemory) {
+ final CollectMemory memory = (CollectMemory) workingMemory.getNodeMemory( this );
+ final CollectContext colctx = (CollectContext) memory.betaMemory.getCreatedHandles().get( leftTuple );
+ // Add and remove to make sure we are in the right bucket and at the end
+ // this is needed to fix for indexing and deterministic iteration
+ memory.betaMemory.getLeftTupleMemory().remove( leftTuple );
+ memory.betaMemory.getLeftTupleMemory().add( leftTuple );
+
+ this.constraints.updateFromTuple( memory.betaMemory.getContext(),
+ workingMemory,
+ leftTuple );
+ LeftTuple childLeftTuple = leftTuple.firstChild;
+
+ RightTupleMemory rightMemory = memory.betaMemory.getRightTupleMemory();
+
+ RightTuple rightTuple = rightMemory.getFirst( leftTuple );
+
+ // first check our index (for indexed nodes only) hasn't changed and we are returning the same bucket
+ if ( childLeftTuple != null && rightMemory.isIndexed() && rightTuple != rightMemory.getFirst( childLeftTuple.getRightParent() ) ) {
+ // our index has changed, so delete all the previous matchings
+
+ removePreviousMatchesForLeftTuple( leftTuple,
+ colctx );
+
+ childLeftTuple = null; // null so the next check will attempt matches for new bucket
+ }
+
+ // we can't do anything if RightTupleMemory is empty
+ if ( rightTuple != null ) {
+ if ( childLeftTuple == null ) {
+ // either we are indexed and changed buckets or
+ // we had no children before, but there is a bucket to potentially match, so try as normal assert
+ for ( ; rightTuple != null; rightTuple = (RightTuple) rightTuple.getNext() ) {
+ final InternalFactHandle handle = rightTuple.getFactHandle();
+ if ( this.constraints.isAllowedCachedLeft( memory.betaMemory.getContext(),
+ handle ) ) {
+ // add a new match
+ addMatch( leftTuple,
+ rightTuple,
+ colctx );
+
+ }
+ }
+ } else {
+ // in the same bucket, so iterate and compare
+ for ( ; rightTuple != null; rightTuple = (RightTuple) rightTuple.getNext() ) {
+ final InternalFactHandle handle = rightTuple.getFactHandle();
+
+ if ( this.constraints.isAllowedCachedLeft( memory.betaMemory.getContext(),
+ handle ) ) {
+ if ( childLeftTuple != null && childLeftTuple.getRightParent() != rightTuple ) {
+ // add a new match
+ addMatch( leftTuple,
+ rightTuple,
+ colctx );
+ } else {
+ // we must re-add this to ensure deterministic iteration
+ childLeftTuple.reAddLeft();
+ }
+ } else if ( childLeftTuple != null && childLeftTuple.getRightParent() == rightTuple ) {
+ // remove the match
+ removeMatch( rightTuple,
+ childLeftTuple,
+ colctx );
+ }
+ // else do nothing, was false before and false now.
+ }
+ }
+ }
+
+ this.constraints.resetTuple( memory.betaMemory.getContext() );
+
}
public void modifyRightTuple(RightTuple rightTuple,
PropagationContext context,
InternalWorkingMemory workingMemory) {
- // TODO Auto-generated method stub
+ final CollectMemory memory = (CollectMemory) workingMemory.getNodeMemory( this );
- }
+ // Add and remove to make sure we are in the right bucket and at the end
+ // this is needed to fix for indexing and deterministic iteration
+ memory.betaMemory.getRightTupleMemory().remove( rightTuple );
+ memory.betaMemory.getRightTupleMemory().add( rightTuple );
- /**
- * Modifies the results match for a tuple, retracting it and repropagating
- * if constraints allow it
- *
- * @param leftTuple
- * @param handle
- * @param context
- * @param workingMemory
- */
- @SuppressWarnings("unchecked")
- public void modifyTuple(final ActivitySource activitySource,
- final boolean isAssert,
- final LeftTuple leftTuple,
- final RightTuple rightTuple,
- final LeftTuple match,
- final PropagationContext context,
- final InternalWorkingMemory workingMemory,
- final CollectMemory memory) {
+ // WTD here
+ // if ( !behavior.assertRightTuple( memory.getBehaviorContext(),
+ // rightTuple,
+ // workingMemory ) ) {
+ // // destroy right tuple
+ // rightTuple.unlinkFromRightParent();
+ // return;
+ // }
- final CollectContext colctx = (CollectContext) memory.betaMemory.getCreatedHandles().get( leftTuple );
+ LeftTuple childLeftTuple = rightTuple.firstChild;
- if ( isAssert ) {
- // linking left and right by creating a new left tuple
- new LeftTuple( leftTuple,
- rightTuple,
- this,
- this.tupleMemoryEnabled );
- } else {
- if ( match != null ) {
- // removing link between left and right
- match.unlinkFromLeftParent();
- match.unlinkFromRightParent();
- }
- }
+ LeftTupleMemory leftMemory = memory.betaMemory.getLeftTupleMemory();
- // if there is a subnetwork, we need to unwrap the object from inside the tuple
- InternalFactHandle handle = rightTuple.getFactHandle();
- if ( this.unwrapRightObject ) {
- handle = ((LeftTuple) handle.getObject()).getLastHandle();
+ LeftTuple leftTuple = leftMemory.getFirst( rightTuple );
+
+ this.constraints.updateFromFactHandle( memory.betaMemory.getContext(),
+ workingMemory,
+ rightTuple.getFactHandle() );
+
+ // first check our index (for indexed nodes only) hasn't changed and we are returning the same bucket
+ if ( childLeftTuple != null && leftMemory.isIndexed() && leftTuple != leftMemory.getFirst( childLeftTuple.getLeftParent() ) ) {
+ // our index has changed, so delete all the previous matches
+ removePreviousMatchesForRightTuple( rightTuple,
+ context,
+ workingMemory,
+ memory,
+ childLeftTuple );
+ childLeftTuple = null; // null so the next check will attempt matches for new bucket
}
- if ( context.getType() == PropagationContext.ASSERTION ) {
- ((Collection) colctx.resultTuple.getFactHandle().getObject()).add( handle.getObject() );
- } else if ( context.getType() == PropagationContext.RETRACTION || context.getType() == PropagationContext.EXPIRATION ) {
- ((Collection) colctx.resultTuple.getFactHandle().getObject()).remove( handle.getObject() );
- } else if ( context.getType() == PropagationContext.MODIFICATION || context.getType() == PropagationContext.RULE_ADDITION || context.getType() == PropagationContext.RULE_REMOVAL ) {
- // TODO: need to review this
- if ( isAssert ) {
- ((Collection) colctx.resultTuple.getFactHandle().getObject()).add( handle.getObject() );
+ // if LeftTupleMemory is empty, there are no matches to modify
+ if ( leftTuple != null ) {
+ if ( childLeftTuple == null ) {
+ // either we are indexed and changed buckets or
+ // we had no children before, but there is a bucket to potentially match, so try as normal assert
+ for ( ; leftTuple != null; leftTuple = (LeftTuple) leftTuple.getNext() ) {
+ if ( this.constraints.isAllowedCachedRight( memory.betaMemory.getContext(),
+ leftTuple ) ) {
+ final CollectContext colctx = (CollectContext) memory.betaMemory.getCreatedHandles().get( leftTuple );
+ // add a new match
+ addMatch( leftTuple,
+ rightTuple,
+ colctx );
+ evaluateResultConstraints( ActivitySource.RIGHT,
+ leftTuple,
+ context,
+ workingMemory,
+ memory,
+ colctx );
+ }
+ }
} else {
- ((Collection) colctx.resultTuple.getFactHandle().getObject()).remove( handle.getObject() );
+ // in the same bucket, so iterate and compare
+ for ( ; leftTuple != null; leftTuple = (LeftTuple) leftTuple.getNext() ) {
+ if ( this.constraints.isAllowedCachedRight( memory.betaMemory.getContext(),
+ leftTuple ) ) {
+ if ( childLeftTuple != null && childLeftTuple.getLeftParent() != leftTuple ) {
+ final CollectContext colctx = (CollectContext) memory.betaMemory.getCreatedHandles().get( leftTuple );
+ // add a new match
+ addMatch( leftTuple,
+ rightTuple,
+ colctx );
+ evaluateResultConstraints( ActivitySource.RIGHT,
+ leftTuple,
+ context,
+ workingMemory,
+ memory,
+ colctx );
+ } else {
+ // we must re-add this to ensure deterministic iteration
+ childLeftTuple.reAddRight();
+ }
+ } else if ( childLeftTuple != null && childLeftTuple.getLeftParent() == leftTuple ) {
+
+ LeftTuple temp = childLeftTuple.getRightParentNext();
+ final CollectContext colctx = (CollectContext) memory.betaMemory.getCreatedHandles().get( leftTuple );
+ // remove the match
+ removeMatch( rightTuple,
+ childLeftTuple,
+ colctx );
+ evaluateResultConstraints( ActivitySource.RIGHT,
+ leftTuple,
+ context,
+ workingMemory,
+ memory,
+ colctx );
+
+ childLeftTuple = temp;
+ }
+ // else do nothing, was false before and false now.
+ }
}
}
- evaluateResultConstraints( activitySource,
- leftTuple,
- context,
- workingMemory,
- memory,
- colctx );
+ this.constraints.resetFactHandle( memory.betaMemory.getContext() );
}
/**
@@ -521,12 +605,17 @@
for ( LeftTuple leftTuple = (LeftTuple) tupleIter.next(); leftTuple != null; leftTuple = (LeftTuple) tupleIter.next() ) {
CollectContext colctx = (CollectContext) memory.betaMemory.getCreatedHandles().get( leftTuple );
if ( colctx.propagated ) {
+ // temporarily break the linked list to avoid wrong interactions
+ LeftTuple[] matchings = splitList( leftTuple,
+ colctx );
sink.assertLeftTuple( new LeftTuple( leftTuple,
colctx.resultTuple,
sink,
this.tupleMemoryEnabled ),
context,
workingMemory );
+ restoreList( leftTuple,
+ matchings );
}
}
}
@@ -540,35 +629,10 @@
}
}
- /* (non-Javadoc)
- * @see org.drools.reteoo.BaseNode#hashCode()
- */
- public int hashCode() {
- return this.leftInput.hashCode() ^ this.rightInput.hashCode() ^ this.collect.hashCode() ^ this.resultsBinder.hashCode() ^ ArrayUtils.hashCode( this.resultConstraints );
+ public short getType() {
+ return NodeTypeEnums.CollectNode;
}
- /* (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 CollectNode) ) {
- return false;
- }
-
- final CollectNode other = (CollectNode) object;
-
- if ( this.getClass() != other.getClass() || (!this.leftInput.equals( other.leftInput )) || (!this.rightInput.equals( other.rightInput )) || (!this.constraints.equals( other.constraints )) ) {
- return false;
- }
-
- return this.collect.equals( other.collect ) && resultsBinder.equals( other.resultsBinder ) && Arrays.equals( this.resultConstraints,
- other.resultConstraints );
- }
-
/**
* Creates a BetaMemory for the BetaNode's memory.
*/
@@ -584,10 +648,95 @@
return memory;
}
- public short getType() {
- return NodeTypeEnums.CollectNode;
+ /**
+ * Adds a match between left and right tuple
+ *
+ * @param leftTuple
+ * @param rightTuple
+ * @param colctx
+ */
+ @SuppressWarnings("unchecked")
+ private void addMatch(final LeftTuple leftTuple,
+ final RightTuple rightTuple,
+ final CollectContext colctx) {
+ InternalFactHandle handle = rightTuple.getFactHandle();
+ if ( this.unwrapRightObject ) {
+ handle = ((LeftTuple) handle.getObject()).getLastHandle();
+ }
+ ((Collection<Object>) colctx.resultTuple.getFactHandle().getObject()).add( handle.getObject() );
+
+ // in sequential mode, we don't need to keep record of matched tuples
+ if ( this.tupleMemoryEnabled ) {
+ // linking left and right by creating a new left tuple
+ new LeftTuple( leftTuple,
+ rightTuple,
+ this,
+ this.tupleMemoryEnabled );
+ }
}
+ /**
+ * Removes a match between left and right tuple
+ *
+ * @param rightTuple
+ * @param match
+ * @param result
+ */
+ @SuppressWarnings("unchecked")
+ public void removeMatch(final RightTuple rightTuple,
+ final LeftTuple match,
+ final CollectContext colctx) {
+ if ( match != null ) {
+ // removing link between left and right
+ match.unlinkFromLeftParent();
+ match.unlinkFromRightParent();
+ }
+
+ // if there is a subnetwork, we need to unwrap the object from inside the tuple
+ InternalFactHandle handle = rightTuple.getFactHandle();
+ if ( this.unwrapRightObject ) {
+ handle = ((LeftTuple) handle.getObject()).getLastHandle();
+ }
+
+ ((Collection<Object>) colctx.resultTuple.getFactHandle().getObject()).remove( handle.getObject() );
+ }
+
+ private void removePreviousMatchesForLeftTuple(final LeftTuple leftTuple,
+ final CollectContext colctx) {
+ // It is cheaper to simply wipe out the matchings from the end of the list than
+ // going through element by element doing proper removal
+
+ // so we just split the list keeping the head
+ LeftTuple[] matchings = splitList( leftTuple,
+ colctx );
+ for ( LeftTuple match = matchings[0]; match != null; match = match.getLeftParentNext() ) {
+ // no need to unlink from the left parent as the left parent is being wiped out
+ match.unlinkFromRightParent();
+ }
+ }
+
+ private void removePreviousMatchesForRightTuple(RightTuple rightTuple,
+ PropagationContext context,
+ InternalWorkingMemory workingMemory,
+ final CollectMemory memory,
+ LeftTuple firstChild) {
+ for ( LeftTuple match = firstChild; match != null; ) {
+ final LeftTuple tmp = match.getRightParentNext();
+ final LeftTuple parent = match.getLeftParent();
+ final CollectContext colctx = (CollectContext) memory.betaMemory.getCreatedHandles().get( parent );
+ removeMatch( rightTuple,
+ match,
+ colctx );
+ evaluateResultConstraints( ActivitySource.RIGHT,
+ parent,
+ context,
+ workingMemory,
+ memory,
+ colctx );
+ match = tmp;
+ }
+ }
+
private LeftTuple[] splitList(final LeftTuple parent,
final CollectContext colctx) {
LeftTuple[] matchings = new LeftTuple[2];
@@ -603,6 +752,9 @@
parent.firstChild = null;
}
parent.lastChild = matchings[0].getLeftParentPrevious();
+ if( parent.lastChild != null ) {
+ parent.lastChild.setLeftParentNext( null );
+ }
}
return matchings;
@@ -621,6 +773,35 @@
}
}
+ /* (non-Javadoc)
+ * @see org.drools.reteoo.BaseNode#hashCode()
+ */
+ public int hashCode() {
+ return this.leftInput.hashCode() ^ this.rightInput.hashCode() ^ this.collect.hashCode() ^ this.resultsBinder.hashCode() ^ ArrayUtils.hashCode( this.resultConstraints );
+ }
+
+ /* (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 CollectNode) ) {
+ return false;
+ }
+
+ final CollectNode other = (CollectNode) object;
+
+ if ( this.getClass() != other.getClass() || (!this.leftInput.equals( other.leftInput )) || (!this.rightInput.equals( other.rightInput )) || (!this.constraints.equals( other.constraints )) ) {
+ return false;
+ }
+
+ return this.collect.equals( other.collect ) && resultsBinder.equals( other.resultsBinder ) && Arrays.equals( this.resultConstraints,
+ other.resultConstraints );
+ }
+
public static class CollectMemory
implements
Externalizable {
Modified: labs/jbossrules/branches/true_modify_20100104/drools-core/src/test/resources/org/drools/reteoo/test/CollectNodeAssertRetractTest.data
===================================================================
--- labs/jbossrules/branches/true_modify_20100104/drools-core/src/test/resources/org/drools/reteoo/test/CollectNodeAssertRetractTest.data 2010-01-11 21:58:14 UTC (rev 31025)
+++ labs/jbossrules/branches/true_modify_20100104/drools-core/src/test/resources/org/drools/reteoo/test/CollectNodeAssertRetractTest.data 2010-01-12 02:27:42 UTC (rev 31026)
@@ -118,100 +118,3 @@
col1
leftMemory []
rightMemory []
-
-/**
- * assert two right and two left, with incremental last removal
- *
-assert
- otnRight1 [h2, h3]
- otnLeft1 [h0, h1]
-join1
- leftMemory [[h0], [h1]]
- rightMemory [h2, h3]
-join2
- leftMemory [[h0, h2], [h0, h3],
- [h1, h2], [h1, h3]]
-retract
- otnLeft1 [h1]
-join1
- leftMemory [[h0]]
- rightMemory [h2, h3]
-join2
- leftMemory [[h0, h2], [h0, h3]]
-retract
- otnRight1 [h3]
-join1
- leftMemory [[h0]]
- rightMemory [h2]
-join2
- leftMemory [[h0, h2]]
-retract
- otnLeft1 [h0]
- otnRight1 [h2]
-join1
- leftMemory []
- rightMemory []
-join2
- leftMemory []
-
-/**
- * assert three left and three right, middle two are removed, and then iterated in reverse order
- *
-assert
- otnLeft1 [h0, h5, h2]
- otnRight1 [h3, h4, h1]
-join1
- leftMemory [[h0], [h5], [h2]]
- rightMemory [h3, h4, h1]
-join2
- leftMemory [[h0, h3], [h5, h3], [h2, h3],
- [h0, h4], [h5, h4], [h2, h4],
- [h0, h1], [h5, h1], [h2, h1]]
- rightMemory []
-retract
- otnLeft1 [h5]
-join1
- leftMemory [[h0], [h2]]
- rightMemory [h3, h4, h1]
-join2
- leftMemory [[h0, h3], [h2, h3],
- [h0, h4], [h2, h4],
- [h0, h1], [h2, h1]]
- rightMemory []
-retract
- otnRight1 [h4]
-join1
- leftMemory [[h0], [h2]]
- rightMemory [h3, h1]
-join2
- leftMemory [[h0, h3], [h2, h3],
- [h0, h1], [h2, h1]]
- rightMemory []
-assert
- otnLeft1 [h5]
- otnRight1 [h4]
-join1
- leftMemory [[h0], [h2], [h5]]
- rightMemory [h3, h1, h4]
-join2
- leftMemory [[h0, h3], [h2, h3],
- [h0, h1], [h2, h1],
- [h5, h3], [h5, h1],
- [h0, h4], [h2, h4], [h5, h4] ]
-retract
- otnRight1 [h4]
-join1
- leftMemory [[h0], [h2], [h5]]
- rightMemory [h3, h1]
-join2
- leftMemory [[h0, h3], [h2, h3],
- [h0, h1], [h2, h1],
- [h5, h3], [h5, h1] ]
-retract
- otnLeft1 [h5]
-join1
- leftMemory [[h0], [h2]]
- rightMemory [h3, h1]
-join2
- leftMemory [[h0, h3], [h2, h3],
- [h0, h1], [h2, h1]] */
\ No newline at end of file
More information about the jboss-svn-commits
mailing list