/** * 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.rule; /* * 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. */ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import org.drools.DroolsTestCase; import org.drools.base.ClassObjectType; import org.drools.core.util.DroolsStreamUtils; import org.drools.spi.ObjectType; public class LogicTransformerTest extends DroolsTestCase { /** * (a||b)&&c * * <pre> * and * / \ * or c * / \ * a b * </pre> * * Should become (a&&c)||(b&&c) * * <pre> * * or * / \ * / \ * / \ * and and * / \ / \ * a c b c * </pre> */ public void testSingleOrAndOrTransformation() throws InvalidPatternException { final ObjectType type = new ClassObjectType( String.class ); final Pattern a = new Pattern( 0, type, "a" ); final Pattern b = new Pattern( 1, type, "b" ); final Pattern c = new Pattern( 2, type, "c" ); final GroupElement or = GroupElementFactory.newOrInstance(); or.addChild( a ); or.addChild( b ); final GroupElement parent = GroupElementFactory.newAndInstance(); parent.addChild( or ); parent.addChild( c ); LogicTransformer.getInstance().applyOrTransformation( parent ); assertLength( 2, parent.getChildren() ); assertEquals( GroupElement.class, parent.getChildren().get( 0 ).getClass() ); assertEquals( GroupElement.class, parent.getChildren().get( 1 ).getClass() ); final GroupElement and1 = (GroupElement) parent.getChildren().get( 0 ); assertTrue( and1.isAnd() ); // transformation MUST keep the order assertEquals( a, and1.getChildren().get( 0 ) ); assertEquals( c, and1.getChildren().get( 1 ) ); final GroupElement and2 = (GroupElement) parent.getChildren().get( 1 ); assertEquals( b, and2.getChildren().get( 0 ) ); assertEquals( c, and2.getChildren().get( 1 ) ); } /** * (a||b)&&c * * <pre> * And * /|\ \__ * _/ | \_ \_ * / | \ \ * or | or not * / \ | / \ | * a b c d e f * </pre> * * Should become (a&&c)||(b&&c) * * <pre> * /\ * _/ \_ * / \ * _/| |\_ * __/ | | \__ * __/ | | \__ * / | | \ * and and and and * /||\ /||\ /||\ /||\ * a cd Not a ce Not b cd Not b ce Not * | | | | * f f f f * </pre> */ public void testMultipleOrAndOrTransformation() throws InvalidPatternException { final ObjectType type = new ClassObjectType( String.class ); final Pattern a = new Pattern( 0, type, "a" ); final Pattern b = new Pattern( 1, type, "b" ); final Pattern c = new Pattern( 2, type, "c" ); final Pattern d = new Pattern( 3, type, "d" ); final Pattern e = new Pattern( 4, type, "e" ); final Pattern f = new Pattern( 5, type, "f" ); final GroupElement parent = GroupElementFactory.newAndInstance(); final GroupElement or = GroupElementFactory.newOrInstance(); or.addChild( a ); or.addChild( b ); parent.addChild( or ); parent.addChild( c ); final GroupElement or2 = GroupElementFactory.newOrInstance(); or2.addChild( d ); or2.addChild( e ); parent.addChild( or2 ); final GroupElement not = GroupElementFactory.newNotInstance(); not.addChild( f ); parent.addChild( not ); LogicTransformer.getInstance().applyOrTransformation( parent ); assertEquals( GroupElement.Type.OR, parent.getType() ); assertLength( 4, parent.getChildren() ); assertEquals( GroupElement.class, parent.getChildren().get( 0 ).getClass() ); assertEquals( GroupElement.class, parent.getChildren().get( 1 ).getClass() ); assertEquals( GroupElement.class, parent.getChildren().get( 2 ).getClass() ); assertEquals( GroupElement.class, parent.getChildren().get( 3 ).getClass() ); GroupElement and1 = (GroupElement) parent.getChildren().get( 0 ); assertTrue( and1.isAnd() ); assertLength( 4, and1.getChildren() ); assertEquals( a, and1.getChildren().get( 0 ) ); assertEquals( c, and1.getChildren().get( 1 ) ); assertEquals( d, and1.getChildren().get( 2 ) ); assertEquals( not, and1.getChildren().get( 3 ) ); and1 = (GroupElement) parent.getChildren().get( 1 ); assertTrue( and1.isAnd() ); assertLength( 4, and1.getChildren() ); assertEquals( a, and1.getChildren().get( 0 ) ); assertEquals( c, and1.getChildren().get( 1 ) ); assertEquals( e, and1.getChildren().get( 2 ) ); assertEquals( not, and1.getChildren().get( 3 ) ); and1 = (GroupElement) parent.getChildren().get( 2 ); assertTrue( and1.isAnd() ); assertLength( 4, and1.getChildren() ); assertEquals( b, and1.getChildren().get( 0 ) ); assertEquals( c, and1.getChildren().get( 1 ) ); assertEquals( d, and1.getChildren().get( 2 ) ); assertEquals( not, and1.getChildren().get( 3 ) ); and1 = (GroupElement) parent.getChildren().get( 3 ); assertTrue( and1.isAnd() ); assertLength( 4, and1.getChildren() ); assertEquals( b, and1.getChildren().get( 0 ) ); assertEquals( c, and1.getChildren().get( 1 ) ); assertEquals( e, and1.getChildren().get( 2 ) ); assertEquals( not, and1.getChildren().get( 3 ) ); } /** * This data structure is now valid * * (Not (OR (A B) ) ) * * <pre> * Not * | * or * / \ * a b * </pre> * * Should become: * * <pre> * And * / \ * Not Not * | | * a b * </pre> * * */ public void testNotOrTransformation() throws InvalidPatternException { final ObjectType type = new ClassObjectType( String.class ); final Pattern a = new Pattern( 0, type, "a" ); final Pattern b = new Pattern( 1, type, "b" ); final GroupElement parent = GroupElementFactory.newNotInstance(); final GroupElement or = GroupElementFactory.newOrInstance(); parent.addChild( or ); or.addChild( a ); or.addChild( b ); LogicTransformer.getInstance().applyOrTransformation( parent ); assertTrue( parent.isAnd() ); assertEquals( 2, parent.getChildren().size() ); // we must ensure order final GroupElement b1 = (GroupElement) parent.getChildren().get( 0 ); final GroupElement b2 = (GroupElement) parent.getChildren().get( 1 ); assertTrue( b1.isNot() ); assertTrue( b2.isNot() ); assertEquals( 1, b1.getChildren().size() ); assertEquals( a, b1.getChildren().get( 0 ) ); assertEquals( 1, b2.getChildren().size() ); assertEquals( b, b2.getChildren().get( 0 ) ); } /** * Exists inside a not is redundant and should be eliminated * * (Not (exists (A) ) ) * * <pre> * Not * | * Exists * | * And * / \ * a b * </pre> * * Should become: * * <pre> * Not * | * And * / \ * a b * </pre> * * */ public void testNotExistsTransformation() throws InvalidPatternException { final ObjectType type = new ClassObjectType( String.class ); final Pattern a = new Pattern( 0, type, "a" ); final Pattern b = new Pattern( 1, type, "b" ); final GroupElement not = GroupElementFactory.newNotInstance(); final GroupElement exists = GroupElementFactory.newExistsInstance(); final GroupElement and = GroupElementFactory.newAndInstance(); not.addChild( exists ); exists.addChild( and ); and.addChild( a ); and.addChild( b ); GroupElement[] transformed = LogicTransformer.getInstance().transform( not ); GroupElement wrapper = transformed[0]; GroupElement notR = (GroupElement) wrapper.getChildren().get( 0 ); assertTrue( notR.isNot() ); assertEquals( 1, notR.getChildren().size() ); assertTrue( notR.getChildren().get( 0 ) instanceof GroupElement ); GroupElement andR = (GroupElement) notR.getChildren().get( 0 ); assertTrue( andR.isAnd() ); assertEquals( 2, andR.getChildren().size() ); assertTrue( andR.getChildren().get( 0 ) instanceof Pattern ); assertTrue( andR.getChildren().get( 1 ) instanceof Pattern ); final Pattern a1 = (Pattern) andR.getChildren().get( 0 ); final Pattern b1 = (Pattern) andR.getChildren().get( 1 ); assertEquals( a, a1 ); assertEquals( b, b1 ); } /** * This data structure is now valid (Exists (OR (A B) ) ) * * <pre> * Exists * | * or * / \ * a b * </pre> * * Should become: * * <pre> * Or * / \ * Exists Exists * | | * a b * </pre> */ public void testExistOrTransformation() throws InvalidPatternException { final ObjectType type = new ClassObjectType( String.class ); final Pattern a = new Pattern( 0, type, "a" ); final Pattern b = new Pattern( 1, type, "b" ); final GroupElement parent = GroupElementFactory.newExistsInstance(); final GroupElement or = GroupElementFactory.newOrInstance(); parent.addChild( or ); or.addChild( a ); or.addChild( b ); LogicTransformer.getInstance().applyOrTransformation( parent ); assertTrue( parent.isOr() ); assertEquals( 2, parent.getChildren().size() ); // we must ensure order final GroupElement b1 = (GroupElement) parent.getChildren().get( 0 ); final GroupElement b2 = (GroupElement) parent.getChildren().get( 1 ); assertTrue( b1.isExists() ); assertTrue( b2.isExists() ); assertEquals( 1, b1.getChildren().size() ); assertEquals( a, b1.getChildren().get( 0 ) ); assertEquals( 1, b2.getChildren().size() ); assertEquals( b, b2.getChildren().get( 0 ) ); } public void testEliminateEmptyBranchesAndDuplications() throws InvalidRuleException { final ObjectType type = new ClassObjectType( String.class ); final Pattern a = new Pattern( 0, type, "a" ); final Pattern b = new Pattern( 1, type, "b" ); final Pattern c = new Pattern( 2, type, "c" ); final Pattern d = new Pattern( 3, type, "d" ); final GroupElement and1 = GroupElementFactory.newAndInstance(); and1.addChild( a ); and1.addChild( b ); final GroupElement and2 = GroupElementFactory.newAndInstance(); and2.addChild( c ); and2.addChild( d ); and1.addChild( and2 ); final GroupElement or = GroupElementFactory.newOrInstance(); and1.addChild( or ); final GroupElement[] result = LogicTransformer.getInstance().transform( and1 ); assertLength( 1, result ); assertLength( 4, result[0].getChildren() ); // we must ensure order assertEquals( a, result[0].getChildren().get( 0 ) ); assertEquals( b, result[0].getChildren().get( 1 ) ); assertEquals( c, result[0].getChildren().get( 2 ) ); assertEquals( d, result[0].getChildren().get( 3 ) ); } /** * <pre> * _/|\_ * __/ | \__ * / | \ * __/ | \__ * / | \ * And and Not * / | \ / \ | * a And d e Or i * / \ / \ * b Not h Exists * | | * Not g * | * c * </pre> * * It is important to ensure that the order of * the elements is not changed after transformation * * <pre> * Or * _/ \__ * __/ \___ * / \__ * __/ \__ * / \__ * / \__ * | \ * And And * /| | | | | \ /| | | | | \ * a b Not d e h Not a b Not d e Exists Not * | | | | | * Not i Not g i * | | * c c * </pre> * * @throws IOException * @throws ClassNotFoundException * * * */ public void testProcessTree() throws IOException, ClassNotFoundException, InvalidPatternException { final ObjectType type = new ClassObjectType( String.class ); final Pattern a = new Pattern( 0, type, "a" ); final Pattern b = new Pattern( 1, type, "b" ); final Pattern c = new Pattern( 2, type, "c" ); final Pattern d = new Pattern( 3, type, "d" ); final Pattern e = new Pattern( 4, type, "e" ); final Pattern g = new Pattern( 5, type, "g" ); final Pattern h = new Pattern( 6, type, "h" ); final Pattern i = new Pattern( 7, type, "i" ); final GroupElement and1 = GroupElementFactory.newAndInstance(); final GroupElement and2 = GroupElementFactory.newAndInstance(); and1.addChild( a ); and1.addChild( and2 ); and2.addChild( b ); final GroupElement not1 = GroupElementFactory.newNotInstance(); final GroupElement not2 = GroupElementFactory.newNotInstance(); not1.addChild( not2 ); not2.addChild( c ); and2.addChild( not1 ); and1.addChild( d ); final GroupElement and3 = GroupElementFactory.newAndInstance(); and3.addChild( e ); final GroupElement or1 = GroupElementFactory.newOrInstance(); and3.addChild( or1 ); final GroupElement exist1 = GroupElementFactory.newExistsInstance(); exist1.addChild( g ); or1.addChild( h ); or1.addChild( exist1 ); final GroupElement not3 = GroupElementFactory.newNotInstance(); not3.addChild( i ); final GroupElement root = GroupElementFactory.newAndInstance(); root.addChild( and1 ); root.addChild( and3 ); root.addChild( not3 ); final GroupElement[] result = LogicTransformer.getInstance().transform( root ); // ---------------------------------------------------------------------------------- // Now construct the result tree so we can test root against what it // should look like // ---------------------------------------------------------------------------------- // Get known correct tree // The binary stream was created from a handchecked correct output // Uncomment this when you need to output a new known correct tree // result writeTree( result, "correct_processTree1.dat" ); final GroupElement[] correctResultRoot = (GroupElement[]) DroolsStreamUtils.streamIn( this.getClass().getResourceAsStream( "correct_processTree1.dat" )); // Make sure they are equal for ( int j = 0; j < correctResultRoot.length; j++ ) { assertEquals( correctResultRoot[j], result[j] ); } } public void testCloneable() { final ObjectType type = new ClassObjectType( String.class ); final Pattern a = new Pattern( 0, type, "a" ); final Pattern b = new Pattern( 1, type, "b" ); final Pattern c = new Pattern( 2, type, "c" ); final Pattern d = new Pattern( 3, type, "d" ); final Pattern e = new Pattern( 4, type, "e" ); final Pattern f = new Pattern( 5, type, "f" ); final Pattern g = new Pattern( 6, type, "g" ); final Pattern h = new Pattern( 7, type, "h" ); // Test against a known false tree final GroupElement and = GroupElementFactory.newAndInstance(); and.addChild( a ); and.addChild( b ); final GroupElement or = GroupElementFactory.newOrInstance(); or.addChild( c ); or.addChild( d ); and.addChild( or ); final GroupElement and2 = GroupElementFactory.newAndInstance(); and2.addChild( e ); and2.addChild( f ); or.addChild( and2 ); final GroupElement not = GroupElementFactory.newNotInstance(); and.addChild( not ); final GroupElement or2 = GroupElementFactory.newOrInstance(); not.addChild( or2 ); or2.addChild( g ); or2.addChild( h ); final GroupElement cloned = (GroupElement) and.clone(); assertEquals( and, cloned ); } /** * * * /** * * <pre> * _/|\_ * __/ | \__ * / | \ * __/ | \__ * / | \ * And or And * / \ / \ / \ * a Or d e Not OR * / \ | / | * b c f g Not * | * h * * * * </pre> * * Each And is a Rete sub rule * * <pre> * * * And___ And___ And___ And___ And__ And___ And___ And___ * ||| | \ ||| | \ ||| | \ ||| | \ ||| | \ ||| | \ ||| | \ ||| | \ * abd Not g abd Not Not abe Not g abe Not Not acd Not g acd Not Not ace Not g ace Not Not * | | | | | | | | | | | | * f f h f f h f f h f f h * * * </pre> * * @throws IOException * @throws ClassNotFoundException * * * * * @throws IOException * @throws ClassNotFoundException * */ public void testTransform() throws IOException, ClassNotFoundException, InvalidPatternException { final ObjectType type = new ClassObjectType( String.class ); final Pattern a = new Pattern( 0, type, "a" ); final Pattern b = new Pattern( 1, type, "b" ); final Pattern c = new Pattern( 2, type, "c" ); final Pattern d = new Pattern( 3, type, "d" ); final Pattern e = new Pattern( 4, type, "e" ); final Pattern f = new Pattern( 5, type, "f" ); final Pattern g = new Pattern( 6, type, "g" ); final Pattern h = new Pattern( 7, type, "h" ); final GroupElement and = GroupElementFactory.newAndInstance(); final GroupElement and1 = GroupElementFactory.newAndInstance(); and1.addChild( a ); final GroupElement or1 = GroupElementFactory.newOrInstance(); or1.addChild( b ); or1.addChild( c ); and1.addChild( or1 ); and.addChild( and1 ); final GroupElement or2 = GroupElementFactory.newOrInstance(); or2.addChild( d ); or2.addChild( e ); and.addChild( or2 ); final GroupElement and2 = GroupElementFactory.newAndInstance(); final GroupElement not1 = GroupElementFactory.newNotInstance(); not1.addChild( f ); final GroupElement or3 = GroupElementFactory.newOrInstance(); or3.addChild( g ); final GroupElement not2 = GroupElementFactory.newNotInstance(); not2.addChild( h ); or3.addChild( not2 ); and2.addChild( not1 ); and2.addChild( or3 ); and.addChild( and2 ); final GroupElement[] ands = LogicTransformer.getInstance().transform( and ); // Uncomment this when you need to output a new known correct tree // result writeTree( ands, "correct_transform1.dat" ); // Now check the main tree // Get known correct tree // The binary stream was created from a handchecked correct output final GroupElement[] correctResultAnds = (GroupElement[]) DroolsStreamUtils.streamIn( this.getClass().getResourceAsStream( "correct_transform1.dat" )); for ( int j = 0; j < ands.length; j++ ) { assertEquals( correctResultAnds[j], ands[j] ); } } private void writeTree(final Object object, final String fileName) throws IOException { final String className = this.getClass().getName(); File file = new File( this.getClass().getResource( className.substring( className.lastIndexOf( '.' ) + 1 ) + ".class" ).getFile() ); file = new File( file.getParent().replaceAll("%20", " "), fileName ); DroolsStreamUtils.streamOut(new FileOutputStream( file ), object); } }