/*
* 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.core.rule;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.drools.core.base.ClassObjectType;
import org.drools.core.test.model.DroolsTestCase;
import org.drools.core.util.DroolsStreamUtils;
import org.drools.core.spi.ObjectType;
import org.junit.Test;
import static org.junit.Assert.*;
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>
*/
@Test
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>
*/
@Test
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>
*/
@Test
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>
*/
@Test
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>
* Not
* |
* And
* / \
* Not Not
* | |
* a b
* </pre>
*/
@Test
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.isNot() );
assertEquals( 1,
parent.getChildren().size() );
final GroupElement and = (GroupElement) parent.getChildren().get( 0 );
assertTrue( and.isAnd() );
assertEquals( 2, and.getChildren().size() );
// we must ensure order
final GroupElement b1 = (GroupElement) and.getChildren().get( 0 );
final GroupElement b2 = (GroupElement) and.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 ) );
}
@Test
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
*/
@Test
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
final File testFile = new File("target/test/LogicTransformerTest_correct_processTree1.dat");
testFile.getParentFile().mkdirs();
DroolsStreamUtils.streamOut(new FileOutputStream(testFile), result);
final GroupElement[] correctResultRoot =
(GroupElement[]) DroolsStreamUtils.streamIn(new FileInputStream(testFile));
// Make sure they are equal
for ( int j = 0; j < correctResultRoot.length; j++ ) {
assertEquals( correctResultRoot[j],
result[j] );
}
}
@Test
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
*/
@Test
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
final File testFile = new File("target/test/LogicTransformerTest_correct_transform1.dat");
testFile.getParentFile().mkdirs();
DroolsStreamUtils.streamOut(new FileOutputStream(testFile), ands);
// 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( new FileInputStream(testFile));
for ( int j = 0; j < ands.length; j++ ) {
assertEquals( correctResultAnds[j],
ands[j] );
}
}
}