//%2005////////////////////////////////////////////////////////////////////////
//
// Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development
// Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems.
// Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.;
// IBM Corp.; EMC Corporation, The Open Group.
// Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.;
// IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group.
// Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.;
// EMC Corporation; VERITAS Software Corporation; The Open Group.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN
// ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//==============================================================================
//
// Author:      Markus Mueller (sedgewick_de@yahoo.de)
//
// Modified By: Humberto Rivero (hurivero@us.ibm.com)
//
//%/////////////////////////////////////////////////////////////////////////////


#include "Cql2Dnf.h"
#include <Pegasus/Common/Stack.h>
#include <Pegasus/Common/Tracer.h>

PEGASUS_USING_STD;
PEGASUS_NAMESPACE_BEGIN

#define PEGASUS_ARRAY_T term_el
# include <Pegasus/Common/ArrayImpl.h>
#undef PEGASUS_ARRAY_T

#define PEGASUS_ARRAY_T eval_el
# include <Pegasus/Common/ArrayImpl.h>
#undef PEGASUS_ARRAY_T

#define PEGASUS_ARRAY_T stack_el
# include <Pegasus/Common/ArrayImpl.h>
#undef PEGASUS_ARRAY_T

//
// Terminal element methods 
//

void term_el::negate()
{
   NOT = true;
};

//
// Evaluation heap element methods
//
stack_el eval_el::getFirst() 
{ 
   return stack_el(opn1, is_terminal1);
}

stack_el eval_el::getSecond()
{
   return stack_el(opn2, is_terminal2);
}

void eval_el::setFirst(const stack_el s)
{
     opn1 = s.opn;
     is_terminal1 = s.is_terminal;
}

void eval_el::setSecond(const stack_el s)
{
    opn2 = s.opn;
    is_terminal2 = s.is_terminal;
}

void eval_el::assign_unary_to_first(const eval_el & assignee)
{
    opn1 = assignee.opn1;
    is_terminal1 = assignee.is_terminal1;
}

void eval_el::assign_unary_to_second(const eval_el & assignee)
{
    opn2 = assignee.opn1;
    is_terminal2 = assignee.is_terminal1;
}

// Ordering operators, so that op1 > op2 for all non-terminals
// and terminals appear in the second operand first
void eval_el::order(void)
{
    int k;
    if ((!is_terminal1) && (!is_terminal2))
        if ((k = opn2) > opn1)
        {
            opn2 = opn1;
            opn1 =  k;
        }
    else if ((is_terminal1) && (!is_terminal2))
        if ((k = opn2) > opn1)
        {
            opn2 = opn1;
            opn1 =  k;
            is_terminal1 = false;
            is_terminal2 = true;
        }
}
/*
static bool operator==(const term_el& x, const term_el& y)
{
	return x._simplePredicate == y._simplePredicate; 
}
*/
//
// CQL Compiler methods
//
/*    
Cql2Dnf::Cql2Dnf() 
{
    eval_heap.reserveCapacity(16);
    terminal_heap.reserveCapacity(16);
}

Cql2Dnf::Cql2Dnf(CQLSelectStatement & cqs)
{
    eval_heap.reserveCapacity(16);
    terminal_heap.reserveCapacity(16);
    compile(&cqs);
}

Cql2Dnf::Cql2Dnf(CQLSelectStatement * cqs)
{
    eval_heap.reserveCapacity(16);
    terminal_heap.reserveCapacity(16);
    compile(cqs);
}
*/
Cql2Dnf::Cql2Dnf(CQLPredicate& topLevel){
    eval_heap.reserveCapacity(16);
    terminal_heap.reserveCapacity(16);
    compile(topLevel);
}

Cql2Dnf::~Cql2Dnf() {}
/*
void Cql2Dnf::compile(CQLSelectStatement * cqs){
	CQLPredicate topLevel = cqs->getPredicate();
	compile(topLevel);
}
*/
void Cql2Dnf::compile(CQLPredicate& topLevel)
{
	PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::compile");

    _strip_ops_operands(topLevel);
    _buildEvalHeap();
    _pushNOTDown();
    _factoring();
    _construct();
    eval_heap.clear();

	PEG_METHOD_EXIT();
}

