/**********************************************************************
 * $Id$
 *
 * Project:  MapServer
 * Purpose:  OGC Filter Encoding implementation
 * Author:   Y. Assefa, DM Solutions Group (assefa@dmsolutions.ca)
 *
 **********************************************************************
 * Copyright (c) 2003, Y. Assefa, DM Solutions Group Inc
 *
 * 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 of this Software or works derived from this 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
 ****************************************************************************/


#define _GNU_SOURCE
#include "mapserver-config.h"

#ifdef USE_OGR
#include "cpl_minixml.h"
#include "cpl_string.h"
#endif

#include "mapogcfilter.h"
#include "mapserver.h"
#include "mapowscommon.h"




int FLTIsNumeric(char *pszValue)
{
  if (pszValue != NULL && *pszValue != '\0' && !isspace(*pszValue)) {
    /*the regex seems to have a problem on windows when mapserver is built using
      PHP regex*/
#if defined(_WIN32) && !defined(__CYGWIN__)
    int i = 0, nLength=0, bString=0;

    nLength = strlen(pszValue);
    for (i=0; i<nLength; i++) {
      if (i == 0) {
        if (!isdigit(pszValue[i]) &&  pszValue[i] != '-') {
          bString = 1;
          break;
        }
      } else if (!isdigit(pszValue[i]) &&  pszValue[i] != '.') {
        bString = 1;
        break;
      }
    }
    if (!bString)
      return MS_TRUE;
#else
    char * p;
    strtod (pszValue, &p);
    if (*p == '\0') return MS_TRUE;
#endif
  }

  return MS_FALSE;
}

/*
** Apply an expression to the layer's filter element.
**
*/
int FLTApplyExpressionToLayer(layerObj *lp, char *pszExpression)
{
  char *pszFinalExpression=NULL, *pszBuffer = NULL;
  /*char *escapedTextString=NULL;*/
  int bConcatWhere=0, bHasAWhere=0;

  if (lp && pszExpression) {
    if (lp->connectiontype == MS_POSTGIS || lp->connectiontype ==  MS_ORACLESPATIAL ||
        lp->connectiontype == MS_SDE || lp->connectiontype == MS_PLUGIN) {
      pszFinalExpression = msStrdup("(");
      pszFinalExpression = msStringConcatenate(pszFinalExpression, pszExpression);
      pszFinalExpression = msStringConcatenate(pszFinalExpression, ")");
    } else if (lp->connectiontype == MS_OGR) {
      pszFinalExpression = msStrdup(pszExpression);
      if (lp->filter.type != MS_EXPRESSION) {
        bConcatWhere = 1;
      } else {
        if (lp->filter.string && EQUALN(lp->filter.string,"WHERE ",6)) {
          bHasAWhere = 1;
          bConcatWhere =1;
        }
      }

    } else
      pszFinalExpression = msStrdup(pszExpression);

    if (bConcatWhere)
      pszBuffer = msStringConcatenate(pszBuffer, "WHERE ");
    /* if the filter is set and it's an expression type, concatenate it with
                this filter. If not just free it */
    if (lp->filter.string && lp->filter.type == MS_EXPRESSION) {
      pszBuffer = msStringConcatenate(pszBuffer, "((");
      if (bHasAWhere)
        pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string+6);
      else
        pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
      pszBuffer = msStringConcatenate(pszBuffer, ") and ");
    } else if (lp->filter.string)
      freeExpression(&lp->filter);

    pszBuffer = msStringConcatenate(pszBuffer, pszFinalExpression);

    if(lp->filter.string && lp->filter.type == MS_EXPRESSION)
      pszBuffer = msStringConcatenate(pszBuffer, ")");

    /*assuming that expression was properly escaped
          escapedTextString = msStringEscape(pszBuffer);
          msLoadExpressionString(&lp->filter,
                                 (char*)CPLSPrintf("%s", escapedTextString));
          msFree(escapedTextString);
    */
    msLoadExpressionString(&lp->filter, pszBuffer);


    msFree(pszFinalExpression);

    if (pszBuffer)
      msFree(pszBuffer);

    return MS_TRUE;
  }

  return MS_FALSE;
}

char *FLTGetExpressionForValuesRanges(layerObj *lp, char *item, char *value,  int forcecharcter)
{
  int bIscharacter, bSqlLayer;
  char *pszExpression = NULL, *pszEscapedStr=NULL, *pszTmpExpression=NULL;
  char **paszElements = NULL, **papszRangeElements=NULL;
  int numelements,i,nrangeelements;
  /* double minval, maxval; */
  if (lp && item && value) {
    if (lp->connectiontype == MS_POSTGIS || lp->connectiontype ==  MS_ORACLESPATIAL ||
        lp->connectiontype == MS_SDE || lp->connectiontype == MS_PLUGIN)
      bSqlLayer = MS_TRUE;
    else
      bSqlLayer = MS_FALSE;

    if (strstr(value, "/") == NULL) {
      /*value(s)*/
      paszElements = msStringSplit (value, ',', &numelements);
      if (paszElements && numelements > 0) {
        if (forcecharcter)
          bIscharacter = MS_TRUE;
        bIscharacter= !FLTIsNumeric(paszElements[0]);

        pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
        for (i=0; i<numelements; i++) {
          pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
          if (bSqlLayer)
            pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
          else {
            if (bIscharacter)
              pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
            pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
            pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
            pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
            if (bIscharacter)
              pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
          }
          if (bIscharacter) {
            if (bSqlLayer)
              pszTmpExpression = msStringConcatenate(pszTmpExpression, " = '");
            else
              pszTmpExpression = msStringConcatenate(pszTmpExpression, " = \"");
          } else
            pszTmpExpression = msStringConcatenate(pszTmpExpression, " = ");

          pszEscapedStr = msLayerEscapeSQLParam(lp, paszElements[i]);
          pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);

          if (bIscharacter) {
            if (bSqlLayer)
              pszTmpExpression = msStringConcatenate(pszTmpExpression, "'");
            else
              pszTmpExpression = msStringConcatenate(pszTmpExpression, "\"");
          }
          pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");

          msFree(pszEscapedStr);
          pszEscapedStr=NULL;

          if (pszExpression != NULL)
            pszExpression = msStringConcatenate(pszExpression, " OR ");

          pszExpression =  msStringConcatenate(pszExpression, pszTmpExpression);

          msFree(pszTmpExpression);
          pszTmpExpression = NULL;
        }
        pszExpression = msStringConcatenate(pszExpression, ")");
        msFreeCharArray(paszElements, numelements);
      }
    } else {
      /*range(s)*/
      paszElements = msStringSplit (value, ',', &numelements);
      if (paszElements && numelements > 0) {
        pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
        for (i=0; i<numelements; i++) {
          papszRangeElements = msStringSplit (paszElements[i], '/', &nrangeelements);
          if (papszRangeElements && nrangeelements > 0) {
            pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
            if (nrangeelements == 2 || nrangeelements == 3) {
              /*
              minval = atof(papszRangeElements[0]);
              maxval = atof(papszRangeElements[1]);
              */
              if (bSqlLayer)
                pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
              else {
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
                pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
              }

              pszTmpExpression = msStringConcatenate(pszTmpExpression, " >= ");

              pszEscapedStr = msLayerEscapeSQLParam(lp, papszRangeElements[0]);
              pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
              msFree(pszEscapedStr);
              pszEscapedStr=NULL;

              pszTmpExpression = msStringConcatenate(pszTmpExpression, " AND ");

              if (bSqlLayer)
                pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
              else {
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
                pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
              }

              pszTmpExpression = msStringConcatenate(pszTmpExpression, " <= ");

              pszEscapedStr = msLayerEscapeSQLParam(lp, papszRangeElements[1]);
              pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
              msFree(pszEscapedStr);
              pszEscapedStr=NULL;

              pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
            } else if (nrangeelements == 1) {
              pszTmpExpression = msStringConcatenate(pszTmpExpression, "(");
              if (bSqlLayer)
                pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
              else {
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "[");
                pszTmpExpression = msStringConcatenate(pszTmpExpression, item);
                pszTmpExpression = msStringConcatenate(pszTmpExpression, "]");
              }

              pszTmpExpression = msStringConcatenate(pszTmpExpression, " = ");

              pszEscapedStr = msLayerEscapeSQLParam(lp, papszRangeElements[0]);
              pszTmpExpression = msStringConcatenate(pszTmpExpression, pszEscapedStr);
              msFree(pszEscapedStr);
              pszEscapedStr=NULL;

              pszTmpExpression = msStringConcatenate(pszTmpExpression, ")");
            }

            if (pszExpression != NULL)
              pszExpression = msStringConcatenate(pszExpression, " OR ");

            pszExpression =  msStringConcatenate(pszExpression, pszTmpExpression);
            msFree(pszTmpExpression);
            pszTmpExpression = NULL;

            msFreeCharArray(papszRangeElements, nrangeelements);
          }
        }
        pszExpression = msStringConcatenate(pszExpression, ")");
        msFreeCharArray(paszElements, numelements);
      }
    }
  }
  return pszExpression;
}

#ifdef USE_OGR


int FLTogrConvertGeometry(OGRGeometryH hGeometry, shapeObj *psShape,
                          OGRwkbGeometryType nType)
{
  return msOGRGeometryToShape(hGeometry, psShape, nType);
}

int FLTShapeFromGMLTree(CPLXMLNode *psTree, shapeObj *psShape , char **ppszSRS)
{
  char *pszSRS = NULL;
  if (psTree && psShape) {
    CPLXMLNode *psNext = psTree->psNext;
    OGRGeometryH hGeometry = NULL;

    psTree->psNext = NULL;
    hGeometry = OGR_G_CreateFromGMLTree(psTree );
    psTree->psNext = psNext;

    if (hGeometry) {
      OGRwkbGeometryType nType;
      nType = OGR_G_GetGeometryType(hGeometry);
      if (nType == wkbPolygon25D || nType == wkbMultiPolygon25D)
        nType = wkbPolygon;
      else if (nType == wkbLineString25D || nType == wkbMultiLineString25D)
        nType = wkbLineString;
      else if (nType == wkbPoint25D  || nType ==  wkbMultiPoint25D)
        nType = wkbPoint;
      FLTogrConvertGeometry(hGeometry, psShape, nType);

      OGR_G_DestroyGeometry(hGeometry);
    }

    pszSRS = (char *)CPLGetXMLValue(psTree, "srsName", NULL);
    if (ppszSRS && pszSRS)
      *ppszSRS = msStrdup(pszSRS);

    return MS_TRUE;
  }

  return MS_FALSE;
}

int FLTGetGeosOperator(char *pszValue)
{
  if (!pszValue)
    return -1;

  if (strcasecmp(pszValue, "Equals") == 0)
    return MS_GEOS_EQUALS;
  else if (strcasecmp(pszValue, "Intersect") == 0 ||
           strcasecmp(pszValue, "Intersects") == 0)
    return MS_GEOS_INTERSECTS;
  else if (strcasecmp(pszValue, "Disjoint") == 0)
    return MS_GEOS_DISJOINT;
  else if (strcasecmp(pszValue, "Touches") == 0)
    return MS_GEOS_TOUCHES;
  else if (strcasecmp(pszValue, "Crosses") == 0)
    return MS_GEOS_CROSSES;
  else if (strcasecmp(pszValue, "Within") == 0)
    return MS_GEOS_WITHIN;
  else if (strcasecmp(pszValue, "Contains") == 0)
    return MS_GEOS_CONTAINS;
  else if (strcasecmp(pszValue, "Overlaps") == 0)
    return MS_GEOS_OVERLAPS;
  else if (strcasecmp(pszValue, "Beyond") == 0)
    return MS_GEOS_BEYOND;
  else if (strcasecmp(pszValue, "DWithin") == 0)
    return MS_GEOS_DWITHIN;

  return -1;
}

int FLTIsGeosNode(char *pszValue)
{
  if (FLTGetGeosOperator(pszValue) == -1)
    return MS_FALSE;

  return MS_TRUE;
}

int FLTParseEpsgString(char *pszEpsg, projectionObj *psProj)
{
  int nStatus = MS_FALSE;
  int nTokens = 0;
  char **tokens = NULL;
  int nEpsgTmp=0;

#ifdef USE_PROJ
  if (pszEpsg && psProj) {
    nTokens = 0;

    /* There are several forms an epsg code may be given. In any case the */
    /* epsg code is the last token.                                       */
    /* TODO: To make sure it is an epsg code check the string, too.       */
    /*  - urn:ogc:def:crs:EPSG:6.5:4326                                   */
    /*  - urn:ogc:def:crs:EPSG::4326                                      */
    /*  - http://www.opengis.net/gml/srs/epsg.xml#4326                    */
    /*  - epsg:4326                                                       */
    tokens = msStringSplit(pszEpsg,'#', &nTokens);
    if( tokens && nTokens == 1 ) {
      msFreeCharArray(tokens, nTokens);
      tokens = msStringSplit(pszEpsg,':', &nTokens);
    }

    if (tokens && nTokens > 1) {
      nEpsgTmp = atoi(tokens[nTokens-1]);

      if (nEpsgTmp > 0) {
        char szTmp[32];
        snprintf(szTmp, sizeof(szTmp), "init=epsg:%d",nEpsgTmp);
        msInitProjection(psProj);
        if (msLoadProjectionString(psProj, szTmp) == 0)
          nStatus = MS_TRUE;
      }
    }

    if (tokens)
      msFreeCharArray(tokens, nTokens);
  }
#endif
  return nStatus;
}



int FLTGML2Shape_XMLNode(CPLXMLNode *psNode, shapeObj *psShp)
{
  lineObj line= {0,NULL};
  CPLXMLNode *psCoordinates = NULL;
  char *pszTmpCoord = NULL;
  char **szCoords = NULL;
  int nCoords = 0;


  if (!psNode || !psShp)
    return MS_FALSE;


  if( strcasecmp(psNode->pszValue,"PointType") == 0
      || strcasecmp(psNode->pszValue,"Point") == 0) {
    psCoordinates = CPLGetXMLNode(psNode, "coordinates");

    if (psCoordinates && psCoordinates->psChild &&
        psCoordinates->psChild->pszValue) {
      pszTmpCoord = psCoordinates->psChild->pszValue;
      szCoords = msStringSplit(pszTmpCoord, ',', &nCoords);
      if (szCoords && nCoords >= 2) {
        line.numpoints = 1;
        line.point = (pointObj *)malloc(sizeof(pointObj));
        line.point[0].x = atof(szCoords[0]);
        line.point[0].y =  atof(szCoords[1]);
#ifdef USE_POINT_Z_M
        line.point[0].m = 0;
#endif

        psShp->type = MS_SHAPE_POINT;

        msAddLine(psShp, &line);
        free(line.point);

        return MS_TRUE;
      }
    }
  }

  return MS_FALSE;
}




/************************************************************************/
/*                        FLTIsSimpleFilterNoSpatial                    */
/*                                                                      */
/*      Filter encoding with only attribute queries                     */
/************************************************************************/
int FLTIsSimpleFilterNoSpatial(FilterEncodingNode *psNode)
{
  if (FLTIsSimpleFilter(psNode) &&
      FLTNumberOfFilterType(psNode, "BBOX") == 0)
    return MS_TRUE;

  return MS_FALSE;
}




/************************************************************************/
/*                      FLTApplySimpleSQLFilter()                       */
/************************************************************************/

