<?php
define('idDiGIR_fltBuilder','$Id: DiGIR_fltBuilder.php,v 1.6 2004/05/18 22:44:06 rdg Exp $');
if (defined('idDIGIR_VERSION_REQUEST'))
    return;
/*
* $Id: DiGIR_fltBuilder.php,v 1.6 2004/05/18 22:44:06 rdg Exp $
*/
include_once("DiGIR_utils.php");

function fOpType($name)
{
    $name = strtoupper($name);
    if ($name=="AND")
        return op_AND;
    if ($name=="OR")
        return op_OR;
    if ($name=="ORNOT")
        return op_ORNOT;
    if ($name=="ANDNOT")
        return op_ANDNOT;
    if ($name =="LESSTHAN")
        return cp_lt;
    if ($name =="LT")
        return cp_lt;
    if ($name =="LESSTHANOREQUALS")
        return cp_lteq;
    if ($name =="LTEQ")
        return cp_lteq;
    if ($name =="EQUALS")
        return cp_eq;
    if ($name =="EQ")
        return cp_eq;
    if ($name =="GREATERTHANOREQUALS")
        return cp_gteq;
    if ($name =="GTEQ")
        return cp_gteq;
    if ($name =="GREATERTHAN")
        return cp_gt;
    if ($name =="GT")
        return cp_gt;
    if ($name =="NOTEQUALS")
        return cp_neq;
    if ($name =="NEQ")
        return cp_neq;
    if ($name =="LIKE")
        return cp_like;
    if ($name =="IN")
        return cp_in;
    return NULL;
}


