########################################################################
#
# File Name: 	        QueryAdapter.py
#
# Documentation:	http://docs.ftsuite.com/4ODS/Drivers/Postgres/QueryAdapter.py.html
#
"""
The Postgres back-end.
WWW: http://4suite.org/4ODS         e-mail: support@4suite.org

Copyright (c) 1999 Fourthought, Inc., USA.   All Rights Reserved.
See  http://4suite.org/COPYRIGHT  for license and copyright information
"""


def pp(tup):
    for t in tup:
        if type(t) == type(()):
            print t[0],
        else:
            print t,
        print ' ',
    print

from Ft.Ods.Parsers.Oql import OqlParseTreeTuple


class QueryAdapter:
        
    def initialize(self):
        self.__varDefs = {}

    #FIXME: Kill the prints once flow is complete
    def runQuery(self, cmd):
        self.__parser = OqlParseTreeTuple.OqlParseTreeTuple()
        self.initialize()
        pTree = self.__parser.parse(cmd)
        return self.processQueryProgram(pTree[0])

    def processQueryProgram(self,qProg):

        if qProg[1][0] == 'QUERY':
            return self.processQuery(qProg[1])
        else:
            raise "Not supported"


    def processQuery(self,q):
        if q[1][0] == 'SELECTEXPR':
            return self.processSelectExpr(q[1])
        else:
            raise "Not Supported"

    def processSelectExpr(self,se):

        #Nodes of a select expression:
        #0 "SELECTEXPR"
        #1  select
        #2  distinct?
        #2  Projection Attributes
        #3  FROM CLAUSE
        #4  WHERE ClAUSE
        #5 groupClause
        #6 havingClause

        paIndex = 2
        fcIndex = 3
        wcIndex = 4

        baseSet = self.processFromClause(se[fcIndex])

        if se[wcIndex]:
            newSet = self.processWhereClause(se[wcIndex],baseSet)
        else:
            newSet = baseSet

        #Use the projection attributes
        #To turn the result list into the final results
        return self.processProjectionAttributes(se[paIndex],newSet)

    def processProjectionAttributes(self,pa,inSet):
        if pa[1] == '*':
            #Turn the inset into a list of instances
            #Anything that is not an instance will bomb!!!!
            return map(lambda x,f=self.getObject:f(x[0]),inSet)
        else:
            raise "Not Supported %s" % str(pa)
        

    def processWhereClause(self,wc,set):
        return filter(lambda x,f=self.processExpr,e=wc[2]: f(e,x),set)


    def processFromClause(self,fc):
        return self.processIteratorDefList(fc[2])


    def processIteratorDefList(self,idl):

        ids = self.processIteratorDef(idl[1])

        if idl[3]:
            ids = ids + self.processIteratorDefList(idl[3])

        return ids

    def processIteratorDef(self,idf):

        if not idf[2] or idf[2] == 'as':
            expr = self.processExpr(idf[1])
            l = self.getExtentTuple(expr)
            self.__varDefs[expr] = l
            if idf[2] == 'as':
                self.__varDefs[idf[3]] = l
            return l
        else:
            raise "Iteratordef IDENT in EXPR not supported"


    def processExpr(self,expr,src=None):
        return self.processCastExpr(expr[1],src)

    def processCastExpr(self,cExpr,src):

        if cExpr[2]:
            raise "(type) castExpr not Supported"
        else:
            return self.processOrExpr(cExpr[1],src)

    def processOrExpr(self,oExpr,src):
        
        if oExpr[2]:
            raise "orelseExpr or orElseExpr not Supported"
        else:
            return self.processOrElseExpr(oExpr[1],src)

    def processOrElseExpr(self,oExpr,src):
        
        if oExpr[2]:
            raise "andExpr orelse andExpr not Supported"
        else:
            return self.processAndExpr(oExpr[1],src)

    def processAndExpr(self,aExpr,src):

        if aExpr[3]:
            left = self.processQuantifierExpr(aExpr[1],src)
            right = self.processQuantifierExpr(aExpr[3],src)
            if aExpr[2] == 'and':
                return left and right
            else:
                raise "NJot suported"
        else:
            return self.processQuantifierExpr(aExpr[1],src)

    def processQuantifierExpr(self,qExpr,src):

        if qExpr[2]:
            raise " %s not Supported" % str(qExpr)
        else:
            return self.processAndThenExpr(qExpr[1],src)

    def processAndThenExpr(self,aExpr,src):

        if aExpr[2]:
            raise " %s not Supported" % str(aExpr)
        else:
            return self.processEqualityExpr(aExpr[1],src)

    def processEqualityExpr(self,eExpr,src):

        if eExpr[4]:
            raise " %s not Supported" % str(eExpr)
        elif eExpr[3]:
            left = self.processRelationalExpr(eExpr[1],src)
            right = self.processRelationalExpr(eExpr[3],src)
            if eExpr[2] == '=':
                return left == right
            else:
                raise "Not Supported %s" % str(eExpr)
        else:
            return self.processRelationalExpr(eExpr[1],src)

    def processRelationalExpr(self,rExpr,src):

        if rExpr[2]:
            raise " %s not Supported" % str(rExpr)
        else:
            return self.processAdditiveExpr(rExpr[1],src)

    def processAdditiveExpr(self,aExpr,src):

        if aExpr[2]:
            raise " %s not Supported" % str(aExpr)
        else:
            return self.processMultiplicativeExpr(aExpr[1],src)

    def processMultiplicativeExpr(self,mExpr,src):

        if mExpr[2]:
            raise " %s not Supported" % str(mExpr)
        else:
            return self.processInExpr(mExpr[1],src)

    def processInExpr(self,iExpr,src):

        if iExpr[2]:
            raise " %s not Supported" % str(iExpr)
        else:
            return self.processUnaryExpr(iExpr[1],src)

    def processUnaryExpr(self,uExpr,src):

        if uExpr[2]:
            raise " %s not Supported" % str(uExpr)
        else:
            return self.processPostfixExpr(uExpr[1],src)

    def processPostfixExpr(self,pExpr,src):

        if pExpr[3]:
            raise " %s not Supported" % str(pExpr)
        elif pExpr[2]:
            t = self.processPrimaryExpr(pExpr[1],src)
            if not t in self.__varDefs.keys():
                raise "Unknown Reference to %s" % t
            #Make sure we are of the correct type
            #Is this the correct place to do this?
            if not src in self.__varDefs[t]:
                return 0
            #We are of the correct type
            return self.processPostfixOperandList(pExpr[2],src)
        else:
            return self.processPrimaryExpr(pExpr[1],src)

    def processPostfixOperandList(self,oList,src):
        if oList[2]:
            raise "Not supported %s" % str(oList)
        else:
            return self.processPostfixOperand(oList[1],src)

    def processPostfixOperand(self,op,src):
        if op[3]:
            raise "Not supported %s" % str(op)
        else:
            #Traverse to the name on index 2
            if op[2] in src[1][1]:
                ind = list(src[1][1]).index(op[2])
                return src[1][2][ind]
            else:
                raise "Traversal to %s not allowed on object %i" % (op[2],src[0])

    def processPrimaryExpr(self,pExpr,src):

        if pExpr[2]:
            raise " %s not Supported" % str(pExpr)
        elif type(pExpr[1]) == type(()):
            if pExpr[1][0] == 'LITERAL':
                #Process a Literal Expression
                return self.processLiteral(pExpr[1])
        else:
            #FIXME Check for others
            return pExpr[1]
        
    def processLiteral(self,lit):
        if lit[1][0] == 'STRINGLITERAL':
            return str(lit[1][1][1:-1])
        if lit[1][0] == 'LONGLITERAL':
            return long(lit[1][1])
        else:
            raise "Not supported %s" % str(lit)

