/******************************************************************************
 * $Id: ogrgeorsslayer.cpp 27729 2014-09-24 00:40:16Z goatbar $
 *
 * Project:  GeoRSS Translator
 * Purpose:  Implements OGRGeoRSSLayer class.
 * Author:   Even Rouault, even dot rouault at mines dash paris dot org
 *
 ******************************************************************************
 * Copyright (c) 2008-2014, Even Rouault <even dot rouault at mines-paris dot org>
 *
 * 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 or substantial portions of the 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
 * DEALINGS IN THE SOFTWARE.
 ****************************************************************************/

#include "ogr_georss.h"
#include "cpl_conv.h"
#include "cpl_minixml.h"
#include "ogr_api.h"
#include "ogr_p.h"

CPL_CVSID("$Id: ogrgeorsslayer.cpp 27729 2014-09-24 00:40:16Z goatbar $");

static const char* apszAllowedATOMFieldNamesWithSubElements[] = { "author", "contributor", NULL };

static
const char* apszAllowedRSSFieldNames[] = {  "title", "link", "description", "author",
                                            "category", "category_domain",
                                            "comments",
                                            "enclosure_url", "enclosure_length", "enclosure_type",
                                            "guid", "guid_isPermaLink",
                                            "pubDate",
                                            "source", "source_url", NULL};

static
const char* apszAllowedATOMFieldNames[] = { "category_term", "category_scheme", "category_label",
                                            "content", "content_type", "content_xml_lang", "content_xml_base",
                                            "summary", "summary_type", "summary_xml_lang", "summary_xml_base",
                                            "author_name", "author_uri", "author_email",
                                            "contributor_name", "contributor_uri", "contributor_email",
                                            "link_href", "link_rel", "link_type", "link_length",
                                            "id", "published", "rights", "source",
                                            "title", "updated", NULL };

#define IS_LAT_ELEMENT(pszName) (strncmp(pszName, "geo:lat", strlen("geo:lat")) == 0 || \
                                 strncmp(pszName, "icbm:lat", strlen("icbm:lat")) == 0 || \
                                 strncmp(pszName, "geourl:lat", strlen("geourl:lat")) == 0)

#define IS_LON_ELEMENT(pszName) (strncmp(pszName, "geo:lon", strlen("geo:lon")) == 0 || \
                                 strncmp(pszName, "icbm:lon", strlen("icbm:lon")) == 0 || \
                                 strncmp(pszName, "geourl:lon", strlen("geourl:lon")) == 0)

#define IS_GEO_ELEMENT(pszName) (strcmp(pszName, "georss:point") == 0 || \
                                 strcmp(pszName, "georss:line") == 0 || \
                                 strcmp(pszName, "georss:box") == 0 || \
                                 strcmp(pszName, "georss:polygon") == 0 || \
                                 strcmp(pszName, "georss:where") == 0 || \
                                 strncmp(pszName, "gml:", strlen("gml:")) == 0 || \
                                 strncmp(pszName, "geo:", strlen("geo:")) == 0 || \
                                 strncmp(pszName, "icbm:", strlen("icbm:")) == 0 || \
                                 strncmp(pszName, "geourl:", strlen("geourl:")) == 0)

/************************************************************************/
/*                            OGRGeoRSSLayer()                          */
/************************************************************************/

OGRGeoRSSLayer::OGRGeoRSSLayer( const char* pszFilename,
                                const char* pszLayerName,
                                OGRGeoRSSDataSource* poDS,
                                OGRSpatialReference *poSRSIn,
                                int bWriteMode)

{
    eof = FALSE;
    nNextFID = 0;

    this->poDS = poDS;
    this->bWriteMode = bWriteMode;

    eFormat = poDS->GetFormat();

    poFeatureDefn = new OGRFeatureDefn( pszLayerName );
    poFeatureDefn->Reference();

    poSRS = poSRSIn;
    if (poSRS)
    {
        poSRS->Reference();
        poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
    }

    nTotalFeatureCount = 0;

    ppoFeatureTab = NULL;
    nFeatureTabIndex = 0;
    nFeatureTabLength = 0;
    pszSubElementName = NULL;
    pszSubElementValue = NULL;
    nSubElementValueLen = 0;
    pszGMLSRSName = NULL;
    pszTagWithSubTag = NULL;
    bStopParsing = FALSE;
    bHasReadSchema = FALSE;
    setOfFoundFields = NULL;
    poGlobalGeom = NULL;
    hasFoundLat = FALSE;
    hasFoundLon = FALSE;

    poFeature = NULL;

#ifdef HAVE_EXPAT
    oParser = NULL;
#endif

    if (bWriteMode == FALSE)
    {
        fpGeoRSS = VSIFOpenL( pszFilename, "r" );
        if( fpGeoRSS == NULL )
        {
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename);
            return;
        }
    }
    else
        fpGeoRSS = NULL;

    ResetReading();
}

/************************************************************************/
/*                            ~OGRGeoRSSLayer()                            */
/************************************************************************/

OGRGeoRSSLayer::~OGRGeoRSSLayer()

{
#ifdef HAVE_EXPAT
    if (oParser)
        XML_ParserFree(oParser);
#endif
    poFeatureDefn->Release();
    
    if( poSRS != NULL )
        poSRS->Release();

    CPLFree(pszSubElementName);
    CPLFree(pszSubElementValue);
    CPLFree(pszGMLSRSName);
    CPLFree(pszTagWithSubTag);
    if (setOfFoundFields)
        CPLHashSetDestroy(setOfFoundFields);
    if (poGlobalGeom)
        delete poGlobalGeom;

    int i;
    for(i=nFeatureTabIndex;i<nFeatureTabLength;i++)
        delete ppoFeatureTab[i];
    CPLFree(ppoFeatureTab);

    if (poFeature)
        delete poFeature;

    if (fpGeoRSS)
        VSIFCloseL( fpGeoRSS );
}


/************************************************************************/
/*                            GetLayerDefn()                            */
/************************************************************************/

OGRFeatureDefn * OGRGeoRSSLayer::GetLayerDefn()
{
    if (!bHasReadSchema)
        LoadSchema();

    return poFeatureDefn;
}



#ifdef HAVE_EXPAT

static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
                                    const char **ppszAttr)
{
    ((OGRGeoRSSLayer*)pUserData)->startElementCbk(pszName, ppszAttr);
}

static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
{
    ((OGRGeoRSSLayer*)pUserData)->endElementCbk(pszName);
}

static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
{
    ((OGRGeoRSSLayer*)pUserData)->dataHandlerCbk(data, nLen);
}

#endif

/************************************************************************/
/*                            ResetReading()                            */
/************************************************************************/

void OGRGeoRSSLayer::ResetReading()

{
    if (bWriteMode)
        return;

    eof = FALSE;
    nNextFID = 0;
    if (fpGeoRSS)
    {
        VSIFSeekL( fpGeoRSS, 0, SEEK_SET );
#ifdef HAVE_EXPAT
        if (oParser)
            XML_ParserFree(oParser);
        
        oParser = OGRCreateExpatXMLParser();
        XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk);
        XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
        XML_SetUserData(oParser, this);
#endif
    }
    bInFeature = FALSE;
    hasFoundLat = FALSE;
    hasFoundLon = FALSE;
    bInSimpleGeometry = FALSE;
    bInGMLGeometry = FALSE;
    bInGeoLat = FALSE;
    bInGeoLong = FALSE;
    eGeomType = wkbUnknown;
    CPLFree(pszSubElementName);
    pszSubElementName = NULL;
    CPLFree(pszSubElementValue);
    pszSubElementValue = NULL;
    nSubElementValueLen = 0;
    CPLFree(pszGMLSRSName);
    pszGMLSRSName = NULL;

    if (setOfFoundFields)
        CPLHashSetDestroy(setOfFoundFields);
    setOfFoundFields = NULL;

    int i;
    for(i=nFeatureTabIndex;i<nFeatureTabLength;i++)
        delete ppoFeatureTab[i];
    CPLFree(ppoFeatureTab);
    nFeatureTabIndex = 0;
    nFeatureTabLength = 0;
    ppoFeatureTab = NULL;
    if (poFeature)
        delete poFeature;
    poFeature = NULL;

    currentDepth = 0;
    featureDepth = 0;
    geometryDepth = 0;
    bInTagWithSubTag = FALSE;
    CPLFree(pszTagWithSubTag);
    pszTagWithSubTag = NULL;
}

#ifdef HAVE_EXPAT

/************************************************************************/
/*                      AddStrToSubElementValue()                       */
/************************************************************************/

void OGRGeoRSSLayer::AddStrToSubElementValue(const char* pszStr)
{
    int len = strlen(pszStr);
    char* pszNewSubElementValue = (char*)
            VSIRealloc(pszSubElementValue, nSubElementValueLen + len + 1);
    if (pszNewSubElementValue == NULL)
    {
        CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
        XML_StopParser(oParser, XML_FALSE);
        bStopParsing = TRUE;
        return;
    }
    pszSubElementValue = pszNewSubElementValue;

    memcpy(pszSubElementValue + nSubElementValueLen, pszStr, len);
    nSubElementValueLen += len;
}

/************************************************************************/
/*              OGRGeoRSS_GetOGRCompatibleTagName()                     */
/************************************************************************/

/** Replace ':' from XML NS element name by '_' more OGR friendly */
static char* OGRGeoRSS_GetOGRCompatibleTagName(const char* pszName)
{
    char* pszModName = CPLStrdup(pszName);
    int i;
    for(i=0;pszModName[i] != 0;i++)
    {
        if (pszModName[i] == ':')
            pszModName[i] = '_';
    }
    return pszModName;
}