int FLTApplySimpleSQLFilter(FilterEncodingNode *psNode, mapObj *map,
                            int iLayerIndex)
{
  layerObj *lp = NULL;
  char *szExpression = NULL;
  rectObj sQueryRect = map->extent;
  char *szEPSG = NULL;
  projectionObj sProjTmp;
  char *pszBuffer = NULL;
  int bConcatWhere = 0;
  int bHasAWhere =0;
  char *pszTmp = NULL, *pszTmp2 = NULL;
  char *tmpfilename = NULL;

  lp = (GET_LAYER(map, iLayerIndex));

  /* if there is a bbox use it */
  szEPSG = FLTGetBBOX(psNode, &sQueryRect);
  if(szEPSG && map->projection.numargs > 0) {
    if (FLTParseEpsgString(szEPSG, &sProjTmp)) {
      msProjectRect(&sProjTmp, &map->projection, &sQueryRect);
      msFreeProjection(&sProjTmp);
    }
  }

  /* make sure that the layer can be queried*/
  if (!lp->template)
    lp->template = msStrdup("ttt.html");

  /* if there is no class, create at least one, so that query by rect
     would work*/
  if (lp->numclasses == 0) {
    if (msGrowLayerClasses(lp) == NULL)
      return MS_FAILURE;
    initClass(lp->class[0]);
  }

  bConcatWhere = 0;
  bHasAWhere = 0;
  if (lp->connectiontype == MS_POSTGIS || lp->connectiontype ==  MS_ORACLESPATIAL ||
      lp->connectiontype == MS_SDE || lp->connectiontype == MS_PLUGIN) {
    szExpression = FLTGetSQLExpression(psNode, lp);
    if (szExpression) {
      pszTmp = msStrdup("(");
      pszTmp = msStringConcatenate(pszTmp, szExpression);
      pszTmp = msStringConcatenate(pszTmp, ")");
      msFree(szExpression);
      szExpression = pszTmp;
    }
  }
  /* concatenates the WHERE clause for OGR layers. This only applies if
     the expression was empty or not of an expression string. If there
     is an sql type expression, it is assumed to have the WHERE clause.
     If it is an expression and does not have a WHERE it is assumed to be a mapserver
     type expression*/
  else if (lp->connectiontype == MS_OGR) {
    if (lp->filter.type != MS_EXPRESSION) {
      szExpression = FLTGetSQLExpression(psNode, lp);
      bConcatWhere = 1;
    } else {
      if (lp->filter.string && EQUALN(lp->filter.string,"WHERE ",6)) {
        szExpression = FLTGetSQLExpression(psNode, lp);
        bHasAWhere = 1;
        bConcatWhere =1;
      } else {
        szExpression = FLTGetCommonExpression(psNode, lp);
      }
    }
  } else {
    szExpression = FLTGetCommonExpression(psNode, lp);

  }


  if (szExpression) {

    if (bConcatWhere)
      pszBuffer = msStringConcatenate(pszBuffer, "WHERE ");

    /* if the filter is set and it's an expression type, concatenate it with
                 this filter. If not just free it */
    if (lp->filter.string && lp->filter.type == MS_EXPRESSION) {
      pszBuffer = msStringConcatenate(pszBuffer, "((");
      if (bHasAWhere)
        pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string+6);
      else
        pszBuffer = msStringConcatenate(pszBuffer, lp->filter.string);
      pszBuffer = msStringConcatenate(pszBuffer, ") and ");
    } else if (lp->filter.string)
      freeExpression(&lp->filter);


    pszBuffer = msStringConcatenate(pszBuffer, szExpression);

    if(lp->filter.string && lp->filter.type == MS_EXPRESSION)
      pszBuffer = msStringConcatenate(pszBuffer, ")");

    msLoadExpressionString(&lp->filter, pszBuffer);
    free(szExpression);
  }

  if (pszBuffer)
    free(pszBuffer);

  map->query.type = MS_QUERY_BY_RECT;
  map->query.mode = MS_QUERY_MULTIPLE;
  map->query.layer = lp->index;
  map->query.rect = sQueryRect;

  if(map->debug == MS_DEBUGLEVEL_VVV) {
    tmpfilename = msTmpFile(map, map->mappath, NULL, "_filter.map");
    if (tmpfilename == NULL) {
      tmpfilename = msTmpFile(map, NULL, NULL, "_filter.map" );
    }
    if (tmpfilename) {
      msSaveMap(map,tmpfilename);
      msDebug("FLTApplySimpleSQLFilter(): Map file after Filter was applied %s\n", tmpfilename);
      msFree(tmpfilename);
    }
  }

  /*for oracle connection, if we have a simple filter with no spatial constraints
    we should set the connection function to NONE to have a better performance
    (#2725)*/

  if (lp->connectiontype ==  MS_ORACLESPATIAL && FLTIsSimpleFilterNoSpatial(psNode)) {
    if (strcasestr(lp->data, "USING") == 0)
      lp->data = msStringConcatenate(lp->data, " USING NONE");
    else if (strcasestr(lp->data, "NONE") == 0) {
      /*if one of the functions is used, just replace it with NONE*/
      if (strcasestr(lp->data, "FILTER"))
        lp->data = msCaseReplaceSubstring(lp->data, "FILTER", "NONE");
      else if (strcasestr(lp->data, "GEOMRELATE"))
        lp->data = msCaseReplaceSubstring(lp->data, "GEOMRELATE", "NONE");
      else if (strcasestr(lp->data, "RELATE"))
        lp->data = msCaseReplaceSubstring(lp->data, "RELATE", "NONE");
      else if (strcasestr(lp->data, "VERSION")) {
        /*should add NONE just before the VERSION. Cases are:
          DATA "ORA_GEOMETRY FROM data USING VERSION 10g
          DATA "ORA_GEOMETRY FROM data  USING UNIQUE FID VERSION 10g"
         */
        pszTmp = (char *)strcasestr(lp->data, "VERSION");
        pszTmp2 = msStringConcatenate(pszTmp2, " NONE ");
        pszTmp2 = msStringConcatenate(pszTmp2, pszTmp);

        lp->data = msCaseReplaceSubstring(lp->data, pszTmp, pszTmp2);

        msFree(pszTmp2);

      } else if (strcasestr(lp->data, "SRID")) {
        lp->data = msStringConcatenate(lp->data, " NONE");
      }
    }
  }

  return msQueryByRect(map);

  /* return MS_SUCCESS; */
}





/************************************************************************/
/*                            FLTIsSimpleFilter                         */
/*                                                                      */
/*      Filter encoding with only attribute queries and only one bbox.  */
/************************************************************************/
int FLTIsSimpleFilter(FilterEncodingNode *psNode)
{
  if (FLTValidForBBoxFilter(psNode)) {
    if (FLTNumberOfFilterType(psNode, "DWithin") == 0 &&
        FLTNumberOfFilterType(psNode, "Intersect") == 0 &&
        FLTNumberOfFilterType(psNode, "Intersects") == 0 &&
        FLTNumberOfFilterType(psNode, "Equals") == 0 &&
        FLTNumberOfFilterType(psNode, "Disjoint") == 0 &&
        FLTNumberOfFilterType(psNode, "Touches") == 0 &&
        FLTNumberOfFilterType(psNode, "Crosses") == 0 &&
        FLTNumberOfFilterType(psNode, "Within") == 0 &&
        FLTNumberOfFilterType(psNode, "Contains") == 0 &&
        FLTNumberOfFilterType(psNode, "Overlaps") == 0 &&
        FLTNumberOfFilterType(psNode, "Beyond") == 0)
      return TRUE;
  }

  return FALSE;
}



/************************************************************************/
/*                          FLTApplyFilterToLayer                       */
/*                                                                      */
/*      Use the filter encoding node to create mapserver expressions    */
/*      and apply it to the layer.                                      */
/************************************************************************/
int FLTApplyFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
                          int iLayerIndex)
{
  layerObj *layer = GET_LAYER(map, iLayerIndex);

  if ( ! layer->vtable) {
    int rv =  msInitializeVirtualTable(layer);
    if (rv != MS_SUCCESS)
      return rv;
  }
  return layer->vtable->LayerApplyFilterToLayer(psNode, map,  iLayerIndex);

}

/************************************************************************/
/*               FLTLayerApplyCondSQLFilterToLayer                       */
/*                                                                      */
/* Helper function for layer virtual table architecture                 */
/************************************************************************/
int FLTLayerApplyCondSQLFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
                                      int iLayerIndex)
{
  /* ==================================================================== */
  /*      Check here to see if it is a simple filter and if that is       */
  /*      the case, we are going to use the FILTER element on             */
  /*      the layer.                                                      */
  /* ==================================================================== */
  if (FLTIsSimpleFilter(psNode)) {
    return FLTApplySimpleSQLFilter(psNode, map, iLayerIndex);
  }

  return FLTLayerApplyPlainFilterToLayer(psNode, map, iLayerIndex);
}

/************************************************************************/
/*                   FLTLayerApplyPlainFilterToLayer                    */
/*                                                                      */
/* Helper function for layer virtual table architecture                 */
/************************************************************************/
int FLTLayerApplyPlainFilterToLayer(FilterEncodingNode *psNode, mapObj *map,
                                    int iLayerIndex)
{
  char *pszExpression  =NULL;
  int status =MS_FALSE;

  pszExpression = FLTGetCommonExpression(psNode,  GET_LAYER(map, iLayerIndex));
  if(map->debug == MS_DEBUGLEVEL_VVV)
    msDebug("FLTLayerApplyPlainFilterToLayer(): %s\n", pszExpression);
  if (pszExpression) {
    status = FLTApplyFilterToLayerCommonExpression(map, iLayerIndex, pszExpression);
    msFree(pszExpression);
  }

  return status;
}



/************************************************************************/
/*            FilterNode *FLTPaserFilterEncoding(char *szXMLString)     */
/*                                                                      */
/*      Parses an Filter Encoding XML string and creates a              */
/*      FilterEncodingNodes corresponding to the string.                */
/*      Returns a pointer to the first node or NULL if                  */
/*      unsuccessfull.                                                  */
/*      Calling function should use FreeFilterEncodingNode function     */
/*      to free memeory.                                                */
/************************************************************************/
FilterEncodingNode *FLTParseFilterEncoding(char *szXMLString)
{
  CPLXMLNode *psRoot = NULL, *psChild=NULL, *psFilter=NULL;
  CPLXMLNode  *psFilterStart = NULL;
  FilterEncodingNode *psFilterNode = NULL;

  if (szXMLString == NULL || strlen(szXMLString) <= 0 ||
      (strstr(szXMLString, "Filter") == NULL))
    return NULL;

  psRoot = CPLParseXMLString(szXMLString);

  if( psRoot == NULL)
    return NULL;

  /* strip namespaces. We srtip all name spaces (#1350)*/
  CPLStripXMLNamespace(psRoot, NULL, 1);

  /* -------------------------------------------------------------------- */
  /*      get the root element (Filter).                                  */
  /* -------------------------------------------------------------------- */
  psChild = psRoot;
  psFilter = NULL;

  while( psChild != NULL ) {
    if (psChild->eType == CXT_Element &&
        EQUAL(psChild->pszValue,"Filter")) {
      psFilter = psChild;
      break;
    } else
      psChild = psChild->psNext;
  }

  if (!psFilter)
    return NULL;

  psChild = psFilter->psChild;
  psFilterStart = NULL;
  while (psChild) {
    if (FLTIsSupportedFilterType(psChild)) {
      psFilterStart = psChild;
      psChild = NULL;
    } else
      psChild = psChild->psNext;
  }

  if (psFilterStart && FLTIsSupportedFilterType(psFilterStart)) {
    psFilterNode = FLTCreateFilterEncodingNode();

    FLTInsertElementInNode(psFilterNode, psFilterStart);
  }

  CPLDestroyXMLNode( psRoot );

  /* -------------------------------------------------------------------- */
  /*      validate the node tree to make sure that all the nodes are valid.*/
  /* -------------------------------------------------------------------- */
  if (!FLTValidFilterNode(psFilterNode))
    return NULL;


  return psFilterNode;
}


/************************************************************************/
/*      int FLTValidFilterNode(FilterEncodingNode *psFilterNode)        */
/*                                                                      */
/*      Validate that all the nodes are filled properly. We could       */
/*      have parts of the nodes that are correct and part which         */
/*      could be incorrect if the filter string sent is corrupted       */
/*      (eg missing a value :<PropertyName><PropertyName>)              */
/************************************************************************/
int FLTValidFilterNode(FilterEncodingNode *psFilterNode)
{
  int  bReturn = 0;

  if (!psFilterNode)
    return 0;

  if (psFilterNode->eType == FILTER_NODE_TYPE_UNDEFINED)
    return 0;

  if (psFilterNode->psLeftNode) {
    bReturn = FLTValidFilterNode(psFilterNode->psLeftNode);
    if (bReturn == 0)
      return 0;
    else if (psFilterNode->psRightNode)
      return FLTValidFilterNode(psFilterNode->psRightNode);
  }

  return 1;
}
/************************************************************************/
/*                          FLTFreeFilterEncodingNode                   */
/*                                                                      */
/*      recursive freeing of Filer Encoding nodes.                      */
/************************************************************************/
void FLTFreeFilterEncodingNode(FilterEncodingNode *psFilterNode)
{
  if (psFilterNode) {
    if (psFilterNode->psLeftNode) {
      FLTFreeFilterEncodingNode(psFilterNode->psLeftNode);
      psFilterNode->psLeftNode = NULL;
    }
    if (psFilterNode->psRightNode) {
      FLTFreeFilterEncodingNode(psFilterNode->psRightNode);
      psFilterNode->psRightNode = NULL;
    }

    if (psFilterNode->pszSRS)
      free( psFilterNode->pszSRS);

    if( psFilterNode->pOther ) {
      if (psFilterNode->pszValue != NULL &&
          strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0) {
        if( ((FEPropertyIsLike *)psFilterNode->pOther)->pszWildCard )
          free( ((FEPropertyIsLike *)psFilterNode->pOther)->pszWildCard );
        if( ((FEPropertyIsLike *)psFilterNode->pOther)->pszSingleChar )
          free( ((FEPropertyIsLike *)psFilterNode->pOther)->pszSingleChar );
        if( ((FEPropertyIsLike *)psFilterNode->pOther)->pszEscapeChar )
          free( ((FEPropertyIsLike *)psFilterNode->pOther)->pszEscapeChar );
      } else if (psFilterNode->eType == FILTER_NODE_TYPE_GEOMETRY_POINT ||
                 psFilterNode->eType == FILTER_NODE_TYPE_GEOMETRY_LINE ||
                 psFilterNode->eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON) {
        msFreeShape((shapeObj *)(psFilterNode->pOther));
      }
      /* else */
      /* TODO free pOther special fields */
      free( psFilterNode->pOther );
    }

    /* Cannot free pszValue before, 'cause we are testing it above */
    if( psFilterNode->pszValue )
      free( psFilterNode->pszValue );

    free(psFilterNode);
  }
}


/************************************************************************/
/*                         FLTCreateFilterEncodingNode                  */
/*                                                                      */
/*      return a FilerEncoding node.                                    */
/************************************************************************/
FilterEncodingNode *FLTCreateFilterEncodingNode(void)
{
  FilterEncodingNode *psFilterNode = NULL;

  psFilterNode =
  (FilterEncodingNode *)malloc(sizeof (FilterEncodingNode));
  psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
  psFilterNode->pszValue = NULL;
  psFilterNode->pOther = NULL;
  psFilterNode->pszSRS = NULL;
  psFilterNode->psLeftNode = NULL;
  psFilterNode->psRightNode = NULL;

  return psFilterNode;
}

FilterEncodingNode *FLTCreateBinaryCompFilterEncodingNode(void)
{
  FilterEncodingNode *psFilterNode = NULL;

  psFilterNode = FLTCreateFilterEncodingNode();
  /* used to store case sensitivity flag. Default is 0 meaning the
     comparing is case snetitive */
  psFilterNode->pOther = (int *)malloc(sizeof(int));
  (*(int *)(psFilterNode->pOther)) = 0;

  return psFilterNode;
}