void Cql2Dnf::_buildEvalHeap()
{
	PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_buildEvalHeap");

    Stack<stack_el> stack;

    // Counter for Operands
    Uint32 j = 0;

    for (Uint32 i = 0, n = _operations.size(); i < n; i++)
    {
        OperationType op = _operations[i];

        switch (op)
        {
            case CQL_OR:
            case CQL_AND:
            {
                PEGASUS_ASSERT(stack.size() >= 2);

                stack_el op1 = stack.top();
                stack.pop();

                stack_el op2 = stack.top();

                // generate Eval expression
                eval_heap.append(eval_el(0, op , op1.opn, op1.is_terminal,
                                 op2.opn , op2.is_terminal));

                stack.top() = stack_el(eval_heap.size()-1, false);

                break;
            }

            case CQL_NOT:
            {
                PEGASUS_ASSERT(stack.size() >= 1);

                stack_el op1 = stack.top();

                // generate Eval expression
                eval_heap.append(eval_el(0, op , op1.opn, op1.is_terminal,
                                 -1, true));

                stack.top() = stack_el(eval_heap.size()-1, false);

                break;
            }

            case CQL_EQ: 
            case CQL_NE:
            case CQL_LT:
            case CQL_LE:
            case CQL_GT:
            case CQL_GE:
				case CQL_ISA:
				case CQL_LIKE:
            {
                PEGASUS_ASSERT(_operands.size() >= 2);

                CQLExpression lhs = _operands[j++];

                CQLExpression rhs = _operands[j++];

		CQLSimplePredicate sp(lhs,rhs,_convertOpType(op));
                terminal_heap.push(term_el(false, sp));

                stack.push(stack_el(terminal_heap.size()-1, true));

                break;
            }

            case CQL_IS_NULL:
            {
                PEGASUS_ASSERT(_operands.size() >= 1);
                CQLExpression expression = _operands[j++];
		CQLSimplePredicate dummy(expression,IS_NULL);
                terminal_heap.push(term_el(false, dummy));

                stack.push(stack_el(terminal_heap.size()-1, true));

                break;
            }

            case CQL_IS_NOT_NULL:
            {
                PEGASUS_ASSERT(_operands.size() >= 1);
                CQLExpression expression = _operands[j++];
                CQLSimplePredicate dummy(expression,IS_NOT_NULL);
                terminal_heap.push(term_el(false, dummy));

                stack.push(stack_el(terminal_heap.size()-1, true));

                break;
            }
	    case CQL_NOOP:
	    default: break;
        }
    }

    PEGASUS_ASSERT(stack.size() == 1);

    PEG_METHOD_EXIT();
}

void Cql2Dnf::_pushNOTDown()
{
    PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_pushNOTDown");

    for (int i=eval_heap.size()-1; i >= 0; i--)
    {
        Boolean _found = false;

        // Order all operators, so that op1 > op2 for non-terminals
        // and terminals appear as second operand

        eval_heap[i].order();

        // First solve the unary NOT operator

        if (eval_heap[i].op == CQL_NOT)
       {
            // This serves as the equivalent of an empty operator
            eval_heap[i].op = CQL_NOOP;

            // Substitute this expression in all higher order eval statements
            // so that this node becomes disconnected from the tree

            for (int j=eval_heap.size()-1; j > i;j--)
            {
               // Test first operand
               if ((!eval_heap[j].is_terminal1) && (eval_heap[j].opn1 == i))

                   eval_heap[j].assign_unary_to_first(eval_heap[i]);

               // Test second operand
               if ((!eval_heap[j].is_terminal2) && (eval_heap[j].opn2 == i))

                   eval_heap[j].assign_unary_to_second(eval_heap[i]);
            }

            // Test: Double NOT created by moving down

            if (eval_heap[i].mark)
               eval_heap[i].mark = false;
            else
               _found = true;
            // else indicate a pending NOT to be pushed down further
        }

        // Simple NOT created by moving down

        if (eval_heap[i].mark)
        {
            // Remove the mark, indicate a pending NOT to be pushed down
            // further and switch operators (AND / OR)

            eval_heap[i].mark=false;
            if (eval_heap[i].op == CQL_OR) eval_heap[i].op = CQL_AND;
            else if (eval_heap[i].op == CQL_AND) eval_heap[i].op = CQL_OR;

            // NOT operator is already ruled out
            _found = true;
        }

        // Push a pending NOT further down
        if (_found)
        {
             // First operand

             int j = eval_heap[i].opn1;
             if (eval_heap[i].is_terminal1)
                 // Flip NOT mark
                 terminal_heap[j].negate();
             else
                 eval_heap[j].mark = !(eval_heap[j].mark);

             //Second operand (if it exists)

             if ((j = eval_heap[i].opn2) >= 0)
             {
                 if (eval_heap[i].is_terminal2)
                     // Flip NOT mark
                     terminal_heap[j].negate();
                 else
                     eval_heap[j].mark = !(eval_heap[j].mark);
             }
        }
    }
	PEG_METHOD_EXIT();
}

