LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/jml - ogrjmllayer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 385 424 90.8 %
Date: 2026-02-12 23:49:34 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  JML Translator
       4             :  * Purpose:  Implements OGRJMLLayer class.
       5             :  *
       6             :  ******************************************************************************
       7             :  * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com>
       8             :  *
       9             :  * SPDX-License-Identifier: MIT
      10             :  ****************************************************************************/
      11             : 
      12             : #include "cpl_conv.h"
      13             : #include "ogr_jml.h"
      14             : #include "ogr_p.h"
      15             : 
      16             : #ifdef HAVE_EXPAT
      17             : 
      18             : constexpr int PARSER_BUF_SIZE = 8192;
      19             : 
      20             : /************************************************************************/
      21             : /*                            OGRJMLLayer()                             */
      22             : /************************************************************************/
      23             : 
      24          42 : OGRJMLLayer::OGRJMLLayer(const char *pszLayerName, OGRJMLDataset *poDSIn,
      25          42 :                          VSILFILE *fpIn)
      26          42 :     : m_poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn(pszLayerName)),
      27             :       nNextFID(0), fp(fpIn), bHasReadSchema(false), currentDepth(0),
      28             :       bStopParsing(false), nWithoutEventCounter(0), nDataHandlerCounter(0),
      29             :       bAccumulateElementValue(false),
      30          84 :       pszElementValue(static_cast<char *>(CPLCalloc(1024, 1))),
      31             :       nElementValueLen(0), nElementValueAlloc(1024), poFeature(nullptr),
      32             :       ppoFeatureTab(nullptr), nFeatureTabLength(0), nFeatureTabIndex(0),
      33             :       bSchemaFinished(false), nJCSGMLInputTemplateDepth(0),
      34             :       nCollectionElementDepth(0), nFeatureCollectionDepth(0),
      35             :       nFeatureElementDepth(0), nGeometryElementDepth(0), nColumnDepth(0),
      36             :       nNameDepth(0), nTypeDepth(0), nAttributeElementDepth(0), iAttr(-1),
      37          84 :       iRGBField(-1)
      38             : {
      39          42 :     SetDescription(poFeatureDefn->GetName());
      40          42 :     poFeatureDefn->Reference();
      41          42 : }
      42             : 
      43             : /************************************************************************/
      44             : /*                            ~OGRJMLLayer()                            */
      45             : /************************************************************************/
      46             : 
      47          84 : OGRJMLLayer::~OGRJMLLayer()
      48             : 
      49             : {
      50          42 :     poFeatureDefn->Release();
      51             : 
      52          42 :     CPLFree(pszElementValue);
      53             : 
      54          42 :     for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
      55           0 :         delete ppoFeatureTab[i];
      56          42 :     CPLFree(ppoFeatureTab);
      57             : 
      58          42 :     if (poFeature)
      59           0 :         delete poFeature;
      60          84 : }
      61             : 
      62             : /************************************************************************/
      63             : /*                            GetLayerDefn()                            */
      64             : /************************************************************************/
      65             : 
      66         346 : const OGRFeatureDefn *OGRJMLLayer::GetLayerDefn() const
      67             : {
      68         346 :     if (!bHasReadSchema)
      69           8 :         const_cast<OGRJMLLayer *>(this)->LoadSchema();
      70             : 
      71         346 :     return poFeatureDefn;
      72             : }
      73             : 
      74        5518 : static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
      75             :                                     const char **ppszAttr)
      76             : {
      77        5518 :     static_cast<OGRJMLLayer *>(pUserData)->startElementCbk(pszName, ppszAttr);
      78        5518 : }
      79             : 
      80        5518 : static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
      81             : {
      82        5518 :     static_cast<OGRJMLLayer *>(pUserData)->endElementCbk(pszName);
      83        5518 : }
      84             : 
      85       16885 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
      86             : {
      87       16885 :     static_cast<OGRJMLLayer *>(pUserData)->dataHandlerCbk(data, nLen);
      88       16885 : }
      89             : 
      90             : /************************************************************************/
      91             : /*                            ResetReading()                            */
      92             : /************************************************************************/
      93             : 
      94         124 : void OGRJMLLayer::ResetReading()
      95             : 
      96             : {
      97         124 :     nNextFID = 0;
      98             : 
      99         124 :     VSIFSeekL(fp, 0, SEEK_SET);
     100         124 :     VSIFClearErrL(fp);
     101             : 
     102         124 :     poParser.reset(OGRCreateExpatXMLParser());
     103         124 :     XML_SetElementHandler(poParser.get(), ::startElementCbk, ::endElementCbk);
     104         124 :     XML_SetCharacterDataHandler(poParser.get(), ::dataHandlerCbk);
     105         124 :     XML_SetUserData(poParser.get(), this);
     106             : 
     107         148 :     for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
     108          24 :         delete ppoFeatureTab[i];
     109         124 :     nFeatureTabIndex = 0;
     110         124 :     nFeatureTabLength = 0;
     111         124 :     delete poFeature;
     112         124 :     poFeature = nullptr;
     113             : 
     114         124 :     currentDepth = 0;
     115             : 
     116         124 :     nCollectionElementDepth = 0;
     117         124 :     nFeatureElementDepth = 0;
     118         124 :     nGeometryElementDepth = 0;
     119         124 :     nAttributeElementDepth = 0;
     120         124 :     iAttr = -1;
     121             : 
     122         124 :     bAccumulateElementValue = false;
     123         124 :     nElementValueLen = 0;
     124         124 :     pszElementValue[0] = '\0';
     125         124 : }
     126             : 
     127             : /************************************************************************/
     128             : /*                          startElementCbk()                           */
     129             : /************************************************************************/
     130             : 
     131        5518 : void OGRJMLLayer::startElementCbk(const char *pszName, const char **ppszAttr)
     132             : {
     133        5518 :     if (bStopParsing)
     134           0 :         return;
     135             : 
     136        5518 :     nWithoutEventCounter = 0;
     137             : 
     138        1408 :     if (nFeatureElementDepth > 0 && nAttributeElementDepth == 0 &&
     139        6926 :         nGeometryElementDepth == 0 && osGeometryElement.compare(pszName) == 0)
     140             :     {
     141         163 :         nGeometryElementDepth = currentDepth;
     142         163 :         bAccumulateElementValue = true;
     143             :     }
     144        5355 :     else if (nFeatureElementDepth > 0 && nAttributeElementDepth == 0 &&
     145        1245 :              nGeometryElementDepth == 0)
     146             :     {
     147             :         /* We assume that attributes are present in the order they are */
     148             :         /* declared, so as a first guess, we can try the aoColumns[iAttr + 1] */
     149         919 :         int i = (iAttr + 1 < poFeatureDefn->GetFieldCount()) ? -1 : 0;
     150        4807 :         for (; i < static_cast<int>(aoColumns.size()); i++)
     151             :         {
     152             :             const OGRJMLColumn &oColumn =
     153        4590 :                 (i < 0) ? aoColumns[iAttr + 1] : aoColumns[i];
     154        4590 :             if (oColumn.osElementName != pszName)
     155        3456 :                 continue;
     156             : 
     157        1134 :             if (oColumn.bIsBody)
     158             :             {
     159        2106 :                 if (!oColumn.osAttributeName.empty() && ppszAttr != nullptr &&
     160        2052 :                     ppszAttr[0] != nullptr && ppszAttr[1] != nullptr &&
     161        3132 :                     oColumn.osAttributeName.compare(ppszAttr[0]) == 0 &&
     162        1026 :                     oColumn.osAttributeValue.compare(ppszAttr[1]) == 0)
     163             :                 {
     164             :                     /* <osElementName
     165             :                      * osAttributeName="osAttributeValue">value</osElementName>
     166             :                      */
     167             : 
     168         594 :                     bAccumulateElementValue = true;
     169         594 :                     nAttributeElementDepth = currentDepth;
     170         594 :                     iAttr = (i < 0) ? iAttr + 1 : i;
     171         594 :                     break;
     172             :                 }
     173         486 :                 else if (oColumn.osAttributeName.empty())
     174             :                 {
     175             :                     /* <osElementName>value</osElementName> */
     176             : 
     177          54 :                     bAccumulateElementValue = true;
     178          54 :                     nAttributeElementDepth = currentDepth;
     179          54 :                     iAttr = (i < 0) ? iAttr + 1 : i;
     180          54 :                     break;
     181             :                 }
     182             :             }
     183         108 :             else if (!oColumn.osAttributeName.empty() && ppszAttr != nullptr &&
     184         162 :                      ppszAttr[0] != nullptr && ppszAttr[1] != nullptr &&
     185          54 :                      oColumn.osAttributeName.compare(ppszAttr[0]) == 0)
     186             :             {
     187             :                 /* <osElementName osAttributeName="value"></osElementName> */
     188             : 
     189          54 :                 AddStringToElementValue(ppszAttr[1], (int)strlen(ppszAttr[1]));
     190             : 
     191          54 :                 nAttributeElementDepth = currentDepth;
     192          54 :                 iAttr = (i < 0) ? iAttr + 1 : i;
     193          54 :                 break;
     194             :             }
     195         919 :         }
     196             :     }
     197        4436 :     else if (nGeometryElementDepth > 0)
     198             :     {
     199         326 :         AddStringToElementValue("<", 1);
     200         326 :         AddStringToElementValue(pszName, (int)strlen(pszName));
     201             : 
     202         326 :         const char **papszIter = ppszAttr;
     203         380 :         while (papszIter && *papszIter != nullptr)
     204             :         {
     205          54 :             AddStringToElementValue(" ", 1);
     206          54 :             AddStringToElementValue(papszIter[0], (int)strlen(papszIter[0]));
     207          54 :             AddStringToElementValue("=\"", 2);
     208          54 :             AddStringToElementValue(papszIter[1], (int)strlen(papszIter[1]));
     209          54 :             AddStringToElementValue("\"", 1);
     210          54 :             papszIter += 2;
     211             :         }
     212             : 
     213         326 :         AddStringToElementValue(">", 1);
     214             :     }
     215        4439 :     else if (nFeatureCollectionDepth > 0 && nFeatureElementDepth == 0 &&
     216         329 :              osFeatureElement.compare(pszName) == 0)
     217             :     {
     218         164 :         nFeatureElementDepth = currentDepth;
     219         164 :         poFeature = new OGRFeature(poFeatureDefn);
     220             :     }
     221        7727 :     else if (nFeatureCollectionDepth == 0 &&
     222        3781 :              osCollectionElement.compare(pszName) == 0)
     223             :     {
     224          56 :         nFeatureCollectionDepth = currentDepth;
     225             :     }
     226             : 
     227        5518 :     currentDepth++;
     228             : }
     229             : 
     230             : /************************************************************************/
     231             : /*                           StopAccumulate()                           */
     232             : /************************************************************************/
     233             : 
     234        1004 : void OGRJMLLayer::StopAccumulate()
     235             : {
     236        1004 :     bAccumulateElementValue = false;
     237        1004 :     nElementValueLen = 0;
     238        1004 :     pszElementValue[0] = '\0';
     239        1004 : }
     240             : 
     241             : /************************************************************************/
     242             : /*                           endElementCbk()                            */
     243             : /************************************************************************/
     244             : 
     245        5518 : void OGRJMLLayer::endElementCbk(const char *pszName)
     246             : {
     247        5518 :     if (bStopParsing)
     248           0 :         return;
     249             : 
     250        5518 :     nWithoutEventCounter = 0;
     251             : 
     252        5518 :     currentDepth--;
     253             : 
     254        5518 :     if (nAttributeElementDepth == currentDepth)
     255             :     {
     256         758 :         if (nElementValueLen)
     257         648 :             poFeature->SetField(iAttr, pszElementValue);
     258         110 :         else if (iAttr >= 0)
     259          54 :             poFeature->SetFieldNull(iAttr);
     260         758 :         nAttributeElementDepth = 0;
     261         758 :         StopAccumulate();
     262             :     }
     263        4760 :     else if (nGeometryElementDepth > 0 && currentDepth > nGeometryElementDepth)
     264             :     {
     265         326 :         AddStringToElementValue("</", 2);
     266         326 :         AddStringToElementValue(pszName, static_cast<int>(strlen(pszName)));
     267         326 :         AddStringToElementValue(">", 1);
     268             :     }
     269        4434 :     else if (nGeometryElementDepth == currentDepth)
     270             :     {
     271         163 :         if (nElementValueLen)
     272             :         {
     273             :             OGRGeometry *poGeom =
     274         109 :                 OGRGeometry::FromHandle(OGR_G_CreateFromGML(pszElementValue));
     275         218 :             if (poGeom != nullptr &&
     276         109 :                 poGeom->getGeometryType() == wkbGeometryCollection &&
     277           0 :                 poGeom->IsEmpty())
     278             :             {
     279           0 :                 delete poGeom;
     280             :             }
     281             :             else
     282         109 :                 poFeature->SetGeometryDirectly(poGeom);
     283             :         }
     284             : 
     285         163 :         nGeometryElementDepth = 0;
     286         163 :         StopAccumulate();
     287             :     }
     288        4271 :     else if (nFeatureElementDepth == currentDepth)
     289             :     {
     290             :         /* Builds a style string from R_G_B if we don't already have a */
     291             :         /* style string */
     292         164 :         OGRGeometry *poGeom = poFeature->GetGeometryRef();
     293         164 :         unsigned int R = 0;
     294         164 :         unsigned int G = 0;
     295         164 :         unsigned int B = 0;
     296         162 :         if (iRGBField >= 0 && poFeature->IsFieldSetAndNotNull(iRGBField) &&
     297         434 :             poFeature->GetStyleString() == nullptr && poGeom != nullptr &&
     298         108 :             sscanf(poFeature->GetFieldAsString(iRGBField), "%02X%02X%02X", &R,
     299             :                    &G, &B) == 3)
     300             :         {
     301             :             const OGRwkbGeometryType eGeomType =
     302         108 :                 wkbFlatten(poGeom->getGeometryType());
     303         108 :             if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint ||
     304          54 :                 eGeomType == wkbLineString || eGeomType == wkbMultiLineString)
     305             :             {
     306          54 :                 poFeature->SetStyleString(
     307          54 :                     CPLSPrintf("PEN(c:#%02X%02X%02X)", R, G, B));
     308             :             }
     309          54 :             else if (eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon)
     310             :             {
     311          54 :                 poFeature->SetStyleString(
     312          54 :                     CPLSPrintf("BRUSH(fc:#%02X%02X%02X)", R, G, B));
     313             :             }
     314             :         }
     315             : 
     316         164 :         poFeature->SetFID(nNextFID++);
     317             : 
     318         310 :         if ((m_poFilterGeom == nullptr || FilterGeometry(poGeom)) &&
     319         146 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
     320             :         {
     321         238 :             ppoFeatureTab = static_cast<OGRFeature **>(CPLRealloc(
     322         119 :                 ppoFeatureTab, sizeof(OGRFeature *) * (nFeatureTabLength + 1)));
     323         119 :             ppoFeatureTab[nFeatureTabLength] = poFeature;
     324         119 :             nFeatureTabLength++;
     325             :         }
     326             :         else
     327             :         {
     328          45 :             delete poFeature;
     329             :         }
     330         164 :         poFeature = nullptr;
     331         164 :         iAttr = -1;
     332             : 
     333         164 :         nFeatureElementDepth = 0;
     334             :     }
     335        4107 :     else if (nFeatureCollectionDepth == currentDepth)
     336             :     {
     337          56 :         nFeatureCollectionDepth = 0;
     338             :     }
     339             : }
     340             : 
     341             : /************************************************************************/
     342             : /*                      AddStringToElementValue()                       */
     343             : /************************************************************************/
     344             : 
     345        3995 : void OGRJMLLayer::AddStringToElementValue(const char *data, int nLen)
     346             : {
     347        3995 :     if (nLen > INT_MAX - nElementValueLen - 1 - 1000)
     348             :     {
     349           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
     350             :                  "Too much data in a single element");
     351           0 :         XML_StopParser(poParser.get(), XML_FALSE);
     352           0 :         bStopParsing = true;
     353           0 :         return;
     354             :     }
     355        3995 :     if (nElementValueLen + nLen + 1 > nElementValueAlloc)
     356             :     {
     357           0 :         char *pszNewElementValue = static_cast<char *>(VSI_REALLOC_VERBOSE(
     358             :             pszElementValue, nElementValueLen + nLen + 1 + 1000));
     359           0 :         if (pszNewElementValue == nullptr)
     360             :         {
     361           0 :             XML_StopParser(poParser.get(), XML_FALSE);
     362           0 :             bStopParsing = true;
     363           0 :             return;
     364             :         }
     365           0 :         nElementValueAlloc = nElementValueLen + nLen + 1 + 1000;
     366           0 :         pszElementValue = pszNewElementValue;
     367             :     }
     368        3995 :     memcpy(pszElementValue + nElementValueLen, data, nLen);
     369        3995 :     nElementValueLen += nLen;
     370        3995 :     pszElementValue[nElementValueLen] = '\0';
     371             : }
     372             : 
     373             : /************************************************************************/
     374             : /*                           dataHandlerCbk()                           */
     375             : /************************************************************************/
     376             : 
     377       16885 : void OGRJMLLayer::dataHandlerCbk(const char *data, int nLen)
     378             : {
     379       16885 :     if (bStopParsing)
     380           0 :         return;
     381             : 
     382       16885 :     nDataHandlerCounter++;
     383       16885 :     if (nDataHandlerCounter >= PARSER_BUF_SIZE)
     384             :     {
     385           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     386             :                  "File probably corrupted (million laugh pattern)");
     387           0 :         XML_StopParser(poParser.get(), XML_FALSE);
     388           0 :         bStopParsing = true;
     389           0 :         return;
     390             :     }
     391             : 
     392       16885 :     nWithoutEventCounter = 0;
     393             : 
     394       16885 :     if (bAccumulateElementValue)
     395             :     {
     396        1715 :         AddStringToElementValue(data, nLen);
     397             :     }
     398             : }
     399             : 
     400             : /************************************************************************/
     401             : /*                           GetNextFeature()                           */
     402             : /************************************************************************/
     403             : 
     404         138 : OGRFeature *OGRJMLLayer::GetNextFeature()
     405             : {
     406         138 :     if (!bHasReadSchema)
     407           0 :         LoadSchema();
     408             : 
     409         138 :     if (bStopParsing)
     410           0 :         return nullptr;
     411             : 
     412         138 :     if (nFeatureTabIndex < nFeatureTabLength)
     413             :     {
     414          50 :         return ppoFeatureTab[nFeatureTabIndex++];
     415             :     }
     416             : 
     417          88 :     if (VSIFEofL(fp) || VSIFErrorL(fp))
     418          32 :         return nullptr;
     419             : 
     420          56 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
     421             : 
     422          56 :     this->nFeatureTabLength = 0;
     423          56 :     nFeatureTabIndex = 0;
     424             : 
     425          56 :     nWithoutEventCounter = 0;
     426             : 
     427          56 :     int nDone = 0;
     428           0 :     do
     429             :     {
     430          56 :         nDataHandlerCounter = 0;
     431             :         unsigned int nLen =
     432          56 :             (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fp);
     433          56 :         nDone = (nLen < aBuf.size());
     434          56 :         if (XML_Parse(poParser.get(), aBuf.data(), nLen, nDone) ==
     435             :             XML_STATUS_ERROR)
     436             :         {
     437           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     438             :                      "XML parsing of JML file failed : %s "
     439             :                      "at line %d, column %d",
     440             :                      XML_ErrorString(XML_GetErrorCode(poParser.get())),
     441           0 :                      (int)XML_GetCurrentLineNumber(poParser.get()),
     442           0 :                      (int)XML_GetCurrentColumnNumber(poParser.get()));
     443           0 :             bStopParsing = true;
     444             :         }
     445          56 :         nWithoutEventCounter++;
     446          56 :     } while (!nDone && !bStopParsing && this->nFeatureTabLength == 0 &&
     447           0 :              nWithoutEventCounter < 10);
     448             : 
     449          56 :     if (nWithoutEventCounter == 10)
     450             :     {
     451           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     452             :                  "Too much data inside one element. File probably corrupted");
     453           0 :         bStopParsing = true;
     454             :     }
     455             : 
     456          56 :     return (this->nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++]
     457          56 :                                      : nullptr;
     458             : }
     459             : 
     460         304 : static void XMLCALL startElementLoadSchemaCbk(void *pUserData,
     461             :                                               const char *pszName,
     462             :                                               const char **ppszAttr)
     463             : {
     464         304 :     static_cast<OGRJMLLayer *>(pUserData)->startElementLoadSchemaCbk(pszName,
     465             :                                                                      ppszAttr);
     466         304 : }
     467             : 
     468         296 : static void XMLCALL endElementLoadSchemaCbk(void *pUserData,
     469             :                                             const char *pszName)
     470             : {
     471         296 :     static_cast<OGRJMLLayer *>(pUserData)->endElementLoadSchemaCbk(pszName);
     472         296 : }
     473             : 
     474             : /************************************************************************/
     475             : /*                             LoadSchema()                             */
     476             : /************************************************************************/
     477             : 
     478             : /** This function parses the beginning of the file to detect the fields */
     479           8 : void OGRJMLLayer::LoadSchema()
     480             : {
     481           8 :     if (bHasReadSchema)
     482           0 :         return;
     483             : 
     484           8 :     bHasReadSchema = true;
     485             : 
     486           8 :     poParser.reset(OGRCreateExpatXMLParser());
     487           8 :     XML_SetElementHandler(poParser.get(), ::startElementLoadSchemaCbk,
     488             :                           ::endElementLoadSchemaCbk);
     489           8 :     XML_SetCharacterDataHandler(poParser.get(), ::dataHandlerCbk);
     490           8 :     XML_SetUserData(poParser.get(), this);
     491             : 
     492           8 :     VSIFSeekL(fp, 0, SEEK_SET);
     493             : 
     494          16 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
     495           8 :     int nDone = 0;
     496           0 :     do
     497             :     {
     498           8 :         nDataHandlerCounter = 0;
     499             :         const unsigned int nLen = static_cast<unsigned int>(
     500           8 :             VSIFReadL(aBuf.data(), 1, aBuf.size(), fp));
     501           8 :         nDone = (nLen < aBuf.size());
     502           8 :         if (XML_Parse(poParser.get(), aBuf.data(), nLen, nDone) ==
     503             :             XML_STATUS_ERROR)
     504             :         {
     505           3 :             CPLError(
     506             :                 CE_Failure, CPLE_AppDefined,
     507             :                 "XML parsing of JML file failed : %s at line %d, "
     508             :                 "column %d",
     509             :                 XML_ErrorString(XML_GetErrorCode(poParser.get())),
     510           3 :                 static_cast<int>(XML_GetCurrentLineNumber(poParser.get())),
     511           3 :                 static_cast<int>(XML_GetCurrentColumnNumber(poParser.get())));
     512           3 :             bStopParsing = true;
     513             :         }
     514           8 :         nWithoutEventCounter++;
     515           8 :     } while (!nDone && !bStopParsing && !bSchemaFinished &&
     516           0 :              nWithoutEventCounter < 10);
     517             : 
     518           8 :     poParser.reset();
     519             : 
     520           8 :     if (nWithoutEventCounter == 10)
     521             :     {
     522           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     523             :                  "Too much data inside one element. File probably corrupted");
     524           0 :         bStopParsing = true;
     525             :     }
     526             : 
     527          15 :     if (osCollectionElement.empty() || osFeatureElement.empty() ||
     528           7 :         osGeometryElement.empty())
     529             :     {
     530           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     531             :                  "Missing CollectionElement, FeatureElement or "
     532             :                  "GeometryElement");
     533           1 :         bStopParsing = true;
     534             :     }
     535             : 
     536           8 :     if (!osSRSName.empty())
     537             :     {
     538           1 :         if (osSRSName.find("http://www.opengis.net/gml/srs/epsg.xml#") == 0)
     539             :         {
     540           1 :             OGRSpatialReference *poSRS = new OGRSpatialReference();
     541           1 :             poSRS->importFromEPSG(atoi(
     542             :                 osSRSName
     543           2 :                     .substr(strlen("http://www.opengis.net/gml/srs/epsg.xml#"))
     544             :                     .c_str()));
     545           1 :             poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     546           1 :             poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
     547           1 :             poSRS->Release();
     548             :         }
     549             :     }
     550             : 
     551           8 :     nJCSGMLInputTemplateDepth = 0;
     552           8 :     nCollectionElementDepth = 0;
     553           8 :     nFeatureCollectionDepth = 0;
     554           8 :     nFeatureElementDepth = 0;
     555           8 :     nGeometryElementDepth = 0;
     556           8 :     nColumnDepth = 0;
     557           8 :     nNameDepth = 0;
     558           8 :     nTypeDepth = 0;
     559           8 :     nAttributeElementDepth = 0;
     560             : 
     561           8 :     ResetReading();
     562             : }
     563             : 
     564             : /************************************************************************/
     565             : /*                     startElementLoadSchemaCbk()                      */
     566             : /************************************************************************/
     567             : 
     568         304 : void OGRJMLLayer::startElementLoadSchemaCbk(const char *pszName,
     569             :                                             const char **ppszAttr)
     570             : {
     571         304 :     if (bStopParsing)
     572           0 :         return;
     573             : 
     574         304 :     nWithoutEventCounter = 0;
     575             : 
     576         304 :     if (nJCSGMLInputTemplateDepth == 0 &&
     577         111 :         strcmp(pszName, "JCSGMLInputTemplate") == 0)
     578           8 :         nJCSGMLInputTemplateDepth = currentDepth;
     579         296 :     else if (nJCSGMLInputTemplateDepth > 0)
     580             :     {
     581         193 :         if (nCollectionElementDepth == 0 &&
     582         193 :             strcmp(pszName, "CollectionElement") == 0)
     583             :         {
     584           7 :             nCollectionElementDepth = currentDepth;
     585           7 :             bAccumulateElementValue = true;
     586             :         }
     587         186 :         else if (nFeatureElementDepth == 0 &&
     588         186 :                  strcmp(pszName, "FeatureElement") == 0)
     589             :         {
     590           7 :             nFeatureElementDepth = currentDepth;
     591           7 :             bAccumulateElementValue = true;
     592             :         }
     593         179 :         else if (nGeometryElementDepth == 0 &&
     594         179 :                  strcmp(pszName, "GeometryElement") == 0)
     595             :         {
     596           7 :             nGeometryElementDepth = currentDepth;
     597           7 :             bAccumulateElementValue = true;
     598             :         }
     599         172 :         else if (nColumnDepth == 0 && strcmp(pszName, "column") == 0)
     600             :         {
     601          32 :             nColumnDepth = currentDepth;
     602          32 :             oCurColumn.osName = "";
     603          32 :             oCurColumn.osType = "";
     604          32 :             oCurColumn.osElementName = "";
     605          32 :             oCurColumn.osAttributeName = "";
     606          32 :             oCurColumn.osAttributeValue = "";
     607          32 :             oCurColumn.bIsBody = false;
     608             :         }
     609         140 :         else if (nColumnDepth > 0)
     610             :         {
     611         128 :             if (nNameDepth == 0 && strcmp(pszName, "name") == 0)
     612             :             {
     613          31 :                 nNameDepth = currentDepth;
     614          31 :                 bAccumulateElementValue = true;
     615             :             }
     616          97 :             else if (nTypeDepth == 0 && strcmp(pszName, "type") == 0)
     617             :             {
     618          31 :                 nTypeDepth = currentDepth;
     619          31 :                 bAccumulateElementValue = true;
     620             :             }
     621          66 :             else if (strcmp(pszName, "valueElement") == 0)
     622             :             {
     623          31 :                 const char **papszIter = ppszAttr;
     624         115 :                 while (papszIter && *papszIter != nullptr)
     625             :                 {
     626          84 :                     if (strcmp(*papszIter, "elementName") == 0)
     627          30 :                         oCurColumn.osElementName = papszIter[1];
     628          54 :                     else if (strcmp(*papszIter, "attributeName") == 0)
     629          25 :                         oCurColumn.osAttributeName = papszIter[1];
     630          29 :                     else if (strcmp(*papszIter, "attributeValue") == 0)
     631          25 :                         oCurColumn.osAttributeValue = papszIter[1];
     632          84 :                     papszIter += 2;
     633             :                 }
     634             :             }
     635          35 :             else if (strcmp(pszName, "valueLocation") == 0)
     636             :             {
     637          31 :                 const char **papszIter = ppszAttr;
     638          64 :                 while (papszIter && *papszIter != nullptr)
     639             :                 {
     640          33 :                     if (strcmp(*papszIter, "position") == 0)
     641          31 :                         oCurColumn.bIsBody = strcmp(papszIter[1], "body") == 0;
     642           2 :                     else if (strcmp(*papszIter, "attributeName") == 0)
     643           2 :                         oCurColumn.osAttributeName = papszIter[1];
     644          33 :                     papszIter += 2;
     645             :                 }
     646             :             }
     647             :         }
     648             :     }
     649         132 :     else if (nFeatureCollectionDepth == 0 &&
     650          29 :              osCollectionElement.compare(pszName) == 0)
     651             :     {
     652           6 :         nFeatureCollectionDepth = currentDepth;
     653             :     }
     654          97 :     else if (nFeatureCollectionDepth > 0 &&
     655          74 :              currentDepth == nFeatureCollectionDepth + 2 &&
     656          15 :              strcmp(pszName, "gml:Box") == 0)
     657             :     {
     658           1 :         const char **papszIter = ppszAttr;
     659           2 :         while (papszIter && *papszIter != nullptr)
     660             :         {
     661           1 :             if (strcmp(*papszIter, "srsName") == 0)
     662           1 :                 osSRSName = papszIter[1];
     663           1 :             papszIter += 2;
     664             :         }
     665           1 :         bSchemaFinished = true;
     666             :     }
     667         288 :     else if (nFeatureCollectionDepth >= 0 &&
     668         184 :              currentDepth >= nFeatureCollectionDepth + 1 &&
     669          88 :              osFeatureElement.compare(pszName) == 0)
     670             :     {
     671          11 :         bSchemaFinished = true;
     672             :     }
     673             : 
     674         304 :     currentDepth++;
     675             : }
     676             : 
     677             : /************************************************************************/
     678             : /*                      endElementLoadSchemaCbk()                       */
     679             : /************************************************************************/
     680             : 
     681         296 : void OGRJMLLayer::endElementLoadSchemaCbk(const char * /* pszName */)
     682             : {
     683         296 :     if (bStopParsing)
     684           0 :         return;
     685             : 
     686         296 :     nWithoutEventCounter = 0;
     687             : 
     688         296 :     currentDepth--;
     689             : 
     690         296 :     if (nJCSGMLInputTemplateDepth == currentDepth)
     691             :     {
     692          12 :         nJCSGMLInputTemplateDepth = 0;
     693             :     }
     694         284 :     else if (nCollectionElementDepth == currentDepth)
     695             :     {
     696           7 :         nCollectionElementDepth = 0;
     697           7 :         osCollectionElement = pszElementValue;
     698             : #ifdef DEBUG_VERBOSE
     699             :         CPLDebug("JML", "osCollectionElement = %s",
     700             :                  osCollectionElement.c_str());
     701             : #endif
     702           7 :         StopAccumulate();
     703             :     }
     704         277 :     else if (nFeatureElementDepth == currentDepth)
     705             :     {
     706           7 :         nFeatureElementDepth = 0;
     707           7 :         osFeatureElement = pszElementValue;
     708             : #ifdef DEBUG_VERBOSE
     709             :         CPLDebug("JML", "osFeatureElement = %s", osFeatureElement.c_str());
     710             : #endif
     711           7 :         StopAccumulate();
     712             :     }
     713         270 :     else if (nGeometryElementDepth == currentDepth)
     714             :     {
     715           7 :         nGeometryElementDepth = 0;
     716           7 :         osGeometryElement = pszElementValue;
     717             : #ifdef DEBUG_VERBOSE
     718             :         CPLDebug("JML", "osGeometryElement = %s", osGeometryElement.c_str());
     719             : #endif
     720           7 :         StopAccumulate();
     721             :     }
     722         263 :     else if (nColumnDepth == currentDepth)
     723             :     {
     724          32 :         bool bIsOK = true;
     725          32 :         if (oCurColumn.osName.empty())
     726           1 :             bIsOK = false;
     727          32 :         if (oCurColumn.osType.empty())
     728           1 :             bIsOK = false;
     729          32 :         if (oCurColumn.osElementName.empty())
     730           2 :             bIsOK = false;
     731          32 :         if (oCurColumn.bIsBody)
     732             :         {
     733          32 :             if (oCurColumn.osAttributeName.empty() &&
     734           4 :                 !oCurColumn.osAttributeValue.empty())
     735           1 :                 bIsOK = false;
     736          52 :             if (!oCurColumn.osAttributeName.empty() &&
     737          24 :                 oCurColumn.osAttributeValue.empty())
     738           1 :                 bIsOK = false;
     739             :             /* Only 2 valid possibilities : */
     740             :             /* <osElementName
     741             :              * osAttributeName="osAttributeValue">value</osElementName> */
     742             :             /* <osElementName>value</osElementName> */
     743             :         }
     744             :         else
     745             :         {
     746             :             /* <osElementName osAttributeName="value"></osElementName> */
     747           4 :             if (oCurColumn.osAttributeName.empty())
     748           1 :                 bIsOK = false;
     749           4 :             if (!oCurColumn.osAttributeValue.empty())
     750           1 :                 bIsOK = false;
     751             :         }
     752             : 
     753          32 :         if (bIsOK)
     754             :         {
     755          24 :             OGRFieldType eType = OFTString;
     756          24 :             if (EQUAL(oCurColumn.osType, "INTEGER"))
     757           2 :                 eType = OFTInteger;
     758          22 :             else if (EQUAL(oCurColumn.osType, "DOUBLE"))
     759           2 :                 eType = OFTReal;
     760          20 :             else if (EQUAL(oCurColumn.osType, "DATE"))
     761           4 :                 eType = OFTDateTime;
     762          48 :             OGRFieldDefn oField(oCurColumn.osName, eType);
     763             : 
     764          24 :             if (oCurColumn.osName == "R_G_B" && eType == OFTString)
     765           2 :                 iRGBField = poFeatureDefn->GetFieldCount();
     766             : 
     767          24 :             poFeatureDefn->AddFieldDefn(&oField);
     768          24 :             aoColumns.push_back(oCurColumn);
     769             :         }
     770             :         else
     771             :         {
     772           8 :             CPLDebug("JML",
     773             :                      "Invalid column definition: name = %s, type = %s, "
     774             :                      "elementName = %s, attributeName = %s, "
     775             :                      "attributeValue = %s, bIsBody = %d",
     776             :                      oCurColumn.osName.c_str(), oCurColumn.osType.c_str(),
     777             :                      oCurColumn.osElementName.c_str(),
     778             :                      oCurColumn.osAttributeName.c_str(),
     779             :                      oCurColumn.osAttributeValue.c_str(),
     780           8 :                      static_cast<int>(oCurColumn.bIsBody));
     781             :         }
     782             : 
     783          32 :         nColumnDepth = 0;
     784             :     }
     785         231 :     else if (nNameDepth == currentDepth)
     786             :     {
     787          31 :         nNameDepth = 0;
     788          31 :         oCurColumn.osName = pszElementValue;
     789             : #ifdef DEBUG_VERBOSE
     790             :         CPLDebug("JML", "oCurColumn.osName = %s", oCurColumn.osName.c_str());
     791             : #endif
     792          31 :         StopAccumulate();
     793             :     }
     794         200 :     else if (nTypeDepth == currentDepth)
     795             :     {
     796          31 :         nTypeDepth = 0;
     797          31 :         oCurColumn.osType = pszElementValue;
     798             : #ifdef DEBUG_VERBOSE
     799             :         CPLDebug("JML", "oCurColumn.osType = %s", oCurColumn.osType.c_str());
     800             : #endif
     801          31 :         StopAccumulate();
     802             :     }
     803             : }
     804             : 
     805             : /************************************************************************/
     806             : /*                           TestCapability()                           */
     807             : /************************************************************************/
     808             : 
     809          37 : int OGRJMLLayer::TestCapability(const char *pszCap) const
     810             : 
     811             : {
     812          37 :     if (EQUAL(pszCap, OLCStringsAsUTF8))
     813          13 :         return true;
     814          24 :     else if (EQUAL(pszCap, OLCZGeometries))
     815           3 :         return true;
     816             : 
     817          21 :     return false;
     818             : }
     819             : 
     820             : #endif /* HAVE_EXPAT */

Generated by: LCOV version 1.14