/************************************************************************/
/*               OGRGeoRSSLayerATOMTagHasSubElement()                   */
/************************************************************************/

static int OGRGeoRSSLayerATOMTagHasSubElement(const char* pszName)
{
    unsigned int i;
    for(i=0;apszAllowedATOMFieldNamesWithSubElements[i] != NULL;i++)
    {
        if (strcmp(pszName, apszAllowedATOMFieldNamesWithSubElements[i]) == 0)
            return TRUE;
    }
    return FALSE;
}

/************************************************************************/
/*                        startElementCbk()                            */
/************************************************************************/

void OGRGeoRSSLayer::startElementCbk(const char *pszName, const char **ppszAttr)
{
    int bSerializeTag = FALSE;

    if (bStopParsing) return;

    if ((eFormat == GEORSS_ATOM && currentDepth == 1 && strcmp(pszName, "entry") == 0) ||
        ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && !bInFeature && (currentDepth == 1 || currentDepth == 2) && strcmp(pszName, "item") == 0))
    {
        featureDepth = currentDepth;

        if (poFeature)
            delete poFeature;

        poFeature = new OGRFeature( poFeatureDefn );
        poFeature->SetFID( nNextFID++ );

        bInFeature = TRUE;
        hasFoundLat = FALSE;
        hasFoundLon = FALSE;
        bInSimpleGeometry = FALSE;
        bInGMLGeometry = FALSE;
        bInGeoLat = FALSE;
        bInGeoLong = FALSE;
        eGeomType = wkbUnknown;
        geometryDepth = 0;
        bInTagWithSubTag = FALSE;

        if (setOfFoundFields)
            CPLHashSetDestroy(setOfFoundFields);
        setOfFoundFields = CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
    }
    else if (bInFeature && bInTagWithSubTag && currentDepth == 3)
    {
        char* pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszName));

        CPLFree(pszSubElementName);
        pszSubElementName = NULL;
        CPLFree(pszSubElementValue);
        pszSubElementValue = NULL;
        nSubElementValueLen = 0;

        iCurrentField = poFeatureDefn->GetFieldIndex(pszFieldName);
        if (iCurrentField >= 0)
            pszSubElementName = CPLStrdup(pszFieldName);

        CPLFree(pszFieldName);
    }
    else if (bInFeature && eFormat == GEORSS_ATOM &&
             currentDepth == 2 && OGRGeoRSSLayerATOMTagHasSubElement(pszName))
    {
        CPLFree(pszTagWithSubTag);
        pszTagWithSubTag = CPLStrdup(pszName);

        int count = 1;
        while(CPLHashSetLookup(setOfFoundFields, pszTagWithSubTag) != NULL)
        {
            count ++;
            CPLFree(pszTagWithSubTag);
            pszTagWithSubTag = CPLStrdup(CPLSPrintf("%s%d", pszName, count));
        }
        CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszTagWithSubTag));

        bInTagWithSubTag = TRUE;
    }
    else if (bInGMLGeometry)
    {
        bSerializeTag = TRUE;
    }
    else if (bInSimpleGeometry || bInGeoLat || bInGeoLong)
    {
        /* Shouldn't happen for a valid document */
    }
    else if (IS_LAT_ELEMENT(pszName))
    {
        CPLFree(pszSubElementValue);
        pszSubElementValue = NULL;
        nSubElementValueLen = 0;
        bInGeoLat = TRUE;
    }
    else if (IS_LON_ELEMENT(pszName))
    {
        CPLFree(pszSubElementValue);
        pszSubElementValue = NULL;
        nSubElementValueLen = 0;
        bInGeoLong = TRUE;
    }
    else if (strcmp(pszName, "georss:point") == 0 ||
             strcmp(pszName, "georss:line") == 0 ||
             strcmp(pszName, "geo:line") == 0 ||
             strcmp(pszName, "georss:polygon") == 0 ||
             strcmp(pszName, "georss:box") == 0)
    {
        CPLFree(pszSubElementValue);
        pszSubElementValue = NULL;
        nSubElementValueLen = 0;
        eGeomType = strcmp(pszName, "georss:point") == 0 ?   wkbPoint :
                      (strcmp(pszName, "georss:line") == 0 ||
                       strcmp(pszName, "geo:line") == 0)  ?  wkbLineString :
                      (strcmp(pszName, "georss:polygon") == 0  ||
                       strcmp(pszName, "georss:box") == 0) ? wkbPolygon :
                                                             wkbUnknown;
        bInSimpleGeometry = TRUE;
        geometryDepth = currentDepth;
    }
    else if (strcmp(pszName, "gml:Point") == 0 ||
             strcmp(pszName, "gml:LineString") == 0 ||
             strcmp(pszName, "gml:Polygon") == 0 ||
             strcmp(pszName, "gml:MultiPoint") == 0 ||
             strcmp(pszName, "gml:MultiLineString") == 0 ||
             strcmp(pszName, "gml:MultiPolygon") == 0 ||
             strcmp(pszName, "gml:Envelope") == 0)
    {
        CPLFree(pszSubElementValue);
        pszSubElementValue = NULL;
        nSubElementValueLen = 0;
        AddStrToSubElementValue(CPLSPrintf("<%s>", pszName));
        bInGMLGeometry = TRUE;
        geometryDepth = currentDepth;
        CPLFree(pszGMLSRSName);
        pszGMLSRSName = NULL;
        for (int i = 0; ppszAttr[i]; i += 2)
        {
            if (strcmp(ppszAttr[i], "srsName") == 0)
            {
                if (pszGMLSRSName == NULL)
                    pszGMLSRSName = CPLStrdup(ppszAttr[i+1]);
            }
        }
    }
    else if (bInFeature && currentDepth == featureDepth + 1)
    {
        CPLFree(pszSubElementName);
        pszSubElementName = NULL;
        CPLFree(pszSubElementValue);
        pszSubElementValue = NULL;
        nSubElementValueLen = 0;
        iCurrentField = -1;

        pszSubElementName = CPLStrdup(pszName);
        int count = 1;
        while(CPLHashSetLookup(setOfFoundFields, pszSubElementName) != NULL)
        {
            count ++;
            CPLFree(pszSubElementName);
            pszSubElementName = CPLStrdup(CPLSPrintf("%s%d", pszName, count));
        }
        CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszSubElementName));

        char* pszCompatibleName = OGRGeoRSS_GetOGRCompatibleTagName(pszSubElementName);
        iCurrentField = poFeatureDefn->GetFieldIndex(pszCompatibleName);
        CPLFree(pszSubElementName);

        for(int i = 0; ppszAttr[i] != NULL && ppszAttr[i+1] != NULL; i+=2)
        {
            char* pszAttrCompatibleName =
                    OGRGeoRSS_GetOGRCompatibleTagName(CPLSPrintf("%s_%s", pszCompatibleName, ppszAttr[i]));
            int iAttrField = poFeatureDefn->GetFieldIndex(pszAttrCompatibleName);
            if (iAttrField >= 0)
            {
                if (poFeatureDefn->GetFieldDefn(iAttrField)->GetType() == OFTReal)
                    poFeature->SetField( iAttrField, CPLAtof(ppszAttr[i+1]) );
                else
                    poFeature->SetField( iAttrField, ppszAttr[i+1] );
            }
            CPLFree(pszAttrCompatibleName);
        }

        if (iCurrentField < 0)
        {
            pszSubElementName = NULL;
        }
        else
        {
            pszSubElementName = CPLStrdup(pszCompatibleName);
        }
        CPLFree(pszCompatibleName);
    }
    else if (bInFeature && currentDepth > featureDepth + 1 && pszSubElementName != NULL)
    {
        bSerializeTag = TRUE;
    }

    if (bSerializeTag)
    {
        AddStrToSubElementValue("<");
        AddStrToSubElementValue(pszName);
        for(int i = 0; ppszAttr[i] != NULL && ppszAttr[i+1] != NULL; i+=2)
        {
            AddStrToSubElementValue(" ");
            AddStrToSubElementValue(ppszAttr[i]);
            AddStrToSubElementValue("=\"");
            AddStrToSubElementValue(ppszAttr[i+1]);
            AddStrToSubElementValue("\"");
        }
        AddStrToSubElementValue(">");
    }

    currentDepth++;
}

/************************************************************************/
/*            OGRGeoRSSLayerTrimLeadingAndTrailingSpaces()              */
/************************************************************************/

static void OGRGeoRSSLayerTrimLeadingAndTrailingSpaces(char* pszStr)
{
    int i;

    /* Trim leading spaces, tabs and newlines */
    i = 0;
    while(pszStr[i] != '\0' &&
          (pszStr[i] == ' ' || pszStr[i] == '\t' || pszStr[i] == '\n'))
        i ++;
    memmove(pszStr, pszStr + i, strlen(pszStr + i) + 1);

    /* Trim trailing spaces, tabs and newlines */
    i = strlen(pszStr) - 1;
    while(i >= 0 &&
          (pszStr[i] == ' ' || pszStr[i] == '\t' || pszStr[i] == '\n'))
    {
        pszStr[i] = '\0';
        i --;
    }
}

/************************************************************************/
/*                           endElementCbk()                            */
/************************************************************************/