/************************************************************************/
/*                           FLTInsertElementInNode                     */
/*                                                                      */
/*      Utility function to parse an XML node and transfter the         */
/*      contennts into the Filer Encoding node structure.               */
/************************************************************************/
void FLTInsertElementInNode(FilterEncodingNode *psFilterNode,
                            CPLXMLNode *psXMLNode)
{
  int nStrLength = 0;
  char *pszTmp = NULL;
  FilterEncodingNode *psCurFilNode= NULL;
  CPLXMLNode *psCurXMLNode = NULL;
  CPLXMLNode *psTmpNode = NULL;
  CPLXMLNode *psFeatureIdNode = NULL;
  char *pszFeatureId=NULL, *pszFeatureIdList=NULL;
  char **tokens = NULL;
  int nTokens = 0;

  if (psFilterNode && psXMLNode && psXMLNode->pszValue) {
    psFilterNode->pszValue = msStrdup(psXMLNode->pszValue);
    psFilterNode->psLeftNode = NULL;
    psFilterNode->psRightNode = NULL;

    /* -------------------------------------------------------------------- */
    /*      Logical filter. AND, OR and NOT are supported. Example of       */
    /*      filer using logical filters :                                   */
    /*      <Filter>                                                        */
    /*        <And>                                                         */
    /*          <PropertyIsGreaterThan>                                     */
    /*            <PropertyName>Person/Age</PropertyName>                   */
    /*            <Literal>50</Literal>                                     */
    /*          </PropertyIsGreaterThan>                                    */
    /*          <PropertyIsEqualTo>                                         */
    /*             <PropertyName>Person/Address/City</PropertyName>         */
    /*             <Literal>Toronto</Literal>                               */
    /*          </PropertyIsEqualTo>                                        */
    /*        </And>                                                        */
    /*      </Filter>                                                       */
    /* -------------------------------------------------------------------- */
    if (FLTIsLogicalFilterType(psXMLNode->pszValue)) {
      psFilterNode->eType = FILTER_NODE_TYPE_LOGICAL;
      if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
          strcasecmp(psFilterNode->pszValue, "OR") == 0) {
        if (psXMLNode->psChild && psXMLNode->psChild->psNext) {
          /*2 operators */
          if (psXMLNode->psChild->psNext->psNext == NULL) {
            psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
            FLTInsertElementInNode(psFilterNode->psLeftNode,
                                   psXMLNode->psChild);
            psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
            FLTInsertElementInNode(psFilterNode->psRightNode,
                                   psXMLNode->psChild->psNext);
          } else {
            psCurXMLNode =  psXMLNode->psChild;
            psCurFilNode = psFilterNode;
            while(psCurXMLNode) {
              if (psCurXMLNode->psNext && psCurXMLNode->psNext->psNext) {
                psCurFilNode->psLeftNode =
                FLTCreateFilterEncodingNode();
                FLTInsertElementInNode(psCurFilNode->psLeftNode,
                                       psCurXMLNode);
                psCurFilNode->psRightNode =
                FLTCreateFilterEncodingNode();
                psCurFilNode->psRightNode->eType =
                FILTER_NODE_TYPE_LOGICAL;
                psCurFilNode->psRightNode->pszValue =
                msStrdup(psFilterNode->pszValue);

                psCurFilNode = psCurFilNode->psRightNode;
                psCurXMLNode = psCurXMLNode->psNext;
              } else if (psCurXMLNode->psNext) { /*last 2 operators*/
                psCurFilNode->psLeftNode =
                FLTCreateFilterEncodingNode();
                FLTInsertElementInNode(psCurFilNode->psLeftNode,
                                       psCurXMLNode);

                psCurFilNode->psRightNode =
                FLTCreateFilterEncodingNode();
                FLTInsertElementInNode(psCurFilNode->psRightNode,
                                       psCurXMLNode->psNext);
                psCurXMLNode = psCurXMLNode->psNext->psNext;

              }
            }
          }
        }
      } else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
        if (psXMLNode->psChild) {
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
          FLTInsertElementInNode(psFilterNode->psLeftNode,
                                 psXMLNode->psChild);
        }
      } else
        psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
    }/* end if is logical */
    /* -------------------------------------------------------------------- */
    /*      Spatial Filter.                                                 */
    /*      BBOX :                                                          */
    /*      <Filter>                                                        */
    /*       <BBOX>                                                         */
    /*        <PropertyName>Geometry</PropertyName>                         */
    /*        <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
    /*          <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
    /*        </gml:Box>                                                    */
    /*       </BBOX>                                                        */
    /*      </Filter>                                                       */
    /*                                                                      */
    /*       DWithin                                                        */
    /*                                                                      */
    /*      <xsd:element name="DWithin"                                     */
    /*      type="ogc:DistanceBufferType"                                   */
    /*      substitutionGroup="ogc:spatialOps"/>                            */
    /*                                                                      */
    /*      <xsd:complexType name="DistanceBufferType">                     */
    /*         <xsd:complexContent>                                         */
    /*            <xsd:extension base="ogc:SpatialOpsType">                 */
    /*               <xsd:sequence>                                         */
    /*                  <xsd:element ref="ogc:PropertyName"/>               */
    /*                  <xsd:element ref="gml:_Geometry"/>                  */
    /*                  <xsd:element name="Distance" type="ogc:DistanceType"/>*/
    /*               </xsd:sequence>                                        */
    /*            </xsd:extension>                                          */
    /*         </xsd:complexContent>                                        */
    /*      </xsd:complexType>                                              */
    /*                                                                      */
    /*                                                                      */
    /*       <Filter>                                                       */
    /*       <DWithin>                                                      */
    /*        <PropertyName>Geometry</PropertyName>                         */
    /*        <gml:Point>                                                   */
    /*          <gml:coordinates>13.0983,31.5899</gml:coordinates>          */
    /*        </gml:Point>                                                  */
    /*        <Distance units="url#m">10</Distance>                         */
    /*       </DWithin>                                                     */
    /*      </Filter>                                                       */
    /*                                                                      */
    /*       Intersect                                                      */
    /*                                                                      */
    /*       type="ogc:BinarySpatialOpType" substitutionGroup="ogc:spatialOps"/>*/
    /*      <xsd:element name="Intersects"                                  */
    /*      type="ogc:BinarySpatialOpType"                                  */
    /*      substitutionGroup="ogc:spatialOps"/>                            */
    /*                                                                      */
    /*      <xsd:complexType name="BinarySpatialOpType">                    */
    /*      <xsd:complexContent>                                            */
    /*      <xsd:extension base="ogc:SpatialOpsType">                       */
    /*      <xsd:sequence>                                                  */
    /*      <xsd:element ref="ogc:PropertyName"/>                           */
    /*      <xsd:choice>                                                    */
    /*      <xsd:element ref="gml:_Geometry"/>                              */
    /*      <xsd:element ref="gml:Box"/>                                    */
    /*      </xsd:sequence>                                                 */
    /*      </xsd:extension>                                                */
    /*      </xsd:complexContent>                                           */
    /*      </xsd:complexType>                                              */
    /* -------------------------------------------------------------------- */
    else if (FLTIsSpatialFilterType(psXMLNode->pszValue)) {
      psFilterNode->eType = FILTER_NODE_TYPE_SPATIAL;

      if (strcasecmp(psXMLNode->pszValue, "BBOX") == 0) {
        char *pszSRS = NULL;
        CPLXMLNode *psPropertyName = NULL;
        CPLXMLNode *psBox = NULL, *psEnvelope=NULL;
        rectObj sBox;

        int bCoordinatesValid = 0;

        psPropertyName = CPLGetXMLNode(psXMLNode, "PropertyName");
        psBox = CPLGetXMLNode(psXMLNode, "Box");
        if (!psBox)
          psBox = CPLGetXMLNode(psXMLNode, "BoxType");

        /*FE 1.0 used box FE1.1 uses envelop*/
        if (psBox)
          bCoordinatesValid = FLTParseGMLBox(psBox, &sBox, &pszSRS);
        else if ((psEnvelope = CPLGetXMLNode(psXMLNode, "Envelope")))
          bCoordinatesValid = FLTParseGMLEnvelope(psEnvelope, &sBox, &pszSRS);

        if (psPropertyName == NULL || !bCoordinatesValid)
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;

        if (psPropertyName && bCoordinatesValid) {
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
          /* not really using the property name anywhere ??  */
          /* Is is always Geometry ?  */
          if (psPropertyName->psChild &&
              psPropertyName->psChild->pszValue) {
            psFilterNode->psLeftNode->eType =
            FILTER_NODE_TYPE_PROPERTYNAME;
            psFilterNode->psLeftNode->pszValue =
            msStrdup(psPropertyName->psChild->pszValue);
          }

          /* srs and coordinates */
          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
          psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BBOX;
          /* srs might be empty */
          if (pszSRS)
            psFilterNode->psRightNode->pszValue = pszSRS;

          psFilterNode->psRightNode->pOther =
          (rectObj *)msSmallMalloc(sizeof(rectObj));
          ((rectObj *)psFilterNode->psRightNode->pOther)->minx = sBox.minx;
          ((rectObj *)psFilterNode->psRightNode->pOther)->miny = sBox.miny;
          ((rectObj *)psFilterNode->psRightNode->pOther)->maxx = sBox.maxx;
          ((rectObj *)psFilterNode->psRightNode->pOther)->maxy =  sBox.maxy;
        }
      } else if (strcasecmp(psXMLNode->pszValue, "DWithin") == 0 ||
                 strcasecmp(psXMLNode->pszValue, "Beyond") == 0)

      {
        shapeObj *psShape = NULL;
        int bPoint = 0, bLine = 0, bPolygon = 0;
        char *pszUnits = NULL;
        char *pszSRS = NULL;


        CPLXMLNode *psGMLElement = NULL, *psDistance=NULL;


        psGMLElement = CPLGetXMLNode(psXMLNode, "Point");
        if (!psGMLElement)
          psGMLElement =  CPLGetXMLNode(psXMLNode, "PointType");
        if (psGMLElement)
          bPoint =1;
        else {
          psGMLElement= CPLGetXMLNode(psXMLNode, "Polygon");
          if (psGMLElement)
            bPolygon = 1;
          else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiPolygon")))
            bPolygon = 1;
          else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiSurface")))
            bPolygon = 1;
          else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Box")))
            bPolygon = 1;
          else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "LineString")))
            bLine = 1;
          else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiPoint")))
            bPoint = 1;
        }

        psDistance = CPLGetXMLNode(psXMLNode, "Distance");
        if (psGMLElement && psDistance && psDistance->psChild &&
            psDistance->psChild->psNext && psDistance->psChild->psNext->pszValue) {
          pszUnits = (char *)CPLGetXMLValue(psDistance, "units", NULL);
          psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
          msInitShape(psShape);
          if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS))
            /* if (FLTGML2Shape_XMLNode(psPoint, psShape)) */
          {
            /*set the srs if available*/
            if (pszSRS)
              psFilterNode->pszSRS = pszSRS;

            psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
            /* not really using the property name anywhere ?? Is is always */
            /* Geometry ?  */

            psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
            psFilterNode->psLeftNode->pszValue = msStrdup("Geometry");

            psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
            if (bPoint)
              psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POINT;
            else if (bLine)
              psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
            else if (bPolygon)
              psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POLYGON;
            psFilterNode->psRightNode->pOther = (shapeObj *)psShape;
            /*the value will be distance;units*/
            psFilterNode->psRightNode->pszValue =
            msStrdup(psDistance->psChild->psNext->pszValue);
            if (pszUnits) {
              psFilterNode->psRightNode->pszValue= msStringConcatenate(psFilterNode->psRightNode->pszValue, ";");
              psFilterNode->psRightNode->pszValue= msStringConcatenate(psFilterNode->psRightNode->pszValue, pszUnits);
            }
          }
        } else
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
      } else if (strcasecmp(psXMLNode->pszValue, "Intersect") == 0 ||
                 strcasecmp(psXMLNode->pszValue, "Intersects") == 0 ||
                 strcasecmp(psXMLNode->pszValue, "Equals") == 0 ||
                 strcasecmp(psXMLNode->pszValue, "Disjoint") == 0 ||
                 strcasecmp(psXMLNode->pszValue, "Touches") == 0 ||
                 strcasecmp(psXMLNode->pszValue, "Crosses") == 0 ||
                 strcasecmp(psXMLNode->pszValue, "Within") == 0 ||
                 strcasecmp(psXMLNode->pszValue, "Contains") == 0 ||
                 strcasecmp(psXMLNode->pszValue, "Overlaps") == 0) {
        shapeObj *psShape = NULL;
        int  bLine = 0, bPolygon = 0, bPoint=0;
        char *pszSRS = NULL;


        CPLXMLNode *psGMLElement = NULL;


        psGMLElement = CPLGetXMLNode(psXMLNode, "Polygon");
        if (psGMLElement)
          bPolygon = 1;
        else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiPolygon")))
          bPolygon = 1;
        else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiSurface")))
          bPolygon = 1;
        else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "Box")))
          bPolygon = 1;
        else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "LineString")))
          bLine = 1;
        else if ((psGMLElement= CPLGetXMLNode(psXMLNode, "MultiPoint")))
          bPoint = 1;
        else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "Point")))
          bPoint = 1;
        else if ((psGMLElement = CPLGetXMLNode(psXMLNode, "PointType")))
          bPoint = 1;

        if (psGMLElement) {
          psShape = (shapeObj *)msSmallMalloc(sizeof(shapeObj));
          msInitShape(psShape);
          if (FLTShapeFromGMLTree(psGMLElement, psShape, &pszSRS))
            /* if (FLTGML2Shape_XMLNode(psPoint, psShape)) */
          {
            /*set the srs if available*/
            if (pszSRS)
              psFilterNode->pszSRS = pszSRS;

            psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
            /* not really using the property name anywhere ?? Is is always */
            /* Geometry ?  */

            psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
            psFilterNode->psLeftNode->pszValue = msStrdup("Geometry");

            psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
            if (bPoint)
              psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POINT;
            if (bLine)
              psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_LINE;
            else if (bPolygon)
              psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_GEOMETRY_POLYGON;
            psFilterNode->psRightNode->pOther = (shapeObj *)psShape;

          }
        } else
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
      }


    }/* end of is spatial */


    /* -------------------------------------------------------------------- */
    /*      Comparison Filter                                               */
    /* -------------------------------------------------------------------- */
    else if (FLTIsComparisonFilterType(psXMLNode->pszValue)) {
      psFilterNode->eType = FILTER_NODE_TYPE_COMPARISON;
      /* -------------------------------------------------------------------- */
      /*      binary comaparison types. Example :                             */
      /*                                                                      */
      /*      <Filter>                                                        */
      /*        <PropertyIsEqualTo>                                           */
      /*          <PropertyName>SomeProperty</PropertyName>                   */
      /*          <Literal>100</Literal>                                      */
      /*        </PropertyIsEqualTo>                                          */
      /*      </Filter>                                                       */
      /* -------------------------------------------------------------------- */
      if (FLTIsBinaryComparisonFilterType(psXMLNode->pszValue)) {
        psTmpNode = CPLSearchXMLNode(psXMLNode,  "PropertyName");
        if (psTmpNode &&  psTmpNode->psChild &&
            psTmpNode->psChild->pszValue &&
            strlen(psTmpNode->psChild->pszValue) > 0) {
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();
          psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
          psFilterNode->psLeftNode->pszValue =
            msStrdup(psTmpNode->psChild->pszValue);

          psTmpNode = CPLSearchXMLNode(psXMLNode,  "Literal");
          if (psTmpNode) {
            psFilterNode->psRightNode = FLTCreateBinaryCompFilterEncodingNode();
            psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;

            if (psTmpNode &&
                psTmpNode->psChild &&
                psTmpNode->psChild->pszValue &&
                strlen(psTmpNode->psChild->pszValue) > 0) {

              psFilterNode->psRightNode->pszValue =
                msStrdup(psTmpNode->psChild->pszValue);

              /*check if the matchCase attribute is set*/
              if (psXMLNode->psChild &&
                  psXMLNode->psChild->eType == CXT_Attribute  &&
                  psXMLNode->psChild->pszValue &&
                  strcasecmp(psXMLNode->psChild->pszValue, "matchCase") == 0 &&
                  psXMLNode->psChild->psChild &&
                  psXMLNode->psChild->psChild->pszValue &&
                  strcasecmp( psXMLNode->psChild->psChild->pszValue, "false") == 0) {
                (*(int *)psFilterNode->psRightNode->pOther) = 1;
              }

            }
            /* special case where the user puts an empty value */
            /* for the Literal so it can end up as an empty  */
            /* string query in the expression */
            else
              psFilterNode->psRightNode->pszValue = NULL;
          }
        }
        if (psFilterNode->psLeftNode == NULL || psFilterNode->psRightNode == NULL)
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
      }

      /* -------------------------------------------------------------------- */
      /*      PropertyIsBetween filter : extract property name and boudary    */
      /*      values. The boundary  values are stored in the right            */
      /*      node. The values are separated by a semi-column (;)             */
      /*      Eg of Filter :                                                  */
      /*      <PropertyIsBetween>                                             */
      /*         <PropertyName>DEPTH</PropertyName>                           */
      /*         <LowerBoundary><Literal>400</Literal></LowerBoundary>        */
      /*         <UpperBoundary><Literal>800</Literal></UpperBoundary>        */
      /*      </PropertyIsBetween>                                            */
      /*                                                                      */
      /*      Or                                                              */
      /*      <PropertyIsBetween>                                             */
      /*         <PropertyName>DEPTH</PropertyName>                           */
      /*         <LowerBoundary>400</LowerBoundary>                           */
      /*         <UpperBoundary>800</UpperBoundary>                           */
      /*      </PropertyIsBetween>                                            */
      /* -------------------------------------------------------------------- */
      else if (strcasecmp(psXMLNode->pszValue, "PropertyIsBetween") == 0) {
        CPLXMLNode *psUpperNode = NULL, *psLowerNode = NULL;
        if (psXMLNode->psChild &&
            strcasecmp(psXMLNode->psChild->pszValue,
                       "PropertyName") == 0 &&
            psXMLNode->psChild->psNext &&
            strcasecmp(psXMLNode->psChild->psNext->pszValue,
                       "LowerBoundary") == 0 &&
            psXMLNode->psChild->psNext->psNext &&
            strcasecmp(psXMLNode->psChild->psNext->psNext->pszValue,
                       "UpperBoundary") == 0) {
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();

          if (psXMLNode->psChild->psChild &&
              psXMLNode->psChild->psChild->pszValue) {
            psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;
            psFilterNode->psLeftNode->pszValue =
              msStrdup(psXMLNode->psChild->psChild->pszValue);
          }

          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();
          if (psXMLNode->psChild->psNext->psChild &&
              psXMLNode->psChild->psNext->psNext->psChild &&
              psXMLNode->psChild->psNext->psChild->pszValue &&
              psXMLNode->psChild->psNext->psNext->psChild->pszValue) {
            /* check if the <Literals> is there */
            psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_BOUNDARY;
            if (psXMLNode->psChild->psNext->psChild->psChild)
              psLowerNode = psXMLNode->psChild->psNext->psChild->psChild;
            else
              psLowerNode = psXMLNode->psChild->psNext->psChild;

            if (psXMLNode->psChild->psNext->psNext->psChild->psChild)
              psUpperNode = psXMLNode->psChild->psNext->psNext->psChild->psChild;
            else
              psUpperNode = psXMLNode->psChild->psNext->psNext->psChild;

            nStrLength =
              strlen(psLowerNode->pszValue);
            nStrLength +=
              strlen(psUpperNode->pszValue);

            nStrLength += 2; /* adding a ; between bounary values */
            psFilterNode->psRightNode->pszValue =
              (char *)malloc(sizeof(char)*(nStrLength));
            strcpy( psFilterNode->psRightNode->pszValue,
                    psLowerNode->pszValue);
            strlcat(psFilterNode->psRightNode->pszValue, ";", nStrLength);
            strlcat(psFilterNode->psRightNode->pszValue,
                    psUpperNode->pszValue, nStrLength);
          }


        } else
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;

      }/* end of PropertyIsBetween  */
      /* -------------------------------------------------------------------- */
      /*      PropertyIsLike                                                  */
      /*                                                                      */
      /*      <Filter>                                                        */
      /*      <PropertyIsLike wildCard="*" singleChar="#" escape="!">         */
      /*      <PropertyName>LAST_NAME</PropertyName>                          */
      /*      <Literal>JOHN*</Literal>                                        */
      /*      </PropertyIsLike>                                               */
      /*      </Filter>                                                       */
      /* -------------------------------------------------------------------- */
      else if (strcasecmp(psXMLNode->pszValue, "PropertyIsLike") == 0) {
        if (CPLSearchXMLNode(psXMLNode,  "Literal") &&
            CPLSearchXMLNode(psXMLNode,  "PropertyName") &&
            CPLGetXMLValue(psXMLNode, "wildCard", "") &&
            CPLGetXMLValue(psXMLNode, "singleChar", "") &&
            (CPLGetXMLValue(psXMLNode, "escape", "") ||
             CPLGetXMLValue(psXMLNode, "escapeChar", "")))
          /*
            psXMLNode->psChild &&
            strcasecmp(psXMLNode->psChild->pszValue, "wildCard") == 0 &&
            psXMLNode->psChild->psNext &&
            strcasecmp(psXMLNode->psChild->psNext->pszValue, "singleChar") == 0 &&
            psXMLNode->psChild->psNext->psNext &&
            strcasecmp(psXMLNode->psChild->psNext->psNext->pszValue, "escape") == 0 &&
            psXMLNode->psChild->psNext->psNext->psNext &&
            strcasecmp(psXMLNode->psChild->psNext->psNext->psNext->pszValue, "PropertyName") == 0 &&
            psXMLNode->psChild->psNext->psNext->psNext->psNext &&
            strcasecmp(psXMLNode->psChild->psNext->psNext->psNext->psNext->pszValue, "Literal") == 0)
          */
        {
          /* -------------------------------------------------------------------- */
          /*      Get the wildCard, the singleChar and the escapeChar used.       */
          /* -------------------------------------------------------------------- */
          psFilterNode->pOther = (FEPropertyIsLike *)malloc(sizeof(FEPropertyIsLike));
          /*default is case sensitive*/
          ((FEPropertyIsLike *)psFilterNode->pOther)->bCaseInsensitive = 0;

          pszTmp = (char *)CPLGetXMLValue(psXMLNode, "wildCard", "");
          if (pszTmp)
            ((FEPropertyIsLike *)psFilterNode->pOther)->pszWildCard =
              msStrdup(pszTmp);
          pszTmp = (char *)CPLGetXMLValue(psXMLNode, "singleChar", "");
          if (pszTmp)
            ((FEPropertyIsLike *)psFilterNode->pOther)->pszSingleChar =
              msStrdup(pszTmp);
          pszTmp = (char *)CPLGetXMLValue(psXMLNode, "escape", "");
          if (pszTmp && strlen(pszTmp)>0)
            ((FEPropertyIsLike *)psFilterNode->pOther)->pszEscapeChar =
              msStrdup(pszTmp);
          else {
            pszTmp = (char *)CPLGetXMLValue(psXMLNode, "escapeChar", "");
            if (pszTmp)
              ((FEPropertyIsLike *)psFilterNode->pOther)->pszEscapeChar =
                msStrdup(pszTmp);
          }
          pszTmp = (char *)CPLGetXMLValue(psXMLNode, "matchCase", "");
          if (pszTmp && strlen(pszTmp) > 0 &&
              strcasecmp(pszTmp, "false") == 0) {
            ((FEPropertyIsLike *)psFilterNode->pOther)->bCaseInsensitive =1;
          }
          /* -------------------------------------------------------------------- */
          /*      Create left and right node for the attribute and the value.     */
          /* -------------------------------------------------------------------- */
          psFilterNode->psLeftNode = FLTCreateFilterEncodingNode();

          psTmpNode = CPLSearchXMLNode(psXMLNode,  "PropertyName");
          if (psTmpNode &&  psTmpNode->psChild &&
              psTmpNode->psChild->pszValue &&
              strlen(psTmpNode->psChild->pszValue) > 0)

          {
            /*
            if (psXMLNode->psChild->psNext->psNext->psNext->psChild &&
            psXMLNode->psChild->psNext->psNext->psNext->psChild->pszValue)
            {
            psFilterNode->psLeftNode->pszValue =
              msStrdup(psXMLNode->psChild->psNext->psNext->psNext->psChild->pszValue);
            */
            psFilterNode->psLeftNode->pszValue =
              msStrdup(psTmpNode->psChild->pszValue);
            psFilterNode->psLeftNode->eType = FILTER_NODE_TYPE_PROPERTYNAME;

          }

          psFilterNode->psRightNode = FLTCreateFilterEncodingNode();


          psTmpNode = CPLSearchXMLNode(psXMLNode,  "Literal");
          if (psTmpNode &&  psTmpNode->psChild &&
              psTmpNode->psChild->pszValue &&
              strlen(psTmpNode->psChild->pszValue) > 0) {
            /*
            if (psXMLNode->psChild->psNext->psNext->psNext->psNext->psChild &&
            psXMLNode->psChild->psNext->psNext->psNext->psNext->psChild->pszValue)
            {

            psFilterNode->psRightNode->pszValue =
              msStrdup(psXMLNode->psChild->psNext->psNext->psNext->psNext->psChild->pszValue);
            */
            psFilterNode->psRightNode->pszValue =
              msStrdup(psTmpNode->psChild->pszValue);

            psFilterNode->psRightNode->eType = FILTER_NODE_TYPE_LITERAL;
          }
        } else
          psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;

      }
    }
    /* -------------------------------------------------------------------- */
    /*      FeatureId Filter                                                */
    /*                                                                      */
    /*      <ogc:Filter>                                                    */
    /*      <ogc:FeatureId fid="INWATERA_1M.1013"/>                         */
    /*      <ogc:FeatureId fid="INWATERA_1M.10"/>                           */
    /*      <ogc:FeatureId fid="INWATERA_1M.13"/>                           */
    /*      <ogc:FeatureId fid="INWATERA_1M.140"/>                          */
    /*      <ogc:FeatureId fid="INWATERA_1M.5001"/>                         */
    /*      <ogc:FeatureId fid="INWATERA_1M.2001"/>                         */
    /*      </ogc:Filter>                                                   */
    /*                                                                      */
    /*                                                                      */
    /*      Note that for FES1.1.0 the featureid has been depricated in     */
    /*      favor of GmlObjectId                                            */
    /*      <GmlObjectId gml:id="TREESA_1M.1234"/>                          */
    /* -------------------------------------------------------------------- */
    else if (FLTIsFeatureIdFilterType(psXMLNode->pszValue)) {
      psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
      pszFeatureId = (char *)CPLGetXMLValue(psXMLNode, "fid", NULL);
      /*for FE 1.1.0 GmlObjectId */
      if (pszFeatureId == NULL)
        pszFeatureId = (char *)CPLGetXMLValue(psXMLNode, "id", NULL);
      pszFeatureIdList = NULL;

      psFeatureIdNode = psXMLNode;
      while (psFeatureIdNode) {
        pszFeatureId = (char *)CPLGetXMLValue(psFeatureIdNode, "fid", NULL);
        if (!pszFeatureId)
          pszFeatureId = (char *)CPLGetXMLValue(psFeatureIdNode, "id", NULL);

        if (pszFeatureId) {
          if (pszFeatureIdList)
            pszFeatureIdList = msStringConcatenate(pszFeatureIdList, ",");

          /*typname could be part of the value : INWATERA_1M.1234*/
          tokens = msStringSplit(pszFeatureId,'.', &nTokens);
          if (tokens && nTokens == 2)
            pszFeatureIdList = msStringConcatenate(pszFeatureIdList, tokens[1]);
          else
            pszFeatureIdList = msStringConcatenate(pszFeatureIdList, pszFeatureId);

          if (tokens)
            msFreeCharArray(tokens, nTokens);
        }
        psFeatureIdNode = psFeatureIdNode->psNext;
      }

      if (pszFeatureIdList) {
        psFilterNode->pszValue =  msStrdup(pszFeatureIdList);
        msFree(pszFeatureIdList);
      } else
        psFilterNode->eType = FILTER_NODE_TYPE_UNDEFINED;
    }

  }
}


