Author: tirelli Date: 2006-03-05 14:33:29 -0500 (Sun, 05 Mar 2006) New Revision: 2756 Added: branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/AlphaNodeSwitch.java branches/labs/jbossrules/alpha-hash/src/test/java/org/drools/reteoo/AlphaNodeSwitchTest.java Modified: branches/labs/jbossrules/alpha-hash/pom.xml branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/BaseEvaluator.java branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/ClassFieldExtractor.java branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/FieldFactory.java branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/AlphaNode.java branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/ObjectTypeNode.java branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/rule/LiteralConstraint.java branches/labs/jbossrules/alpha-hash/src/test/java/org/drools/spi/MockField.java Log: JBRULES-51 * Adding equals() and hashCode() methods to several classes to solve node sharing problem * Adding AlphaNodeSwitch class and test case to allow Alpha Node hashing * Implementing AlphaNode hashing in ObjectTypeNode class * Changing scope for dependencies on pom.xml from "optional" to "compile" Modified: branches/labs/jbossrules/alpha-hash/pom.xml =================================================================== --- branches/labs/jbossrules/alpha-hash/pom.xml 2006-03-05 17:04:27 UTC (rev 2755) +++ branches/labs/jbossrules/alpha-hash/pom.xml 2006-03-05 19:33:29 UTC (rev 2756) @@ -25,7 +25,7 @@ <groupId>jung</groupId> <artifactId>jung</artifactId> <version>1.7.2</version> - <scope>optional</scope> + <scope>compile</scope> </dependency> <dependency> @@ -38,11 +38,11 @@ <groupId>xstream</groupId> <artifactId>xstream</artifactId> <version>1.1.3</version> - <scope>optional</scope> + <scope>compile</scope> </dependency> </dependencies> -</project> \ No newline at end of file +</project> Modified: branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/BaseEvaluator.java =================================================================== --- branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/BaseEvaluator.java 2006-03-05 17:04:27 UTC (rev 2755) +++ branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/BaseEvaluator.java 2006-03-05 19:33:29 UTC (rev 2756) @@ -32,5 +32,22 @@ public abstract boolean evaluate(Object object1, Object object2); + + public boolean equals(Object other) { + if( this == other) { + return true; + } + if( ! this.getClass().equals(other.getClass())) { + return false; + } + return (this.getOperator() == ((Evaluator) other).getOperator()) && + (this.getType() == ((Evaluator) other).getType()); + } + + public int hashCode() { + return (this.getType() * 17) ^ + (this.getOperator() * 11) ^ + (this.getClass().hashCode()); + } } Modified: branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/ClassFieldExtractor.java =================================================================== --- branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/ClassFieldExtractor.java 2006-03-05 17:04:27 UTC (rev 2755) +++ branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/ClassFieldExtractor.java 2006-03-05 19:33:29 UTC (rev 2756) @@ -3,8 +3,6 @@ import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import org.drools.RuntimeDroolsException; import org.drools.spi.FieldExtractor; @@ -84,6 +82,22 @@ } return fieldType; - } + + public boolean equals(Object other) { + if( this == other) { + return true; + } + if( ! (other instanceof ClassFieldExtractor)) { + return false; + } + ClassFieldExtractor extr = (ClassFieldExtractor) other; + return this.objectType.equals( extr.objectType ) && + this.index == extr.index; + } + + public int hashCode() { + return this.objectType.hashCode() * 17 + + this.index; + } } \ No newline at end of file Modified: branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/FieldFactory.java =================================================================== --- branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/FieldFactory.java 2006-03-05 17:04:27 UTC (rev 2755) +++ branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/base/FieldFactory.java 2006-03-05 19:33:29 UTC (rev 2756) @@ -71,6 +71,23 @@ public Object getValue() { return this.value; } + + public boolean equals(Object other) { + if(this == other) { + return true; + } + if(!(other instanceof FieldImpl)) { + return false; + } + FieldImpl field = (FieldImpl) other; + + return (((this.value == null ) && (field.value == null)) || + ((this.value != null ) && (this.value.equals(field.value)))); + } + + public int hashCode() { + return this.value.hashCode(); + } } Modified: branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/AlphaNode.java =================================================================== --- branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/AlphaNode.java 2006-03-05 17:04:27 UTC (rev 2755) +++ branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/AlphaNode.java 2006-03-05 19:33:29 UTC (rev 2756) @@ -188,6 +188,11 @@ return this.objectSource.equals( other.objectSource ) && this.constraint.equals( other.constraint ); } + + public int hashCode() { + return this.objectSource.hashCode() * 17 + + ((this.constraint != null) ? this.constraint.hashCode() : 0); + } public void remove() { // TODO Auto-generated method stub Added: branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/AlphaNodeSwitch.java =================================================================== --- branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/AlphaNodeSwitch.java 2006-03-05 17:04:27 UTC (rev 2755) +++ branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/AlphaNodeSwitch.java 2006-03-05 19:33:29 UTC (rev 2756) @@ -0,0 +1,80 @@ +/* + * 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.util.HashMap; +import java.util.Map; + +import org.drools.WorkingMemory; +import org.drools.rule.LiteralConstraint; + +/** + * AlphaNodeSwitch + * A LiteralConstraint wrapper to be used on alpha node hashing algorithm + * + * @author <a href="mailto:eds...@au...">Edson Tirelli</a> + * + * Created: 04/03/2006 + */ +public class AlphaNodeSwitch { + private final LiteralConstraint constraint; + private final Map alphaSwitch; + + public AlphaNodeSwitch(LiteralConstraint constraint) { + this.constraint = (LiteralConstraint) constraint; + this.alphaSwitch = new HashMap(); + } + + public void addAlphaNode(AlphaNode node) { + LiteralConstraint constraint = (LiteralConstraint) node.getConstraint(); + this.alphaSwitch.put(constraint.getField().getValue(), node); + } + + public boolean removeAlphaNode(AlphaNode node) { + LiteralConstraint constraint = (LiteralConstraint) node.getConstraint(); + return this.alphaSwitch.remove(constraint.getField().getValue()) != null; + } + + public AlphaNode getNode(WorkingMemory workingMemory, FactHandleImpl handle) { + Object value = this.constraint.getFieldExtractor().getValue( workingMemory.getObject( handle ) ); + return (AlphaNode) this.alphaSwitch.get(value); + } + + public int getSwitchCount() { + return this.alphaSwitch.size(); + } + + public boolean equals(Object otherConstraint) { + if ( this == otherConstraint ) { + return true; + } + + if ( (otherConstraint != null) && (otherConstraint instanceof AlphaNodeSwitch) ) { + AlphaNodeSwitch other = (AlphaNodeSwitch) otherConstraint; + if ( (this.constraint.getEvaluator().getOperator() == other.constraint.getEvaluator().getOperator()) && + (this.constraint.getFieldExtractor().getIndex() == other.constraint.getFieldExtractor().getIndex()) ) { + return true; + } + } + return false; + } + + public int hashCode() { + return this.constraint.getEvaluator().getOperator() * 17 + this.constraint.getFieldExtractor().getIndex(); + } + +} Modified: branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/ObjectTypeNode.java =================================================================== --- branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/ObjectTypeNode.java 2006-03-05 17:04:27 UTC (rev 2755) +++ branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/reteoo/ObjectTypeNode.java 2006-03-05 19:33:29 UTC (rev 2756) @@ -1,4 +1,5 @@ package org.drools.reteoo; + /* * Copyright 2005 JBoss Inc * @@ -16,8 +17,15 @@ */ import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; +import java.util.List; +import java.util.Map; +import org.drools.rule.LiteralConstraint; +import org.drools.spi.Evaluator; +import org.drools.spi.FieldConstraint; import org.drools.spi.ObjectType; import org.drools.spi.PropagationContext; import org.drools.util.PrimitiveLongMap; @@ -58,6 +66,11 @@ /** The parent Rete node */ private final Rete rete; + /** A switch map for hashed alpha nodes */ + private Map alphaSwitch = new HashMap(); + /** A list for not hashed sinks */ + private List otherSinks = new ArrayList(1); + // ------------------------------------------------------------ // Constructors // ------------------------------------------------------------ @@ -81,6 +94,93 @@ } // ------------------------------------------------------------ + // Overriding superclass methods to implement alpha node hash + // ------------------------------------------------------------ + /** + * @inheritDoc + */ + protected void addObjectSink(ObjectSink objectSink) { + super.addObjectSink( objectSink ); + if (( objectSink instanceof AlphaNode ) && + (((AlphaNode) objectSink).getConstraint() instanceof LiteralConstraint ) && + (((LiteralConstraint)((AlphaNode) objectSink).getConstraint()).getEvaluator().getOperator() == Evaluator.EQUAL)) { + + FieldConstraint constraint = ((AlphaNode)objectSink).getConstraint(); + AlphaNodeSwitch wrapper = + new AlphaNodeSwitch((LiteralConstraint) constraint); + + AlphaNodeSwitch aux = (AlphaNodeSwitch) this.alphaSwitch.get(wrapper); + if(aux == null) { + this.alphaSwitch.put(wrapper, wrapper); + aux = wrapper; + } + aux.addAlphaNode((AlphaNode) objectSink); + } else { + this.otherSinks.add(objectSink); + } + } + + /** + * Removes the <code>ObjectSink</code> + * + * @param objectSink + * The <code>ObjectSink</code> to remove + */ + protected void removeObjectSink(ObjectSink objectSink) { + super.removeObjectSink( objectSink ); + if (( objectSink instanceof AlphaNode ) && + (((AlphaNode) objectSink).getConstraint() instanceof LiteralConstraint ) && + (((LiteralConstraint)((AlphaNode) objectSink).getConstraint()).getEvaluator().getOperator() == Evaluator.EQUAL)) { + + FieldConstraint constraint = ((AlphaNode)objectSink).getConstraint(); + AlphaNodeSwitch wrapper = + new AlphaNodeSwitch((LiteralConstraint) constraint); + + wrapper = (AlphaNodeSwitch) this.alphaSwitch.get(wrapper); + wrapper.removeAlphaNode((AlphaNode) objectSink); + if(wrapper.getSwitchCount() == 0) { + this.alphaSwitch.remove(wrapper); + } + } else { + this.otherSinks.remove(objectSink); + } + } + + /** + * Propagate the assertion of a <code>FactHandleImpl/code> to this node's + * <code>ObjectSink</code>s. + * + * @param handle + * the FactHandleImpl to be asserted + * @param context + * The <code>PropagationContext</code> of the <code>WorkingMemory<code> action + * @param workingMemory + * the <code>WorkingMemory</code> session. + */ + protected void propagateAssertObject(FactHandleImpl handle, + PropagationContext context, + WorkingMemoryImpl workingMemory) { + if ( !this.attachingNewNode ) { + // for hashed alpha nodes, propagate only to those that meet constraints + for (Iterator i = this.alphaSwitch.values().iterator(); i.hasNext(); ) { + AlphaNodeSwitch wrapper = (AlphaNodeSwitch) i.next(); + AlphaNode node = wrapper.getNode(workingMemory, handle); + node.assertObject(handle, context, workingMemory); + } + // propagate to all not hashed nodes + for ( int i = 0, size = this.otherSinks.size(); i < size; i++ ) { + ((ObjectSink) this.otherSinks.get( i )).assertObject( handle, + context, + workingMemory ); + } + } else { + super.propagateAssertObject(handle, + context, + workingMemory); + } + } + + // ------------------------------------------------------------ // Instance methods // ------------------------------------------------------------ @@ -151,7 +251,7 @@ context, workingMemory ); } - + public void modifyObject(FactHandleImpl handle, PropagationContext context, WorkingMemoryImpl workingMemory) { @@ -160,7 +260,7 @@ propagateModifyObject( handle, context, workingMemory ); - } + } /* (non-Javadoc) * @see org.drools.reteoo.BaseNode#updateNewNode(org.drools.reteoo.WorkingMemoryImpl, org.drools.spi.PropagationContext) @@ -172,7 +272,7 @@ PrimitiveLongMap memory = (PrimitiveLongMap) workingMemory.getNodeMemory( this ); for ( Iterator it = memory.values().iterator(); it.hasNext(); ) { - FactHandleImpl handle = (FactHandleImpl) it.next(); + FactHandleImpl handle = (FactHandleImpl) it.next(); propagateAssertObject( handle, context, workingMemory ); Modified: branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/rule/LiteralConstraint.java =================================================================== --- branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/rule/LiteralConstraint.java 2006-03-05 17:04:27 UTC (rev 2755) +++ branches/labs/jbossrules/alpha-hash/src/main/java/org/drools/rule/LiteralConstraint.java 2006-03-05 19:33:29 UTC (rev 2756) @@ -3,9 +3,9 @@ import org.drools.FactHandle; import org.drools.WorkingMemory; import org.drools.spi.Evaluator; -import org.drools.spi.FieldValue; import org.drools.spi.FieldConstraint; import org.drools.spi.FieldExtractor; +import org.drools.spi.FieldValue; import org.drools.spi.Tuple; public class LiteralConstraint @@ -35,6 +35,10 @@ public FieldValue getField() { return this.field; } + + public FieldExtractor getFieldExtractor() { + return this.extractor; + } /** * Literal constraints cannot have required declarations, so always return an empty array. @@ -51,4 +55,29 @@ return evaluator.evaluate( this.field.getValue(), this.extractor.getValue( workingMemory.getObject( handle ) ) ); } + + public boolean equals(Object other) { + if(this == other) { + return true; + } + if(!(other instanceof LiteralConstraint)) { + return false; + } + LiteralConstraint lit = (LiteralConstraint) other; + + return this.field.equals(lit.field) && + this.extractor.equals(lit.extractor) && + this.evaluator.equals(lit.evaluator); + } + + public int hashCode() { + return (this.field.hashCode() * 17) ^ + (this.extractor.hashCode() * 11) ^ + (this.evaluator.hashCode()); + + } + + public String toString() { + return "LiteralConstraint { Field("+this.extractor.getIndex()+") "+this.evaluator.toString()+" ["+this.field.getValue()+"] }"; + } }; Added: branches/labs/jbossrules/alpha-hash/src/test/java/org/drools/reteoo/AlphaNodeSwitchTest.java =================================================================== --- branches/labs/jbossrules/alpha-hash/src/test/java/org/drools/reteoo/AlphaNodeSwitchTest.java 2006-03-05 17:04:27 UTC (rev 2755) +++ branches/labs/jbossrules/alpha-hash/src/test/java/org/drools/reteoo/AlphaNodeSwitchTest.java 2006-03-05 19:33:29 UTC (rev 2756) @@ -0,0 +1,151 @@ +package org.drools.reteoo; + +import junit.framework.Assert; +import junit.framework.TestCase; + +import org.drools.Cheese; +import org.drools.base.ClassFieldExtractor; +import org.drools.base.EvaluatorFactory; +import org.drools.rule.LiteralConstraint; +import org.drools.spi.Evaluator; +import org.drools.spi.FieldExtractor; +import org.drools.spi.FieldValue; +import org.drools.spi.MockField; + +public class AlphaNodeSwitchTest extends TestCase { + AlphaNode alphaNode1; + AlphaNode alphaNode2; + LiteralConstraint constraint1; + LiteralConstraint constraint2; + LiteralConstraint constraint3; + FactHandleImpl f0; + WorkingMemoryImpl workingMemory; + + protected void setUp() throws Exception { + super.setUp(); + + workingMemory = new WorkingMemoryImpl( new RuleBaseImpl() ); + MockObjectSource source = new MockObjectSource( 15 ); + MockObjectSink sink = new MockObjectSink(); + + FieldExtractor extractor = new ClassFieldExtractor( Cheese.class, + "type" ); + FieldExtractor extractor2 = new ClassFieldExtractor( Cheese.class, + "price" ); + FieldValue field1 = new MockField( "cheddar" ); + FieldValue field2 = new MockField( "mussarela" ); + + Evaluator evaluator = EvaluatorFactory.getEvaluator( Evaluator.OBJECT_TYPE, + Evaluator.EQUAL ); + constraint1 = new LiteralConstraint( field1, + extractor, + evaluator ); + constraint2 = new LiteralConstraint( field2, + extractor, + evaluator ); + constraint3 = new LiteralConstraint( field2, + extractor2, + evaluator ); + + alphaNode1 = new AlphaNode( 2, + constraint1, + source ); + alphaNode1.addObjectSink( sink ); + + alphaNode2 = new AlphaNode( 2, + constraint2, + source ); + alphaNode2.addObjectSink( sink ); + + Cheese cheddar = new Cheese( "mussarela", + 5 ); + + f0 = new FactHandleImpl( 0 ); + workingMemory.putObject( f0, + cheddar ); + + } + + protected void tearDown() throws Exception { + super.tearDown(); + } + + /* + * Test method for 'org.drools.reteoo.AlphaNodeSwitch.hashCode()' + */ + public void testHashCode() { + AlphaNodeSwitch alphaSwitch1 = new AlphaNodeSwitch( constraint1 ); + Assert.assertTrue( "hashCode() should be different of 0", + alphaSwitch1.hashCode() != 0 ); + + AlphaNodeSwitch alphaSwitch2 = new AlphaNodeSwitch( constraint2 ); + Assert.assertEquals( "hashCode() should be the same", + alphaSwitch1.hashCode(), + alphaSwitch2.hashCode() ); + + AlphaNodeSwitch alphaSwitch3 = new AlphaNodeSwitch( constraint3 ); + Assert.assertTrue( "hashCode() should not be the same", + alphaSwitch1.hashCode() != alphaSwitch3.hashCode() ); + } + + /* + * Test method for 'org.drools.reteoo.AlphaNodeSwitch.equals(Object)' + */ + public void testEqualsObject() { + AlphaNodeSwitch alphaSwitch1 = new AlphaNodeSwitch( constraint1 ); + AlphaNodeSwitch alphaSwitch2 = new AlphaNodeSwitch( constraint2 ); + Assert.assertTrue( "equals() should return true", + alphaSwitch1.equals( alphaSwitch2 ) ); + + AlphaNodeSwitch alphaSwitch3 = new AlphaNodeSwitch( constraint3 ); + Assert.assertFalse( "equals() should return false", + alphaSwitch1.equals( alphaSwitch3 ) ); + } + + /* + * Test method for 'org.drools.reteoo.AlphaNodeSwitch.addAlphaNode(AlphaNode)' + */ + public void testAddAlphaNode() { + AlphaNodeSwitch alphaSwitch1 = new AlphaNodeSwitch( constraint1 ); + + Assert.assertEquals( "AlphaSwitch should be empty", + 0, + alphaSwitch1.getSwitchCount() ); + + alphaSwitch1.addAlphaNode( alphaNode1 ); + + Assert.assertEquals( "AlphaSwitch should not be empty", + 1, + alphaSwitch1.getSwitchCount() ); + } + + /* + * Test method for 'org.drools.reteoo.AlphaNodeSwitch.removeAlphaNode(AlphaNode)' + */ + public void testRemoveAlphaNode() { + AlphaNodeSwitch alphaSwitch1 = new AlphaNodeSwitch( constraint1 ); + alphaSwitch1.addAlphaNode( alphaNode1 ); + Assert.assertEquals( "AlphaSwitch should not be empty", + 1, + alphaSwitch1.getSwitchCount() ); + alphaSwitch1.removeAlphaNode( alphaNode1 ); + Assert.assertEquals( "AlphaSwitch should be empty", + 0, + alphaSwitch1.getSwitchCount() ); + } + + /* + * Test method for 'org.drools.reteoo.AlphaNodeSwitch.getNode(WorkingMemory, FactHandleImpl)' + */ + public void testGetNode() { + AlphaNodeSwitch alphaSwitch1 = new AlphaNodeSwitch( constraint1 ); + alphaSwitch1.addAlphaNode( alphaNode1 ); + alphaSwitch1.addAlphaNode( alphaNode2 ); + + AlphaNode node = alphaSwitch1.getNode(workingMemory, f0); + + Assert.assertSame("Switch should have returned alphaNode2", alphaNode2, node); + + } + +} Modified: branches/labs/jbossrules/alpha-hash/src/test/java/org/drools/spi/MockField.java =================================================================== --- branches/labs/jbossrules/alpha-hash/src/test/java/org/drools/spi/MockField.java 2006-03-05 17:04:27 UTC (rev 2755) +++ branches/labs/jbossrules/alpha-hash/src/test/java/org/drools/spi/MockField.java 2006-03-05 19:33:29 UTC (rev 2756) @@ -1,5 +1,6 @@ package org.drools.spi; +import org.drools.base.FieldFactory.FieldImpl; import org.drools.spi.FieldValue; public final class MockField @@ -15,4 +16,22 @@ public Object getValue() { return this.value; } + + public boolean equals(Object other) { + if(this == other) { + return true; + } + if(!(other instanceof MockField)) { + return false; + } + MockField field = (MockField) other; + + return (((this.value == null ) && (field.value == null)) || + ((this.value != null ) && (this.value.equals(field.value)))); + } + + public int hashCode() { + return this.value.hashCode(); + } + } \ No newline at end of file |