void OGRGeoRSSLayer::endElementCbk(const char *pszName)
{
    OGRGeometry* poGeom = NULL;

    if (bStopParsing) return;

    currentDepth--;

    if ((eFormat == GEORSS_ATOM && currentDepth == 1 && strcmp(pszName, "entry") == 0) ||
        ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && (currentDepth == 1 || currentDepth == 2) && strcmp(pszName, "item") == 0))
    {
        bInFeature = FALSE;
        bInTagWithSubTag = FALSE;

        if (hasFoundLat && hasFoundLon)
            poFeature->SetGeometryDirectly( new OGRPoint( lonVal, latVal ) );
        else if (poFeature->GetGeometryRef() == NULL && poGlobalGeom != NULL)
            poFeature->SetGeometry(poGlobalGeom);

        hasFoundLat = FALSE;
        hasFoundLon = FALSE;

        if (poSRS != NULL && poFeature->GetGeometryRef() != NULL)
            poFeature->GetGeometryRef()->assignSpatialReference(poSRS);

        if( (m_poFilterGeom == NULL
                || FilterGeometry( poFeature->GetGeometryRef() ) )
            && (m_poAttrQuery == NULL
                || m_poAttrQuery->Evaluate( poFeature )) )
        {
            ppoFeatureTab = (OGRFeature**)
                    CPLRealloc(ppoFeatureTab,
                                sizeof(OGRFeature*) * (nFeatureTabLength + 1));
            ppoFeatureTab[nFeatureTabLength] = poFeature;
            nFeatureTabLength++;
        }
        else
        {
            delete poFeature;
        }
        poFeature = NULL;
        return;
    }

    if (bInTagWithSubTag && currentDepth == 3)
    {
        char* pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszName));

        if (iCurrentField != -1 && pszSubElementName &&
            strcmp(pszFieldName, pszSubElementName) == 0 && poFeature &&
            pszSubElementValue && nSubElementValueLen)
        {
            pszSubElementValue[nSubElementValueLen] = 0;
            if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() == OFTReal)
                poFeature->SetField( iCurrentField, CPLAtof(pszSubElementValue) );
            else
                poFeature->SetField( iCurrentField, pszSubElementValue);
        }

        CPLFree(pszSubElementName);
        pszSubElementName = NULL;
        CPLFree(pszSubElementValue);
        pszSubElementValue = NULL;
        nSubElementValueLen = 0;

        CPLFree(pszFieldName);
    }
    else if (bInFeature && eFormat == GEORSS_ATOM &&
             currentDepth == 2 && OGRGeoRSSLayerATOMTagHasSubElement(pszName))
    {
        bInTagWithSubTag = FALSE;
    }
    else if (bInGMLGeometry)
    {
        AddStrToSubElementValue("</");
        AddStrToSubElementValue(pszName);
        AddStrToSubElementValue(">");
        if (currentDepth > geometryDepth)
        {
        }
        else
        {
            pszSubElementValue[nSubElementValueLen] = 0;
            CPLAssert(strncmp(pszName, "gml:", 4) == 0);
            poGeom = (OGRGeometry*) OGR_G_CreateFromGML(pszSubElementValue);

            if (poGeom != NULL && !poGeom->IsEmpty() )
            {
                int bSwapCoordinates = FALSE;
                if (pszGMLSRSName)
                {
                    OGRSpatialReference* poSRSFeature = new OGRSpatialReference();
                    poSRSFeature->importFromURN(pszGMLSRSName);
                    poGeom->assignSpatialReference(poSRSFeature);
                    poSRSFeature->Release();
                }
                else
                    bSwapCoordinates = TRUE; /* lat, lon WGS 84 */

                if (bSwapCoordinates)
                {
                    poGeom->swapXY();
                }
            }
            bInGMLGeometry = FALSE;
        }
    }
    else if (bInSimpleGeometry)
    {
        if (currentDepth > geometryDepth)
        {
            /* Shouldn't happen for a valid document */
        }
        else
        {
            if (pszSubElementValue)
            {
                pszSubElementValue[nSubElementValueLen] = 0;

                /* Trim any leading and trailing spaces, tabs, newlines, etc... */
                OGRGeoRSSLayerTrimLeadingAndTrailingSpaces(pszSubElementValue);

                /* Caution : Order is latitude, longitude */
                char** papszTokens =
                        CSLTokenizeStringComplex( pszSubElementValue,
                                                    " ,", TRUE, FALSE );

                int nTokens = CSLCount(papszTokens);
                if ((nTokens % 2) != 0 ||
                     (eGeomType == wkbPoint && nTokens != 2) ||
                     (eGeomType == wkbLineString && nTokens < 4) ||
                     (strcmp(pszName, "georss:polygon") == 0 && nTokens < 6) ||
                     (strcmp(pszName, "georss:box") == 0 && nTokens != 4))
                {
                    CPLError(CE_Failure, CPLE_AppDefined,
                             "Wrong number of coordinates in %s",
                             pszSubElementValue);
                }
                else if (eGeomType == wkbPoint)
                {
                    poGeom = new OGRPoint( CPLAtof(papszTokens[1]),
                                           CPLAtof(papszTokens[0]) );
                }
                else if (eGeomType == wkbLineString)
                {
                    OGRLineString* poLineString = new OGRLineString ();
                    poGeom = poLineString;
                    int i;
                    for(i=0;i<nTokens;i+=2)
                    {
                        poLineString->addPoint( CPLAtof(papszTokens[i+1]),
                                              CPLAtof(papszTokens[i]) );
                    }
                }
                else if (eGeomType == wkbPolygon)
                {
                    OGRPolygon* poPolygon = new OGRPolygon();
                    OGRLinearRing* poLinearRing = new OGRLinearRing();
                    poGeom = poPolygon;
                    poPolygon->addRingDirectly(poLinearRing);
                    if (strcmp(pszName, "georss:polygon") == 0)
                    {
                        int i;
                        for(i=0;i<nTokens;i+=2)
                        {
                            poLinearRing->addPoint( CPLAtof(papszTokens[i+1]),
                                                    CPLAtof(papszTokens[i]) );
                        }
                    }
                    else
                    {
                        double lat1 = CPLAtof(papszTokens[0]);
                        double lon1 = CPLAtof(papszTokens[1]);
                        double lat2 = CPLAtof(papszTokens[2]);
                        double lon2 = CPLAtof(papszTokens[3]);
                        poLinearRing->addPoint( lon1, lat1 );
                        poLinearRing->addPoint( lon1, lat2 );
                        poLinearRing->addPoint( lon2, lat2 );
                        poLinearRing->addPoint( lon2, lat1 );
                        poLinearRing->addPoint( lon1, lat1 );
                    }
                }

                CSLDestroy(papszTokens);
            }
            bInSimpleGeometry = FALSE;
        }
    }
    else if (IS_LAT_ELEMENT(pszName))
    {
        if (pszSubElementValue)
        {
            hasFoundLat = TRUE;
            pszSubElementValue[nSubElementValueLen] = 0;
            latVal = CPLAtof(pszSubElementValue);
        }
        bInGeoLat = FALSE;
    }
    else if (IS_LON_ELEMENT(pszName))
    {
        if (pszSubElementValue)
        {
            hasFoundLon = TRUE;
            pszSubElementValue[nSubElementValueLen] = 0;
            lonVal = CPLAtof(pszSubElementValue);
        }
        bInGeoLong = FALSE;
    }
    else if (bInFeature && currentDepth == featureDepth + 1)
    {
        if (iCurrentField != -1 && pszSubElementName &&
            poFeature && pszSubElementValue && nSubElementValueLen)
        {
            pszSubElementValue[nSubElementValueLen] = 0;
            if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() == OFTDateTime)
            {
                int year, month, day, hour, minute, TZ;
                int nsecond;
                float fsecond;
                if (OGRParseRFC822DateTime(pszSubElementValue, &year, &month, &day,
                                                 &hour, &minute, &nsecond, &TZ))
                {
                    poFeature->SetField(iCurrentField, year, month, day,
                                        hour, minute, nsecond, TZ);
                }
                else if (OGRParseXMLDateTime(pszSubElementValue, &year, &month, &day,
                                                   &hour, &minute, &fsecond, &TZ))
                {
                    poFeature->SetField(iCurrentField, year, month, day,
                                        hour, minute, (int)(fsecond + .5), TZ);
                }
                else
                {
                    CPLError(CE_Warning, CPLE_AppDefined,
                                "Could not parse %s as a valid dateTime", pszSubElementValue);
                }
            }
            else
            {
                if (poFeatureDefn->GetFieldDefn(iCurrentField)->GetType() == OFTReal)
                    poFeature->SetField( iCurrentField, CPLAtof(pszSubElementValue) );
                else
                    poFeature->SetField( iCurrentField, pszSubElementValue);
            }
        }

        CPLFree(pszSubElementName);
        pszSubElementName = NULL;
        CPLFree(pszSubElementValue);
        pszSubElementValue = NULL;
        nSubElementValueLen = 0;
    }
    else if (bInFeature && currentDepth > featureDepth + 1 && pszSubElementName != NULL)
    {
        AddStrToSubElementValue("</");
        AddStrToSubElementValue(pszName);
        AddStrToSubElementValue(">");
    }

    if (poGeom != NULL)
    {
        if (poFeature != NULL)
        {
            poFeature->SetGeometryDirectly(poGeom);
        }
        else if (!bInFeature)
        {
            if (poGlobalGeom != NULL)
                delete poGlobalGeom;
            poGlobalGeom = poGeom;
        }
        else
            delete poGeom;
    }
    else if (!bInFeature && hasFoundLat && hasFoundLon)
    {
        if (poGlobalGeom != NULL)
                delete poGlobalGeom;
        poGlobalGeom = new OGRPoint( lonVal, latVal );
        hasFoundLat = hasFoundLon = FALSE;
    }
}

/************************************************************************/
/*                          dataHandlerCbk()                            */
/************************************************************************/