/************************************************************************/
/*            int FLTIsLogicalFilterType((char *pszValue)                  */
/*                                                                      */
/*      return TRUE if the value of the node is of logical filter       */
/*       encoding type.                                                 */
/************************************************************************/
int FLTIsLogicalFilterType(char *pszValue)
{
  if (pszValue) {
    if (strcasecmp(pszValue, "AND") == 0 ||
        strcasecmp(pszValue, "OR") == 0 ||
        strcasecmp(pszValue, "NOT") == 0)
      return MS_TRUE;
  }

  return MS_FALSE;
}

/************************************************************************/
/*         int FLTIsBinaryComparisonFilterType(char *pszValue)             */
/*                                                                      */
/*      Binary comparison filter type.                                  */
/************************************************************************/
int FLTIsBinaryComparisonFilterType(char *pszValue)
{
  if (pszValue) {
    if (strcasecmp(pszValue, "PropertyIsEqualTo") == 0 ||
        strcasecmp(pszValue, "PropertyIsNotEqualTo") == 0 ||
        strcasecmp(pszValue, "PropertyIsLessThan") == 0 ||
        strcasecmp(pszValue, "PropertyIsGreaterThan") == 0 ||
        strcasecmp(pszValue, "PropertyIsLessThanOrEqualTo") == 0 ||
        strcasecmp(pszValue, "PropertyIsGreaterThanOrEqualTo") == 0)
      return MS_TRUE;
  }

  return MS_FALSE;
}

/************************************************************************/
/*            int FLTIsComparisonFilterType(char *pszValue)                */
/*                                                                      */
/*      return TRUE if the value of the node is of comparison filter    */
/*      encoding type.                                                  */
/************************************************************************/
int FLTIsComparisonFilterType(char *pszValue)
{
  if (pszValue) {
    if (FLTIsBinaryComparisonFilterType(pszValue) ||
        strcasecmp(pszValue, "PropertyIsLike") == 0 ||
        strcasecmp(pszValue, "PropertyIsBetween") == 0)
      return MS_TRUE;
  }

  return MS_FALSE;
}

/************************************************************************/
/*            int FLTIsFeatureIdFilterType(char *pszValue)              */
/*                                                                      */
/*      return TRUE if the value of the node is of featureid filter     */
/*      encoding type.                                                  */
/************************************************************************/
int FLTIsFeatureIdFilterType(char *pszValue)
{
  if (pszValue && (strcasecmp(pszValue, "FeatureId") == 0 ||
                   strcasecmp(pszValue, "GmlObjectId") == 0))

    return MS_TRUE;

  return MS_FALSE;
}

