########################################################################
#
# File Name:            Optimizer.py
#
#
"""
Support routines for Versa optimizations
WWW: http://4suite.org/4XSLT        e-mail: support@4suite.org

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

import types

import DataTypes, NamedExpressions
import ResourceExpressions, CoreFunctions, Literals, Traversal
from Ft.Lib import boolean, number


def IsCoreFunction(expr, name):
    if isinstance(expr, NamedExpressions.FunctionCall) and \
       expr._name == name:
        return 1
    return 0


def IsSimpleTextSearch(expr):
    if isinstance(expr, Traversal.ForwardTraversal) and expr.isFilter and \
       isinstance(expr.resources, ResourceExpressions.CurrentExpression) and \
       (isinstance(expr.predicates, Literals.ResourceLiteral) or \
        isinstance(expr.predicates, ResourceExpressions.PureQNameExpression)) and \
       IsCoreFunction(expr.filterExpr, "contains") and \
       isinstance(expr.filterExpr._args[0], Literals.StringLiteral):
        return expr.predicates, expr.filterExpr._args[0].evaluate(con)
    return None


def IsSimpleForwardTraverse(expr):
    if isinstance(expr, Traversal.ForwardTraversal) and \
       isinstance(expr.resources, ResourceExpressions.CurrentExpression) and \
       isinstance(expr.filterExpr, ResourceExpressions.LiteralExpression) and \
       expr.filterExpr.value == boolean.true and \
       (isinstance(expr.predicates, Literals.ResourceLiteral) or \
        isinstance(expr.predicates, ResourceExpressions.PureQNameExpression)):
        return expr.predicates
    return None


def IsSimpleBackwardTraverse(expr):
    if isinstance(expr, Traversal.BackwardTraversal) and \
       isinstance(expr.objects, ResourceExpressions.CurrentExpression) and \
       isinstance(expr.filterExpr, ResourceExpressions.LiteralExpression) and \
       expr.filterExpr.value == boolean.true and \
       (isinstance(expr.predicates, Literals.ResourceLiteral) or \
        isinstance(expr.predicates, ResourceExpressions.PureQNameExpression)):
        return expr.predicates
    return None


def IsSimpleForwardTraverseChain(expr, con):
    if IsSimpleForwardTraverse(expr):
        return [expr.predicates.evaluate(con)]
    if isinstance(expr, Traversal.ForwardTraversal) and \
       isinstance(expr.resources, Traversal.ForwardTraversal) and \
       isinstance(expr.filterExpr, ResourceExpressions.LiteralExpression) and \
       expr.filterExpr.value == boolean.true and \
       (isinstance(expr.predicates, Literals.ResourceLiteral) or \
        isinstance(expr.predicates, ResourceExpressions.PureQNameExpression)):
        return IsSimpleForwardTraverseChain(expr.resources, con) + \
               [expr.predicates.evaluate(con)]
    return []


def IsFixedResourceCollection(expr):
    if isinstance(expr, Literals.ResourceLiteral) or \
       isinstance(expr, ResourceExpressions.PureQNameExpression):
        return 1
    if IsCoreFunction(expr, 'set') or \
       IsCoreFunction(expr, 'list'):
        if len([ i for i in expr._args
             if isinstance(i, Literals.ResourceLiteral) or \
             isinstance(i, ResourceExpressions.PureQNameExpression)
             ]) == len(expr._args):
            return 1
    return 0


def IsDotExpression(expr):
    if isinstance(expr, ResourceExpressions.CurrentExpression):
        return 1
    return 0


#def SubQueryFunction(expr, con, relations):
#    pred = IsSimpleForwardTraverse(expr)
#    if pred and not relations.has_key(pred):
#        relations[pred] = ResourceExpressions.GetRelations(None, pred, con)
#        return lambda con: 

def SubQueryFunction(expr, con):
    """
    Handles subqueries of functions such as distribute, filter, map, etc.
    The main optimization is that in some cases, e.g. simple traversal,
    one overall relation can be computed and it's a simple dictionary lookup
    for each cycle of the function
    """
    sft_pred = IsSimpleForwardTraverse(expr)
    sts_info = IsSimpleTextSearch(expr)
    sbt_pred = IsSimpleBackwardTraverse(expr)
    sftc_preds = IsSimpleForwardTraverseChain(expr, con)
    if sft_pred:
        #print "OPTIMIZER: sub-query as simple traversal"
        relations = ResourceExpressions.GetRelations(None, sft_pred.evaluate(con), con, 0)
        return lambda con, r=relations: r.get(DataTypes.ToResource(con.current), [])
    elif sbt_pred:
        #print "OPTIMIZER: sub-query as simple backward traversal"
        relations = ResourceExpressions.GetRelations(None, sbt_pred.evaluate(con), con, 1)
        return lambda con, r=relations: r.get(con.current, [])
    elif IsDotExpression(expr):
        #print "OPTIMIZER: sub-query as dot expression"
        return lambda con: con.current
    elif sftc_preds:
        #print "OPTIMIZER: sub-query as simple traversal chain"
        relations = ResourceExpressions.GetRelations(None, sftc_preds[0], con, 0)
        for p in sftc_preds[1:]:
            rel = ResourceExpressions.GetRelations(None, p, con, 0)
            for k in relations.keys():
                next = []
                for subj in relations[k]:
                    next.extend(rel.get(subj, []))
                relations[k] = next
        return lambda con, r=relations: r.get(DataTypes.ToResource(con.current), [])
##    elif sts_info:
##        pred, criteria = sts_info
##        searched = TextSearch(None, criteria, con, 0)
##        return lambda con, s=searched, p=preds: s.get(DataTypes.ToResource(con.current), [])
    else:
        #print "OPTIMIZER: unoptimized sub-query"
        return lambda con, e=expr: e.evaluate(con)


def TextSearch(start, criteria, con, useSubProps=1):
    """
    Returns a dictionary of lists.  The dict maps properties to text matches.
    Each list contains subjects that had the given text match for each property
    """
    start = start and DataTypes.ToList(start) or None
    suspects = con.driver.complete(None, None, '.*'+criteria+'.*', None,
                                   con.scope,
                                   flags={"objectFlags": Model.REGEX})
    sdict = {}
    for s in suspects:
        subj = DataTypes.ToResource(s[0])
        if not start or subj in start:
            pred = DataTypes.ToResource(s[1])
            if not sdict.has_key(pred):
                sdict[pred] = []
            sdict[pred].append(subj)
    return sdict