void OGRGeoRSSLayer::dataHandlerCbk(const char *data, int nLen)
{
    if (bStopParsing) return;

    if (bInGMLGeometry == TRUE || bInSimpleGeometry == TRUE ||
        bInGeoLat == TRUE || bInGeoLong == TRUE ||
        pszSubElementName != NULL)
    {
        char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue,
                                               nSubElementValueLen + nLen + 1);
        if (pszNewSubElementValue == NULL)
        {
            CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
            XML_StopParser(oSchemaParser, XML_FALSE);
            bStopParsing = TRUE;
            return;
        }
        pszSubElementValue = pszNewSubElementValue;
        memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
        nSubElementValueLen += nLen;
    }
}
#endif

/************************************************************************/
/*                           GetNextFeature()                           */
/************************************************************************/

OGRFeature *OGRGeoRSSLayer::GetNextFeature()
{
    if (bWriteMode)
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Cannot read features when writing a GeoRSS file");
        return NULL;
    }

    if (fpGeoRSS == NULL)
        return NULL;

    if (!bHasReadSchema)
        LoadSchema();

    if (bStopParsing)
        return NULL;

#ifdef HAVE_EXPAT
    if (nFeatureTabIndex < nFeatureTabLength)
    {
        return ppoFeatureTab[nFeatureTabIndex++];
    }
    
    if (VSIFEofL(fpGeoRSS))
        return NULL;
    
    char aBuf[BUFSIZ];
    
    CPLFree(ppoFeatureTab);
    ppoFeatureTab = NULL;
    nFeatureTabLength = 0;
    nFeatureTabIndex = 0;

    int nDone;
    do
    {
        unsigned int nLen =
                (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGeoRSS );
        nDone = VSIFEofL(fpGeoRSS);
        if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "XML parsing of GeoRSS file failed : %s "
                     "at line %d, column %d",
                     XML_ErrorString(XML_GetErrorCode(oParser)),
                     (int)XML_GetCurrentLineNumber(oParser),
                     (int)XML_GetCurrentColumnNumber(oParser));
            bStopParsing = TRUE;
        }
    } while (!nDone && !bStopParsing && nFeatureTabLength == 0);
    
    return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : NULL;
#else
    return NULL;
#endif
}

/************************************************************************/
/*              OGRGeoRSSLayerIsStandardFieldInternal()                 */
/************************************************************************/

static int OGRGeoRSSLayerIsStandardFieldInternal(const char* pszName,
                                                 const char** papszNames)
{
    unsigned int i;
    for( i = 0; papszNames[i] != NULL; i++)
    {
        if (strcmp(pszName, papszNames[i]) == 0)
        {
            return TRUE;
        }

        const char* pszUnderscore = strchr(papszNames[i], '_');
        if (pszUnderscore == NULL)
        {
            int nLen = strlen(papszNames[i]);
            if (strncmp(pszName, papszNames[i], nLen) == 0)
            {
                int k = nLen;
                while(pszName[k] >= '0' && pszName[k] <= '9')
                    k++;
                if (pszName[k] == '\0')
                    return TRUE;
            }
        }
        else
        {
            int nLen = pszUnderscore - papszNames[i];
            if (strncmp(pszName, papszNames[i], nLen) == 0)
            {
                int k = nLen;
                while(pszName[k] >= '0' && pszName[k] <= '9')
                    k++;
                if (pszName[k] == '_' && strcmp(pszName + k, pszUnderscore) == 0)
                    return TRUE;
            }
        }
    }
    return FALSE;
}

/************************************************************************/
/*               OGRGeoRSSLayer::IsStandardField()                      */
/************************************************************************/

int OGRGeoRSSLayer::IsStandardField(const char* pszName)
{
    if (eFormat == GEORSS_RSS)
    {
        return OGRGeoRSSLayerIsStandardFieldInternal(pszName,
                apszAllowedRSSFieldNames);
    }
    else
    {
        return OGRGeoRSSLayerIsStandardFieldInternal(pszName,
                apszAllowedATOMFieldNames);
    }
}

/************************************************************************/
/*                 OGRGeoRSSLayerSplitComposedField()                   */
/************************************************************************/

static void OGRGeoRSSLayerSplitComposedField(const char* pszName,
                                             char** ppszElementName,
                                             char** ppszNumber,
                                             char** ppszAttributeName)
{
    *ppszElementName = CPLStrdup(pszName);

    int i = 0;
    while(pszName[i] != '\0' && pszName[i] != '_' &&
          !(pszName[i] >= '0' && pszName[i] <= '9'))
    {
        i++;
    }

    (*ppszElementName)[i] = '\0';

    if (pszName[i] >= '0' && pszName[i] <= '9')
    {
        *ppszNumber = CPLStrdup(pszName + i);
        char* pszUnderscore = strchr(*ppszNumber, '_');
        if (pszUnderscore)
        {
            *pszUnderscore = '\0';
            *ppszAttributeName = CPLStrdup(pszUnderscore + 1);
        }
        else
        {
            *ppszAttributeName = NULL;
        }
    }
    else
    {
        *ppszNumber = CPLStrdup("");
        if (pszName[i] == '_')
        {
            *ppszAttributeName = CPLStrdup(pszName + i + 1);
        }
        else
        {
            *ppszAttributeName = NULL;
        }
    }
}

/************************************************************************/
/*                 OGRGeoRSSLayerWriteSimpleElement()                   */
/************************************************************************/

static void OGRGeoRSSLayerWriteSimpleElement(VSILFILE* fp,
                                             const char* pszElementName,
                                             const char* pszNumber,
                                             const char** papszNames,
                                             OGRFeatureDefn* poFeatureDefn,
                                             OGRFeature* poFeature)
{
    VSIFPrintfL(fp, "      <%s", pszElementName);

    unsigned k;
    for( k = 0; papszNames[k] != NULL ; k++)
    {
        if (strncmp(papszNames[k], pszElementName, strlen(pszElementName)) == 0 &&
            papszNames[k][strlen(pszElementName)] == '_')
        {
            const char* pszAttributeName = papszNames[k] + strlen(pszElementName) + 1;
            char* pszFieldName = CPLStrdup(CPLSPrintf("%s%s_%s", pszElementName, pszNumber, pszAttributeName));
            int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
            if (iIndex != -1 && poFeature->IsFieldSet( iIndex ))
            {
                char* pszValue =
                        OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex ));
                if (poFeatureDefn->GetFieldDefn(iIndex)->GetType() == OFTReal)
                {
                    char* pszComma = strchr(pszValue, ',');
                    if (pszComma)
                        *pszComma = '.';
                }
                VSIFPrintfL(fp, " %s=\"%s\"", pszAttributeName, pszValue);
                CPLFree(pszValue);
            }
            CPLFree(pszFieldName);
        }
    }

    char* pszFieldName = CPLStrdup(CPLSPrintf("%s%s", pszElementName, pszNumber));
    int iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
    if (iIndex != -1 && poFeature->IsFieldSet( iIndex ))
    {
        VSIFPrintfL(fp, ">");

        char* pszValue =
                OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex ));
        if (poFeatureDefn->GetFieldDefn(iIndex)->GetType() == OFTReal)
        {
            char* pszComma = strchr(pszValue, ',');
            if (pszComma)
                *pszComma = '.';
        }
        VSIFPrintfL(fp, "%s", pszValue);
        CPLFree(pszValue);

        VSIFPrintfL(fp, "</%s>\n", pszElementName);
    }
    else
    {
        VSIFPrintfL(fp, "/>\n");
    }
    CPLFree(pszFieldName);
}

/************************************************************************/
/*                           CreateFeature()                            */
/************************************************************************/

OGRErr OGRGeoRSSLayer::CreateFeature( OGRFeature *poFeature )