/************************************************************************/
/*            int FLTIsSpatialFilterType(char *pszValue)                */
/*                                                                      */
/*      return TRUE if the value of the node is of spatial filter       */
/*      encoding type.                                                  */
/************************************************************************/
int FLTIsSpatialFilterType(char *pszValue)
{
  if (pszValue) {
    if ( strcasecmp(pszValue, "BBOX") == 0 ||
         strcasecmp(pszValue, "DWithin") == 0 ||
         strcasecmp(pszValue, "Intersect") == 0 ||
         strcasecmp(pszValue, "Intersects") == 0 ||
         strcasecmp(pszValue, "Equals") == 0 ||
         strcasecmp(pszValue, "Disjoint") == 0 ||
         strcasecmp(pszValue, "Touches") == 0 ||
         strcasecmp(pszValue, "Crosses") == 0 ||
         strcasecmp(pszValue, "Within") == 0 ||
         strcasecmp(pszValue, "Contains") == 0 ||
         strcasecmp(pszValue, "Overlaps") == 0 ||
         strcasecmp(pszValue, "Beyond") == 0)
      return MS_TRUE;
  }

  return MS_FALSE;
}

/************************************************************************/
/*           int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode)           */
/*                                                                      */
/*      Verfify if the value of the node is one of the supported        */
/*      filter type.                                                    */
/************************************************************************/
int FLTIsSupportedFilterType(CPLXMLNode *psXMLNode)
{
  if (psXMLNode) {
    if (FLTIsLogicalFilterType(psXMLNode->pszValue) ||
        FLTIsSpatialFilterType(psXMLNode->pszValue) ||
        FLTIsComparisonFilterType(psXMLNode->pszValue) ||
        FLTIsFeatureIdFilterType(psXMLNode->pszValue))
      return MS_TRUE;
  }

  return MS_FALSE;
}

/************************************************************************/
/*                          FLTNumberOfFilterType                       */
/*                                                                      */
/*      Loop trhough the nodes and return the number of nodes of        */
/*      specified value.                                                */
/************************************************************************/
int FLTNumberOfFilterType(FilterEncodingNode *psFilterNode, const char *szType)
{
  int nCount = 0;
  int nLeftNode=0 , nRightNode = 0;

  if (!psFilterNode || !szType || !psFilterNode->pszValue)
    return 0;

  if (strcasecmp(psFilterNode->pszValue, (char*)szType) == 0)
    nCount++;

  if (psFilterNode->psLeftNode)
    nLeftNode = FLTNumberOfFilterType(psFilterNode->psLeftNode, szType);

  nCount += nLeftNode;

  if (psFilterNode->psRightNode)
    nRightNode = FLTNumberOfFilterType(psFilterNode->psRightNode, szType);
  nCount += nRightNode;

  return nCount;
}




/************************************************************************/
/*                          FLTValidForBBoxFilter                       */
/*                                                                      */
/*      Validate if there is only one BBOX filter node. Here is waht    */
/*      is supported (is valid) :                                       */
/*        - one node which is a BBOX                                    */
/*        - a logical AND with a valid BBOX                             */
/*                                                                      */
/*      eg 1: <Filter>                                                  */
/*            <BBOX>                                                    */
/*              <PropertyName>Geometry</PropertyName>                   */
/*              <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
/*                <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
/*              </gml:Box>                                              */
/*            </BBOX>                                                   */
/*          </Filter>                                                   */
/*                                                                      */
/*      eg 2 :<Filter>                                                  */
/*              <AND>                                                   */
/*               <BBOX>                                                 */
/*                <PropertyName>Geometry</PropertyName>                  */
/*                <gml:Box srsName="http://www.opengis.net/gml/srs/epsg.xml#4326">*/
/*                  <gml:coordinates>13.0983,31.5899 35.5472,42.8143</gml:coordinates>*/
/*                </gml:Box>                                            */
/*               </BBOX>                                                */
/*               <PropertyIsEqualTo>                                    */
/*               <PropertyName>SomeProperty</PropertyName>              */
/*                <Literal>100</Literal>                                */
/*              </PropertyIsEqualTo>                                    */
/*             </AND>                                                   */
/*           </Filter>                                                  */
/*                                                                      */
/************************************************************************/
int FLTValidForBBoxFilter(FilterEncodingNode *psFilterNode)
{
  int nCount = 0;

  if (!psFilterNode || !psFilterNode->pszValue)
    return 1;

  nCount = FLTNumberOfFilterType(psFilterNode, "BBOX");

  if (nCount > 1)
    return 0;
  else if (nCount == 0)
    return 1;

  /* nCount ==1  */
  if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
    return 1;

  if (strcasecmp(psFilterNode->pszValue, "AND") == 0) {
    if (strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") ==0 ||
        strcasecmp(psFilterNode->psRightNode->pszValue, "BBOX") ==0)
      return 1;
  }

  return 0;
}

int FLTIsLineFilter(FilterEncodingNode *psFilterNode)
{
  if (!psFilterNode || !psFilterNode->pszValue)
    return 0;

  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
      psFilterNode->psRightNode &&
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_LINE)
    return 1;

  return 0;
}

int FLTIsPolygonFilter(FilterEncodingNode *psFilterNode)
{
  if (!psFilterNode || !psFilterNode->pszValue)
    return 0;

  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
      psFilterNode->psRightNode &&
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON)
    return 1;

  return 0;
}

int FLTIsPointFilter(FilterEncodingNode *psFilterNode)
{
  if (!psFilterNode || !psFilterNode->pszValue)
    return 0;

  if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL &&
      psFilterNode->psRightNode &&
      psFilterNode->psRightNode->eType == FILTER_NODE_TYPE_GEOMETRY_POINT)
    return 1;

  return 0;
}

int FLTIsBBoxFilter(FilterEncodingNode *psFilterNode)
{
  if (!psFilterNode || !psFilterNode->pszValue)
    return 0;

  if (strcasecmp(psFilterNode->pszValue, "BBOX") == 0)
    return 1;

  /*    if (strcasecmp(psFilterNode->pszValue, "AND") == 0)
  {
    if (strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") ==0 ||
        strcasecmp(psFilterNode->psRightNode->pszValue, "BBOX") ==0)
      return 1;
  }
  */
  return 0;
}

shapeObj *FLTGetShape(FilterEncodingNode *psFilterNode, double *pdfDistance,
                      int *pnUnit)
{
  char **tokens = NULL;
  int nTokens = 0;
  FilterEncodingNode *psNode = psFilterNode;
  char *szUnitStr = NULL;
  char *szUnit = NULL;

  if (psNode) {
    if (psNode->eType == FILTER_NODE_TYPE_SPATIAL && psNode->psRightNode)
      psNode = psNode->psRightNode;

    if (psNode->eType == FILTER_NODE_TYPE_GEOMETRY_POINT ||
        psNode->eType == FILTER_NODE_TYPE_GEOMETRY_LINE ||
        psNode->eType == FILTER_NODE_TYPE_GEOMETRY_POLYGON) {

      if (psNode->pszValue && pdfDistance) {
        /*
        sytnax expected is "distance;unit" or just "distance"
        if unit is there syntax is "URI#unit" (eg http://..../#m)
        or just "unit"
        */
        tokens = msStringSplit(psNode->pszValue,';', &nTokens);
        if (tokens && nTokens >= 1) {
          *pdfDistance = atof(tokens[0]);

          if (nTokens == 2 && pnUnit) {
            szUnitStr = msStrdup(tokens[1]);
            msFreeCharArray(tokens, nTokens);
            nTokens = 0;
            tokens = msStringSplit(szUnitStr,'#', &nTokens);
            msFree(szUnitStr);
            if (tokens && nTokens >= 1) {
              if (nTokens ==1)
                szUnit = tokens[0];
              else
                szUnit = tokens[1];

              if (strcasecmp(szUnit,"m") == 0 ||
                  strcasecmp(szUnit,"meters") == 0 )
                *pnUnit = MS_METERS;
              else if (strcasecmp(szUnit,"km") == 0 ||
                       strcasecmp(szUnit,"kilometers") == 0)
                *pnUnit = MS_KILOMETERS;
              else if (strcasecmp(szUnit,"NM") == 0 ||
                       strcasecmp(szUnit,"nauticalmiles") == 0)
                *pnUnit = MS_NAUTICALMILES;
              else if (strcasecmp(szUnit,"mi") == 0 ||
                       strcasecmp(szUnit,"miles") == 0)
                *pnUnit = MS_MILES;
              else if (strcasecmp(szUnit,"in") == 0 ||
                       strcasecmp(szUnit,"inches") == 0)
                *pnUnit = MS_INCHES;
              else if (strcasecmp(szUnit,"ft") == 0 ||
                       strcasecmp(szUnit,"feet") == 0)
                *pnUnit = MS_FEET;
              else if (strcasecmp(szUnit,"deg") == 0 ||
                       strcasecmp(szUnit,"dd") == 0)
                *pnUnit = MS_DD;
              else if (strcasecmp(szUnit,"px") == 0)
                *pnUnit = MS_PIXELS;

              msFreeCharArray(tokens, nTokens);
            }
          }
        }

      }

      return (shapeObj *)psNode->pOther;
    }
  }
  return NULL;
}

/************************************************************************/
/*                                FLTGetBBOX                            */
/*                                                                      */
/*      Loop through the nodes are return the coordinates of the        */
/*      first bbox node found. The retrun value is the epsg code of     */
/*      the bbox.                                                       */
/************************************************************************/
char *FLTGetBBOX(FilterEncodingNode *psFilterNode, rectObj *psRect)
{
  char *pszReturn = NULL;

  if (!psFilterNode || !psRect)
    return NULL;

  if (psFilterNode->pszValue && strcasecmp(psFilterNode->pszValue, "BBOX") == 0) {
    if (psFilterNode->psRightNode && psFilterNode->psRightNode->pOther) {
      psRect->minx = ((rectObj *)psFilterNode->psRightNode->pOther)->minx;
      psRect->miny = ((rectObj *)psFilterNode->psRightNode->pOther)->miny;
      psRect->maxx = ((rectObj *)psFilterNode->psRightNode->pOther)->maxx;
      psRect->maxy = ((rectObj *)psFilterNode->psRightNode->pOther)->maxy;

      return psFilterNode->psRightNode->pszValue;

    }
  } else {
    pszReturn = FLTGetBBOX(psFilterNode->psLeftNode, psRect);
    if (pszReturn)
      return pszReturn;
    else
      return  FLTGetBBOX(psFilterNode->psRightNode, psRect);
  }

  return pszReturn;
}

/************************************************************************/
/*                          GetMapserverExpression                      */
/*                                                                      */
/*      Return a mapserver expression base on the Filer encoding nodes. */
/************************************************************************/
char *FLTGetMapserverExpression(FilterEncodingNode *psFilterNode, layerObj *lp)
{
  char *pszExpression = NULL;
  const char *pszAttribute = NULL;
  char szTmp[256];
  char **tokens = NULL;
  int nTokens = 0, i=0,bString=0;
  char *pszTmp;

  if (!psFilterNode)
    return NULL;

  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON) {
    if ( psFilterNode->psLeftNode && psFilterNode->psRightNode) {
      if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue)) {
        pszExpression = FLTGetBinaryComparisonExpresssion(psFilterNode, lp);
      } else if (strcasecmp(psFilterNode->pszValue,
                            "PropertyIsBetween") == 0) {
        pszExpression = FLTGetIsBetweenComparisonExpresssion(psFilterNode, lp);
      } else if (strcasecmp(psFilterNode->pszValue,
                            "PropertyIsLike") == 0) {
        pszExpression = FLTGetIsLikeComparisonExpression(psFilterNode);
      }
    }
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL) {
    if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
        strcasecmp(psFilterNode->pszValue, "OR") == 0) {
      pszExpression = FLTGetLogicalComparisonExpresssion(psFilterNode, lp);
    } else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
      pszExpression = FLTGetLogicalComparisonExpresssion(psFilterNode, lp);
    }
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL) {
    /* TODO */
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
#if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR)
    if (psFilterNode->pszValue) {
      pszAttribute = msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
      if (pszAttribute) {
        tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens);
        if (tokens && nTokens > 0) {
          for (i=0; i<nTokens; i++) {
            if (i == 0) {
              pszTmp = tokens[0];
              if(FLTIsNumeric(pszTmp) == MS_FALSE)
                bString = 1;
            }
            if (bString)
              snprintf(szTmp, sizeof(szTmp), "('[%s]' = '%s')" , pszAttribute, tokens[i]);
            else
              snprintf(szTmp, sizeof(szTmp), "([%s] = %s)" , pszAttribute, tokens[i]);

            if (pszExpression != NULL)
              pszExpression = msStringConcatenate(pszExpression, " OR ");
            else
              pszExpression = msStringConcatenate(pszExpression, "(");
            pszExpression = msStringConcatenate(pszExpression, szTmp);
          }

          msFreeCharArray(tokens, nTokens);
        }
      }
      /*opening and closing brackets are needed for mapserver expressions*/
      if (pszExpression)
        pszExpression = msStringConcatenate(pszExpression, ")");
    }
#else
    msSetError(MS_MISCERR, "OWS support is not available.",
               "FLTGetMapserverExpression()");
    return(MS_FAILURE);
#endif

  }
  return pszExpression;
}


/************************************************************************/
/*                           FLTGetSQLExpression                        */
/*                                                                      */
/*      Build SQL expressions from the mapserver nodes.                 */
/************************************************************************/
char *FLTGetSQLExpression(FilterEncodingNode *psFilterNode, layerObj *lp)
{
  char *pszExpression = NULL;
  const char *pszAttribute = NULL;
  char szTmp[256];
  char **tokens = NULL;
  int nTokens = 0, i=0, bString=0;

  if (psFilterNode == NULL || lp == NULL)
    return NULL;

  if (psFilterNode->eType == FILTER_NODE_TYPE_COMPARISON) {
    if ( psFilterNode->psLeftNode && psFilterNode->psRightNode) {
      if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue)) {
        pszExpression =
          FLTGetBinaryComparisonSQLExpresssion(psFilterNode, lp);
      } else if (strcasecmp(psFilterNode->pszValue,
                            "PropertyIsBetween") == 0) {
        pszExpression =
          FLTGetIsBetweenComparisonSQLExpresssion(psFilterNode, lp);
      } else if (strcasecmp(psFilterNode->pszValue,
                            "PropertyIsLike") == 0) {
        pszExpression =
          FLTGetIsLikeComparisonSQLExpression(psFilterNode, lp);

      }
    }
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_LOGICAL) {
    if (strcasecmp(psFilterNode->pszValue, "AND") == 0 ||
        strcasecmp(psFilterNode->pszValue, "OR") == 0) {
      pszExpression =
        FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);

    } else if (strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
      pszExpression =
        FLTGetLogicalComparisonSQLExpresssion(psFilterNode, lp);

    }
  }

  else if (psFilterNode->eType == FILTER_NODE_TYPE_SPATIAL) {
    /* TODO */
  } else if (psFilterNode->eType == FILTER_NODE_TYPE_FEATUREID) {
#if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR)
    if (psFilterNode->pszValue) {
      pszAttribute = msOWSLookupMetadata(&(lp->metadata), "OFG", "featureid");
      if (pszAttribute) {
        tokens = msStringSplit(psFilterNode->pszValue,',', &nTokens);
        bString = 0;
        if (tokens && nTokens > 0) {
          for (i=0; i<nTokens; i++) {
            char *pszEscapedStr = NULL;
            if (strlen(tokens[i]) <= 0)
              continue;

            if (FLTIsNumeric((tokens[i])) == MS_FALSE)
              bString = 1;

            pszEscapedStr = msLayerEscapeSQLParam(lp, tokens[i]);
            if (bString)
              snprintf(szTmp, sizeof(szTmp), "(%s = '%s')" , pszAttribute, pszEscapedStr);
            else
              snprintf(szTmp, sizeof(szTmp), "(%s = %s)" , pszAttribute, pszEscapedStr);

            msFree(pszEscapedStr);
            pszEscapedStr=NULL;

            if (pszExpression != NULL)
              pszExpression = msStringConcatenate(pszExpression, " OR ");
            else
              /*opening and closing brackets*/
              pszExpression = msStringConcatenate(pszExpression, "(");

            pszExpression = msStringConcatenate(pszExpression, szTmp);
          }

          msFreeCharArray(tokens, nTokens);
        }
      }
      /*opening and closing brackets*/
      if (pszExpression)
        pszExpression = msStringConcatenate(pszExpression, ")");
    }
#else
    msSetError(MS_MISCERR, "OWS support is not available.",
               "FLTGetSQLExpression()");
    return(MS_FAILURE);
#endif

  }

  return pszExpression;
}

