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

Generated by: LCOV version 1.14