{
    VSILFILE* fp = poDS->GetOutputFP();
    if (fp == NULL)
        return CE_Failure;

    nNextFID ++;

    /* Verify that compulsory feeds are set. Otherwise put some default value in them */
    if (eFormat == GEORSS_RSS)
    {
        int iFieldTitle = poFeatureDefn->GetFieldIndex( "title" );
        int iFieldDescription = poFeatureDefn->GetFieldIndex( "description" );

        VSIFPrintfL(fp, "    <item>\n");

        if ((iFieldTitle == -1 || poFeature->IsFieldSet( iFieldTitle ) == FALSE) &&
            (iFieldDescription == -1 || poFeature->IsFieldSet( iFieldDescription ) == FALSE))
        {
            VSIFPrintfL(fp, "      <title>Feature %d</title>\n", nNextFID);
        }
    }
    else
    {
        VSIFPrintfL(fp, "    <entry>\n");

        int iFieldId = poFeatureDefn->GetFieldIndex( "id" );
        int iFieldTitle = poFeatureDefn->GetFieldIndex( "title" );
        int iFieldUpdated = poFeatureDefn->GetFieldIndex( "updated" );

        if (iFieldId == -1 || poFeature->IsFieldSet( iFieldId ) == FALSE)
        {
            VSIFPrintfL(fp, "      <id>Feature %d</id>\n", nNextFID);
        }

        if (iFieldTitle == -1 || poFeature->IsFieldSet( iFieldTitle ) == FALSE)
        {
            VSIFPrintfL(fp, "      <title>Title for feature %d</title>\n", nNextFID);
        }

        if (iFieldUpdated == -1 || poFeature->IsFieldSet(iFieldUpdated ) == FALSE)
        {
            VSIFPrintfL(fp, "      <updated>2009-01-01T00:00:00Z</updated>\n");
        }
    }

    int nFieldCount = poFeatureDefn->GetFieldCount();
    int* pbUsed = (int*)CPLCalloc(sizeof(int), nFieldCount);

    for(int i = 0; i < nFieldCount; i ++)
    {
        OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( i );
        const char* pszName = poFieldDefn->GetNameRef();

        if ( ! poFeature->IsFieldSet( i ) )
            continue;

        char* pszElementName;
        char* pszNumber;
        char* pszAttributeName;
        OGRGeoRSSLayerSplitComposedField(pszName, &pszElementName, &pszNumber, &pszAttributeName);

        int bWillSkip = FALSE;
        /* Handle Atom entries with elements with sub-elements like */
        /* <author><name>...</name><uri>...</uri></author */
        if (eFormat == GEORSS_ATOM)
        {
            unsigned int k;
            for (k=0;apszAllowedATOMFieldNamesWithSubElements[k] != NULL;k++)
            {
                if (strcmp(pszElementName, apszAllowedATOMFieldNamesWithSubElements[k]) == 0 &&
                    pszAttributeName != NULL)
                {
                    bWillSkip = TRUE;
                    if (pbUsed[i])
                        break;

                    VSIFPrintfL(fp, "      <%s>\n", pszElementName);

                    int j;
                    for(j = i; j < nFieldCount; j ++)
                    {
                        poFieldDefn = poFeatureDefn->GetFieldDefn( j );
                        if ( ! poFeature->IsFieldSet( j ) )
                            continue;

                        char* pszElementName2;
                        char* pszNumber2;
                        char* pszAttributeName2;
                        OGRGeoRSSLayerSplitComposedField(poFieldDefn->GetNameRef(),
                                &pszElementName2, &pszNumber2, &pszAttributeName2);

                        if (strcmp(pszElementName2, pszElementName) == 0 &&
                            strcmp(pszNumber, pszNumber2) == 0 && pszAttributeName2 != NULL)
                        {
                            pbUsed[j] = TRUE;

                            char* pszValue =
                                    OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( j ));
                            if (poFeatureDefn->GetFieldDefn(j)->GetType() == OFTReal)
                            {
                                char* pszComma = strchr(pszValue, ',');
                                if (pszComma)
                                    *pszComma = '.';
                            }
                            VSIFPrintfL(fp, "        <%s>%s</%s>\n", pszAttributeName2, pszValue, pszAttributeName2);
                            CPLFree(pszValue);
                        }
                        CPLFree(pszElementName2);
                        CPLFree(pszNumber2);
                        CPLFree(pszAttributeName2);
                    }

                    VSIFPrintfL(fp, "      </%s>\n", pszElementName);

                    break;
                }
            }
        }

        if (bWillSkip)
        {
            /* Do nothing */
        }
        else if (eFormat == GEORSS_RSS &&
            strcmp(pszName, "pubDate") == 0)
        {
            int year, month, day, hour, minute, second, TZFlag;
            if (poFeature->GetFieldAsDateTime(i, &year, &month, &day,
                                                    &hour, &minute, &second, &TZFlag))
            {
                char* pszDate = OGRGetRFC822DateTime(year, month, day, hour, minute, second, TZFlag);
                VSIFPrintfL(fp, "      <%s>%s</%s>\n",
                        pszName, pszDate, pszName);
                CPLFree(pszDate);
            }
        }
        else if (eFormat == GEORSS_ATOM &&
                 (strcmp(pszName, "updated") == 0 || strcmp(pszName, "published") == 0))
        {
            int year, month, day, hour, minute, second, TZFlag;
            if (poFeature->GetFieldAsDateTime(i, &year, &month, &day,
                                                    &hour, &minute, &second, &TZFlag))
            {
                char* pszDate = OGRGetXMLDateTime(year, month, day, hour, minute, second, TZFlag);
                VSIFPrintfL(fp, "      <%s>%s</%s>\n",
                        pszName, pszDate, pszName);
                CPLFree(pszDate);
            }
        }
        else if (strcmp(pszName, "dc_date") == 0)
        {
            int year, month, day, hour, minute, second, TZFlag;
            if (poFeature->GetFieldAsDateTime(i, &year, &month, &day,
                                                    &hour, &minute, &second, &TZFlag))
            {
                char* pszDate = OGRGetXMLDateTime(year, month, day, hour, minute, second, TZFlag);
                VSIFPrintfL(fp, "      <%s>%s</%s>\n",
                        "dc:date", pszDate, "dc:date");
                CPLFree(pszDate);
            }
        }
        /* RSS fields with content and attributes */
        else if (eFormat == GEORSS_RSS &&
                 (strcmp(pszElementName, "category") == 0 ||
                  strcmp(pszElementName, "guid") == 0 ||
                  strcmp(pszElementName, "source") == 0 ))
        {
            if (pszAttributeName == NULL)
            {
                OGRGeoRSSLayerWriteSimpleElement(fp, pszElementName, pszNumber,
                                       apszAllowedRSSFieldNames, poFeatureDefn, poFeature);
            }
        }
        /* RSS field with attribute only */
        else if (eFormat == GEORSS_RSS &&
                 strcmp(pszElementName, "enclosure") == 0)
        {
            if (pszAttributeName != NULL && strcmp(pszAttributeName, "url") == 0)
            {
                OGRGeoRSSLayerWriteSimpleElement(fp, pszElementName, pszNumber,
                                       apszAllowedRSSFieldNames, poFeatureDefn, poFeature);
            }
        }
        /* ATOM fields with attribute only */
        else if (eFormat == GEORSS_ATOM &&
                 (strcmp(pszElementName, "category") == 0 || strcmp(pszElementName, "link") == 0))
        {
            if (pszAttributeName != NULL &&
                ((strcmp(pszElementName, "category") == 0 && strcmp(pszAttributeName, "term") == 0) ||
                 (strcmp(pszElementName, "link") == 0 && strcmp(pszAttributeName, "href") == 0)))
            {
                OGRGeoRSSLayerWriteSimpleElement(fp, pszElementName, pszNumber,
                                       apszAllowedATOMFieldNames, poFeatureDefn, poFeature);
            }
        }
        else if (eFormat == GEORSS_ATOM &&
                 (strncmp(pszName, "content", strlen("content")) == 0 ||
                  strncmp(pszName, "summary", strlen("summary")) == 0))
        {
            char* pszFieldName;
            int iIndex;
            if (strchr(pszName, '_') == NULL)
            {
                VSIFPrintfL(fp, "      <%s", pszName);

                int bIsXHTML = FALSE;
                pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszName, "type"));
                iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
                if (iIndex != -1 && poFeature->IsFieldSet( iIndex ))
                {
                    bIsXHTML = strcmp(poFeature->GetFieldAsString( iIndex ), "xhtml") == 0;
                    char* pszValue =
                            OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex ));
                    VSIFPrintfL(fp, " %s=\"%s\"", "type", pszValue);
                    CPLFree(pszValue);
                }
                CPLFree(pszFieldName);

                pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_lang"));
                iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
                if (iIndex != -1 && poFeature->IsFieldSet( iIndex ))
                {
                    char* pszValue =
                            OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex ));
                    VSIFPrintfL(fp, " %s=\"%s\"", "xml:lang", pszValue);
                    CPLFree(pszValue);
                }
                CPLFree(pszFieldName);

                pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_base"));
                iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
                if (iIndex != -1 && poFeature->IsFieldSet( iIndex ))
                {
                    char* pszValue =
                            OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex ));
                    VSIFPrintfL(fp, " %s=\"%s\"", "xml:base", pszValue);
                    CPLFree(pszValue);
                }
                CPLFree(pszFieldName);

                VSIFPrintfL(fp, ">");
                if (bIsXHTML)
                    VSIFPrintfL(fp, "%s", poFeature->GetFieldAsString(i));
                else
                {
                    char* pszValue =
                            OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( i ));
                    VSIFPrintfL(fp, "%s", pszValue);
                    CPLFree(pszValue);
                }
                VSIFPrintfL(fp, "      </%s>\n", pszName);
            }
        }
        else if (strncmp(pszName, "dc_subject", strlen("dc_subject")) == 0)
        {
            char* pszFieldName;
            int iIndex;
            if (strchr(pszName+strlen("dc_subject"), '_') == NULL)
            {
                VSIFPrintfL(fp, "      <%s", "dc:subject");

                pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszName, "xml_lang"));
                iIndex = poFeatureDefn->GetFieldIndex(pszFieldName);
                if (iIndex != -1 && poFeature->IsFieldSet( iIndex ))
                {
                    char* pszValue =
                            OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( iIndex ));
                    VSIFPrintfL(fp, " %s=\"%s\"", "xml:lang", pszValue);
                    CPLFree(pszValue);
                }
                CPLFree(pszFieldName);

                char* pszValue =
                        OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( i ));
                VSIFPrintfL(fp, ">%s</%s>\n", pszValue, "dc:subject");
                CPLFree(pszValue);
            }
        }
        else
        {
            char* pszTagName = CPLStrdup(pszName);
            if (IsStandardField(pszName) == FALSE)
            {
                int j;
                int nCountUnderscore = 0;
                for(j=0;pszTagName[j] != 0;j++)
                {
                    if (pszTagName[j] == '_')
                    {
                        if (nCountUnderscore == 0)
                            pszTagName[j] = ':';
                        nCountUnderscore ++;
                    }
                    else if (pszTagName[j] == ' ')
                        pszTagName[j] = '_';
                }
                if (nCountUnderscore == 0)
                {
                    char* pszTemp = CPLStrdup(CPLSPrintf("ogr:%s", pszTagName));
                    CPLFree(pszTagName);
                    pszTagName = pszTemp;
                }
            }
            char* pszValue =
                        OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( i ));
            if (poFeatureDefn->GetFieldDefn(i)->GetType() == OFTReal)
            {
                char* pszComma = strchr(pszValue, ',');
                if (pszComma)
                    *pszComma = '.';
            }
            VSIFPrintfL(fp, "      <%s>%s</%s>\n", pszTagName, pszValue, pszTagName);
            CPLFree(pszValue);
            CPLFree(pszTagName);
        }

        CPLFree(pszElementName);
        CPLFree(pszNumber);
        CPLFree(pszAttributeName);
    }

    CPLFree(pbUsed);

    OGRGeoRSSGeomDialect eGeomDialect = poDS->GetGeomDialect();
    OGRGeometry* poGeom = poFeature->GetGeometryRef();
    if ( poGeom != NULL && !poGeom->IsEmpty() )
    {
        char* pszURN = NULL;
        int bSwapCoordinates = FALSE;
        if (eGeomDialect == GEORSS_GML)
        {
            if (poSRS != NULL)
            {
                const char* pszAuthorityName = poSRS->GetAuthorityName(NULL);
                const char* pszAuthorityCode = poSRS->GetAuthorityCode(NULL);
                if (pszAuthorityName != NULL && EQUAL(pszAuthorityName, "EPSG") &&
                    pszAuthorityCode != NULL)
                {
                    if (!EQUAL(pszAuthorityCode, "4326"))
                        pszURN = CPLStrdup(CPLSPrintf("urn:ogc:def:crs:EPSG::%s", pszAuthorityCode));

                    /* In case the SRS is a geographic SRS and that we have no axis */
                    /* defintion, we assume that the order is lon/lat */
                    const char* pszAxisName = poSRS->GetAxis(NULL, 0, NULL);
                    if (poSRS->IsGeographic() &&
                        (pszAxisName == NULL || EQUALN(pszAxisName, "Lon", 3)))
                    {
                        bSwapCoordinates = TRUE;
                    }
                }
                else
                {
                    static int bOnce = FALSE;
                    if (!bOnce)
                    {
                        bOnce = TRUE;
                        CPLError(CE_Warning, CPLE_AppDefined, "Could not translate SRS into GML urn");
                    }
                }
            }
            else
            {
                bSwapCoordinates = TRUE;
            }
        }

        char szCoord[75];
        switch( wkbFlatten(poGeom->getGeometryType()) )
        {
            case wkbPoint:
            {
                OGRPoint* poPoint = (OGRPoint*)poGeom;
                double x = poPoint->getX();
                double y = poPoint->getY();
                if (eGeomDialect == GEORSS_GML)
                {
                    VSIFPrintfL(fp, "      <georss:where><gml:Point");
                    if (pszURN != NULL)
                        VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
                    if (poGeom->getCoordinateDimension() == 3)
                    {
                        OGRMakeWktCoordinate(szCoord, (bSwapCoordinates) ? y : x, (bSwapCoordinates) ? x : y,
                                             poPoint->getZ(), 3);
                        VSIFPrintfL(fp, " srsDimension=\"3\"><gml:pos>%s", szCoord);
                    }
                    else
                    {
                        OGRMakeWktCoordinate(szCoord, (bSwapCoordinates) ? y : x, (bSwapCoordinates) ? x : y,
                                             0, 2);
                        VSIFPrintfL(fp, "><gml:pos>%s", szCoord);
                    }
                    VSIFPrintfL(fp, "</gml:pos></gml:Point></georss:where>\n");
                }
                else if (eGeomDialect == GEORSS_SIMPLE)
                {
                    OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
                    VSIFPrintfL(fp, "      <georss:point>%s</georss:point>\n", szCoord);
                }
                else if (eGeomDialect == GEORSS_W3C_GEO)
                {
                    OGRFormatDouble( szCoord, sizeof(szCoord), y, '.' );
                    VSIFPrintfL(fp, "      <geo:lat>%s</geo:lat>\n", szCoord);
                    OGRFormatDouble( szCoord, sizeof(szCoord), x, '.' );
                    VSIFPrintfL(fp, "      <geo:long>%s</geo:long>\n", szCoord);
                }
                break;
            }

            case wkbLineString:
            {
                OGRLineString* poLineString = (OGRLineString*)poGeom;
                if (eGeomDialect == GEORSS_GML)
                {
                    VSIFPrintfL(fp, "      <georss:where><gml:LineString");
                    if (pszURN != NULL)
                        VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
                    VSIFPrintfL(fp, "><gml:posList>\n");
                    int n = poLineString->getNumPoints();
                    for(int i=0;i<n;i++)
                    {
                        double x = poLineString->getX(i);
                        double y = poLineString->getY(i);
                        OGRMakeWktCoordinate(szCoord, (bSwapCoordinates) ? y : x, (bSwapCoordinates) ? x : y,
                                             0, 2);
                        VSIFPrintfL(fp, "%s ", szCoord);
                    }
                    VSIFPrintfL(fp, "</gml:posList></gml:LineString></georss:where>\n");
                }
                else if (eGeomDialect == GEORSS_SIMPLE)
                {
                    VSIFPrintfL(fp, "      <georss:line>\n");
                    int n = poLineString->getNumPoints();
                    for(int i=0;i<n;i++)
                    {
                        double x = poLineString->getX(i);
                        double y = poLineString->getY(i);
                        OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
                        VSIFPrintfL(fp, "%s ", szCoord);
                    }
                    VSIFPrintfL(fp, "</georss:line>\n");
                }
                else
                {
                    /* Not supported */
                }
                break;
            }

            case wkbPolygon:
            {
                OGRPolygon* poPolygon = (OGRPolygon*)poGeom;
                OGRLineString* poLineString = poPolygon->getExteriorRing();
                if (poLineString == NULL)
                    break;

                if (eGeomDialect == GEORSS_GML)
                {
                    VSIFPrintfL(fp, "      <georss:where><gml:Polygon");
                    if (pszURN != NULL)
                        VSIFPrintfL(fp, " srsName=\"%s\"", pszURN);
                    VSIFPrintfL(fp, "><gml:exterior><gml:LinearRing><gml:posList>\n");
                    int n = poLineString->getNumPoints();
                    for(int i=0;i<n;i++)
                    {
                        double x = poLineString->getX(i);
                        double y = poLineString->getY(i);
                        OGRMakeWktCoordinate(szCoord, (bSwapCoordinates) ? y : x, (bSwapCoordinates) ? x : y,
                                             0, 2);
                        VSIFPrintfL(fp, "%s ", szCoord);
                    }
                    VSIFPrintfL(fp, "</gml:posList></gml:LinearRing></gml:exterior></gml:Polygon></georss:where>\n");
                }
                else if (eGeomDialect == GEORSS_SIMPLE)
                {
                    VSIFPrintfL(fp, "      <georss:polygon>\n");
                    int n = poLineString->getNumPoints();
                    for(int i=0;i<n;i++)
                    {
                        double x = poLineString->getX(i);
                        double y = poLineString->getY(i);
                        OGRMakeWktCoordinate(szCoord, y, x, 0, 2);
                        VSIFPrintfL(fp, "%s ", szCoord);
                    }
                    VSIFPrintfL(fp, "</georss:polygon>\n");
                }
                else
                {
                    /* Not supported */
                }
                break;
            }

            default:
                /* Not supported */
                break;
        }
        CPLFree(pszURN);
    }

    if (eFormat == GEORSS_RSS)
        VSIFPrintfL(fp, "    </item>\n");
    else
        VSIFPrintfL(fp, "    </entry>\n");

    return OGRERR_NONE;
}