/************************************************************************/
/*                            FLTGetNodeExpression                      */
/*                                                                      */
/*      Return the expresion for a specific node.                       */
/************************************************************************/
char *FLTGetNodeExpression(FilterEncodingNode *psFilterNode, layerObj *lp)
{
  char *pszExpression = NULL;
  if (!psFilterNode)
    return NULL;

  if (FLTIsLogicalFilterType(psFilterNode->pszValue))
    pszExpression = FLTGetLogicalComparisonExpresssion(psFilterNode, lp);
  else if (FLTIsComparisonFilterType(psFilterNode->pszValue)) {
    if (FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
      pszExpression = FLTGetBinaryComparisonExpresssion(psFilterNode, lp);
    else if (strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0)
      pszExpression = FLTGetIsBetweenComparisonExpresssion(psFilterNode, lp);
    else if (strcasecmp(psFilterNode->pszValue, "PropertyIsLike") == 0)
      pszExpression = FLTGetIsLikeComparisonExpression(psFilterNode);
  }

  return pszExpression;
}


/************************************************************************/
/*                     FLTGetLogicalComparisonSQLExpresssion            */
/*                                                                      */
/*      Return the expression for logical comparison expression.        */
/************************************************************************/
char *FLTGetLogicalComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
    layerObj *lp)
{
  char *pszBuffer = NULL;
  char *pszTmp = NULL;
  int nTmp = 0;

  if (lp == NULL)
    return NULL;

  /* ==================================================================== */
  /*      special case for BBOX node.                                     */
  /* ==================================================================== */
  if (psFilterNode->psLeftNode && psFilterNode->psRightNode &&
      ((strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") == 0) ||
       (strcasecmp(psFilterNode->psRightNode->pszValue, "BBOX") == 0))) {
    if (strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") != 0)
      pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
    else
      pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);

    if (!pszTmp)
      return NULL;

    pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) + 1));
    sprintf(pszBuffer, "%s", pszTmp);
  }

  /* -------------------------------------------------------------------- */
  /*      OR and AND                                                      */
  /* -------------------------------------------------------------------- */
  else if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
    pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
    if (!pszTmp)
      return NULL;

    pszBuffer = (char *)malloc(sizeof(char) *
                               (strlen(pszTmp) +
                                strlen(psFilterNode->pszValue) + 5));
    pszBuffer[0] = '\0';
    strcat(pszBuffer, " (");
    strcat(pszBuffer, pszTmp);
    strcat(pszBuffer, " ");
    strcat(pszBuffer, psFilterNode->pszValue);
    strcat(pszBuffer, " ");

    free( pszTmp );

    nTmp = strlen(pszBuffer);
    pszTmp = FLTGetSQLExpression(psFilterNode->psRightNode, lp);
    if (!pszTmp)
      return NULL;

    pszBuffer = (char *)realloc(pszBuffer,
                                sizeof(char) * (strlen(pszTmp) + nTmp +3));
    strcat(pszBuffer, pszTmp);
    strcat(pszBuffer, ") ");
  }
  /* -------------------------------------------------------------------- */
  /*      NOT                                                             */
  /* -------------------------------------------------------------------- */
  else if (psFilterNode->psLeftNode &&
           strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
    pszTmp = FLTGetSQLExpression(psFilterNode->psLeftNode, lp);
    if (!pszTmp)
      return NULL;

    pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) +  9));
    pszBuffer[0] = '\0';

    strcat(pszBuffer, " (NOT ");
    strcat(pszBuffer, pszTmp);
    strcat(pszBuffer, ") ");
  } else
    return NULL;

  /* -------------------------------------------------------------------- */
  /*      Cleanup.                                                        */
  /* -------------------------------------------------------------------- */
  if( pszTmp != NULL )
    free( pszTmp );
  return pszBuffer;

}

/************************************************************************/
/*                     FLTGetLogicalComparisonExpresssion               */
/*                                                                      */
/*      Return the expression for logical comparison expression.        */
/************************************************************************/
char *FLTGetLogicalComparisonExpresssion(FilterEncodingNode *psFilterNode, layerObj *lp)
{
  char *pszTmp = NULL;
  char *pszBuffer = NULL;
  int nTmp = 0;

  if (!psFilterNode || !FLTIsLogicalFilterType(psFilterNode->pszValue))
    return NULL;


  /* ==================================================================== */
  /*      special case for BBOX node.                                     */
  /* ==================================================================== */
  if (psFilterNode->psLeftNode && psFilterNode->psRightNode &&
      (strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") == 0 ||
       strcasecmp(psFilterNode->psRightNode->pszValue, "BBOX") == 0 ||
       FLTIsGeosNode(psFilterNode->psLeftNode->pszValue) ||
       FLTIsGeosNode(psFilterNode->psRightNode->pszValue)))


  {

    /*strcat(szBuffer, " (");*/
    if (strcasecmp(psFilterNode->psLeftNode->pszValue, "BBOX") != 0 &&
        strcasecmp(psFilterNode->psLeftNode->pszValue, "DWithin") != 0 &&
        FLTIsGeosNode(psFilterNode->psLeftNode->pszValue) == MS_FALSE)
      pszTmp = FLTGetNodeExpression(psFilterNode->psLeftNode, lp);
    else
      pszTmp = FLTGetNodeExpression(psFilterNode->psRightNode, lp);

    if (!pszTmp)
      return NULL;

    pszBuffer = (char *)malloc(sizeof(char) * (strlen(pszTmp) + 3));
    pszBuffer[0] = '\0';
    /*
    if (strcasecmp(psFilterNode->psLeftNode->pszValue, "PropertyIsLike") == 0 ||
        strcasecmp(psFilterNode->psRightNode->pszValue, "PropertyIsLike") == 0)
      sprintf(pszBuffer, "%s", pszTmp);
    else
    */
    sprintf(pszBuffer, "(%s)", pszTmp);

    free(pszTmp);

    return pszBuffer;
  }


  /* -------------------------------------------------------------------- */
  /*      OR and AND                                                      */
  /* -------------------------------------------------------------------- */
  if (psFilterNode->psLeftNode && psFilterNode->psRightNode) {
    pszTmp = FLTGetNodeExpression(psFilterNode->psLeftNode, lp);
    if (!pszTmp)
      return NULL;

    pszBuffer = (char *)malloc(sizeof(char) *
                               (strlen(pszTmp) + strlen(psFilterNode->pszValue) + 5));
    pszBuffer[0] = '\0';
    strcat(pszBuffer, " (");

    strcat(pszBuffer, pszTmp);
    strcat(pszBuffer, " ");
    strcat(pszBuffer, psFilterNode->pszValue);
    strcat(pszBuffer, " ");
    free(pszTmp);

    pszTmp = FLTGetNodeExpression(psFilterNode->psRightNode, lp);
    if (!pszTmp)
      return NULL;

    nTmp = strlen(pszBuffer);
    pszBuffer = (char *)realloc(pszBuffer,
                                sizeof(char) * (strlen(pszTmp) + nTmp +3));

    strcat(pszBuffer, pszTmp);
    strcat(pszBuffer, ") ");
    free(pszTmp);
  }
  /* -------------------------------------------------------------------- */
  /*      NOT                                                             */
  /* -------------------------------------------------------------------- */
  else if (psFilterNode->psLeftNode &&
           strcasecmp(psFilterNode->pszValue, "NOT") == 0) {
    pszTmp = FLTGetNodeExpression(psFilterNode->psLeftNode, lp);
    if (!pszTmp)
      return NULL;

    pszBuffer = (char *)malloc(sizeof(char) *
                               (strlen(pszTmp) +  9));
    pszBuffer[0] = '\0';
    strcat(pszBuffer, " (NOT ");
    strcat(pszBuffer, pszTmp);
    strcat(pszBuffer, ") ");

    free(pszTmp);
  } else
    return NULL;

  return pszBuffer;

}



/************************************************************************/
/*                      FLTGetBinaryComparisonExpresssion               */
/*                                                                      */
/*      Return the expression for a binary comparison filter node.      */
/************************************************************************/
char *FLTGetBinaryComparisonExpresssion(FilterEncodingNode *psFilterNode, layerObj *lp)
{
  const size_t bufferSize = 1024;
  char szBuffer[1024];
  int bString=0;
  char szTmp[256];

  szBuffer[0] = '\0';
  if (!psFilterNode || !FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
    return NULL;

  /* -------------------------------------------------------------------- */
  /*      check if the value is a numeric value or alphanumeric. If it    */
  /*      is alphanumeric, add quotes around attribute and values.        */
  /* -------------------------------------------------------------------- */
  bString = 0;
  if (psFilterNode->psRightNode->pszValue) {
    const char* pszOFGType;
    snprintf(szTmp, sizeof(szTmp), "%s_type",  psFilterNode->psLeftNode->pszValue);
    pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
    if (pszOFGType!= NULL && strcasecmp(pszOFGType, "Character") == 0)
      bString = 1;
    else if (FLTIsNumeric(psFilterNode->psRightNode->pszValue) == MS_FALSE)
      bString = 1;
  }

  /* specical case to be able to have empty strings in the expression. */
  if (psFilterNode->psRightNode->pszValue == NULL)
    bString = 1;


  if (bString)
    strlcat(szBuffer, " (\"[", bufferSize);
  else
    strlcat(szBuffer, " ([", bufferSize);
  /* attribute */

  strlcat(szBuffer, psFilterNode->psLeftNode->pszValue, bufferSize);
  if (bString)
    strlcat(szBuffer, "]\" ", bufferSize);
  else
    strlcat(szBuffer, "] ", bufferSize);


  /* logical operator */
  if (strcasecmp(psFilterNode->pszValue,
                 "PropertyIsEqualTo") == 0) {
    /*case insensitive set ? */
    if (psFilterNode->psRightNode->pOther &&
        (*(int *)psFilterNode->psRightNode->pOther) == 1) {
      strlcat(szBuffer, "IEQ", bufferSize);
    } else
      strlcat(szBuffer, "=", bufferSize);
  } else if (strcasecmp(psFilterNode->pszValue,
                        "PropertyIsNotEqualTo") == 0)
    strlcat(szBuffer, "!=", bufferSize);
  else if (strcasecmp(psFilterNode->pszValue,
                      "PropertyIsLessThan") == 0)
    strlcat(szBuffer, "<", bufferSize);
  else if (strcasecmp(psFilterNode->pszValue,
                      "PropertyIsGreaterThan") == 0)
    strlcat(szBuffer, ">", bufferSize);
  else if (strcasecmp(psFilterNode->pszValue,
                      "PropertyIsLessThanOrEqualTo") == 0)
    strlcat(szBuffer, "<=", bufferSize);
  else if (strcasecmp(psFilterNode->pszValue,
                      "PropertyIsGreaterThanOrEqualTo") == 0)
    strlcat(szBuffer, ">=", bufferSize);

  strlcat(szBuffer, " ", bufferSize);

  /* value */
  if (bString)
    strlcat(szBuffer, "\"", bufferSize);

  if (psFilterNode->psRightNode->pszValue)
    strlcat(szBuffer, psFilterNode->psRightNode->pszValue, bufferSize);

  if (bString)
    strlcat(szBuffer, "\"", bufferSize);

  strlcat(szBuffer, ") ", bufferSize);

  return msStrdup(szBuffer);
}




/************************************************************************/
/*                      FLTGetBinaryComparisonSQLExpresssion            */
/*                                                                      */
/*      Return the expression for a binary comparison filter node.      */
/************************************************************************/
char *FLTGetBinaryComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
    layerObj *lp)
{
  const size_t bufferSize = 1024;
  char szBuffer[1024];
  int bString=0;
  char szTmp[256];
  char* pszEscapedStr = NULL;

  szBuffer[0] = '\0';
  if (!psFilterNode || !
      FLTIsBinaryComparisonFilterType(psFilterNode->pszValue))
    return NULL;

  /* -------------------------------------------------------------------- */
  /*      check if the value is a numeric value or alphanumeric. If it    */
  /*      is alphanumeric, add quotes around attribute and values.        */
  /* -------------------------------------------------------------------- */
  bString = 0;
  if (psFilterNode->psRightNode->pszValue) {
    const char* pszOFGType;
    snprintf(szTmp, sizeof(szTmp), "%s_type",  psFilterNode->psLeftNode->pszValue);
    pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
    if (pszOFGType!= NULL && strcasecmp(pszOFGType, "Character") == 0)
      bString = 1;

    else if (FLTIsNumeric(psFilterNode->psRightNode->pszValue) == MS_FALSE)
      bString = 1;
  }

  /* specical case to be able to have empty strings in the expression. */
  if (psFilterNode->psRightNode->pszValue == NULL)
    bString = 1;


  /*opening bracket*/
  strlcat(szBuffer, " (", bufferSize);

  pszEscapedStr = msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);


  /* attribute */
  /*case insensitive set ? */
  if (bString &&
      strcasecmp(psFilterNode->pszValue,
                 "PropertyIsEqualTo") == 0 &&
      psFilterNode->psRightNode->pOther &&
      (*(int *)psFilterNode->psRightNode->pOther) == 1) {
    snprintf(szTmp, sizeof(szTmp), "lower(%s) ",  pszEscapedStr);
    strlcat(szBuffer, szTmp, bufferSize);
  } else
    strlcat(szBuffer, pszEscapedStr, bufferSize);

  msFree(pszEscapedStr);
  pszEscapedStr = NULL;


  /* logical operator */
  if (strcasecmp(psFilterNode->pszValue,
                 "PropertyIsEqualTo") == 0)
    strlcat(szBuffer, "=", bufferSize);
  else if (strcasecmp(psFilterNode->pszValue,
                      "PropertyIsNotEqualTo") == 0)
    strlcat(szBuffer, "<>", bufferSize);
  else if (strcasecmp(psFilterNode->pszValue,
                      "PropertyIsLessThan") == 0)
    strlcat(szBuffer, "<", bufferSize);
  else if (strcasecmp(psFilterNode->pszValue,
                      "PropertyIsGreaterThan") == 0)
    strlcat(szBuffer, ">", bufferSize);
  else if (strcasecmp(psFilterNode->pszValue,
                      "PropertyIsLessThanOrEqualTo") == 0)
    strlcat(szBuffer, "<=", bufferSize);
  else if (strcasecmp(psFilterNode->pszValue,
                      "PropertyIsGreaterThanOrEqualTo") == 0)
    strlcat(szBuffer, ">=", bufferSize);

  strlcat(szBuffer, " ", bufferSize);

  /* value */

  if (bString &&
      psFilterNode->psRightNode->pszValue &&
      strcasecmp(psFilterNode->pszValue,
                 "PropertyIsEqualTo") == 0 &&
      psFilterNode->psRightNode->pOther &&
      (*(int *)psFilterNode->psRightNode->pOther) == 1) {
    char* pszEscapedStr;
    pszEscapedStr = msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
    snprintf(szTmp, sizeof(szTmp), "lower('%s') ", pszEscapedStr);
    msFree(pszEscapedStr);
    strlcat(szBuffer, szTmp, bufferSize);
  } else {
    if (bString)
      strlcat(szBuffer, "'", bufferSize);

    if (psFilterNode->psRightNode->pszValue) {
      if (bString) {
        char* pszEscapedStr;
        pszEscapedStr = msLayerEscapeSQLParam(lp, psFilterNode->psRightNode->pszValue);
        strlcat(szBuffer, pszEscapedStr, bufferSize);
        msFree(pszEscapedStr);
        pszEscapedStr=NULL;
      } else
        strlcat(szBuffer, psFilterNode->psRightNode->pszValue, bufferSize);
    }

    if (bString)
      strlcat(szBuffer, "'", bufferSize);

  }
  /*closing bracket*/
  strlcat(szBuffer, ") ", bufferSize);

  return msStrdup(szBuffer);
}


