package org.drools.base;

/*
 * 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.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import junit.framework.TestCase;

import org.drools.agent.MockRuleAgent;
import org.drools.base.evaluators.EvaluatorRegistry;
import org.drools.common.DefaultFactHandle;
import org.drools.common.InternalWorkingMemory;
import org.drools.reteoo.LeftTuple;
import org.drools.reteoo.MockLeftTupleSink;
import org.drools.reteoo.MockObjectSink;
import org.drools.reteoo.MockRightTupleSink;
import org.drools.reteoo.ReteTest;
import org.drools.reteoo.RightTuple;
import org.drools.rule.Declaration;
import org.drools.rule.VariableRestriction.BooleanVariableContextEntry;
import org.drools.rule.VariableRestriction.CharVariableContextEntry;
import org.drools.rule.VariableRestriction.DoubleVariableContextEntry;
import org.drools.rule.VariableRestriction.LongVariableContextEntry;
import org.drools.rule.VariableRestriction.ObjectVariableContextEntry;
import org.drools.rule.VariableRestriction.VariableContextEntry;
import org.drools.spi.Evaluator;
import org.drools.spi.FieldValue;
import org.drools.spi.InternalReadAccessor;
import org.drools.util.MathUtils;

/**
 * Some test coverage goodness for the evaluators.
 * Evaluator concrete instances are inside the factory at this time.
 * @author Michael Neale
 */
public class EvaluatorFactoryTest extends TestCase {

    private EvaluatorRegistry registry = new EvaluatorRegistry();
    