/************************************************************************/
/*                            CreateField()                             */
/************************************************************************/

OGRErr OGRGeoRSSLayer::CreateField( OGRFieldDefn *poFieldDefn, CPL_UNUSED int bApproxOK )
{
    const char* pszName = poFieldDefn->GetNameRef();
    if (((eFormat == GEORSS_RSS && strcmp(pszName, "pubDate") == 0) ||
         (eFormat == GEORSS_ATOM && (strcmp(pszName, "updated") == 0 ||
                                     strcmp(pszName, "published") == 0 )) ||
          strcmp(pszName, "dc:date") == 0) &&
        poFieldDefn->GetType() != OFTDateTime)
    {
        CPLError(CE_Failure, CPLE_AppDefined, "Wrong field type for %s", pszName);
        return OGRERR_FAILURE;
    }

    for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
    {
        if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
                   pszName ) == 0)
        {
            return OGRERR_FAILURE;
        }
    }

    if (IsStandardField(pszName))
    {
        poFeatureDefn->AddFieldDefn( poFieldDefn );
        return OGRERR_NONE;
    }

    if (poDS->GetUseExtensions() == FALSE)
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                "Field of name '%s' is not supported in %s schema. "
                 "Use USE_EXTENSIONS creation option to allow use of extensions.",
                 pszName, (eFormat == GEORSS_RSS) ? "RSS" : "ATOM");
        return OGRERR_FAILURE;
    }
    else
    {
        poFeatureDefn->AddFieldDefn( poFieldDefn );
        return OGRERR_NONE;
    }
}

#ifdef HAVE_EXPAT

static void XMLCALL startElementLoadSchemaCbk(void *pUserData, const char *pszName, const char **ppszAttr)
{
    ((OGRGeoRSSLayer*)pUserData)->startElementLoadSchemaCbk(pszName, ppszAttr);
}

static void XMLCALL endElementLoadSchemaCbk(void *pUserData, const char *pszName)
{
    ((OGRGeoRSSLayer*)pUserData)->endElementLoadSchemaCbk(pszName);
}

