########################################################################
#
# File Name:            RifParserImp.py
#
# Documentation:        http://docs.4suite.org/4Rdf/Alternative.py.html
#
"""
Ril Implementation
WWW: http://4suite.org/4RDF         e-mail: support@4suite.org

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

import string
import cStringIO
import ExtClasses

from Ft.Rdf.Parsers import Ril
ruleCtr = 0
from Ft import Rdf
from Ft.Lib import pDomlette

from Ft.Rdf.Inference import Common,Assert,Query, Action, Command, Predicate, Rule



class RilParserImp:
    def __init__(self):
        self.__defs = []

    def parse(self,st):
        s = cStringIO.StringIO(st)
        r = pDomlette.PyExpatReader()
        
        doc = r.fromStream(s)
        self.__defs = []

        for n in doc.documentElement.childNodes:
            if n.nodeType != pDomlette.Node.ELEMENT_NODE:
                continue
            self.__defs = self.__defs + self.__processTopLevelNode(n)

        return self.__defs
    
    def __processTopLevelNode(self,node):
        if node.namespaceURI != Rdf.RIL_NAMESPACE:
            #It must be an extension action
            return self.__processAction(node)
        if node.localName == 'assert':
            return self.__processAssert(node)
        elif node.localName == 'query':
            return self.__processQuery(node)
        elif node.localName == 'rule':
            return self.__processRule(node)
        else:
            return self.__processAction(node)

    def __processRule(self,node):

        #One premise and one conlusion
        premise = None
        conclusion = None
        for n in node.childNodes:
            if n.nodeType != pDomlette.Node.ELEMENT_NODE:
                continue
            if n.namespaceURI != Rdf.RIL_NAMESPACE:
                raise Ril.RilException(Ril.RULE_WRONG_CHILD,n.tagName)
            if n.localName == 'premise':
                if premise:
                    raise Ril.RilException(Ril.RULE_WRONG_CHILD,n.tagName)
                premise = n
            elif n.localName == 'conclusion':
                if conclusion:
                    raise Ril.RilException(Ril.RULE_WRONG_CHILD,n.tagName)
                conclusion = n
            else:
                raise Ril.RilException(Ril.RULE_WRONG_CHILD,n.tagName)

        premise = self.__processPremise(premise)
        conclusion = self.__processConclusion(conclusion)
        global ruleCtr
        ruleId = "%s#rule-%d" % (Rdf.RIL_NAMESPACE,ruleCtr)
        ruleCtr = ruleCtr + 1
        rule = Rule.Rule(ruleId,premise,conclusion)
        return [rule]

    def __processConclusion(self,node):
        conclusions = []
        for n in node.childNodes:
            if n.nodeType != pDomlette.Node.ELEMENT_NODE:
                continue
            if n.namespaceURI == Rdf.RIL_NAMESPACE and n.localName == 'rule':
                raise Ril.RilException(Ril.RULE_WRONG_CHILD,n.tagName)
            conclusions = conclusions + self.__processTopLevelNode(n)
        return conclusions

    def __processPremise(self,node):
        predicates = []
        for n in node.childNodes:
            if n.nodeType != pDomlette.Node.ELEMENT_NODE:
                continue
            predicates = predicates + self.__processPredicate(n)
        return predicates

    def __processPredicate(self,node):
        predicates = []
        if node.namespaceURI == Rdf.RIL_NAMESPACE:
            #Must be and,orm or not

            #Get the children of this node
            children = filter(lambda x,t=pDomlette.Node.ELEMENT_NODE:x.nodeType == t,node.childNodes)
            for child in children:
                args = self.__processArgs(child)
                fullName = child.namespaceURI + '#' + child.localName
                if node.localName == 'and':
                    if len(args) == 1:
                        predicates.append(Predicate.AndSinglePredicate(fullName,args))
                    elif len(args) == 2:
                        predicates.append(Predicate.AndDualPredicate(fullName,args))
                    else:
                        raise Ril.RilException(Ril.INVALID_PREDICATE,child.tagName)
                elif node.localName == 'or':
                    if len(args) == 1:
                        predicates.append(Predicate.OrSinglePredicate(fullName,args))
                    elif len(args) == 2:
                        predicates.append(Predicate.OrSinglePredicate(fullName,args))
                    else:
                        raise Ril.RilException(Ril.INVALID_PREDICATE,child.tagName)
                elif node.localName == 'not':
                    if len(args) == 1:
                        predicates.append(Predicate.NotSinglePredicate(fullName,args))
                    elif len(args) == 2:
                        predicates.append(Predicate.NotSinglePredicate(fullName,args))
                    else:
                        raise Ril.RilException(Ril.INVALID_PREDICATE,child.tagName)
                else:
                    raise Ril.RilException(Ril.INVALID_PREDICATE,node.tagName)
        else:
            #An and
            args = self.__processArgs(node)
            fullName = node.namespaceURI + '#' + node.localName
            if len(args) == 1:
                predicates.append(Predicate.AndSinglePredicate(fullName,args))
            elif len(args) == 2:
                predicates.append(Predicate.AndDualPredicate(fullName,args))
            else:
                raise Ril.RilException(Ril.INVALID_PREDICATE,node.tagName)
            
                        
        return predicates

    def __processAction(self,node):
        action = None
        if node.namespaceURI != Rdf.RIL_NAMESPACE:
            raise Exception("elements in the %s namespace not supported as actions" % node.namespaceURI)

        if node.localName == 'subject':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(SUBJECT_ACTION_ALLOWS_ONE_CHILD)
            action = Action.StatementSubject(args[0])
        elif node.localName == 'predicate':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(PREDICATE_ACTION_ALLOWS_ONE_CHILD)
            action = Action.StatementPredicate(args[0])
        elif node.localName == 'object':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(OBJECT_ACTION_ALLOWS_ONE_CHILD)
            action = Action.StatementObject(args[0])
        elif node.localName == 'paths':
            startNode = None
            endNode = None
            pList = None
            for child in node.childNodes:
                if child.nodeType == pDomlette.Node.ELEMENT_NODE:
                    if child.namespaceURI == Rdf.RIL_NAMESPACE:
                        if child.localName == 'start':
                            startNode = child
                        elif child.localName == 'end':
                            endNode = child
                        elif child.localName == 'predicate-list':
                            pList = child
            if startNode == None or endNode == None or pList == None:
                raise Ril.RilException(Ril.PATH_MISSING_CHILD_NODES)
            start = self.__processArgs(startNode)
            if len(start) != 1:
                raise Ril.RilException(PATH_START_WRONG_CHILDREN)
            start = start[0]
            end = self.__processArgs(endNode)
            if len(end) != 1:
                raise Ril.RilException(PATH_END_WRONG_CHILDREN)
            end = end[0]

            predicates = []
            for child in pList.childNodes:
                if child.nodeType == pDomlette.Node.ELEMENT_NODE:
                    predicates.append(child.namespaceURI + '#' + child.localName)
            action = Action.PathsAction(start,end,Common.StringListLiteralArgument("arg",predicates))
        elif node.localName == 'reverse-paths':
            startNode = None
            endNode = None
            pList = None
            for child in node.childNodes:
                if child.nodeType == pDomlette.Node.ELEMENT_NODE:
                    if child.namespaceURI == Rdf.RIL_NAMESPACE:
                        if child.localName == 'start':
                            startNode = child
                        elif child.localName == 'end':
                            endNode = child
                        elif child.localName == 'predicate-list':
                            pList = child
            if startNode == None or endNode == None or pList == None:
                raise Ril.RilException(Ril.PATH_MISSING_CHILD_NODES)
            start = self.__processArgs(startNode)
            if len(start) != 1:
                raise Ril.RilException(PATH_START_WRONG_CHILDREN)
            start = start[0]
            end = self.__processArgs(endNode)
            if len(end) != 1:
                raise Ril.RilException(PATH_END_WRONG_CHILDREN)
            end = end[0]

            predicates = []
            for child in pList.childNodes:
                if child.nodeType == pDomlette.Node.ELEMENT_NODE:
                    predicates.append(child.namespaceURI + '#' + child.localName)
            action = Action.ReversePathsAction(start,end,Common.StringListLiteralArgument("arg",predicates))
        elif node.localName == 'index':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(INDEX_ACTION_ALLOWS_ONE_CHILD)
            index = node.getAttributeNS("","index")
            if index == "":
                raise Ril.RilException(INDEX_ACTION_REQUIRES_INDEX_ATTRIBUTE)
                
            action = Action.IndexAction(args[0],index)
                    
        elif node.localName == 'reverse':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(REVERSE_ACTION_ALLOWS_ONE_CHILD)
            action = Action.ReverseAction(args[0])
                    
        elif node.localName == 'slice':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(SLICE_ACTION_ALLOWS_ONE_CHILD)
            start = node.getAttributeNS("","start")
            if start == "":
                raise Ril.RilException(SLICE_ACTION_REQUIRES_START_ATTRIBUTE)

            end = node.getAttributeNS("","end")
            if end == "":
                raise Ril.RilException(SLICE_ACTION_REQUIRES_END_ATTRIBUTE)
                
            action = Action.SliceAction(args[0],start,end)
                    
        elif node.localName == 'unique':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(UNIQUE_ACTION_ALLOWS_ONE_CHILD)
            action = Action.UniqueAction(args[0])
                    
        elif node.localName == 'intersection':
            args = self.__processArgs(node)
            if len(args) != 2:
                raise Ril.RilException(INTERSECTION_ACTION_REQUIRES_TWO_CHILD)
            action = Action.IntersectionAction(args[0],args[1])
        elif node.localName == 'union':
            args = self.__processArgs(node)
            if len(args) != 2:
                raise Ril.RilException(UNION_ACTION_REQUIRES_TWO_CHILD)
            action = Action.UnionAction(args[0],args[1])
        elif node.localName == 'difference':
            args = self.__processArgs(node)
            if len(args) != 2:
                raise Ril.RilException(DIFFERENCE_ACTION_REQUIRES_TWO_CHILD)
            action = Action.DifferenceAction(args[0],args[1])
        elif node.localName == 'count':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(COUNT_ACTION_ALLOWS_ONE_CHILD)
            action = Action.CountAction(args[0])
        elif node.localName == 'sum':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(SUM_ACTION_ALLOWS_ONE_CHILD)
            action = Action.SumAction(args[0])
        elif node.localName == 'average':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(AVERAGE_ACTION_ALLOWS_ONE_CHILD)
            action = Action.AverageAction(args[0])
        elif node.localName == 'sort':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(SORT_ACTION_ALLOWS_ONE_CHILD)
            mode = node.getAttributeNS('','mode')
            if mode == '':
                mode = 'NUMBER'
            if not mode in ['NUMBER','TEXT']:
                raise Ril.RilException(SORT_INVALID_MODE,mode)
            if mode == 'NUMBER':
                mode = Action.SortType.NUMBER_SORT
            else:
                mode = Action.SortType.STRING_SORT
            key = node.getAttributeNS('','key')
            if key == '':
                key='object'
            action = Action.SortAction(args[0],mode,key)
        elif node.localName == 'variable-set':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(VARIABLE_SET_ACTION_ALLOWS_ONE_CHILD)
            name = node.getAttributeNS('','name')
            if name == '':
                raise Ril.RilException(VARIABLE_SET_REQUIRES_NAME_ATTRIBUTE)
            action = Action.VariableSetAction(name,args[0])
        elif node.localName == 'variable-ref':
            action = self.__processVariableRef(node)
        elif node.localName == 'param':
            args = self.__processArgs(node)
            if len(args) != 1:
                raise Ril.RilException(PARAM_ACTION_ALLOWS_ONE_CHILD)
            name = node.getAttributeNS('','name')
            if name == '':
                raise Ril.RilException(PARAM_REQUIRES_NAME_ATTRIBUTE)
            action = Action.ExternalParamAction(name,args[0])
        elif node.localName == 'message':
            args = self.__processArgs(node)
            action = Command.MessageCommand(args)
        elif node.localName == 'fire':
            action = Command.FireCommand()
        elif node.localName == 'stop':
            action = Command.StopCommand()
        else:
            raise Ril.RilException(Ril.UNKNOWN_ACTION,node.tagName)
        return [action]

    def __processQuery(self,node):
        #Only one child allowed
        query = None
        for n in node.childNodes:
            if n.nodeType != pDomlette.Node.ELEMENT_NODE:
                continue
            if query:
                raise Ril.RilException(Ril.QUERY_SINGLE_CHILD,n.tagName)

            args = self.__processArgs(n)
            fullName = n.namespaceURI + '#' + n.localName
            if ExtClasses.g_extensionQueries.has_key(fullName):
                klass = ExtClasses.g_extensionQueries[fullName]
                query = klass(args)
            elif len(args) == 1:
                #Create a single query
                query = Query.SingleQuery(fullName,args)
            elif len(args) == 2:
                query = Query.DualQuery(fullName,args)
            else:
                raise Ril.RilException(EXTENSION_QUERY_REQUIRED,"More then 2 arguments specified for default query %s" % fullName)

        return [query]

    def __processAssert(self,node):
        #We can have multiple element children
        asserts = []
        for n in node.childNodes:
            if n.nodeType != pDomlette.Node.ELEMENT_NODE:
                continue
            args = self.__processArgs(n)

            #Check for the negate attribute
            negate = 0
            if n.getAttributeNS('','negate') != '':
                negate = 1

            fullName = n.namespaceURI + '#' + n.localName
            if ExtClasses.g_extensionAsserts.has_key(fullName):
                klass = ExtClasses.g_extensionPredicates[fullName]
                assertsd.append(klass(negate,args))
            elif len(args) == 1:
                #Create a single predicate
                asserts.append(Assert.SingleAssert(fullName,negate,args))
            elif len(args) == 2:
                asserts.append(Assert.DualAssert(fullName,negate,args))
            else:
                raise Ril.RilException(EXTENSION_ASSERT_REQUIRED,"More then 2 arguments specified for default assert %s" % fullName)

        return asserts

    def __processArgs(self,node):        
        args = []
        for n in node.childNodes:
            if n.nodeType != pDomlette.Node.ELEMENT_NODE:
                continue
            if n.namespaceURI != Rdf.RIL_NAMESPACE:
                raise Ril.RilException(Ril.ARGUMENT_NOT_IN_RIL_NAMESPACE,n.tagName)
            if n.localName == 'string-list':
                args.append(self.__processStringList(n))
            elif n.localName == 'string':
                args.append(Common.StringLiteralArgument("arg",self.__loadString(n)))
            elif n.localName == 'variable':
                args.append(self.__processSkolemVariable(n))
            elif n.localName == 'variable-ref':
                args.append(self.__processVariableRef(n))
            elif n.localName == 'query':
                args.append(self.__processQuery(n)[0])
            else:
                #Must be an action
                args = args + self.__processAction(n)
        return args

    def __processStringList(self,node):
        strs = []
        for n in node.childNodes:
            if n.nodeType != pDomlette.Node.ELEMENT_NODE:
                continue
            if n.namespaceURI != Rdf.RIL_NAMESPACE:
                raise Ril.RilException(Ril.STRING_NOT_IN_RIL_NAMESPACE,n.tagName)
            if n.localName == 'string':
                strs.append(self.__loadString(n))
            else:
                raise Ril.RilException(Ril.UNKNOWN_STRING_TYPE,n.tagName)
        return Common.StringListLiteralArgument("arg",strs)

    def __processSkolemVariable(self,node):
        name = node.getAttributeNS('','name')
        if name == '':
            raise Ril.RilException(SKOLEM_VARIABLE_MISSING_NAME,n.tagName)
        return Common.SkolemVariableArgument(name)

    def __processVariableRef(self,node):
        name = node.getAttributeNS('','name')
        if name == '':
            raise Ril.RilException(VARIABLE_REF_MISSING_NAME,n.tagName)
        return Common.VariableReferenceArgument(name)

            

    def __loadString(self,node):
        node.normalize()
        if len(node.childNodes) == 0:
            return ""
        if node.firstChild.nodeType != pDomlette.Node.TEXT_NODE:
            raise Ril.RilException(Ril.INVALID_STRING_CHILD,n.tagName)

        return node.firstChild.data
                          



if __name__ == '__main__':

    import sys
    p = RilParserImp()
    if len(sys.argv)==2:
        l = open(sys.argv[1],'r').read()
    else:
        l = raw_input(">>>")

    rt = p.parse(l)
    print "The Definitions"
    print "<ril:expression xmlns:ril = '%s'>" % Rdf.RIL_NAMESPACE
    for d in rt:
        #print d
        print d._4rdf_dump(indent=1)

    print "</ril:expression>" 