    public void testObject() {

        final List list = new ArrayList();
        list.add( "foo" );
        list.add( null );

        Collection col = Arrays.asList( new String[]{"foo", "bar", null} );

        final Object[][] data = {{"foo", "==", "bar", Boolean.FALSE}, {"foo", "==", "foo", Boolean.TRUE}, {"foo", "!=", "bar", Boolean.TRUE}, {list, "contains", "foo", Boolean.TRUE}, {list, "contains", "bar", Boolean.FALSE},
                {list, "contains", null, Boolean.TRUE}, {null, "contains", "bar", Boolean.FALSE}, {null, "contains", null, Boolean.FALSE}, {list, "==", null, Boolean.FALSE}, {list, "!=", null, Boolean.TRUE}, {null, "==", null, Boolean.TRUE},
                {null, "==", list, Boolean.FALSE}, {null, "!=", list, Boolean.TRUE}, {null, "<", new Integer( 43 ), Boolean.FALSE}, {null, ">=", new Integer( -10 ), Boolean.FALSE}, {null, ">", new Integer( -10 ), Boolean.FALSE},
                {null, "<=", new Integer( 42 ), Boolean.FALSE}, {new BigDecimal( "42.42" ), "<", new BigDecimal( "43" ), Boolean.TRUE}, {new BigDecimal( "42.42" ), ">", new BigDecimal( "43" ), Boolean.FALSE},
                {new BigDecimal( "42.42" ), "<=", new BigDecimal( "42.42" ), Boolean.TRUE}, {new BigInteger( "42" ), ">=", new BigInteger( "43" ), Boolean.FALSE}, {new BigInteger( "42" ), ">=", new BigInteger( "43" ), Boolean.FALSE},
                {list, "excludes", "baz", Boolean.TRUE}, {list, "excludes", "foo", Boolean.FALSE}, {"foo", "memberOf", col, Boolean.TRUE}, {"xyz", "memberOf", col, Boolean.FALSE}, {null, "memberOf", col, Boolean.TRUE},
                {"foo", "memberOf", null, Boolean.FALSE}, {"foo", "not memberOf", col, Boolean.FALSE}, {"xyz", "not memberOf", col, Boolean.TRUE}, {null, "not memberOf", col, Boolean.FALSE}, {"foo", "not memberOf", null, Boolean.FALSE},
                {Boolean.TRUE, "==", "xyz", Boolean.FALSE}, {Boolean.TRUE, "==", "true", Boolean.TRUE}, {Boolean.FALSE, "==", "xyz", Boolean.TRUE}, {Boolean.FALSE, "==", "false", Boolean.TRUE}, {Boolean.FALSE, "==", "true", Boolean.FALSE},
                {Boolean.TRUE, "!=", "xyz", Boolean.TRUE}, {Boolean.TRUE, "!=", "true", Boolean.FALSE}, {Boolean.FALSE, "!=", "xyz", Boolean.FALSE}, {Boolean.FALSE, "!=", "true", Boolean.TRUE}, {Boolean.FALSE, "!=", "false", Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.OBJECT_TYPE );

    }

    public void testArrayType() {

        final Object[] field = new Object[]{"foo", "bar"};

        final Object[][] data = {{field, "==", new Object[]{"foo"}, Boolean.FALSE}, {field, "==", field, Boolean.TRUE}, {field, "!=", new Object[]{"foo"}, Boolean.TRUE}, /*{field, "contains", "foo", Boolean.TRUE},   */

                {field, "!=", null, Boolean.TRUE}, {field, "==", null, Boolean.FALSE}, {null, "==", field, Boolean.FALSE}, {null, "==", null, Boolean.TRUE},
                {null, "!=", field, Boolean.TRUE}, {null, "!=", null, Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.ARRAY_TYPE );

    }

    public void testString() {

        Collection col = Arrays.asList( new String[]{"foo", "bar", null} );

        final Object[][] data = {{"foo", "==", "bar", Boolean.FALSE}, {"foo", "==", "foo", Boolean.TRUE}, {"foo", "!=", "bar", Boolean.TRUE}, {"something foo", "matches", ".*foo", Boolean.TRUE}, {"foo", "matches", ".*foo", Boolean.TRUE},
                {"foo", "matches", "bar", Boolean.FALSE}, {null, "matches", ".*foo", Boolean.FALSE}, {"something", "matches", "something", Boolean.TRUE}, {"something", "matches", "hello ;=", Boolean.FALSE},
                {"something", "not matches", "something", Boolean.FALSE}, {"something", "not matches", "hello ;=", Boolean.TRUE}, {"foo", "==", null, Boolean.FALSE}, {"foo", "!=", null, Boolean.TRUE}, {null, "==", null, Boolean.TRUE},
                {"foo", "!=", null, Boolean.TRUE}, {null, "!=", "foo", Boolean.TRUE}, {null, "!=", null, Boolean.FALSE}, {"foo", "memberOf", col, Boolean.TRUE}, {"xyz", "memberOf", col, Boolean.FALSE}, {null, "memberOf", col, Boolean.TRUE},
                {"foo", "memberOf", null, Boolean.FALSE}, {"foo", "not memberOf", col, Boolean.FALSE}, {"xyz", "not memberOf", col, Boolean.TRUE}, {null, "not memberOf", col, Boolean.FALSE}, {"foo", "not memberOf", null, Boolean.FALSE},
                {"foobar", "soundslike", "fubar", Boolean.TRUE}, {"fubar", "soundslike", "foobar", Boolean.TRUE}, {"foobar", "soundslike", "wanklerotaryengine", Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.STRING_TYPE );

    }

    public void testInteger() {

        Collection col = new ArrayList();
        col.add( new Integer( 42 ) );
        col.add( new Integer( 45 ) );

        final Object[][] data = {{new Integer( 42 ), "==", new Integer( 42 ), Boolean.TRUE}, {new Integer( 42 ), "<", new Integer( 43 ), Boolean.TRUE}, {new Integer( 42 ), ">=", new Integer( 41 ), Boolean.TRUE},
                {new Integer( 42 ), "!=", new Integer( 41 ), Boolean.TRUE}, {new Integer( 42 ), ">", new Integer( 41 ), Boolean.TRUE}, {new Integer( 42 ), "<=", new Integer( 42 ), Boolean.TRUE},
                {new Integer( 42 ), ">", new Integer( 100 ), Boolean.FALSE}, {new Integer( 42 ), "!=", null, Boolean.TRUE}, {new Integer( 42 ), "==", null, Boolean.FALSE}, {null, "==", null, Boolean.TRUE}, {null, "!=", null, Boolean.FALSE},
                {null, "!=", new Integer( 42 ), Boolean.TRUE}, {null, "==", new Integer( 42 ), Boolean.FALSE}, {null, "<", new Integer( 43 ), Boolean.FALSE}, {null, ">=", new Integer( -10 ), Boolean.FALSE},
                {null, ">", new Integer( -10 ), Boolean.FALSE}, {null, "<=", new Integer( 42 ), Boolean.FALSE}, {new Integer( 42 ), "memberOf", col, Boolean.TRUE}, {new Integer( 43 ), "memberOf", col, Boolean.FALSE},
                {null, "memberOf", col, Boolean.FALSE}, {new Integer( 42 ), "memberOf", null, Boolean.FALSE}, {new Integer( 42 ), "not memberOf", col, Boolean.FALSE}, {new Integer( 43 ), "not memberOf", col, Boolean.TRUE},
                {null, "not memberOf", col, Boolean.TRUE}, {new Integer( 42 ), "not memberOf", null, Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.PINTEGER_TYPE );

    }

    public void testBigDecimal() {

        final ValueType type = ValueType.determineValueType( BigDecimal.class );
        assertSame( type,
                    ValueType.BIG_DECIMAL_TYPE );

        Collection col = new ArrayList();
        col.add( new BigDecimal( 42.0 ) );
        col.add( new BigDecimal( 45.0 ) );

        final Object[][] data = {
                {new BigDecimal( 42 ), "==", new BigDecimal( 42 ), Boolean.TRUE}, 
                {new BigDecimal( 42 ), "==", new BigDecimal( "42.0" ), Boolean.TRUE}, 
                {new BigDecimal( 42 ), "!=", new BigDecimal( "42.0" ), Boolean.FALSE}, 
                {new BigDecimal( 45 ), "==", new Double( 45.0 ), Boolean.TRUE}, 
                {new BigDecimal( 45 ), "!=", new Double( 45.0 ), Boolean.FALSE}, 
                {new BigDecimal( 42 ), "==", new BigInteger( "42" ), Boolean.TRUE}, 
                {new BigDecimal( 42 ), "!=", new BigInteger( "42" ), Boolean.FALSE}, 
                {new BigDecimal( 42 ), "<", new BigDecimal( 43 ), Boolean.TRUE}, 
                {new BigDecimal( 42 ), ">=", new BigDecimal( 41 ), Boolean.TRUE},
                {new BigDecimal( 42 ), ">=", new BigDecimal( "41.0" ), Boolean.TRUE},
                {new BigDecimal( 42 ), "!=", new BigDecimal( 41 ), Boolean.TRUE}, 
                {new BigDecimal( 42 ), ">", new BigDecimal( 41 ), Boolean.TRUE}, 
                {new BigDecimal( 42 ), "<=", new BigDecimal( 42 ), Boolean.TRUE},
                {new BigDecimal( 42 ), ">", new BigDecimal( 100 ), Boolean.FALSE}, 
                {new BigDecimal( 42 ), "<", new Double( 43 ), Boolean.TRUE}, 
                {new BigDecimal( 42 ), ">=", new Double( 41 ), Boolean.TRUE},
                {new BigDecimal( 42 ), ">", new Double( 41 ), Boolean.TRUE}, 
                {new BigDecimal( 42 ), "<=", new Double( 42 ), Boolean.TRUE},
                {new BigDecimal( 42 ), ">", new Double( 100 ), Boolean.FALSE}, 
                {new BigDecimal( 42 ), "<", new BigInteger( "43" ), Boolean.TRUE}, 
                {new BigDecimal( 42 ), ">=", new BigInteger( "41" ), Boolean.TRUE},
                {new BigDecimal( 42 ), ">", new BigInteger( "41" ), Boolean.TRUE}, 
                {new BigDecimal( 42 ), "<=", new BigInteger( "42" ), Boolean.TRUE},
                {new BigDecimal( 42 ), ">", new BigInteger( "100" ), Boolean.FALSE}, 
                {new BigDecimal( 42 ), "==", null, Boolean.FALSE}, 
                {new BigDecimal( 42 ), "!=", null, Boolean.TRUE}, 
                {null, "==", new BigDecimal( 42 ), Boolean.FALSE},
                {null, "!=", new BigDecimal( 42 ), Boolean.TRUE}, 
                {null, "<", new BigDecimal( 43 ), Boolean.FALSE}, 
                {null, ">=", new BigDecimal( -10 ), Boolean.FALSE}, 
                {null, ">", new BigDecimal( -10 ), Boolean.FALSE},
                {null, "<=", new BigDecimal( 42 ), Boolean.FALSE}, 
                {new BigDecimal( 42 ), "memberOf", col, Boolean.TRUE}, 
                {new BigDecimal( 43 ), "memberOf", col, Boolean.FALSE}, 
                {null, "memberOf", col, Boolean.FALSE},
                {new BigDecimal( 42 ), "memberOf", null, Boolean.FALSE}, 
                {new BigDecimal( 42 ), "not memberOf", col, Boolean.FALSE}, 
                {new BigDecimal( 43 ), "not memberOf", col, Boolean.TRUE}, 
                {null, "not memberOf", col, Boolean.TRUE},
                {new BigDecimal( 42 ), "not memberOf", null, Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.BIG_DECIMAL_TYPE );

    }

    public void testBigInteger() {

        final ValueType type = ValueType.determineValueType( BigInteger.class );
        assertSame( type,
                    ValueType.BIG_INTEGER_TYPE );

        Collection col = new ArrayList();
        col.add( new BigInteger( "42" ) );
        col.add( new BigInteger( "45" ) );

        final Object[][] data = {
                {new BigInteger( "42" ), "==", new BigInteger( "42" ), Boolean.TRUE}, 
                {new BigInteger( "42" ), "==", new BigDecimal( "42.0" ), Boolean.TRUE}, 
                {new BigInteger( "42" ), "==", new Double( 42 ), Boolean.TRUE}, 
                {new BigInteger( "42" ), "!=", new BigDecimal( "43.0" ), Boolean.TRUE}, 
                {new BigInteger( "42" ), "!=", new Double( 43 ), Boolean.TRUE}, 
                {new BigInteger( "42" ), "<", new BigInteger( "43" ), Boolean.TRUE}, 
                {new BigInteger( "42" ), ">=", new BigInteger( "41" ), Boolean.TRUE},
                {new BigInteger( "42" ), "!=", new BigInteger( "41" ), Boolean.TRUE}, 
                {new BigInteger( "42" ), ">", new BigInteger( "41" ), Boolean.TRUE}, 
                {new BigInteger( "42" ), "<=", new BigInteger( "42" ), Boolean.TRUE},
                {new BigInteger( "42" ), ">", new BigInteger( "100" ), Boolean.FALSE},
                {new BigInteger( "42" ), "<", new Long( "43" ), Boolean.TRUE}, 
                {new BigInteger( "42" ), ">=", new Long( "41" ), Boolean.TRUE},
                {new BigInteger( "42" ), "!=", new Long( "41" ), Boolean.TRUE}, 
                {new BigInteger( "42" ), ">", new Long( "41" ), Boolean.TRUE}, 
                {new BigInteger( "42" ), "<=", new Long( "42" ), Boolean.TRUE},
                {new BigInteger( "42" ), ">", new Long( "100" ), Boolean.FALSE}, 
                {new BigInteger( "42" ), "==", null, Boolean.FALSE}, 
                {new BigInteger( "42" ), "!=", null, Boolean.TRUE}, 
                {null, "==", new BigInteger( "42" ), Boolean.FALSE},
                {null, "!=", new BigInteger( "42" ), Boolean.TRUE}, 
                {null, "<", new BigInteger( "43" ), Boolean.FALSE}, 
                {null, ">=", new BigInteger( "-10" ), Boolean.FALSE}, 
                {null, ">", new BigInteger( "-10" ), Boolean.FALSE},
                {null, "<=", new BigInteger( "42" ), Boolean.FALSE}, 
                {new BigInteger( "42" ), "memberOf", col, Boolean.TRUE}, 
                {new BigInteger( "43" ), "memberOf", col, Boolean.FALSE}, 
                {null, "memberOf", col, Boolean.FALSE},
                {new BigInteger( "42" ), "memberOf", null, Boolean.FALSE}, 
                {new BigInteger( "42" ), "not memberOf", col, Boolean.FALSE}, 
                {new BigInteger( "43" ), "not memberOf", col, Boolean.TRUE}, 
                {null, "not memberOf", col, Boolean.TRUE},
                {new BigInteger( "42" ), "not memberOf", null, Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.BIG_INTEGER_TYPE );

    }

    public void testShort() {

        Collection col = new ArrayList();
        col.add( new Short( (short) 42 ) );
        col.add( new Short( (short) 45 ) );

        //Test data: Obj1, Operand, Obj2
        final Object[][] data = {{new Short( (short) 42 ), "==", new Short( (short) 42 ), Boolean.TRUE}, {new Short( (short) 42 ), "<", new Short( (short) 43 ), Boolean.TRUE}, {new Short( (short) 42 ), ">=", new Short( (short) 41 ), Boolean.TRUE},
                {new Short( (short) 42 ), "!=", new Short( (short) 41 ), Boolean.TRUE}, {new Short( (short) 42 ), "==", null, Boolean.FALSE}, {null, "==", null, Boolean.TRUE}, {null, "!=", null, Boolean.FALSE},
                {null, "!=", new Short( (short) 42 ), Boolean.TRUE}, {null, "==", new Short( (short) 42 ), Boolean.FALSE}, {null, "<", new Short( (short) 43 ), Boolean.FALSE}, {null, ">=", new Short( (short) -10 ), Boolean.FALSE},
                {null, ">", new Short( (short) -10 ), Boolean.FALSE}, {null, "<=", new Short( (short) 42 ), Boolean.FALSE}, {new Short( (short) 42 ), "memberOf", col, Boolean.TRUE}, {new Short( (short) 43 ), "memberOf", col, Boolean.FALSE},
                {null, "memberOf", col, Boolean.FALSE}, {new Short( (short) 42 ), "memberOf", null, Boolean.FALSE}, {new Short( (short) 42 ), "not memberOf", col, Boolean.FALSE}, {new Short( (short) 43 ), "not memberOf", col, Boolean.TRUE},
                {null, "not memberOf", col, Boolean.TRUE}, {new Short( (short) 42 ), "not memberOf", null, Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.PSHORT_TYPE );
    }

    public void testBoolean() {

        Collection col = new ArrayList();
        col.add( new Boolean( true ) );
        col.add( new Boolean( true ) );

        //Test data: Obj1, Operand, Obj2
        final Object[][] data = {{new Boolean( true ), "==", new Boolean( true ), Boolean.TRUE}, {new Boolean( false ), "!=", new Boolean( true ), Boolean.TRUE}, {new Boolean( true ), "==", new Boolean( false ), Boolean.FALSE},
                {new Boolean( true ), "!=", new Boolean( false ), Boolean.TRUE}, {new Boolean( true ), "==", null, Boolean.FALSE}, {null, "==", null, Boolean.TRUE}, {null, "!=", null, Boolean.FALSE}, {null, "!=", new Boolean( true ), Boolean.TRUE},
                {null, "==", new Boolean( true ), Boolean.FALSE}, {new Boolean( true ), "memberOf", col, Boolean.TRUE}, {new Boolean( false ), "memberOf", col, Boolean.FALSE}, {null, "memberOf", col, Boolean.FALSE},
                {new Boolean( true ), "memberOf", null, Boolean.FALSE}, {new Boolean( true ), "not memberOf", col, Boolean.FALSE}, {new Boolean( false ), "not memberOf", col, Boolean.TRUE}, {null, "not memberOf", col, Boolean.TRUE},
                {new Boolean( true ), "not memberOf", null, Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.PBOOLEAN_TYPE );
    }

    public void testDouble() {
        Collection col = new ArrayList();
        col.add( new Double( 42 ) );
        col.add( new Double( 45 ) );

        final Object[][] data = {{new Double( 42 ), "==", new Double( 42 ), Boolean.TRUE}, {new Double( 42 ), "<", new Double( 43 ), Boolean.TRUE}, {new Double( 42 ), ">=", new Double( 41 ), Boolean.TRUE},
                {new Double( 42 ), "!=", new Double( 41 ), Boolean.TRUE}, {new Double( 42 ), ">", new Double( 41 ), Boolean.TRUE}, {new Double( 42 ), ">=", new Double( 41 ), Boolean.TRUE}, {new Double( 42 ), ">=", new Double( 42 ), Boolean.TRUE},
                {new Double( 42 ), ">=", new Double( 100 ), Boolean.FALSE}, {new Double( 42 ), "<", new Double( 1 ), Boolean.FALSE}, {new Double( 42 ), "==", null, Boolean.FALSE}, {null, "==", null, Boolean.TRUE}, {null, "!=", null, Boolean.FALSE},
                {null, "!=", new Double( 42 ), Boolean.TRUE}, {null, "==", new Double( 42 ), Boolean.FALSE}, {null, "<", new Double( 43 ), Boolean.FALSE}, {null, ">=", new Double( -10 ), Boolean.FALSE}, {null, ">", new Double( -10 ), Boolean.FALSE},
                {null, "<=", new Double( 42 ), Boolean.FALSE}, {new Double( 42 ), "memberOf", col, Boolean.TRUE}, {new Double( 43 ), "memberOf", col, Boolean.FALSE}, {null, "memberOf", col, Boolean.FALSE},
                {new Double( 42 ), "memberOf", null, Boolean.FALSE}, {new Double( 42 ), "not memberOf", col, Boolean.FALSE}, {new Double( 43 ), "not memberOf", col, Boolean.TRUE}, {null, "not memberOf", col, Boolean.TRUE},
                {new Double( 42 ), "not memberOf", null, Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.PDOUBLE_TYPE );
    }

    public void testFloat() {
        Collection col = new ArrayList();
        col.add( new Float( 42 ) );
        col.add( new Float( 45 ) );

        final Object[][] data = {{new Float( 42 ), "==", new Float( 42 ), Boolean.TRUE}, {new Float( 42 ), "<", new Float( 43 ), Boolean.TRUE}, {new Float( 42 ), ">=", new Float( 41 ), Boolean.TRUE},
                {new Float( 42 ), "!=", new Float( 41 ), Boolean.TRUE}, {new Float( 42 ), ">", new Float( 41 ), Boolean.TRUE}, {new Float( 42 ), ">=", new Float( 41 ), Boolean.TRUE}, {new Float( 42 ), ">=", new Float( 42 ), Boolean.TRUE},
                {new Float( 42 ), ">=", new Float( 100 ), Boolean.FALSE}, {new Float( 42 ), "<", new Float( 1 ), Boolean.FALSE}, {new Float( 42 ), "==", null, Boolean.FALSE}, {null, "==", null, Boolean.TRUE}, {null, "!=", null, Boolean.FALSE},
                {null, "!=", new Float( 42 ), Boolean.TRUE}, {null, "==", new Float( 42 ), Boolean.FALSE}, {null, "<", new Float( 43 ), Boolean.FALSE}, {null, ">=", new Float( -10 ), Boolean.FALSE}, {null, ">", new Float( -10 ), Boolean.FALSE},
                {null, "<=", new Float( 42 ), Boolean.FALSE}, {new Float( 42 ), "memberOf", col, Boolean.TRUE}, {new Float( 43 ), "memberOf", col, Boolean.FALSE}, {null, "memberOf", col, Boolean.FALSE},
                {new Float( 42 ), "memberOf", null, Boolean.FALSE}, {new Float( 42 ), "not memberOf", col, Boolean.FALSE}, {new Float( 43 ), "not memberOf", col, Boolean.TRUE}, {null, "not memberOf", col, Boolean.TRUE},
                {new Float( 42 ), "not memberOf", null, Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.PFLOAT_TYPE );
    }

    public void testLong() {
        Collection col = new ArrayList();
        col.add( new Long( 42 ) );
        col.add( new Long( 45 ) );

        final Object[][] data = {{new Long( 42 ), "==", new Long( 42 ), Boolean.TRUE}, {new Long( 42 ), "<", new Long( 43 ), Boolean.TRUE}, {new Long( 42 ), ">=", new Long( 41 ), Boolean.TRUE}, {new Long( 42 ), "!=", new Long( 41 ), Boolean.TRUE},
                {new Long( 42 ), ">", new Long( 41 ), Boolean.TRUE}, {new Long( 42 ), ">=", new Long( 41 ), Boolean.TRUE}, {new Long( 42 ), ">=", new Long( 42 ), Boolean.TRUE}, {new Long( 42 ), ">=", new Long( 100 ), Boolean.FALSE},
                {new Long( 42 ), "<", new Long( 1 ), Boolean.FALSE}, {new Long( 42 ), "==", null, Boolean.FALSE}, {null, "==", null, Boolean.TRUE}, {null, "!=", null, Boolean.FALSE}, {null, "!=", new Long( 42 ), Boolean.TRUE},
                {null, "==", new Long( 42 ), Boolean.FALSE}, {null, "<", new Long( 43 ), Boolean.FALSE}, {null, ">=", new Long( -10 ), Boolean.FALSE}, {null, ">", new Long( -10 ), Boolean.FALSE}, {null, "<=", new Long( 42 ), Boolean.FALSE},
                {new Long( 42 ), "memberOf", col, Boolean.TRUE}, {new Long( 43 ), "memberOf", col, Boolean.FALSE}, {null, "memberOf", col, Boolean.FALSE}, {new Long( 42 ), "memberOf", null, Boolean.FALSE},
                {new Long( 42 ), "not memberOf", col, Boolean.FALSE}, {new Long( 43 ), "not memberOf", col, Boolean.TRUE}, {null, "not memberOf", col, Boolean.TRUE}, {new Long( 42 ), "not memberOf", null, Boolean.FALSE}};

        runEvaluatorTest( data,
                          ValueType.PLONG_TYPE );
    }

    public void testCharacter() {
        Collection col = new ArrayList();
        col.add( new Character( 'a' ) );
        col.add( new Character( 'b' ) );

        final Object[][] data = {{new Character( 'a' ), "==", new Character( 'a' ), Boolean.TRUE}, {new Character( 'a' ), "<", new Character( 'b' ), Boolean.TRUE}, {new Character( 'a' ), ">=", new Character( 'a' ), Boolean.TRUE},
                {new Character( 'a' ), "!=", new Character( 'Z' ), Boolean.TRUE}, {new Character( 'z' ), ">", new Character( 'a' ), Boolean.TRUE}, {new Character( 'z' ), ">=", new Character( 'z' ), Boolean.TRUE},
                {new Character( 'z' ), ">=", new Character( 'a' ), Boolean.TRUE}, {new Character( 'a' ), ">=", new Character( 'z' ), Boolean.FALSE}, {new Character( 'z' ), "<", new Character( 'a' ), Boolean.FALSE},
                {new Character( 'z' ), "==", null, Boolean.FALSE}, {null, "==", null, Boolean.TRUE}, {null, "!=", null, Boolean.FALSE}, {null, "!=", new Character( 'z' ), Boolean.TRUE}, {null, "==", new Character( 'z' ), Boolean.FALSE},
                {null, "<", new Character( 'a' ), Boolean.FALSE}, {null, ">=", new Character( '\0' ), Boolean.FALSE}, {null, ">", new Character( '\0' ), Boolean.FALSE}, {null, "<=", new Character( 'a' ), Boolean.FALSE},
                {new Character( 'a' ), "memberOf", col, Boolean.TRUE}, {new Character( 'z' ), "memberOf", col, Boolean.FALSE}, {null, "memberOf", col, Boolean.FALSE}, {new Character( 'a' ), "memberOf", null, Boolean.FALSE},
                {new Character( 'a' ), "not memberOf", col, Boolean.FALSE}, {new Character( 'z' ), "not memberOf", col, Boolean.TRUE}, {null, "not memberOf", col, Boolean.TRUE}, {new Character( 'a' ), "not memberOf", null, Boolean.FALSE}};
        runEvaluatorTest( data,
                          ValueType.PCHAR_TYPE );
    }

    public void testDate() throws Exception {

        final SimpleDateFormat df = new SimpleDateFormat( "dd-MMM-yyyy",
                                                          Locale.ENGLISH );
        Collection col = new ArrayList();
        col.add( df.parse( "10-Jul-1974" ) );
        col.add( df.parse( "11-Jul-1974" ) );

        //note that strings are also allowed on the right
        final Object[][] data = {{df.parse( "10-Jul-1974" ), "==", df.parse( "10-Jul-1974" ), Boolean.TRUE}, {df.parse( "10-Jul-1974" ), "<", df.parse( "11-Jul-1974" ), Boolean.TRUE},
                {df.parse( "10-Jul-1974" ), ">=", df.parse( "10-Jul-1974" ), Boolean.TRUE}, {df.parse( "10-Jul-1974" ), "!=", df.parse( "11-Jul-1974" ), Boolean.TRUE}, {df.parse( "10-Jul-2000" ), ">", df.parse( "10-Jul-1974" ), Boolean.TRUE},
                {df.parse( "10-Jul-1974" ), ">=", df.parse( "10-Jul-1974" ), Boolean.TRUE}, {df.parse( "11-Jul-1974" ), ">=", df.parse( "10-Jul-1974" ), Boolean.TRUE}, {df.parse( "10-Jul-1974" ), ">=", df.parse( "11-Jul-1974" ), Boolean.FALSE},
                {df.parse( "10-Jul-2000" ), "<", df.parse( "10-Jul-1974" ), Boolean.FALSE}, {df.parse( "10-Jul-1974" ), "<", df.parse( "11-Jul-1974" ), Boolean.TRUE}, {df.parse( "10-Jul-1974" ), "==", null, Boolean.FALSE},
                {df.parse( "10-Jul-1974" ), "!=", null, Boolean.TRUE}, {null, "==", null, Boolean.TRUE}, {null, "==", df.parse( "10-Jul-1974" ), Boolean.FALSE}, {null, "!=", null, Boolean.FALSE},
                {null, "!=", df.parse( "10-Jul-1974" ), Boolean.TRUE}, {null, "<", df.parse( "10-Jul-1974" ), Boolean.FALSE}, {null, ">=", new Date( 0 ), Boolean.FALSE}, {null, ">", new Date( 0 ), Boolean.FALSE},
                {null, "<=", df.parse( "10-Jul-1974" ), Boolean.FALSE}, {df.parse( "10-Jul-1974" ), "memberOf", col, Boolean.TRUE}, {df.parse( "15-Jul-1974" ), "memberOf", col, Boolean.FALSE}, {null, "memberOf", col, Boolean.FALSE},
                {df.parse( "10-Jul-1974" ), "memberOf", null, Boolean.FALSE}, {df.parse( "10-Jul-1974" ), "not memberOf", col, Boolean.FALSE}, {df.parse( "15-Jul-1974" ), "not memberOf", col, Boolean.TRUE}, {null, "not memberOf", col, Boolean.TRUE},
                {df.parse( "10-Jul-1974" ), "not memberOf", null, Boolean.FALSE}};
        runEvaluatorTest( data,
                          ValueType.DATE_TYPE );
    }

    public void testByte() {
        Collection col = new ArrayList();
        col.add( new Byte( "1" ) );
        col.add( new Byte( "2" ) );

        final Object[][] data = {{new Byte( "1" ), "==", new Byte( "1" ), Boolean.TRUE}, {new Byte( "1" ), "==", new Byte( "2" ), Boolean.FALSE}, {new Byte( "1" ), "!=", new Byte( "2" ), Boolean.TRUE},
                {new Byte( "1" ), "!=", new Byte( "1" ), Boolean.FALSE}, {new Byte( "1" ), "<=", new Byte( "1" ), Boolean.TRUE}, {new Byte( "1" ), "==", null, Boolean.FALSE}, {new Byte( "1" ), "<", new Byte( "2" ), Boolean.TRUE},
                {new Byte( "2" ), ">=", new Byte( "1" ), Boolean.TRUE}, {new Byte( "2" ), ">", new Byte( "1" ), Boolean.TRUE}, {new Byte( "1" ), "<=", new Byte( "2" ), Boolean.TRUE}, {null, "==", null, Boolean.TRUE},
                {null, "!=", null, Boolean.FALSE}, {null, "!=", new Byte( "1" ), Boolean.TRUE}, {null, "==", new Byte( "1" ), Boolean.FALSE}, {null, "<", new Byte( Byte.MAX_VALUE ), Boolean.FALSE},
                {null, ">=", new Byte( Byte.MIN_VALUE ), Boolean.FALSE}, {null, ">", new Byte( Byte.MIN_VALUE ), Boolean.FALSE}, {null, "<=", new Byte( Byte.MAX_VALUE ), Boolean.FALSE}, {new Byte( "1" ), "memberOf", col, Boolean.TRUE},
                {new Byte( "3" ), "memberOf", col, Boolean.FALSE}, {null, "memberOf", col, Boolean.FALSE}, {new Byte( "1" ), "memberOf", null, Boolean.FALSE}, {new Byte( "1" ), "not memberOf", col, Boolean.FALSE},
                {new Byte( "3" ), "not memberOf", col, Boolean.TRUE}, {null, "not memberOf", col, Boolean.TRUE}, {new Byte( "1" ), "not memberOf", null, Boolean.FALSE}};
        runEvaluatorTest( data,
                          ValueType.PBYTE_TYPE );

    }

    /**
     * Test utility to play the data through the evaluators.
     * @param data The data to try out : Array of {arg1, operator, arg2}
     * @param valueType The Evaluator.**_TYPE to test
     */
    private void runEvaluatorTest(final Object[][] data,
                                  final ValueType valueType) {
        final InternalReadAccessor extractor = new MockExtractor();
        for ( int i = 0; i < data.length; i++ ) {
            final Object[] row = data[i];
            boolean isNegated = ((String) row[1]).startsWith( "not " );
            String evaluatorStr = isNegated ? ((String) row[1]).substring( 4 ) : (String) row[1];
            final Evaluator evaluator = (Evaluator) registry.getEvaluatorDefinition( evaluatorStr ).getEvaluator( valueType,
                                                                                                      evaluatorStr,
                                                                                                      isNegated,
                                                                                                      null );
            checkEvaluatorMethodWithFieldValue( valueType,
                                                extractor,
                                                row,
                                                evaluator );
            checkEvaluatorMethodCachedRight( valueType,
                                             extractor,
                                             row,
                                             evaluator );
            checkEvaluatorMethodCachedLeft( valueType,
                                            extractor,
                                            row,
                                            evaluator );
            checkEvaluatorMethodWith2Extractors( valueType,
                                                 extractor,
                                                 row,
                                                 evaluator );

            assertEquals( valueType,
                          evaluator.getValueType() );

        }
    }

    /**
     * @param valueType
     * @param extractor
     * @param row
     * @param evaluator
     */
    private void checkEvaluatorMethodWithFieldValue(final ValueType valueType,
                                                    final InternalReadAccessor extractor,
                                                    final Object[] row,
                                                    final Evaluator evaluator) {
        final FieldValue value = FieldFactory.getFieldValue( row[2] );
        final boolean result = evaluator.evaluate( null,
                                                   extractor,
                                                   row[0],
                                                   value );
        final String message = "The evaluator type: [" + valueType + "] with FieldValue incorrectly returned " + result + " for [" + row[0] + " " + row[1] + " " + row[2] + "]. It was asserted to return " + row[3];

        if ( row[3] == Boolean.TRUE ) {
            assertTrue( message,
                        result );
        } else {
            assertFalse( message,
                         result );
        }
    }

    /**
     * @param valueType
     * @param extractor
     * @param row
     * @param evaluator
     */
    private void checkEvaluatorMethodCachedRight(final ValueType valueType,
                                                 final InternalReadAccessor extractor,
                                                 final Object[] row,
                                                 final Evaluator evaluator) {
        final VariableContextEntry context = this.getContextEntry( evaluator,
                                                                   extractor,
                                                                   valueType,
                                                                   row );
        final boolean result = evaluator.evaluateCachedRight( null,
                                                              context,
                                                              row[2] );
        final String message = "The evaluator type: [" + valueType + "] with CachedRight incorrectly returned " + result + " for [" + row[0] + " " + row[1] + " " + row[2] + "]. It was asserted to return " + row[3];

        if ( row[3] == Boolean.TRUE ) {
            assertTrue( message,
                        result );
        } else {
            assertFalse( message,
                         result );
        }
    }

    /**
     * @param valueType
     * @param extractor
     * @param row
     * @param evaluator
     */
    private void checkEvaluatorMethodCachedLeft(final ValueType valueType,
                                                final InternalReadAccessor extractor,
                                                final Object[] row,
                                                final Evaluator evaluator) {
        final VariableContextEntry context = this.getContextEntry( evaluator,
                                                                   extractor,
                                                                   valueType,
                                                                   row );
        final boolean result = evaluator.evaluateCachedLeft( null,
                                                             context,
                                                             row[0] );
        final String message = "The evaluator type: [" + valueType + "] with CachedLeft incorrectly returned " + result + " for [" + row[0] + " " + row[1] + " " + row[2] + "]. It was asserted to return " + row[3];

        if ( row[3] == Boolean.TRUE ) {
            assertTrue( message,
                        result );
        } else {
            assertFalse( message,
                         result );
        }
    }

    /**
     * @param valueType
     * @param extractor
     * @param row
     * @param evaluator
     */
    private void checkEvaluatorMethodWith2Extractors(final ValueType valueType,
                                                     final InternalReadAccessor extractor,
                                                     final Object[] row,
                                                     final Evaluator evaluator) {
        final boolean result = evaluator.evaluate( null,
                                                   extractor,
                                                   row[0],
                                                   extractor,
                                                   row[2] );
        final String message = "The evaluator type: [" + valueType + "] with 2 extractors incorrectly returned " + result + " for [" + row[0] + " " + row[1] + " " + row[2] + "]. It was asserted to return " + row[3];

        if ( row[3] == Boolean.TRUE ) {
            assertTrue( message,
                        result );
        } else {
            assertFalse( message,
                         result );
        }
    }

    private VariableContextEntry getContextEntry(final Evaluator evaluator,
                                                 final InternalReadAccessor extractor,
                                                 final ValueType valueType,
                                                 final Object[] row) {
        final Declaration declaration = new Declaration( "test",
                                                         extractor,
                                                         null );
        final ValueType coerced = evaluator.getCoercedValueType();
        
        if ( coerced.isIntegerNumber() ) {
            final LongVariableContextEntry context = new LongVariableContextEntry( extractor,
                                                                                   declaration,
                                                                                   evaluator );                                 
            
            if ( row[2] == null ) {
                context.leftNull = true;
            } else {
                context.left = ((Number) row[2]).longValue();
            }

            if ( row[0] == null ) {
                context.rightNull = true;
            } else {
                context.right = ((Number) row[0]).longValue();
            }
            return context;
        } else if ( coerced.isChar() ) {
            final CharVariableContextEntry context = new CharVariableContextEntry( extractor,
                                                                                   declaration,
                                                                                   evaluator );

            if ( row[2] == null ) {
                context.leftNull = true;
            } else {
                context.left = ((Character) row[2]).charValue();
            }

            if ( row[0] == null ) {
                context.rightNull = true;
            } else {
                context.right = ((Character) row[0]).charValue();
            }
            return context;
        } else if ( coerced.isBoolean() ) {
            final BooleanVariableContextEntry context = new BooleanVariableContextEntry( extractor,
                                                                                         declaration,
                                                                                         evaluator );

            if ( row[2] == null ) {
                context.leftNull = true;
            } else {
                context.left = ((Boolean) row[2]).booleanValue();
            }

            if ( row[0] == null ) {
                context.rightNull = true;
            } else {
                context.right = ((Boolean) row[0]).booleanValue();
            }
            return context;
        } else if ( coerced.isFloatNumber() ) {
            final DoubleVariableContextEntry context = new DoubleVariableContextEntry( extractor,
                                                                                       declaration,
                                                                                       evaluator );
            if ( row[2] == null ) {
                context.leftNull = true;
            } else {
                context.left = ((Number) row[2]).doubleValue();
            }

            if ( row[0] == null ) {
                context.rightNull = true;
            } else {
                context.right = ((Number) row[0]).doubleValue();
            }
            return context;
        } else {
            final ObjectVariableContextEntry context = new ObjectVariableContextEntry( extractor,
                                                                                       declaration,
                                                                                       evaluator );
            if ( row[2] == null ) {
                context.leftNull = true;
            } else {
                context.left = row[2];
            }

            if ( row[0] == null ) {
                context.rightNull = true;
            } else {
                context.right = row[0];
            }
            return context;
        }
    }

    private static class MockExtractor
        implements
        InternalReadAccessor {

        private static final long serialVersionUID = 400L;

        public void readExternal(ObjectInput in) throws IOException,
                                                ClassNotFoundException {
        }

        public void writeExternal(ObjectOutput out) throws IOException {
        }

        public boolean isSelfReference() {
            return false;
        }

        public boolean getBooleanValue(InternalWorkingMemory workingMemory,
                                       final Object object) {
            return object != null ? ((Boolean) object).booleanValue() : false;
        }

        public byte getByteValue(InternalWorkingMemory workingMemory,
                                 final Object object) {
            return object != null ? ((Number) object).byteValue() : (byte) 0;
        }

        public char getCharValue(InternalWorkingMemory workingMemory,
                                 final Object object) {
            return object != null ? ((Character) object).charValue() : '\0';
        }

        public double getDoubleValue(InternalWorkingMemory workingMemory,
                                     final Object object) {
            return object != null ? ((Number) object).doubleValue() : 0.0;
        }

        public Class getExtractToClass() {
            return null;
        }

        public String getExtractToClassName() {
            return null;
        }

        public float getFloatValue(InternalWorkingMemory workingMemory,
                                   final Object object) {
            return object != null ? ((Number) object).floatValue() : (float) 0.0;
        }

        public int getHashCode(InternalWorkingMemory workingMemory,
                               final Object object) {
            return 0;
        }

        public int getIntValue(InternalWorkingMemory workingMemory,
                               final Object object) {
            return object != null ? ((Number) object).intValue() : 0;
        }

        public long getLongValue(InternalWorkingMemory workingMemory,
                                 final Object object) {
            return object != null ? ((Number) object).longValue() : 0;
        }

        public Method getNativeReadMethod() {
            return null;
        }

        public short getShortValue(InternalWorkingMemory workingMemory,
                                   final Object object) {
            return object != null ? ((Number) object).shortValue() : (short) 0;
        }

        public Object getValue(InternalWorkingMemory workingMemory,
                               final Object object) {
            return object;
        }

        public boolean isNullValue(InternalWorkingMemory workingMemory,
                                   final Object object) {
            return object == null;
        }

        public ValueType getValueType() {
            // TODO Auto-generated method stub
            return null;
        }

        public int getIndex() {
            return 0;
        }

        public boolean isGlobal() {
            return false;
        }

        public boolean getBooleanValue(Object object) {
            // TODO Auto-generated method stub
            return false;
        }

        public byte getByteValue(Object object) {
            // TODO Auto-generated method stub
            return 0;
        }

        public char getCharValue(Object object) {
            // TODO Auto-generated method stub
            return 0;
        }

        public double getDoubleValue(Object object) {
            // TODO Auto-generated method stub
            return 0;
        }

        public float getFloatValue(Object object) {
            // TODO Auto-generated method stub
            return 0;
        }

        public int getHashCode(Object object) {
            // TODO Auto-generated method stub
            return 0;
        }

        public int getIntValue(Object object) {
            // TODO Auto-generated method stub
            return 0;
        }

        public long getLongValue(Object object) {
            // TODO Auto-generated method stub
            return 0;
        }

        public short getShortValue(Object object) {
            // TODO Auto-generated method stub
            return 0;
        }

        public Object getValue(Object object) {
            // TODO Auto-generated method stub
            return null;
        }

        public boolean isNullValue(Object object) {
            // TODO Auto-generated method stub
            return false;
        }

        public BigDecimal getBigDecimalValue(InternalWorkingMemory workingMemory,
                                             Object object) {
            return MathUtils.getBigDecimal( object );
        }

        public BigInteger getBigIntegerValue(InternalWorkingMemory workingMemory,
                                             Object object) {
            return MathUtils.getBigInteger( object );
        }

        public BigDecimal getBigDecimalValue(Object object) {
            return MathUtils.getBigDecimal( object );
        }

        public BigInteger getBigIntegerValue(Object object) {
            return MathUtils.getBigInteger( object );
        }

    }

    //    public void testRegexFoo() {
    //        Pattern p = Pattern.compile( ".*foo" );
    //        boolean b;
    //        long start = System.currentTimeMillis();
    //        for (int i = 0; i < 1000000; i++) {
    //            b = ("something foo".matches( ".*foo" ));
    //        }
    //        System.out.println("time: " + (System.currentTimeMillis() - start));
    //
    //        start = System.currentTimeMillis();
    //        for (int i = 0; i < 1000000; i++) {
    //            Matcher m = p.matcher( "something foo" );
    //            b = m.matches();
    //        }
    //        System.out.println("time: " + (System.currentTimeMillis() - start));
    //    }

}