void Cql2Dnf::_factoring(void)
{
	PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_factoring");

    int i = 0,n = eval_heap.size();
    //for (int i=eval_heap.size()-1; i >= 0; i--)
    while (i < n)
    {
        int _found = 0;
        int index = 0;

        // look for expressions (A | B) & C  ---> A & C | A & B
        if (eval_heap[i].op == CQL_AND)
        {
            if (!eval_heap[i].is_terminal1)
            {
                index = eval_heap[i].opn1; // remember the index
                if (eval_heap[index].op == CQL_OR) _found = 1;
            }

            if ((_found == 0) && (!eval_heap[i].is_terminal2))
            {
                index = eval_heap[i].opn2; // remember the index
                if (eval_heap[index].op == CQL_OR) _found = 2;
            }

            if (_found != 0)
            {
                 //int u1,u1_t,u2,u2_t,u3,u3_t;
                 stack_el s;

                 if (_found == 1)
                     s = eval_heap[i].getSecond();
                 else
                     s = eval_heap[i].getFirst();

                 // insert two new expression before entry i
                 eval_el evl;

                 evl = eval_el(false, CQL_OR, i+1, false, i, false);
                 if ((Uint32 )i < eval_heap.size()-1)
                     eval_heap.insert(i+1, evl);
                 else
                     eval_heap.append(evl);
                 eval_heap.insert(i+1, evl);

                 for (int j=eval_heap.size()-1; j > i + 2; j--)
                 {
                     //eval_heap[j] = eval_heap[j-2];

                     // adjust pointers

                     if ((!eval_heap[j].is_terminal1)&&
                         (eval_heap[j].opn1 >= i))
                         eval_heap[j].opn1 += 2;
                     if ((!eval_heap[j].is_terminal2)&&
                         (eval_heap[j].opn2 >= i))
                         eval_heap[j].opn2 += 2;
                 }

                 n+=2; // increase size of array

                 // generate the new expressions : new OR expression


                 // first new AND expression
                 eval_heap[i+1].mark = false;
                 eval_heap[i+1].op = CQL_AND;
                 eval_heap[i+1].setFirst(s);
                 eval_heap[i+1].setSecond( eval_heap[index].getFirst());
                 eval_heap[i+1].order();


                 // second new AND expression
                 eval_heap[i].mark = false;
                 eval_heap[i].op = CQL_AND;
                 eval_heap[i].setFirst(s);
                 eval_heap[i].setSecond( eval_heap[index].getSecond());
                 eval_heap[i].order();

                 // mark the indexed expression as inactive
                 //eval_heap[index].op = WQL_IS_TRUE; possible disconnects
                 i--;

            } /* endif _found > 0 */

        } /* endif found AND operator */

        i++; // increase pointer
    }

	PEG_METHOD_EXIT();
}

void Cql2Dnf::_strip_ops_operands(CQLPredicate& topLevel)
{
	PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_strip_ops_operands");
	//
	// depth first search for all operations and operands
	// extract operations and operands and store in respective arrays for later processing
	//
	_destruct(topLevel);
	if(topLevel.getInverted()){
        	_operations.append(CQL_NOT);
        }
	PEG_METHOD_EXIT();
}