/////////////////////////////////////////////////////////////////////////////
/**
* This method attempts to load a set of terms for an IN comparison by loading
* the values from another DiGIR resource.  The maximum number of terms retrieved
* is set by DIGIR_MAX_JOIN_TERMS.  If DIGIR_ALLOW_REMOTE_JOINS is false then this operation
* returns an empty array and sets a diagnostic.
*/
function loadRemoteJoinTerms($url)
{
    $res = array();
    if (!DIGIR_ALLOW_REMOTE_JOINS)
    {
        appendError(DC_NOREMOTEJOINS,"A remote join was attempted with $url but
        are disabled in the configuration of this provider.",DIAG_ERROR);
        return $res;
    }
    
    // include the response parser
    require_once("DiGIR_searchResponse.php");
    $handler = new DiGIRSearchResponse(0,$url,DIGIR_MAX_JOIN_TERMS);
    $hres = $handler->Load();
    if ($hres)
    {
        appendError(DC_REMOTEJOINFAIL,"A remote join was attempted with $url but
        the request failed.",DIAG_ERROR);
        return $res;
    }
    if (count($handler->columns) > 1)
    {
        appendError(DC_REMOTEJOIN,"More than one column was returned in the remote
        join with $url.  The first column (".$handler->columns[0].") was used.",
        DIAG_WARN);
    }
    // push the first element of each of the response records
    foreach ($handler->records as $row)
    {
        array_push($res,$row[0]);
    }
    return $res;
}


class filterBuilderXP
{
    var $filter;
    var $cNode;
    var $opTypes;
    var $namespaces;
    var $schemaNS;
    var $schemaNSinst;
    var $DiGIRNS;
    //indicates if the filter is local (from the provider
    //configuration) or not (from regular requests)
    var $local;

    function filterBuilderXP(&$xp,$nameSpaces,$local=false)
    {
        $this->filter = null;
        $this->cNode = null;
        $this->nameSpaces = $nameSpaces;
        $this->local = $local;
        $this->lopTypes = array(
            'and'=>op_AND,
            'or'=>op_OR,
            'andnot'=>op_ANDNOT,
            'ornot'=>op_ORNOT);

        $this->opTypes = array(
            'lessthan'=>cp_lt,
            'lt'=>cp_lt,
            'lessthanorequals'=>cp_lteq,
            'lteq'=>cp_lteq,
            'equals'=>cp_eq,
            'eq'=>cp_eq,
            'greaterthanorequals'=>cp_gteq,
            'gteq'=>cp_gteq,
            'greaterthan'=>cp_gt,
            'gt'=>cp_gt,
            'notequals'=>cp_neq,
            'neq'=>cp_neq,
            'like'=>cp_like,
            'in'=>cp_in);

        //load the name spaces that might be used
        $this->nameSpaces = getNamespaces($xp,$this->nameSpaces);
        
        //determine the namespaces handles of critical namespaces:
        // - DiGIR protocol, usually the default namespace
        // - XMLSchema namepace
        // - XMLSchema-instance namespace
        
        //XMLSchema Namespace
        $this->schemaNS = "xs";
        foreach ($this->nameSpaces as $k=>$v)
        {
            if ($v == XMLSCHEMANS)
            {
              if ($k === 0)
                  $this->schemaNS = '';
              else
                  $this->schemaNS = $k;
            }
        }

        if ($this->schemaNS != '')
            $this->schemaNS .= ':';
            
        //XMLSchema Namespace
        $this->schemaNSinst = "xsi";
        foreach ($this->nameSpaces as $k=>$v)
        {
            if ($v == XMLSCHEMAINST)
            {
                if ($k === 0)
                  $this->schemaNSinst = '';
                else
                  $this->schemaNSinst = $k;
            }
        }

        if ($this->schemaNSinst != '')
            $this->schemaNSinst .= ':';

        //DiGIR namespace
        $this->DiGIRNS = NULL;
        foreach ($this->nameSpaces as $k=>$v)
        {
            if ($v = DiGIRNameSpace)
            {
                if ($k === 0)
                    $this->DiGIRNS = '';
                else
                {
                    if (is_null($this->DiGIRNS))
                        $this->DiGIRNS = $k.':';
                }
            }
        }
        if (is_null($this->DiGIRNS))
            $this->DiGIRNS = '';
    }
    
    function _rootQuery()
    {
        // the start of the filter structure
        $query = "//".$this->DiGIRNS."filter";
        return $query;
    }
    
    function parse(&$xp,$query=NULL)
    {
        if (is_null($query))
        {
            $query = $this->_rootQuery();
        }
        $this->_parseQry($xp,$query);
    }

    function loadCOP(&$xp,$apath)
    {
        $node = $xp->getNode($apath);
        $nname = strtolower(trim($node['name']));
        if (isset($this->opTypes[$nname]))
        {
            $tmp = new filterCOP($this->opTypes[$nname],$this->local);

            //set properties of COP here since a copy is sent to cNode
            $tmp->m_conceptType = XMLConceptType;
            switch ($tmp->m_COPop)
            {
            case cp_in:
                $qry = $apath."/".$this->DiGIRNS."list/child::*";
                $nodes = $xp->match($qry);
                if (count($nodes) > 0)
                {
                    $inTerm = array();
                    foreach ($nodes as $pth)
                    {
                        $cnode = $xp->getNode($pth);
                        if ($this->local)
                        {
                            //local filters do not use concept mapping,
                            //the related values are taken from its xml attributes
                            $tmp->m_attrs = $xp->getAttributes($pth);
                        }
                        $aname = split(":",$cnode['name']);
                        if (count($aname) > 1)
                        {
                            $tmp->m_concept = $aname[1];
                            $tmp->m_nameSpace = $this->nameSpaces[$aname[0]];
                        }
                        else
                        {
                            $tmp->m_concept = $aname[0];
                        }
                        array_push($inTerm,$xp->wholeText($pth));
                    }
                    $tmp->m_term = $inTerm;
                }
                else
                {
                    //this is where we do a remote join
                    /*
                    There should be one child - a node that is a concept whose
                    value is a URL that references a dataset with values to be
                    matched against the concept.
                    */
                    $qry = $apath."/child::*";
                    $nodes = $xp->match($qry);
                    if (count($nodes == 1))
                    {
                        $ares = loadRemoteJoinTerms($xp->wholeText($nodes[0]));
                        if (count($ares) > 0)
                        {
                            $cnode = $xp->getNode($nodes[0]);
                            $tmp->m_term = $ares;
                            $aname = split(":",$cnode['name']);
                            if (count($aname > 1))
                            {
                                $tmp->m_concept = $aname[1];
                                $tmp->m_nameSpace = $this->nameSpaces[$aname[0]];
                            }
                            else
                            {
                                $tmp->m_concept = $aname[0];
                            }
                        }
                    }
                }
                break;
            default:
                // this is the name of the concept
                $qry = $apath."/child::*";
                $nodes = $xp->match($qry);
                foreach ($nodes as $pth)
                {
                    $cnode = $xp->getNode($pth);
                    
                    //check to see if a NULL was sent as the term
                    $tmpattrs = $xp->getAttributes($pth);

                    if ($this->local)
                    {
                        //local filters do not use concept mapping,
                        //the related values are taken from its xml attributes
                        $tmp->m_attrs = $tmpattrs;
                    }
                    $nilname = $this->schemaNSinst.'nill';
                    $nilval = false;
                    if (isset($tmpattrs[$nilname]))
                    {
                        //check to see if true
                        $nilval = (boolean)$tmpattrs[$nilname];
                    }
                   if ($nilval === true)
                    {
                        $tmp->m_term = NULL;
                    }
                    else
                    {
                        $tmp->m_term = $xp->wholeText($pth);
                    }

                    $aname = split(":",$cnode['name']);
                    if (count($aname) > 1)
                    {
                        $tmp->m_concept = $aname[1];
                        $tmp->m_nameSpace = $this->nameSpaces[$aname[0]];
                    }
                    else
                    {
                        $tmp->m_concept = $aname[0];
                    }
                }
                break;
            }
            //don't set properties past here.
            if ($this->filter != null)
            {
                $this->cNode->setChild($tmp);
            }
            else
            {
                $this->filter = $tmp;
                $this->cNode = &$this->filter;
            }
        }
        else
        {
            //undefined COP in filter
            AppendError(DC_INVALIDQUERY,
                        "Problem parsing filter structure at (".$apath.")",
                        DIAG_ERROR);
        }
    }

    function _parseQry(&$xp,$qry)
    {
        $qtmp = $qry."/child::*";
        $nodes = $xp->match($qtmp);
        $nnodes = count($nodes);
        foreach ($nodes as $apath)
        {
            $node = $xp->getNode($apath);
            $nname = strtolower(trim($node['name']));
            if (isset($this->lopTypes[$nname]))
            {
                //LOP operation
                $tmp = new filterLOP($this->lopTypes[$nname]);
                if (is_null($this->cNode))
                {
                    $this->filter = $tmp;
                    $this->cNode = &$this->filter;
                }
                else
                {
                    $res = $this->cNode->setChild($tmp);
                    $this->cNode =& $this->cNode->getChild($res);
                }
                $this->_parseQry($xp,$apath);
                //step cNode up to it's parent
                if (!is_null($this->cNode->getParent()))
                {
                    $this->cNode =& $this->cNode->getParent();
                }
            }
            else
            {
                //try and make a COP
                $this->loadCOP($xp,$apath);
            }
        }
    }
}


?>