static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, const char *data, int nLen)
{
    ((OGRGeoRSSLayer*)pUserData)->dataHandlerLoadSchemaCbk(data, nLen);
}


/************************************************************************/
/*                       LoadSchema()                         */
/************************************************************************/

/** This function parses the whole file to detect the fields */
void OGRGeoRSSLayer::LoadSchema()
{
    if (bHasReadSchema)
        return;

    bHasReadSchema = TRUE;

    if (fpGeoRSS == NULL)
        return;

    oSchemaParser = OGRCreateExpatXMLParser();
    XML_SetElementHandler(oSchemaParser, ::startElementLoadSchemaCbk, ::endElementLoadSchemaCbk);
    XML_SetCharacterDataHandler(oSchemaParser, ::dataHandlerLoadSchemaCbk);
    XML_SetUserData(oSchemaParser, this);

    VSIFSeekL( fpGeoRSS, 0, SEEK_SET );

    bInFeature = FALSE;
    currentDepth = 0;
    currentFieldDefn = NULL;
    pszSubElementName = NULL;
    pszSubElementValue = NULL;
    nSubElementValueLen = 0;
    bSameSRS = TRUE;
    CPLFree(pszGMLSRSName);
    pszGMLSRSName = NULL;
    eGeomType = wkbUnknown;
    bFoundGeom = FALSE;
    bInTagWithSubTag = FALSE;
    pszTagWithSubTag = NULL;
    bStopParsing = FALSE;
    nWithoutEventCounter = 0;
    nTotalFeatureCount = 0;
    setOfFoundFields = NULL;

    char aBuf[BUFSIZ];
    int nDone;
    do
    {
        nDataHandlerCounter = 0;
        unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGeoRSS );
        nDone = VSIFEofL(fpGeoRSS);
        if (XML_Parse(oSchemaParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "XML parsing of GeoRSS file failed : %s at line %d, column %d",
                     XML_ErrorString(XML_GetErrorCode(oSchemaParser)),
                     (int)XML_GetCurrentLineNumber(oSchemaParser),
                     (int)XML_GetCurrentColumnNumber(oSchemaParser));
            bStopParsing = TRUE;
        }
        nWithoutEventCounter ++;
    } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);

    XML_ParserFree(oSchemaParser);

    if (nWithoutEventCounter == 10)
    {
        CPLError(CE_Failure, CPLE_AppDefined,
                 "Too much data inside one element. File probably corrupted");
        bStopParsing = TRUE;
    }

    CPLAssert(poSRS == NULL);
    if (bSameSRS && bFoundGeom)
    {
        if (pszGMLSRSName == NULL)
        {
            poSRS = new OGRSpatialReference();
            poSRS->SetWellKnownGeogCS( "WGS84" ); /* no AXIS definition ! */
        }
        else
        {
            poSRS = new OGRSpatialReference();
            poSRS->importFromURN(pszGMLSRSName);
        }
    }

    if (eGeomType != wkbUnknown)
        poFeatureDefn->SetGeomType(eGeomType);
    if( poFeatureDefn->GetGeomFieldCount() != 0 )
        poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);

    if (setOfFoundFields)
        CPLHashSetDestroy(setOfFoundFields);
    setOfFoundFields = NULL;
    CPLFree(pszGMLSRSName);
    pszGMLSRSName = NULL;
    CPLFree(pszTagWithSubTag);
    pszTagWithSubTag = NULL;

    VSIFSeekL( fpGeoRSS, 0, SEEK_SET );
}


/************************************************************************/
/*                         OGRGeoRSSIsInt()                             */
/************************************************************************/

static int OGRGeoRSSIsInt(const char* pszStr)
{
    int i;

    while(*pszStr == ' ')
        pszStr++;

    for(i=0;pszStr[i];i++)
    {
        if (pszStr[i] == '+' || pszStr[i] == '-')
        {
            if (i != 0)
                return FALSE;
        }
        else if (!(pszStr[i] >= '0' && pszStr[i] <= '9'))
            return FALSE;
    }
    return TRUE;
}

/************************************************************************/
/*                  startElementLoadSchemaCbk()                         */
/************************************************************************/

void OGRGeoRSSLayer::startElementLoadSchemaCbk(const char *pszName, const char **ppszAttr)
{
    if (bStopParsing) return;

    nWithoutEventCounter = 0;

    if ((eFormat == GEORSS_ATOM && currentDepth == 1 && strcmp(pszName, "entry") == 0) ||
        ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && !bInFeature && (currentDepth == 1 || currentDepth == 2) && strcmp(pszName, "item") == 0))
    {
        bInFeature = TRUE;
        featureDepth = currentDepth;

        nTotalFeatureCount ++;

        if (setOfFoundFields)
            CPLHashSetDestroy(setOfFoundFields);
        setOfFoundFields = CPLHashSetNew(CPLHashSetHashStr, CPLHashSetEqualStr, CPLFree);
    }
    else if (bInTagWithSubTag && currentDepth == 3)
    {
        char* pszFieldName = CPLStrdup(CPLSPrintf("%s_%s", pszTagWithSubTag, pszName));
        if (poFeatureDefn->GetFieldIndex(pszFieldName) == -1)
        {
            OGRFieldDefn newFieldDefn(pszFieldName, OFTString);
            poFeatureDefn->AddFieldDefn(&newFieldDefn);

            if (poFeatureDefn->GetFieldCount() == 100)
            {
                CPLError(CE_Failure, CPLE_AppDefined,
                        "Too many fields. File probably corrupted");
                XML_StopParser(oSchemaParser, XML_FALSE);
                bStopParsing = TRUE;
            }
        }
        CPLFree(pszFieldName);
    }
    else if (bInFeature && eFormat == GEORSS_ATOM &&
             currentDepth == 2 && OGRGeoRSSLayerATOMTagHasSubElement(pszName))
    {
        CPLFree(pszTagWithSubTag);
        pszTagWithSubTag = CPLStrdup(pszName);

        int count = 1;
        while(CPLHashSetLookup(setOfFoundFields, pszTagWithSubTag) != NULL)
        {
            count ++;
            CPLFree(pszTagWithSubTag);
            pszTagWithSubTag = CPLStrdup(CPLSPrintf("%s%d", pszName, count));
            if (pszTagWithSubTag[0] == 0)
            {
                XML_StopParser(oSchemaParser, XML_FALSE);
                bStopParsing = TRUE;
                break;
            }
        }
        CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszTagWithSubTag));

        bInTagWithSubTag = TRUE;
    }
    else if (bInFeature && currentDepth == featureDepth + 1 && !IS_GEO_ELEMENT(pszName))
    {
        CPLFree(pszSubElementName);
        pszSubElementName = CPLStrdup(pszName);

        int count = 1;
        while(CPLHashSetLookup(setOfFoundFields, pszSubElementName) != NULL)
        {
            count ++;
            CPLFree(pszSubElementName);
            pszSubElementName = CPLStrdup(CPLSPrintf("%s%d", pszName, count));
        }
        CPLHashSetInsert(setOfFoundFields, CPLStrdup(pszSubElementName));

        /* Create field definition for element */
        char* pszCompatibleName = OGRGeoRSS_GetOGRCompatibleTagName(pszSubElementName);
        int iField = poFeatureDefn->GetFieldIndex(pszCompatibleName);
        if (iField >= 0)
        {
            currentFieldDefn = poFeatureDefn->GetFieldDefn(iField);
        }
        else if ( ! ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && strcmp(pszName, "enclosure") == 0) &&
                  ! (eFormat == GEORSS_ATOM && strcmp(pszName, "link") == 0) &&
                  ! (eFormat == GEORSS_ATOM && strcmp(pszName, "category") == 0))
        {
            OGRFieldType eFieldType;
            if (((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && strcmp(pszName, "pubDate") == 0) ||
                (eFormat == GEORSS_ATOM && strcmp(pszName, "updated") == 0) ||
                (eFormat == GEORSS_ATOM && strcmp(pszName, "published") == 0) ||
                strcmp(pszName, "dc:date") == 0)
                eFieldType = OFTDateTime;
            else
                eFieldType = OFTInteger;

            OGRFieldDefn newFieldDefn(pszCompatibleName, eFieldType);
            poFeatureDefn->AddFieldDefn(&newFieldDefn);
            currentFieldDefn = poFeatureDefn->GetFieldDefn(poFeatureDefn->GetFieldCount() - 1);

            if (poFeatureDefn->GetFieldCount() == 100)
            {
                CPLError(CE_Failure, CPLE_AppDefined,
                        "Too many fields. File probably corrupted");
                XML_StopParser(oSchemaParser, XML_FALSE);
                bStopParsing = TRUE;
            }
        }

        /* Create field definitions for attributes */
        for(int i=0; ppszAttr[i] != NULL && ppszAttr[i+1] != NULL && !bStopParsing; i+= 2)
        {
            char* pszAttrCompatibleName =
                    OGRGeoRSS_GetOGRCompatibleTagName(CPLSPrintf("%s_%s", pszSubElementName, ppszAttr[i]));
            iField = poFeatureDefn->GetFieldIndex(pszAttrCompatibleName);
            OGRFieldDefn* currentAttrFieldDefn;
            if (iField >= 0)
            {
                currentAttrFieldDefn = poFeatureDefn->GetFieldDefn(iField);
            }
            else
            {
                OGRFieldDefn newFieldDefn(pszAttrCompatibleName, OFTInteger);
                poFeatureDefn->AddFieldDefn(&newFieldDefn);
                currentAttrFieldDefn = poFeatureDefn->GetFieldDefn(poFeatureDefn->GetFieldCount() - 1);

                if (poFeatureDefn->GetFieldCount() == 100)
                {
                    CPLError(CE_Failure, CPLE_AppDefined,
                            "Too many fields. File probably corrupted");
                    XML_StopParser(oSchemaParser, XML_FALSE);
                    bStopParsing = TRUE;
                }
            }
            if (currentAttrFieldDefn->GetType() == OFTInteger ||
                currentAttrFieldDefn->GetType() == OFTReal)
            {
                char* pszRemainingStr = NULL;
                CPLStrtod(ppszAttr[i + 1], &pszRemainingStr);
                if (pszRemainingStr == NULL ||
                    *pszRemainingStr == 0 ||
                    *pszRemainingStr == ' ')
                {
                    if (currentAttrFieldDefn->GetType() == OFTInteger)
                    {
                        if (OGRGeoRSSIsInt(ppszAttr[i + 1]) == FALSE)
                        {
                            currentAttrFieldDefn->SetType(OFTReal);
                        }
                    }
                }
                else
                {
                    currentAttrFieldDefn->SetType(OFTString);
                }
            }
            CPLFree(pszAttrCompatibleName);
        }

        CPLFree(pszCompatibleName);
    }
    else if (strcmp(pszName, "georss:point") == 0 ||
             strcmp(pszName, "georss:line") == 0 ||
             strcmp(pszName, "geo:line") == 0 ||
             IS_LAT_ELEMENT(pszName) ||
             strcmp(pszName, "georss:polygon") == 0 ||
             strcmp(pszName, "georss:box") == 0)
    {
        if (bSameSRS)
        {
            if (pszGMLSRSName != NULL)
                bSameSRS = FALSE;
        }
    }
    else if (strcmp(pszName, "gml:Point") == 0 ||
             strcmp(pszName, "gml:LineString") == 0 ||
             strcmp(pszName, "gml:Polygon") == 0 ||
             strcmp(pszName, "gml:MultiPoint") == 0 ||
             strcmp(pszName, "gml:MultiLineString") == 0 ||
             strcmp(pszName, "gml:MultiPolygon") == 0 ||
             strcmp(pszName, "gml:Envelope") == 0)
    {
        if (bSameSRS)
        {
            int bFoundSRS = FALSE;
            for(int i = 0; ppszAttr[i] != NULL; i+=2)
            {
                if (strcmp(ppszAttr[i], "srsName") == 0)
                {
                    bFoundSRS = TRUE;
                    if (pszGMLSRSName != NULL)
                    {
                        if (strcmp(pszGMLSRSName , ppszAttr[i+1]) != 0)
                            bSameSRS = FALSE;
                    }
                    else
                        pszGMLSRSName = CPLStrdup(ppszAttr[i+1]);
                    break;
                }
            }
            if (!bFoundSRS && pszGMLSRSName != NULL)
                bSameSRS = FALSE;
        }
    }

    if (!bInFeature || currentDepth >= featureDepth + 1)
    {
        int nDimension = 2;
        for(int i = 0; ppszAttr[i] != NULL; i+=2)
        {
            if (strcmp(ppszAttr[i], "srsDimension") == 0)
            {
                nDimension = atoi(ppszAttr[i+1]);
                break;
            }
        }

        OGRwkbGeometryType eFoundGeomType = wkbUnknown;
        if (strcmp(pszName, "georss:point") == 0 ||
            IS_LAT_ELEMENT(pszName) ||
            strcmp(pszName, "gml:Point") == 0)
        {
            eFoundGeomType = wkbPoint;
        }
        else if (strcmp(pszName, "gml:MultiPoint") == 0)
        {
            eFoundGeomType = wkbMultiPoint;
        }
        else if (strcmp(pszName, "georss:line") == 0 ||
                strcmp(pszName, "geo:line") == 0 ||
                strcmp(pszName, "gml:LineString") == 0)
        {
            eFoundGeomType = wkbLineString;
        }
        else if (strcmp(pszName, "gml:MultiLineString") == 0)
        {
            eFoundGeomType = wkbMultiLineString;
        }
        else if (strcmp(pszName, "georss:polygon") == 0 ||
                 strcmp(pszName, "gml:Polygon") == 0 ||
                 strcmp(pszName, "gml:Envelope") == 0 ||
                 strcmp(pszName, "georss:box") == 0)
        {
            eFoundGeomType = wkbPolygon;
        }
        else if (strcmp(pszName, "gml:MultiPolygon") == 0)
        {
            eFoundGeomType = wkbMultiPolygon;
        }

        if (eFoundGeomType != wkbUnknown)
        {
            if (!bFoundGeom)
            {
                eGeomType = eFoundGeomType;
                bFoundGeom = TRUE;
            }
            else if (wkbFlatten(eGeomType) != eFoundGeomType)
                eGeomType = wkbUnknown;

            if (nDimension == 3)
                eGeomType = (OGRwkbGeometryType) (eGeomType | wkb25DBit);
        }
    }

    currentDepth++;
}