OperationType Cql2Dnf::_convertOpType(ExpressionOpType op){
	switch(op){
		case EQ: return CQL_EQ;
		case NE: return CQL_NE;
		case GT: return CQL_GT;
		case LT: return CQL_LT;
		case GE: return CQL_GE;
		case LE: return CQL_LE;
		case IS_NULL: return CQL_IS_NULL;
		case IS_NOT_NULL: return CQL_IS_NOT_NULL;
		case ISA: return CQL_ISA;
		case LIKE: return CQL_LIKE;
		default: return CQL_NOOP;
	}
}

ExpressionOpType Cql2Dnf::_convertOpType(OperationType op){
        switch(op){
                case CQL_EQ: return EQ;
                case CQL_NE: return NE;
                case CQL_GT: return GT;
                case CQL_LT: return LT;
                case CQL_GE: return GE;
                case CQL_LE: return LE;
                case CQL_IS_NULL: return IS_NULL; 
                case CQL_IS_NOT_NULL: return IS_NOT_NULL;
					 case CQL_ISA: return ISA;
					 case CQL_LIKE: return LIKE;
                default: return EQ; // should never get here
        }
	return EQ; // should never get here
}

void Cql2Dnf::_destruct(CQLPredicate& _p){
	if(_p.isSimple()){
		CQLSimplePredicate _sp = _p.getSimplePredicate();
		_operations.append(_convertOpType(_sp.getOperation()));
		_operands.append(_sp.getLeftExpression());
		if((_operations[_operations.size()-1] != CQL_IS_NULL) && (_operations[_operations.size()-1] != CQL_IS_NOT_NULL))
			_operands.append(_sp.getRightExpression());
	}
	else{
		Array<CQLPredicate> _preds = _p.getPredicates();
		Array<BooleanOpType> _boolops = _p.getOperators();
		for(Uint32 i=0;i<_preds.size();i++){
			_destruct(_preds[i]);
			if(_preds[i].getInverted()){
				_operations.append(CQL_NOT);
			}
			if(i > 0){
				if(_boolops[i-1] == AND){
					_operations.append(CQL_AND);
				}
				if(_boolops[i-1] == OR){
                                        _operations.append(CQL_OR);
                                }
			}
		}
	}
}