/************************************************************************/
/*                    FLTGetIsBetweenComparisonSQLExpresssion           */
/*                                                                      */
/*      Build an SQL expresssion for IsBteween Filter.                  */
/************************************************************************/
char *FLTGetIsBetweenComparisonSQLExpresssion(FilterEncodingNode *psFilterNode,
    layerObj *lp)
{
  const size_t bufferSize = 1024;
  char szBuffer[1024];
  char **aszBounds = NULL;
  int nBounds = 0;
  int bString=0;
  char szTmp[256];
  char* pszEscapedStr;

  szBuffer[0] = '\0';
  if (!psFilterNode ||
      !(strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0))
    return NULL;

  if (!psFilterNode->psLeftNode || !psFilterNode->psRightNode )
    return NULL;

  /* -------------------------------------------------------------------- */
  /*      Get the bounds value which are stored like boundmin;boundmax    */
  /* -------------------------------------------------------------------- */
  aszBounds = msStringSplit(psFilterNode->psRightNode->pszValue, ';', &nBounds);
  if (nBounds != 2)
    return NULL;
  /* -------------------------------------------------------------------- */
  /*      check if the value is a numeric value or alphanumeric. If it    */
  /*      is alphanumeric, add quotes around attribute and values.        */
  /* -------------------------------------------------------------------- */
  bString = 0;
  if (aszBounds[0]) {
    const char* pszOFGType;
    snprintf(szTmp, sizeof(szTmp), "%s_type",  psFilterNode->psLeftNode->pszValue);
    pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
    if (pszOFGType!= NULL && strcasecmp(pszOFGType, "Character") == 0)
      bString = 1;
    else if (FLTIsNumeric(aszBounds[0]) == MS_FALSE)
      bString = 1;
  }
  if (!bString) {
    if (aszBounds[1]) {
      if (FLTIsNumeric(aszBounds[1]) == MS_FALSE)
        bString = 1;
    }
  }


  /* -------------------------------------------------------------------- */
  /*      build expresssion.                                              */
  /* -------------------------------------------------------------------- */
  /*opening paranthesis */
  strlcat(szBuffer, " (", bufferSize);

  /* attribute */
  pszEscapedStr = msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);

  strlcat(szBuffer, pszEscapedStr, bufferSize);
  msFree(pszEscapedStr);
  pszEscapedStr = NULL;

  /*between*/
  strlcat(szBuffer, " BETWEEN ", bufferSize);

  /*bound 1*/
  if (bString)
    strlcat(szBuffer,"'", bufferSize);
  pszEscapedStr = msLayerEscapeSQLParam( lp, aszBounds[0]);
  strlcat(szBuffer, pszEscapedStr, bufferSize);
  msFree(pszEscapedStr);
  pszEscapedStr=NULL;

  if (bString)
    strlcat(szBuffer,"'", bufferSize);

  strlcat(szBuffer, " AND ", bufferSize);

  /*bound 2*/
  if (bString)
    strlcat(szBuffer, "'", bufferSize);
  pszEscapedStr = msLayerEscapeSQLParam( lp, aszBounds[1]);
  strlcat(szBuffer, pszEscapedStr, bufferSize);
  msFree(pszEscapedStr);
  pszEscapedStr=NULL;

  if (bString)
    strlcat(szBuffer,"'", bufferSize);

  /*closing paranthesis*/
  strlcat(szBuffer, ")", bufferSize);


  return msStrdup(szBuffer);
}

/************************************************************************/
/*                    FLTGetIsBetweenComparisonExpresssion              */
/*                                                                      */
/*      Build expresssion for IsBteween Filter.                         */
/************************************************************************/
char *FLTGetIsBetweenComparisonExpresssion(FilterEncodingNode *psFilterNode,
    layerObj *lp)
{
  const size_t bufferSize = 1024;
  char szBuffer[1024];
  char **aszBounds = NULL;
  int nBounds = 0;
  int bString=0;
  char szTmp[256];


  szBuffer[0] = '\0';
  if (!psFilterNode ||
      !(strcasecmp(psFilterNode->pszValue, "PropertyIsBetween") == 0))
    return NULL;

  if (!psFilterNode->psLeftNode || !psFilterNode->psRightNode )
    return NULL;

  /* -------------------------------------------------------------------- */
  /*      Get the bounds value which are stored like boundmin;boundmax    */
  /* -------------------------------------------------------------------- */
  aszBounds = msStringSplit(psFilterNode->psRightNode->pszValue, ';', &nBounds);
  if (nBounds != 2) {
    msFreeCharArray(aszBounds, nBounds);
    return NULL;
  }
  /* -------------------------------------------------------------------- */
  /*      check if the value is a numeric value or alphanumeric. If it    */
  /*      is alphanumeric, add quotes around attribute and values.        */
  /* -------------------------------------------------------------------- */
  bString = 0;
  if (aszBounds[0]) {
    const char* pszOFGType;
    snprintf(szTmp, sizeof(szTmp), "%s_type",  psFilterNode->psLeftNode->pszValue);
    pszOFGType = msOWSLookupMetadata(&(lp->metadata), "OFG", szTmp);
    if (pszOFGType!= NULL && strcasecmp(pszOFGType, "Character") == 0)
      bString = 1;
    else if (FLTIsNumeric(aszBounds[0]) == MS_FALSE)
      bString = 1;
  }
  if (!bString) {
    if (aszBounds[1]) {
      if (FLTIsNumeric(aszBounds[1]) == MS_FALSE)
        bString = 1;
    }
  }


  /* -------------------------------------------------------------------- */
  /*      build expresssion.                                              */
  /* -------------------------------------------------------------------- */
  if (bString)
    strlcat(szBuffer, " (\"[", bufferSize);
  else
    strlcat(szBuffer, " ([", bufferSize);

  /* attribute */
  strlcat(szBuffer, psFilterNode->psLeftNode->pszValue, bufferSize);

  if (bString)
    strlcat(szBuffer, "]\" ", bufferSize);
  else
    strlcat(szBuffer, "] ", bufferSize);


  strlcat(szBuffer, " >= ", bufferSize);
  if (bString)
    strlcat(szBuffer,"\"", bufferSize);
  strlcat(szBuffer, aszBounds[0], bufferSize);
  if (bString)
    strlcat(szBuffer,"\"", bufferSize);

  strlcat(szBuffer, " AND ", bufferSize);

  if (bString)
    strlcat(szBuffer, " \"[", bufferSize);
  else
    strlcat(szBuffer, " [", bufferSize);

  /* attribute */
  strlcat(szBuffer, psFilterNode->psLeftNode->pszValue, bufferSize);

  if (bString)
    strlcat(szBuffer, "]\" ", bufferSize);
  else
    strlcat(szBuffer, "] ", bufferSize);

  strlcat(szBuffer, " <= ", bufferSize);
  if (bString)
    strlcat(szBuffer,"\"", bufferSize);
  strlcat(szBuffer, aszBounds[1], bufferSize);
  if (bString)
    strlcat(szBuffer,"\"", bufferSize);
  strlcat(szBuffer, ")", bufferSize);

  msFreeCharArray(aszBounds, nBounds);

  return msStrdup(szBuffer);
}

/************************************************************************/
/*                      FLTGetIsLikeComparisonExpression               */
/*                                                                      */
/*      Build expression for IsLike filter.                             */
/************************************************************************/
char *FLTGetIsLikeComparisonExpression(FilterEncodingNode *psFilterNode)
{
  const size_t bufferSize = 1024;
  char szBuffer[1024];
  char szTmp[256];
  char *pszValue = NULL;

  char *pszWild = NULL;
  char *pszSingle = NULL;
  char *pszEscape = NULL;
  int  bCaseInsensitive = 0;

  int nLength=0, i=0, iTmp=0;


  if (!psFilterNode || !psFilterNode->pOther || !psFilterNode->psLeftNode ||
      !psFilterNode->psRightNode || !psFilterNode->psRightNode->pszValue)
    return NULL;

  pszWild = ((FEPropertyIsLike *)psFilterNode->pOther)->pszWildCard;
  pszSingle = ((FEPropertyIsLike *)psFilterNode->pOther)->pszSingleChar;
  pszEscape = ((FEPropertyIsLike *)psFilterNode->pOther)->pszEscapeChar;
  bCaseInsensitive = ((FEPropertyIsLike *)psFilterNode->pOther)->bCaseInsensitive;

  if (!pszWild || strlen(pszWild) == 0 ||
      !pszSingle || strlen(pszSingle) == 0 ||
      !pszEscape || strlen(pszEscape) == 0)
    return NULL;


  /* -------------------------------------------------------------------- */
  /*      Use operand with regular expressions.                           */
  /* -------------------------------------------------------------------- */
  szBuffer[0] = '\0';
  sprintf(szTmp, "%s", " (\"[");
  szTmp[4] = '\0';

  strlcat(szBuffer, szTmp, bufferSize);

  /* attribute */
  strlcat(szBuffer, psFilterNode->psLeftNode->pszValue, bufferSize);
  szBuffer[strlen(szBuffer)] = '\0';

  /*#3521 */
  if(bCaseInsensitive == 1)
    sprintf(szTmp, "%s", "]\" ~* /");
  else
    sprintf(szTmp, "%s", "]\" =~ /");
  szTmp[7] = '\0';
  strlcat(szBuffer, szTmp, bufferSize);
  szBuffer[strlen(szBuffer)] = '\0';


  pszValue = psFilterNode->psRightNode->pszValue;
  nLength = strlen(pszValue);

  iTmp =0;
  if (nLength > 0 && pszValue[0] != pszWild[0] &&
      pszValue[0] != pszSingle[0] &&
      pszValue[0] != pszEscape[0]) {
    szTmp[iTmp]= '^';
    iTmp++;
  }
  for (i=0; i<nLength; i++) {
    if (pszValue[i] != pszWild[0] &&
        pszValue[i] != pszSingle[0] &&
        pszValue[i] != pszEscape[0]) {
      szTmp[iTmp] = pszValue[i];
      iTmp++;
      szTmp[iTmp] = '\0';
    } else if  (pszValue[i] == pszSingle[0]) {
      szTmp[iTmp] = '.';
      iTmp++;
      szTmp[iTmp] = '\0';
    } else if  (pszValue[i] == pszEscape[0]) {
      szTmp[iTmp] = '\\';
      iTmp++;
      szTmp[iTmp] = '\0';

    } else if (pszValue[i] == pszWild[0]) {
      /* strcat(szBuffer, "[0-9,a-z,A-Z,\\s]*"); */
      /* iBuffer+=17; */
      szTmp[iTmp++] = '.';
      szTmp[iTmp++] = '*';
      szTmp[iTmp] = '\0';
    }
  }
  szTmp[iTmp] = '/';
  szTmp[++iTmp] = '\0';

  strlcat(szBuffer, szTmp, bufferSize);
  strlcat(szBuffer, ")", bufferSize);
  return msStrdup(szBuffer);
}

/************************************************************************/
/*                      FLTGetIsLikeComparisonSQLExpression             */
/*                                                                      */
/*      Build an sql expression for IsLike filter.                      */
/************************************************************************/
char *FLTGetIsLikeComparisonSQLExpression(FilterEncodingNode *psFilterNode,
    layerObj *lp)
{
  const size_t bufferSize = 1024;
  char szBuffer[1024];
  char *pszValue = NULL;

  char *pszWild = NULL;
  char *pszSingle = NULL;
  char *pszEscape = NULL;
  char szTmp[4];

  int nLength=0, i=0, j=0;
  int  bCaseInsensitive = 0;

  char *pszEscapedStr = NULL;

  if (!psFilterNode || !psFilterNode->pOther || !psFilterNode->psLeftNode ||
      !psFilterNode->psRightNode || !psFilterNode->psRightNode->pszValue)
    return NULL;

  pszWild = ((FEPropertyIsLike *)psFilterNode->pOther)->pszWildCard;
  pszSingle = ((FEPropertyIsLike *)psFilterNode->pOther)->pszSingleChar;
  pszEscape = ((FEPropertyIsLike *)psFilterNode->pOther)->pszEscapeChar;
  bCaseInsensitive = ((FEPropertyIsLike *)psFilterNode->pOther)->bCaseInsensitive;

  if (!pszWild || strlen(pszWild) == 0 ||
      !pszSingle || strlen(pszSingle) == 0 ||
      !pszEscape || strlen(pszEscape) == 0)
    return NULL;

  if (pszEscape[0] == '\'') {
    /* This might be valid, but the risk of SQL injection is too high */
    /* and the below code is not ready for that */
    /* Someone who does this has clearly suspect intentions ! */
    msSetError(MS_MISCERR, "Single quote character is not allowed as an escaping character.",
               "FLTGetIsLikeComparisonSQLExpression()");
    return NULL;
  }


  szBuffer[0] = '\0';
  /*opening bracket*/
  strlcat(szBuffer, " (", bufferSize);

  /* attribute name */
  pszEscapedStr = msLayerEscapePropertyName(lp, psFilterNode->psLeftNode->pszValue);

  strlcat(szBuffer, pszEscapedStr, bufferSize);
  msFree(pszEscapedStr);
  pszEscapedStr = NULL;

  if (lp->connectiontype == MS_POSTGIS) {
    if (bCaseInsensitive == 1)
      strlcat(szBuffer, "::text ilike '", bufferSize);
    else
      strlcat(szBuffer, "::text like '", bufferSize);
  } else
    strlcat(szBuffer, " like '", bufferSize);

  pszValue = psFilterNode->psRightNode->pszValue;
  nLength = strlen(pszValue);

  pszEscapedStr = (char*) msSmallMalloc( 3 * nLength + 1);

  for (i=0, j=0; i<nLength; i++) {
    char c = pszValue[i];
    if (c != pszWild[0] &&
        c != pszSingle[0] &&
        c != pszEscape[0]) {
      if (c == '\'') {
        pszEscapedStr[j++] = '\'';
        pszEscapedStr[j++] = '\'';
      } else if (c == '\\') {
        pszEscapedStr[j++] = '\\';
        pszEscapedStr[j++] = '\\';
      } else
        pszEscapedStr[j++] = c;
    } else if  (c == pszSingle[0]) {
      pszEscapedStr[j++] = '_';
    } else if  (c == pszEscape[0]) {
      pszEscapedStr[j++] = pszEscape[0];
      if (i+1<nLength) {
        char nextC = pszValue[i+1];
        i++;
        if (nextC == '\'') {
          pszEscapedStr[j++] = '\'';
          pszEscapedStr[j++] = '\'';
        } else
          pszEscapedStr[j++] = nextC;
      }
    } else if (c == pszWild[0]) {
      pszEscapedStr[j++] = '%';
    }
  }
  pszEscapedStr[j++] = 0;
  strlcat(szBuffer, pszEscapedStr, bufferSize);
  msFree(pszEscapedStr);

  strlcat(szBuffer, "'", bufferSize);
  if (lp->connectiontype != MS_OGR) {
    strlcat(szBuffer, " escape '", bufferSize);
    szTmp[0] = pszEscape[0];
    if (pszEscape[0] == '\\') {
      szTmp[1] = '\\';
      szTmp[2] = '\'';
      szTmp[3] = '\0';
    } else {
      szTmp[1] = '\'';
      szTmp[2] = '\0';
    }

    strlcat(szBuffer,  szTmp, bufferSize);
  }
  strlcat(szBuffer,  ") ", bufferSize);

  return msStrdup(szBuffer);
}