/************************************************************************/
/*                   endElementLoadSchemaCbk()                          */
/************************************************************************/

void OGRGeoRSSLayer::endElementLoadSchemaCbk(const char *pszName)
{
    if (bStopParsing) return;

    nWithoutEventCounter = 0;

    currentDepth--;

    if (!bInFeature)
        return;

    if ((eFormat == GEORSS_ATOM && currentDepth == 1 && strcmp(pszName, "entry") == 0) ||
        ((eFormat == GEORSS_RSS || eFormat == GEORSS_RSS_RDF) && (currentDepth == 1 || currentDepth == 2) && strcmp(pszName, "item") == 0))
    {
        bInFeature = FALSE;
    }
    else if (bInFeature && eFormat == GEORSS_ATOM &&
                currentDepth == 2 && OGRGeoRSSLayerATOMTagHasSubElement(pszName))
    {
        bInTagWithSubTag = FALSE;
    }
    else if (currentDepth == featureDepth + 1 && pszSubElementName)
    {
        /* Patch field type */
        if (pszSubElementValue && nSubElementValueLen && currentFieldDefn)
        {
            pszSubElementValue[nSubElementValueLen] = 0;
            if (currentFieldDefn->GetType() == OFTInteger ||
                currentFieldDefn->GetType() == OFTReal)
            {
                char* pszRemainingStr = NULL;
                CPLStrtod(pszSubElementValue, &pszRemainingStr);
                if (pszRemainingStr == NULL ||
                    *pszRemainingStr == 0 ||
                    *pszRemainingStr == ' ')
                {
                    if (currentFieldDefn->GetType() == OFTInteger)
                    {
                        if (OGRGeoRSSIsInt(pszSubElementValue) == FALSE)
                        {
                            currentFieldDefn->SetType(OFTReal);
                        }
                    }
                }
                else
                {
                    currentFieldDefn->SetType(OFTString);
                }
            }
        }

        CPLFree(pszSubElementName);
        pszSubElementName = NULL;
        CPLFree(pszSubElementValue);
        pszSubElementValue = NULL;
        nSubElementValueLen = 0;
        currentFieldDefn = NULL;
    }
}

/************************************************************************/
/*                   dataHandlerLoadSchemaCbk()                         */
/************************************************************************/

void OGRGeoRSSLayer::dataHandlerLoadSchemaCbk(const char *data, int nLen)
{
    if (bStopParsing) return;

    nDataHandlerCounter ++;
    if (nDataHandlerCounter >= BUFSIZ)
    {
        CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)");
        XML_StopParser(oSchemaParser, XML_FALSE);
        bStopParsing = TRUE;
        return;
    }

    nWithoutEventCounter = 0;

    if (pszSubElementName)
    {
        char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue, nSubElementValueLen + nLen + 1);
        if (pszNewSubElementValue == NULL)
        {
            CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
            XML_StopParser(oSchemaParser, XML_FALSE);
            bStopParsing = TRUE;
            return;
        }
        pszSubElementValue = pszNewSubElementValue;
        memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
        nSubElementValueLen += nLen;
        if (nSubElementValueLen > 100000)
        {
            CPLError(CE_Failure, CPLE_AppDefined,
                     "Too much data inside one element. File probably corrupted");
            XML_StopParser(oSchemaParser, XML_FALSE);
            bStopParsing = TRUE;
        }
    }
}
#else
void OGRGeoRSSLayer::LoadSchema()
{
}
#endif

/************************************************************************/
/*                           TestCapability()                           */
/************************************************************************/

int OGRGeoRSSLayer::TestCapability( const char * pszCap )

{
    if( EQUAL(pszCap,OLCFastFeatureCount) )
        return !bWriteMode && bHasReadSchema &&
                m_poFilterGeom == NULL && m_poAttrQuery == NULL;

    else if( EQUAL(pszCap,OLCStringsAsUTF8) )
        return TRUE;

    else if( EQUAL(pszCap,OLCSequentialWrite) )
        return bWriteMode;
    else if( EQUAL(pszCap,OLCCreateField) )
        return bWriteMode;
    else 
        return FALSE;
}

/************************************************************************/
/*                          GetFeatureCount()                           */
/************************************************************************/

int OGRGeoRSSLayer::GetFeatureCount( int bForce )

{
    if (bWriteMode)
    {
        CPLError(CE_Failure, CPLE_NotSupported,
                 "Cannot read features when writing a GeoRSS file");
        return 0;
    }

    if (!bHasReadSchema)
        LoadSchema();

    if( m_poFilterGeom != NULL || m_poAttrQuery != NULL )
        return OGRLayer::GetFeatureCount( bForce );
    else
        return nTotalFeatureCount;
}