void Cql2Dnf::_construct(){

	PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_construct");
	//
	// Each eval_el on the eval heap contains all the information needed to make a CQLPredicate.  
	// We will build a CQLPredicate for every element in the eval heap. So there is a 1 to 1 correspondence
	// between elements in the eval heap and elements in the CQLPredicate array used below.  
	// The first eval_el on the eval heap will always contain at least one terminal if the operation is a NOT
	// or two terminals if the operation is AND or OR.  We are guaranteed to build a CQLPredicate from the first
	// position in the eval_heap array. 
	//
	// The key to the algorithm is the isterminalX flag.  When set to true, we go to the 
	// term_heap and get the CQLSimplePredicate.  When set to false, we go to the _preds array below
	// and get the CQLPredicate.  Since there is a 1 - 1 correspondence, as explained above, the index
	// referred to by eval.opn1 or eval.opn2 is valid into the _preds array.
	//
	// For ANDs and ORs, we need two operands, as explained above, we get those operands
	// from either the term_heap or the _preds array.  For NOTs, we need only 1 operand, and that
	// comes from either the term_heap or the _preds array.
	//
	// When finished, the last element in the _preds array contains the top level CQLPredicate (the rebuilt tree)
	//
	// Example:  a=b^(!c=d v e=f)
	// If the current eval_heap looks like: 
	//	0,NOT,1,True,-1,True [index = 0]
	// 	0,OR,2,True,0,False  [index = 1]
	// 	0,AND,1,False,0,True [index = 2]
	//
	// And the current term_heap looks like:
	//	CQLSimplePredicate(a=b) [index = 0]
	// 	CQLSimplePredicate(c=d) [index = 1]
	//      CQLSimplePredicate(e=f) [index = 0]
	//
	// The _preds array at the end would look like:
	//	CQLPredicate(!c==d)        [index = 0]
	//	CQLPredicate(e==f v !c==d) [index = 1]
	// 	CQLPredicate((e==f v !c==d) ^ a==b) [index = 2]  (the rebuilt tree)
	//

	if(eval_heap.size() > 0){
	   Array<CQLPredicate> _preds;
	   CQLPredicate pred;
	   for(Uint32 i=0;i<eval_heap.size();i++){
		eval_el eval = eval_heap[i];
		if(eval.is_terminal1 && eval.is_terminal2){
			switch(eval.op){
				case CQL_NOT:
           	{
              	_preds.append(CQLPredicate(terminal_heap[eval.opn1]._simplePredicate,true));
					break;
            }
				case CQL_NOOP:
				{
					CQLPredicate p(terminal_heap[eval.opn1]._simplePredicate,false);
					if(terminal_heap[eval.opn1].NOT == true)
					   p.setInverted(true);
					_preds.append(p);
					break;
				}
            case CQL_AND:
            {
					CQLPredicate p;
					CQLPredicate p1(terminal_heap[eval.opn2]._simplePredicate,false);
					if(terminal_heap[eval.opn2].NOT == true)
					   p1.setInverted(true);
					p.appendPredicate(p1);
					CQLPredicate p2(terminal_heap[eval.opn1]._simplePredicate,false);
					if(terminal_heap[eval.opn1].NOT == true)
					   p2.setInverted(true);
					p.appendPredicate(p2,AND);
					_preds.append(p);
					break;
            }
            case CQL_OR:
            {
				   CQLPredicate p;
               CQLPredicate p1(terminal_heap[eval.opn2]._simplePredicate,false);
					if(terminal_heap[eval.opn2].NOT == true)
					   p1.setInverted(true);
               p.appendPredicate(p1);
               CQLPredicate p2(terminal_heap[eval.opn1]._simplePredicate,false);
					if(terminal_heap[eval.opn1].NOT == true)
                  p2.setInverted(true);
               p.appendPredicate(p2,OR);
               _preds.append(p);
               break;
            }
				case CQL_EQ:
                		case CQL_NE:
                		case CQL_GT:
                		case CQL_LT:
                		case CQL_GE: 
                		case CQL_LE:
							case CQL_ISA:
							case CQL_LIKE:
                		case CQL_IS_NULL:
                		case CQL_IS_NOT_NULL: break;
				
			}
		}else if(eval.is_terminal1 && !eval.is_terminal2){
			switch(eval.op){
				case CQL_NOT:
            {
               _preds.append(CQLPredicate(terminal_heap[eval.opn1]._simplePredicate,true));
					break;
            }
				case CQL_NOOP:
            {
               CQLPredicate p(terminal_heap[eval.opn1]._simplePredicate,false);
               if(terminal_heap[eval.opn1].NOT == true)
                  p.setInverted(true);
               _preds.append(p);
               break;
            }
            case CQL_AND:
            {
               CQLPredicate p;
               CQLPredicate p1(terminal_heap[eval.opn1]._simplePredicate,false);
			      if(terminal_heap[eval.opn1].NOT == true)
                  p1.setInverted(true);
               p = _preds[eval.opn2];
               p.appendPredicate(p1,AND);
               _preds.append(p);
               break;
            }
            case CQL_OR:
            {
              CQLPredicate p;
              CQLPredicate p1(terminal_heap[eval.opn1]._simplePredicate,false);
				  if(terminal_heap[eval.opn1].NOT == true)
                 p1.setInverted(true);
              p = _preds[eval.opn2];
              p.appendPredicate(p1,OR);
              _preds.append(p);
              break;
            }
				case CQL_EQ:
                                case CQL_NE:
                                case CQL_GT:
                                case CQL_LT:
                                case CQL_GE:
                                case CQL_LE:
										  case CQL_ISA:
							           case CQL_LIKE:
                                case CQL_IS_NULL:
                                case CQL_IS_NOT_NULL: break;
                               
			}
		}else if(!eval.is_terminal1 && eval.is_terminal2){
         switch(eval.op){
            case CQL_NOT:
            {
				   CQLPredicate p = _preds[eval.opn1];
               p.setInverted(true);
                _preds.append(p);
					break;
            }
				case CQL_NOOP:
            {
               _preds.append(_preds[eval.opn1]);
					break;
            }
            case CQL_AND:
            {
               CQLPredicate p;
               CQLPredicate p1(terminal_heap[eval.opn2]._simplePredicate,false);
				   if(terminal_heap[eval.opn2].NOT == true)
                  p1.setInverted(true);
               p = _preds[eval.opn1];
               p.appendPredicate(p1,AND);
               _preds.append(p);
               break;
            }
            case CQL_OR:
            {
               CQLPredicate p;
               CQLPredicate p1(terminal_heap[eval.opn2]._simplePredicate,false);
				   if(terminal_heap[eval.opn2].NOT == true)
                  p1.setInverted(true);
               p = _preds[eval.opn1];
               p.appendPredicate(p1,OR);
               _preds.append(p);
               break;
            }
				case CQL_EQ:
                                case CQL_NE:
                                case CQL_GT:
                                case CQL_LT:
                                case CQL_GE:
                                case CQL_LE:
										  case CQL_ISA:
										  case CQL_LIKE:
                                case CQL_IS_NULL:
                                case CQL_IS_NOT_NULL: break;
                               
                        }                                                                                                                                    
                }else{ // !eval.is_terminal1 && !eval.is_terminal2
			switch(eval.op){
            case CQL_NOT:
            {
					CQLPredicate p = _preds[eval.opn1];
					p.setInverted(true);
               _preds.append(p);
					break;
            }
			   case CQL_NOOP:
            {
               _preds.append(_preds[eval.opn1]);
					break;
            }
            case CQL_AND:
            {
					CQLPredicate p = _preds[eval.opn2];
               _flattenANDappend(p,AND,_preds[eval.opn1]);
               _preds.append(p);
               break;
            }
            case CQL_OR:
            {
					CQLPredicate p = _preds[eval.opn2];
               _flattenANDappend(p,OR,_preds[eval.opn1]);
               _preds.append(p);
               break;
            }
				case CQL_EQ:
                                case CQL_NE:
                                case CQL_GT:
                                case CQL_LT:
                                case CQL_GE:
                                case CQL_LE:
										  case CQL_ISA:
										  case CQL_LIKE:
                                case CQL_IS_NULL:
                                case CQL_IS_NOT_NULL: break;
                                
                        }

		}
	   } // end for(...)

	   _dnfPredicate = _preds[_preds.size()-1];

	} // end if
	else{ // we just have a CQLSimplePredicate on the terminal_heap
		PEGASUS_ASSERT(terminal_heap.size() == 1);
		_dnfPredicate = CQLPredicate(terminal_heap[0]._simplePredicate,false);
	}

	PEG_METHOD_EXIT();
}