/************************************************************************/
/*                           FLTHasSpatialFilter                        */
/*                                                                      */
/*      Utility function to see if a spatial filter is included in      */
/*      the node.                                                       */
/************************************************************************/
int FLTHasSpatialFilter(FilterEncodingNode *psNode)
{
  int bResult = MS_FALSE;

  if (!psNode)
    return MS_FALSE;

  if (psNode->eType == FILTER_NODE_TYPE_LOGICAL) {
    if (psNode->psLeftNode)
      bResult = FLTHasSpatialFilter(psNode->psLeftNode);

    if (bResult)
      return MS_TRUE;

    if (psNode->psRightNode)
      bResult = FLTHasSpatialFilter(psNode->psRightNode);

    if (bResult)
      return MS_TRUE;
  } else if (FLTIsBBoxFilter(psNode) || FLTIsPointFilter(psNode) ||
             FLTIsLineFilter(psNode) || FLTIsPolygonFilter(psNode))
    return MS_TRUE;


  return MS_FALSE;
}


/************************************************************************/
/*                     FLTCreateFeatureIdFilterEncoding                 */
/*                                                                      */
/*      Utility function to create a filter node of FeatureId type.     */
/************************************************************************/
FilterEncodingNode *FLTCreateFeatureIdFilterEncoding(char *pszString)
{
  FilterEncodingNode *psFilterNode = NULL;
  char **tokens = NULL;
  int nTokens = 0;

  if (pszString) {
    psFilterNode = FLTCreateFilterEncodingNode();
    psFilterNode->eType = FILTER_NODE_TYPE_FEATUREID;
    /*split if tyname is included in the string*/
    tokens = msStringSplit(pszString,'.', &nTokens);
    if (tokens && nTokens == 2)
      psFilterNode->pszValue = msStrdup(tokens[1]);
    else
      psFilterNode->pszValue =  msStrdup(pszString);

    if (tokens)
      msFreeCharArray(tokens, nTokens);
    return psFilterNode;
  }
  return NULL;
}


/************************************************************************/
/*                              FLTParseGMLBox                          */
/*                                                                      */
/*      Parse gml box. Used for FE 1.0                                  */
/************************************************************************/
int FLTParseGMLBox(CPLXMLNode *psBox, rectObj *psBbox, char **ppszSRS)
{
  int bCoordinatesValid = 0;
  CPLXMLNode *psCoordinates = NULL, *psCoordChild=NULL;
  CPLXMLNode *psCoord1 = NULL, *psCoord2 = NULL;
  CPLXMLNode *psX = NULL, *psY = NULL;
  char **papszCoords=NULL, **papszMin=NULL, **papszMax = NULL;
  int nCoords = 0, nCoordsMin = 0, nCoordsMax = 0;
  char *pszTmpCoord = NULL;
  const char *pszSRS = NULL;
  const char *pszTS = NULL;
  const char *pszCS = NULL;
  double minx = 0.0, miny = 0.0, maxx = 0.0, maxy = 0.0;

  if (psBox) {
    pszSRS = CPLGetXMLValue(psBox, "srsName", NULL);
    if (ppszSRS && pszSRS)
      *ppszSRS = msStrdup(pszSRS);

    psCoordinates = CPLGetXMLNode(psBox, "coordinates");
    if (!psCoordinates)
      return 0;
    pszTS = CPLGetXMLValue(psCoordinates, "ts", NULL);
    pszCS = CPLGetXMLValue(psCoordinates, "cs", NULL);

    psCoordChild =  psCoordinates->psChild;
    while (psCoordinates && psCoordChild && psCoordChild->eType != CXT_Text) {
      psCoordChild = psCoordChild->psNext;
    }
    if (psCoordChild && psCoordChild->pszValue) {
      pszTmpCoord = psCoordChild->pszValue;
      if (pszTS)
        papszCoords = msStringSplit(pszTmpCoord, pszTS[0], &nCoords);
      else
        papszCoords = msStringSplit(pszTmpCoord, ' ', &nCoords);
      if (papszCoords && nCoords == 2) {
        if (pszCS)
          papszMin = msStringSplit(papszCoords[0], pszCS[0], &nCoordsMin);
        else
          papszMin = msStringSplit(papszCoords[0], ',', &nCoordsMin);
        if (papszMin && nCoordsMin == 2) {
          if (pszCS)
            papszMax = msStringSplit(papszCoords[1], pszCS[0], &nCoordsMax);
          else
            papszMax = msStringSplit(papszCoords[1], ',', &nCoordsMax);
        }
        if (papszMax && nCoordsMax == 2) {
          bCoordinatesValid =1;
          minx =  atof(papszMin[0]);
          miny =  atof(papszMin[1]);
          maxx =  atof(papszMax[0]);
          maxy =  atof(papszMax[1]);
        }

        msFreeCharArray(papszMin, nCoordsMin);
        msFreeCharArray(papszMax, nCoordsMax);
      }

      msFreeCharArray(papszCoords, nCoords);
    } else {
      psCoord1 = CPLGetXMLNode(psBox, "coord");
      if (psCoord1 && psCoord1->psNext &&
          psCoord1->psNext->pszValue &&
          strcmp(psCoord1->psNext->pszValue, "coord") ==0) {
        psCoord2 = psCoord1->psNext;
        psX =  CPLGetXMLNode(psCoord1, "X");
        psY =  CPLGetXMLNode(psCoord1, "Y");
        if (psX && psY && psX->psChild && psY->psChild &&
            psX->psChild->pszValue && psY->psChild->pszValue) {
          minx = atof(psX->psChild->pszValue);
          miny = atof(psY->psChild->pszValue);

          psX =  CPLGetXMLNode(psCoord2, "X");
          psY =  CPLGetXMLNode(psCoord2, "Y");
          if (psX && psY && psX->psChild && psY->psChild &&
              psX->psChild->pszValue && psY->psChild->pszValue) {
            maxx = atof(psX->psChild->pszValue);
            maxy = atof(psY->psChild->pszValue);
            bCoordinatesValid = 1;
          }
        }
      }

    }
  }

  if (bCoordinatesValid) {
    psBbox->minx =  minx;
    psBbox->miny =  miny;

    psBbox->maxx =  maxx;
    psBbox->maxy =  maxy;
  }

  return bCoordinatesValid;
}
/************************************************************************/
/*                           FLTParseGMLEnvelope                        */
/*                                                                      */
/*      Utility function to parse a gml:Enevelop (used for SOS and FE1.1)*/
/************************************************************************/
int FLTParseGMLEnvelope(CPLXMLNode *psRoot, rectObj *psBbox, char **ppszSRS)
{
  CPLXMLNode *psChild=NULL;
  CPLXMLNode *psUpperCorner=NULL, *psLowerCorner=NULL;
  char *pszLowerCorner=NULL, *pszUpperCorner=NULL;
  int bValid = 0;
  char **tokens;
  int n;

  if (psRoot && psBbox && psRoot->eType == CXT_Element &&
      EQUAL(psRoot->pszValue,"Envelope")) {
    /*Get the srs if available*/
    if (ppszSRS) {
      psChild = psRoot->psChild;
      while (psChild != NULL) {
        if (psChild->eType == CXT_Attribute && psChild->pszValue &&
            EQUAL(psChild->pszValue, "srsName") && psChild->psChild &&
            psChild->psChild->pszValue) {
          *ppszSRS = msStrdup(psChild->psChild->pszValue);
          break;
        }
        psChild = psChild->psNext;
      }
    }
    psLowerCorner = CPLSearchXMLNode(psRoot, "lowerCorner");
    psUpperCorner = CPLSearchXMLNode(psRoot, "upperCorner");

    if (psLowerCorner && psUpperCorner && EQUAL(psLowerCorner->pszValue,"lowerCorner") &&
        EQUAL(psUpperCorner->pszValue,"upperCorner")) {
      /*get the values*/
      psChild = psLowerCorner->psChild;
      while (psChild != NULL) {
        if (psChild->eType != CXT_Text)
          psChild = psChild->psNext;
        else
          break;
      }
      if (psChild && psChild->eType == CXT_Text)
        pszLowerCorner = psChild->pszValue;

      psChild = psUpperCorner->psChild;
      while (psChild != NULL) {
        if (psChild->eType != CXT_Text)
          psChild = psChild->psNext;
        else
          break;
      }
      if (psChild && psChild->eType == CXT_Text)
        pszUpperCorner = psChild->pszValue;

      if (pszLowerCorner && pszUpperCorner) {
        tokens = msStringSplit(pszLowerCorner, ' ', &n);
        if (tokens && n >= 2) {
          psBbox->minx = atof(tokens[0]);
          psBbox->miny = atof(tokens[1]);

          msFreeCharArray(tokens, n);

          tokens = msStringSplit(pszUpperCorner, ' ', &n);
          if (tokens && n >= 2) {
            psBbox->maxx = atof(tokens[0]);
            psBbox->maxy = atof(tokens[1]);
            msFreeCharArray(tokens, n);

            bValid = 1;
          }
        }
      }
    }
  }
  if (bValid && ppszSRS && *ppszSRS) {
    projectionObj sProjTmp;
    msInitProjection(&sProjTmp);
    if (msLoadProjectionStringEPSG(&sProjTmp, *ppszSRS) == 0) {
      msAxisNormalizePoints( &sProjTmp, 1, &psBbox->minx, &psBbox->miny);
      msAxisNormalizePoints( &sProjTmp, 1, &psBbox->maxx, &psBbox->maxy);
    }
  }
  return bValid;
}


static void FLTReplacePropertyName(FilterEncodingNode *psFilterNode,
                                   const char *pszOldName, char *pszNewName)
{
  if (psFilterNode && pszOldName && pszNewName) {
    if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
      if (psFilterNode->pszValue &&
          strcasecmp(psFilterNode->pszValue, pszOldName) == 0) {
        msFree(psFilterNode->pszValue);
        psFilterNode->pszValue = msStrdup(pszNewName);
      }
    }
    if (psFilterNode->psLeftNode)
      FLTReplacePropertyName(psFilterNode->psLeftNode, pszOldName,
                             pszNewName);
    if (psFilterNode->psRightNode)
      FLTReplacePropertyName(psFilterNode->psRightNode, pszOldName,
                             pszNewName);
  }
}


static void FLTStripNameSpacesFromPropertyName(FilterEncodingNode *psFilterNode)
{
  char **tokens=NULL;
  int n=0;

  if (psFilterNode) {
    if (psFilterNode->eType == FILTER_NODE_TYPE_PROPERTYNAME) {
      if (psFilterNode->pszValue &&
          strstr(psFilterNode->pszValue, ":")) {
        tokens = msStringSplit(psFilterNode->pszValue, ':', &n);
        if (tokens && n==2) {
          msFree(psFilterNode->pszValue);
          psFilterNode->pszValue = msStrdup(tokens[1]);
        }
        if (tokens && n>0)
          msFreeCharArray(tokens, n);
      }
    }
    if (psFilterNode->psLeftNode)
      FLTStripNameSpacesFromPropertyName(psFilterNode->psLeftNode);
    if (psFilterNode->psRightNode)
      FLTStripNameSpacesFromPropertyName(psFilterNode->psRightNode);
  }

}

/************************************************************************/
/*                        FLTPreParseFilterForAlias                     */
/*                                                                      */
/*      Utility function to replace aliased' attributes with their      */
/*      real name.                                                      */
/************************************************************************/
void FLTPreParseFilterForAlias(FilterEncodingNode *psFilterNode,
                               mapObj *map, int i, const char *namespaces)
{
  layerObj *lp=NULL;
  char szTmp[256];
  const char *pszFullName = NULL;
  int layerWasOpened =  MS_FALSE;

#if defined(USE_WMS_SVR) || defined (USE_WFS_SVR) || defined (USE_WCS_SVR) || defined(USE_SOS_SVR)

  if (psFilterNode && map && i>=0 && i<map->numlayers) {
    /*strip name speces befor hand*/
    FLTStripNameSpacesFromPropertyName(psFilterNode);

    lp = GET_LAYER(map, i);
    layerWasOpened = msLayerIsOpen(lp);
    if (msLayerOpen(lp) == MS_SUCCESS && msLayerGetItems(lp) == MS_SUCCESS) {
      for(i=0; i<lp->numitems; i++) {
        if (!lp->items[i] || strlen(lp->items[i]) <= 0)
          continue;
        snprintf(szTmp, sizeof(szTmp), "%s_alias", lp->items[i]);
        pszFullName = msOWSLookupMetadata(&(lp->metadata), namespaces, szTmp);
        if (pszFullName) {
          FLTReplacePropertyName(psFilterNode, pszFullName,
                                 lp->items[i]);
        }
      }
      if (!layerWasOpened) /* do not close the layer if it has been opened somewhere else (paging?) */
        msLayerClose(lp);
    }
  }
#else
  msSetError(MS_MISCERR, "OWS support is not available.",
             "FLTPreParseFilterForAlias()");

#endif
}


#ifdef USE_LIBXML2

xmlNodePtr FLTGetCapabilities(xmlNsPtr psNsParent, xmlNsPtr psNsOgc, int bTemporal)
{
  xmlNodePtr psRootNode = NULL, psNode = NULL, psSubNode = NULL, psSubSubNode = NULL;

  psRootNode = xmlNewNode(psNsParent, BAD_CAST "Filter_Capabilities");

  psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Spatial_Capabilities", NULL);

  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "GeometryOperands", NULL);
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:Point");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:LineString");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:Polygon");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "GeometryOperand", BAD_CAST "gml:Envelope");

  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "SpatialOperators", NULL);
#ifdef USE_GEOS
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Equals");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Disjoint");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Touches");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Within");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Overlaps");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Crosses");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Intersects");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Contains");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "DWithin");
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "Beyond");
#endif
  psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "SpatialOperator", NULL);
  xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "BBOX");

  if (bTemporal) {
    psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Temporal_Capabilities", NULL);
    psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperands", NULL);
    psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand", BAD_CAST "gml:TimePeriod");
    psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperand", BAD_CAST "gml:TimeInstant");

    psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "TemporalOperators", NULL);
    psSubSubNode = xmlNewChild(psSubNode, psNsOgc, BAD_CAST "TemporalOperator", NULL);
    xmlNewProp(psSubSubNode, BAD_CAST "name", BAD_CAST "TM_Equals");
  }
  psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Scalar_Capabilities", NULL);
  xmlNewChild(psNode, psNsOgc, BAD_CAST "LogicalOperators", NULL);
  psNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperators", NULL);
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "LessThan");
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "GreaterThan");
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "LessThanEqualTo");
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "GreaterThanEqualTo");
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "EqualTo");
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "NotEqualTo");
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "Like");
  psSubNode = xmlNewChild(psNode, psNsOgc, BAD_CAST "ComparisonOperator", BAD_CAST "Between");

  psNode = xmlNewChild(psRootNode, psNsOgc, BAD_CAST "Id_Capabilities", NULL);
  xmlNewChild(psNode, psNsOgc, BAD_CAST "EID", NULL);
  xmlNewChild(psNode, psNsOgc, BAD_CAST "FID", NULL);
  return psRootNode;
}
#endif
#endif