CQLPredicate Cql2Dnf::getDnfPredicate(){
	return _dnfPredicate;
}

CQLPredicate Cql2Dnf::_flattenANDappend(CQLPredicate& topLevel, BooleanOpType op, CQLPredicate& p){

	PEG_METHOD_ENTER(TRC_CQL, "Cql2Dnf::_flattenANDappend");
	//
	// this is to prevent appending complex predicates to the top level predicate
	// the final DNFed predicate must only have simple predicates inside its predicate array
	//
	// example:
	// say P(top level) = A AND B
	// say P1 = C AND D
	// say we need to OR them together
	// we cant call P.appendPredicate(P1,OR) because this creates one more complex predicate layer
	// instead we would:
	// -> get P1s predicates (which should all be simple)
	// -> append its first predicate to P along with the operator passed into us
	// -> at this point we have P = A AND B OR C
	// -> then go through P1s remaining predicates and append them and P1s operators to P
	// -> when finished, we have P = A AND B OR C AND D INSTEAD of having P = A AND B OR P1 where P1 is a complex predicate
	//

	if(!p.isSimple()){
		Array<CQLPredicate> preds = p.getPredicates();
		Array<BooleanOpType> ops = p.getOperators();
		for(Uint32 i=0;i<preds.size();i++){
			if(i==0) topLevel.appendPredicate(preds[i],op);
			else topLevel.appendPredicate(preds[i],ops[i-1]);
		}
	}else{
		topLevel.appendPredicate(p,op);
	}

	PEG_METHOD_EXIT();
	return topLevel;
}

PEGASUS_NAMESPACE_END
