1 /******************************************************************************
2  * Project:  OGR
3  * Purpose:  OGRGMLASDriver implementation
4  * Author:   Even Rouault, <even dot rouault at spatialys dot com>
5  *
6  * Initial development funded by the European Earth observation programme
7  * Copernicus
8  *
9  ******************************************************************************
10  * Copyright (c) 2016, Even Rouault, <even dot rouault at spatialys dot com>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 // Must be first for DEBUG_BOOL case
32 #include "ogr_gmlas.h"
33 
34 #include "ogr_p.h"
35 
36 #include "cpl_json_header.h"
37 
38 CPL_CVSID("$Id: ogrgmlasreader.cpp 13fb5edfd2b4d28203c98db3b755d014a36ede02 2021-08-22 17:23:55 +0200 Even Rouault $")
39 
40 /************************************************************************/
41 /*                        GMLASBinInputStream                           */
42 /************************************************************************/
43 
44 class GMLASBinInputStream : public BinInputStream
45 {
46     VSILFILE*         m_fp;
47 
48 public :
49 
50     explicit GMLASBinInputStream(VSILFILE* fp);
51     virtual ~GMLASBinInputStream();
52 
53     virtual XMLFilePos curPos() const override;
54     virtual XMLSize_t readBytes(XMLByte* const toFill, const XMLSize_t maxToRead) override;
55     virtual const XMLCh* getContentType() const override ;
56 };
57 
58 /************************************************************************/
59 /*                        GMLASBinInputStream()                         */
60 /************************************************************************/
61 
GMLASBinInputStream(VSILFILE * fp)62 GMLASBinInputStream::GMLASBinInputStream(VSILFILE* fp)
63 {
64     m_fp = fp;
65     VSIFSeekL(fp, 0, SEEK_SET);
66 }
67 
68 /************************************************************************/
69 /*                       ~GMLASBinInputStream()                         */
70 /************************************************************************/
71 
~GMLASBinInputStream()72 GMLASBinInputStream::~ GMLASBinInputStream()
73 {
74 }
75 
76 /************************************************************************/
77 /*                                curPos()                              */
78 /************************************************************************/
79 
curPos() const80 XMLFilePos GMLASBinInputStream::curPos() const
81 {
82     return (XMLFilePos)VSIFTellL(m_fp);
83 }
84 
85 /************************************************************************/
86 /*                               readBytes()                            */
87 /************************************************************************/
88 
readBytes(XMLByte * const toFill,const XMLSize_t maxToRead)89 XMLSize_t GMLASBinInputStream::readBytes(XMLByte* const toFill,
90                                          const XMLSize_t maxToRead)
91 {
92     return (XMLSize_t)VSIFReadL(toFill, 1, maxToRead, m_fp);
93 }
94 
95 /************************************************************************/
96 /*                            getContentType()                          */
97 /************************************************************************/
98 
getContentType() const99 const XMLCh* GMLASBinInputStream::getContentType() const
100 {
101     return nullptr;
102 }
103 
104 /************************************************************************/
105 /*                          GMLASInputSource()                          */
106 /************************************************************************/
107 
GMLASInputSource(const char * pszFilename,VSILFILE * fp,bool bOwnFP,MemoryManager * const manager)108 GMLASInputSource::GMLASInputSource(const char* pszFilename,
109                                    VSILFILE* fp,
110                                    bool bOwnFP,
111                                    MemoryManager* const manager)
112     : InputSource(manager),
113       m_osFilename( pszFilename )
114 {
115     m_fp = fp;
116     m_bOwnFP = bOwnFP;
117     try
118     {
119         XMLCh* pFilename = XMLString::transcode(pszFilename);
120         setPublicId(pFilename);
121         setSystemId(pFilename);
122         XMLString::release( &pFilename );
123     }
124     catch( const TranscodingException& e )
125     {
126         CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
127                  transcode(e.getMessage()).c_str());
128     }
129     m_nCounter = 0;
130     m_pnCounter = &m_nCounter;
131     m_cbk = nullptr;
132 }
133 
134 /************************************************************************/
135 /*                        SetClosingCallback()                          */
136 /************************************************************************/
137 
SetClosingCallback(IGMLASInputSourceClosing * cbk)138 void GMLASInputSource::SetClosingCallback( IGMLASInputSourceClosing* cbk )
139 {
140     m_cbk = cbk;
141 }
142 
143 /************************************************************************/
144 /*                         ~GMLASInputSource()                          */
145 /************************************************************************/
146 
~GMLASInputSource()147 GMLASInputSource::~GMLASInputSource()
148 {
149     if( m_cbk )
150         m_cbk->notifyClosing( m_osFilename );
151     if( m_bOwnFP && m_fp )
152         VSIFCloseL(m_fp);
153 }
154 
155 /************************************************************************/
156 /*                              makeStream()                            */
157 /************************************************************************/
158 
makeStream() const159 BinInputStream* GMLASInputSource::makeStream() const
160 {
161     // This is a lovely cheating around the const qualifier of this method !
162     // We cannot modify m_nCounter directly, but we can change the value
163     // pointed by m_pnCounter...
164     if( *m_pnCounter != 0 )
165     {
166         CPLError(CE_Failure, CPLE_AppDefined,
167                  "makeStream() called several times on same GMLASInputSource");
168         return nullptr;
169     }
170     (*m_pnCounter) ++;
171     if( m_fp == nullptr )
172         return nullptr;
173     return new GMLASBinInputStream(m_fp);
174 }
175 
176 /************************************************************************/
177 /*                            warning()                                 */
178 /************************************************************************/
179 
warning(const SAXParseException & e)180 void GMLASErrorHandler::warning (const SAXParseException& e)
181 {
182     handle (e, CE_Warning);
183 }
184 
185 /************************************************************************/
186 /*                             error()                                  */
187 /************************************************************************/
188 
error(const SAXParseException & e)189 void GMLASErrorHandler::error (const SAXParseException& e)
190 {
191     m_bFailed = true;
192     handle (e, CE_Failure);
193 }
194 
195 /************************************************************************/
196 /*                          fatalError()                                */
197 /************************************************************************/
198 
fatalError(const SAXParseException & e)199 void GMLASErrorHandler::fatalError (const SAXParseException& e)
200 {
201     m_bFailed = true;
202     handle (e, CE_Failure);
203 }
204 
205 /************************************************************************/
206 /*                            handle()                                  */
207 /************************************************************************/
208 
handle(const SAXParseException & e,CPLErr eErr)209 void GMLASErrorHandler::handle (const SAXParseException& e, CPLErr eErr)
210 {
211     const XMLCh* resourceId (e.getPublicId());
212 
213     if ( resourceId == nullptr || resourceId[0] == 0 )
214         resourceId = e.getSystemId();
215 
216     CPLString osErrorMsg(transcode(e.getMessage()));
217     if( m_bSchemaFullChecking &&
218         osErrorMsg.find("forbidden restriction of any particle") !=
219                                                             std::string::npos )
220     {
221         osErrorMsg += ". You may retry with the " +
222                       CPLString(szSCHEMA_FULL_CHECKING_OPTION) +
223                       "=NO open option";
224     }
225     else if( !m_bHandleMultipleImports && osErrorMsg.find("not found") !=
226                                                             std::string::npos )
227     {
228         osErrorMsg += ". You may retry with the " +
229                       CPLString(szHANDLE_MULTIPLE_IMPORTS_OPTION) +
230                       "=YES open option";
231     }
232     CPLError(eErr, CPLE_AppDefined, "%s:%d:%d %s",
233              transcode(resourceId).c_str(),
234              static_cast<int>(e.getLineNumber()),
235              static_cast<int>(e.getColumnNumber()),
236              osErrorMsg.c_str());
237 }
238 
239 /************************************************************************/
240 /*                     GMLASBaseEntityResolver()                        */
241 /************************************************************************/
242 
GMLASBaseEntityResolver(const CPLString & osBasePath,GMLASXSDCache & oCache)243 GMLASBaseEntityResolver::GMLASBaseEntityResolver(const CPLString& osBasePath,
244                                                  GMLASXSDCache& oCache)
245     : m_oCache(oCache)
246 {
247     m_aosPathStack.push_back(osBasePath);
248 }
249 
250 /************************************************************************/
251 /*                    ~GMLASBaseEntityResolver()                        */
252 /************************************************************************/
253 
~GMLASBaseEntityResolver()254 GMLASBaseEntityResolver::~GMLASBaseEntityResolver()
255 {
256     CPLAssert( m_aosPathStack.size() == 1 );
257 }
258 
259 /************************************************************************/
260 /*                            notifyClosing()                           */
261 /************************************************************************/
262 
263 /* Called by GMLASInputSource destructor. This is useful for use to */
264 /* know where a .xsd has been finished from processing. Note that we */
265 /* strongly depend on Xerces behavior here... */
notifyClosing(const CPLString & osFilename)266 void GMLASBaseEntityResolver::notifyClosing(const CPLString& osFilename )
267 {
268     CPLDebug("GMLAS", "Closing %s", osFilename.c_str());
269 
270     CPLAssert( m_aosPathStack.back() ==
271                                 CPLString(CPLGetDirname(osFilename)) );
272     m_aosPathStack.pop_back();
273 }
274 
275 /************************************************************************/
276 /*                            SetBasePath()                             */
277 /************************************************************************/
278 
SetBasePath(const CPLString & osBasePath)279 void GMLASBaseEntityResolver::SetBasePath(const CPLString& osBasePath)
280 {
281     CPLAssert( m_aosPathStack.size() == 1 );
282     m_aosPathStack[0] = osBasePath;
283 }
284 
285 /************************************************************************/
286 /*                         DoExtraSchemaProcessing()                    */
287 /************************************************************************/
288 
DoExtraSchemaProcessing(const CPLString &,VSILFILE *)289 void GMLASBaseEntityResolver::DoExtraSchemaProcessing(
290                                              const CPLString& /*osFilename*/,
291                                              VSILFILE* /*fp*/)
292 {
293 }
294 
295 /************************************************************************/
296 /*                         resolveEntity()                              */
297 /************************************************************************/
298 
resolveEntity(const XMLCh * const,const XMLCh * const systemId)299 InputSource* GMLASBaseEntityResolver::resolveEntity(
300                                                 const XMLCh* const /*publicId*/,
301                                                 const XMLCh* const systemId)
302 {
303     // Can happen on things like <xs:import namespace="http://www.w3.org/XML/1998/namespace"/>
304     if( systemId == nullptr )
305         return nullptr;
306 
307     CPLString osSystemId(transcode(systemId));
308 
309     if( osSystemId.find("/gml/2.1.2/") != std::string::npos )
310         m_osGMLVersionFound = "2.1.2";
311     else if( osSystemId.find("/gml/3.1.1/") != std::string::npos )
312         m_osGMLVersionFound = "3.1.1";
313     else if( osSystemId.find("/gml/3.2.1/") != std::string::npos )
314         m_osGMLVersionFound = "3.2.1";
315 
316     CPLString osNewPath;
317     VSILFILE* fp = m_oCache.Open(osSystemId,
318                                  m_aosPathStack.back(),
319                                  osNewPath);
320 
321     if( fp != nullptr )
322     {
323         if( osNewPath.find("/vsicurl_streaming/") == 0 )
324             m_oSetSchemaURLs.insert(
325                             osNewPath.substr(strlen("/vsicurl_streaming/")));
326         else
327             m_oSetSchemaURLs.insert(osNewPath);
328 
329         CPLDebug("GMLAS", "Opening %s", osNewPath.c_str());
330         DoExtraSchemaProcessing( osNewPath, fp );
331     }
332 
333     m_aosPathStack.push_back( CPLGetDirname(osNewPath) );
334     GMLASInputSource* poIS = new GMLASInputSource(osNewPath, fp, true);
335     poIS->SetClosingCallback(this);
336     return poIS;
337 }
338 
339 /************************************************************************/
340 /*                             Dump()                                   */
341 /************************************************************************/
342 
Dump() const343 void GMLASReader::Context::Dump() const
344 {
345     CPLDebug("GMLAS", "Context");
346     CPLDebug("GMLAS", "  m_nLevel = %d", m_nLevel);
347     CPLDebug("GMLAS", "  m_poFeature = %p", m_poFeature);
348     const char* pszDebug = CPLGetConfigOption("CPL_DEBUG", "OFF");
349     if( EQUAL(pszDebug, "ON") || EQUAL(pszDebug, "GMLAS") )
350     {
351         if( m_poFeature )
352             m_poFeature->DumpReadable(stderr);
353     }
354     CPLDebug("GMLAS", "  m_poLayer = %p (%s)",
355              m_poLayer, m_poLayer ? m_poLayer->GetName() : "");
356     CPLDebug("GMLAS", "  m_poGroupLayer = %p (%s)",
357              m_poGroupLayer, m_poGroupLayer ? m_poGroupLayer->GetName() : "");
358     CPLDebug("GMLAS", "  m_nGroupLayerLevel = %d", m_nGroupLayerLevel);
359     CPLDebug("GMLAS", "  m_nLastFieldIdxGroupLayer = %d",
360              m_nLastFieldIdxGroupLayer);
361     CPLDebug("GMLAS", "  m_osCurSubXPath = %s",
362              m_osCurSubXPath.c_str());
363 }
364 
365 /************************************************************************/
366 /*                             GMLASReader()                            */
367 /************************************************************************/
368 
GMLASReader(GMLASXSDCache & oCache,const GMLASXPathMatcher & oIgnoredXPathMatcher,GMLASXLinkResolver & oXLinkResolver)369 GMLASReader::GMLASReader(GMLASXSDCache& oCache,
370                          const GMLASXPathMatcher& oIgnoredXPathMatcher,
371                          GMLASXLinkResolver& oXLinkResolver)
372     : m_oCache(oCache)
373     , m_oIgnoredXPathMatcher(oIgnoredXPathMatcher)
374     , m_oXLinkResolver(oXLinkResolver)
375 {
376     m_bParsingError = false;
377     m_poSAXReader = nullptr;
378     m_fp = nullptr;
379     m_GMLInputSource = nullptr;
380     m_bFirstIteration = true;
381     m_bEOF = false;
382     m_bInterrupted = false;
383     m_papoLayers = nullptr;
384     m_nLevel = 0;
385     m_oCurCtxt.m_nLevel = 0;
386     m_oCurCtxt.m_poLayer = nullptr;
387     m_oCurCtxt.m_poGroupLayer = nullptr;
388     m_oCurCtxt.m_nGroupLayerLevel = -1;
389     m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
390     m_oCurCtxt.m_poFeature = nullptr;
391     m_nCurFieldIdx = -1;
392     m_nCurGeomFieldIdx = -1;
393     m_nCurFieldLevel = 0;
394     m_bIsXMLBlob = false;
395     m_bIsXMLBlobIncludeUpper = false;
396     m_nTextContentListEstimatedSize = 0;
397     m_poLayerOfInterest = nullptr;
398     m_nMaxLevel = atoi(CPLGetConfigOption("GMLAS_XML_MAX_LEVEL", "100"));
399     m_nMaxContentSize = static_cast<size_t>(
400           atoi(CPLGetConfigOption("GMLAS_XML_MAX_CONTENT_SIZE", "512000000")));
401     m_bValidate = false;
402     m_poEntityResolver = nullptr;
403     m_nLevelSilentIgnoredXPath = -1;
404     m_eSwapCoordinates = GMLAS_SWAP_AUTO;
405     m_bInitialPass = false;
406     m_bProcessSWEDataArray = false;
407     m_bProcessSWEDataRecord = false;
408     m_nSWEDataArrayLevel = -1;
409     m_nSWEDataRecordLevel = -1;
410     m_poFieldsMetadataLayer = nullptr;
411     m_poLayersMetadataLayer = nullptr;
412     m_poRelationshipsLayer = nullptr;
413     m_nFileSize = 0;
414     m_bWarnUnexpected =
415         CPLTestBool(CPLGetConfigOption("GMLAS_WARN_UNEXPECTED", "FALSE"));
416     m_nSWEDataArrayLayerIdx = 0;
417 }
418 
419 /************************************************************************/
420 /*                            ~GMLASReader()                            */
421 /************************************************************************/
422 
~GMLASReader()423 GMLASReader::~GMLASReader()
424 {
425     delete m_poSAXReader;
426     delete m_GMLInputSource;
427     if( m_oCurCtxt.m_poFeature != nullptr && !m_aoStackContext.empty() &&
428         m_oCurCtxt.m_poFeature != m_aoStackContext.back().m_poFeature )
429     {
430         CPLDebug("GMLAS", "Delete feature m_oCurCtxt.m_poFeature=%p",
431                  m_oCurCtxt.m_poFeature);
432         delete m_oCurCtxt.m_poFeature;
433     }
434     for( size_t i = 0; i < m_aoStackContext.size(); i++ )
435     {
436         if( i == 0 ||
437             m_aoStackContext[i].m_poFeature !=
438                                         m_aoStackContext[i-1].m_poFeature )
439         {
440             CPLDebug("GMLAS",
441                      "Delete feature m_aoStackContext[%d].m_poFeature=%p",
442                     static_cast<int>(i), m_aoStackContext[i].m_poFeature);
443             delete m_aoStackContext[i].m_poFeature;
444         }
445     }
446     for( size_t i = 0; i < m_aoFeaturesReady.size(); i++ )
447     {
448         CPLDebug("GMLAS", "Delete feature m_aoFeaturesReady[%d].first=%p",
449                  static_cast<int>(i), m_aoFeaturesReady[i].first);
450         delete m_aoFeaturesReady[i].first;
451     }
452     if( !m_apsXMLNodeStack.empty() )
453     {
454         CPLDestroyXMLNode(m_apsXMLNodeStack[0].psNode);
455     }
456     // No need to take care of m_apoSWEDataArrayLayers. Ownerships belongs to
457     // the datasource.
458     delete m_poEntityResolver;
459 }
460 
461 /************************************************************************/
462 /*                          SetLayerOfInterest()                        */
463 /************************************************************************/
464 
SetLayerOfInterest(OGRGMLASLayer * poLayer)465 void GMLASReader::SetLayerOfInterest( OGRGMLASLayer* poLayer )
466 {
467     m_poLayerOfInterest = poLayer;
468 }
469 
470 /************************************************************************/
471 /*                        SetSWEDataArrayLayers()                       */
472 /************************************************************************/
473 
SetSWEDataArrayLayers(const std::vector<OGRGMLASLayer * > & ar)474 void GMLASReader::SetSWEDataArrayLayers( const std::vector<OGRGMLASLayer*>& ar )
475 {
476     m_apoSWEDataArrayLayers = ar;
477     m_bProcessSWEDataArray = !ar.empty();
478 }
479 
480 /************************************************************************/
481 /*                          LoadXSDInParser()                           */
482 /************************************************************************/
483 
LoadXSDInParser(SAX2XMLReader * poParser,GMLASXSDCache & oCache,GMLASBaseEntityResolver & oXSDEntityResolver,const CPLString & osBaseDirname,const CPLString & osXSDFilename,Grammar ** ppoGrammar,bool bSchemaFullChecking,bool bHandleMultipleImports)484 bool GMLASReader::LoadXSDInParser( SAX2XMLReader* poParser,
485                                    GMLASXSDCache& oCache,
486                                    GMLASBaseEntityResolver& oXSDEntityResolver,
487                                    const CPLString& osBaseDirname,
488                                    const CPLString& osXSDFilename,
489                                    Grammar** ppoGrammar,
490                                    bool bSchemaFullChecking,
491                                    bool bHandleMultipleImports )
492 {
493     if( ppoGrammar != nullptr )
494         *ppoGrammar = nullptr;
495 
496     const CPLString osModifXSDFilename(
497         (osXSDFilename.find("http://") != 0 &&
498         osXSDFilename.find("https://") != 0 &&
499         CPLIsFilenameRelative(osXSDFilename)) ?
500             CPLString(CPLFormFilename(osBaseDirname, osXSDFilename, nullptr)) :
501             osXSDFilename );
502     CPLString osResolvedFilename;
503     VSILFILE* fpXSD = oCache.Open( osModifXSDFilename, CPLString(),
504                                    osResolvedFilename );
505     if( fpXSD == nullptr )
506     {
507         return false;
508     }
509 
510     poParser->setFeature (XMLUni::fgXercesSchemaFullChecking,
511                             bSchemaFullChecking);
512     poParser->setFeature( XMLUni::fgXercesHandleMultipleImports,
513                             bHandleMultipleImports );
514 
515     // Install a temporary entity resolved based on the current XSD
516     CPLString osXSDDirname( CPLGetDirname(osModifXSDFilename) );
517     if( osXSDFilename.find("http://") == 0 ||
518         osXSDFilename.find("https://") == 0 )
519     {
520         osXSDDirname = CPLGetDirname(("/vsicurl_streaming/" +
521                                      osModifXSDFilename).c_str());
522     }
523     oXSDEntityResolver.SetBasePath(osXSDDirname);
524     oXSDEntityResolver.DoExtraSchemaProcessing( osResolvedFilename, fpXSD );
525 
526     EntityResolver* poOldEntityResolver = poParser->getEntityResolver();
527     poParser->setEntityResolver( &oXSDEntityResolver );
528 
529     // Install a temporary error handler
530     GMLASErrorHandler oErrorHandler;
531     oErrorHandler.SetSchemaFullCheckingEnabled( bSchemaFullChecking );
532     oErrorHandler.SetHandleMultipleImportsEnabled( bHandleMultipleImports );
533     ErrorHandler* poOldErrorHandler = poParser->getErrorHandler();
534     poParser->setErrorHandler( &oErrorHandler);
535 
536     GMLASInputSource oSource(osResolvedFilename, fpXSD, false);
537     const bool bCacheGrammar = true;
538     Grammar* poGrammar = nullptr;
539     std::string osLoadGrammarErrorMsg("loadGrammar failed");
540     try
541     {
542         poGrammar = poParser->loadGrammar(oSource,
543                                             Grammar::SchemaGrammarType,
544                                             bCacheGrammar);
545     }
546     catch( const SAXException& e )
547     {
548         osLoadGrammarErrorMsg += ": "+ transcode(e.getMessage());
549     }
550     catch( const XMLException& e )
551     {
552         osLoadGrammarErrorMsg += ": "+ transcode(e.getMessage());
553     }
554     catch( const DOMException& e )
555     {
556         // Can happen with a .xsd that has a bad <?xml version="
557         // declaration.
558         osLoadGrammarErrorMsg += ": "+ transcode(e.getMessage());
559     }
560 
561     // Restore previous handlers
562     poParser->setEntityResolver( poOldEntityResolver );
563     poParser->setErrorHandler( poOldErrorHandler );
564     VSIFCloseL(fpXSD);
565 
566     if( poGrammar == nullptr )
567     {
568         CPLError(CE_Failure, CPLE_AppDefined, "%s",
569                  osLoadGrammarErrorMsg.c_str());
570         return false;
571     }
572     if( oErrorHandler.hasFailed() )
573     {
574         return false;
575     }
576 
577     if( ppoGrammar != nullptr )
578         *ppoGrammar = poGrammar;
579 
580     return true;
581 }
582 
583 /************************************************************************/
584 /*                                  Init()                              */
585 /************************************************************************/
586 
Init(const char * pszFilename,VSILFILE * fp,const std::map<CPLString,CPLString> & oMapURIToPrefix,std::vector<OGRGMLASLayer * > * papoLayers,bool bValidate,const std::vector<PairURIFilename> & aoXSDs,bool bSchemaFullChecking,bool bHandleMultipleImports)587 bool GMLASReader::Init(const char* pszFilename,
588                        VSILFILE* fp,
589                        const std::map<CPLString, CPLString>& oMapURIToPrefix,
590                        std::vector<OGRGMLASLayer*>* papoLayers,
591                        bool bValidate,
592                        const std::vector<PairURIFilename>& aoXSDs,
593                        bool bSchemaFullChecking,
594                        bool bHandleMultipleImports)
595 {
596     m_oMapURIToPrefix = oMapURIToPrefix;
597     m_papoLayers = papoLayers;
598     m_bValidate = bValidate;
599 
600     m_poSAXReader = XMLReaderFactory::createXMLReader();
601 
602     // Commonly useful configuration.
603     //
604     m_poSAXReader->setFeature (XMLUni::fgSAX2CoreNameSpaces, true);
605     m_poSAXReader->setFeature (XMLUni::fgSAX2CoreNameSpacePrefixes, true);
606 
607     m_poSAXReader->setContentHandler( this );
608     m_poSAXReader->setLexicalHandler( this );
609     m_poSAXReader->setDTDHandler( this );
610 
611     m_oErrorHandler.SetSchemaFullCheckingEnabled( bSchemaFullChecking );
612     m_oErrorHandler.SetHandleMultipleImportsEnabled( bHandleMultipleImports );
613     m_poSAXReader->setErrorHandler(&m_oErrorHandler);
614 
615     m_poSAXReader->setFeature (XMLUni::fgXercesSchemaFullChecking,
616                             bSchemaFullChecking);
617     m_poSAXReader->setFeature( XMLUni::fgXercesHandleMultipleImports,
618                             bHandleMultipleImports );
619 
620     if( bValidate )
621     {
622         // Enable validation.
623         m_poSAXReader->setFeature (XMLUni::fgSAX2CoreValidation, true);
624         m_poSAXReader->setFeature (XMLUni::fgXercesSchema, true);
625 
626         // We want all errors to be reported
627         // coverity[unsafe_xml_parse_config]
628         m_poSAXReader->setFeature (XMLUni::fgXercesValidationErrorAsFatal, false);
629 
630         CPLString osBaseDirname( CPLGetDirname(pszFilename) );
631 
632         // In the case the schemas are explicitly passed, we must do special
633         // processing
634         if( !aoXSDs.empty() )
635         {
636             GMLASBaseEntityResolver oXSDEntityResolver( CPLString(), m_oCache );
637             for( size_t i = 0; i < aoXSDs.size(); i++ )
638             {
639                 const CPLString osXSDFilename(aoXSDs[i].second);
640                 if( !LoadXSDInParser( m_poSAXReader, m_oCache,
641                                       oXSDEntityResolver,
642                                       osBaseDirname, osXSDFilename,
643                                       nullptr,
644                                       bSchemaFullChecking,
645                                       bHandleMultipleImports) )
646                 {
647                     return false;
648                 }
649             }
650 
651             // Make sure our previously loaded schemas are used
652             m_poSAXReader->setFeature (XMLUni::fgXercesUseCachedGrammarInParse,
653                                        true);
654 
655             // Don't load schemas from any other source (e.g., from XML document's
656             // xsi:schemaLocation attributes).
657             //
658             m_poSAXReader->setFeature (XMLUni::fgXercesLoadSchema, false);
659         }
660 
661         // Install entity resolver based on XML file
662         m_poEntityResolver = new GMLASBaseEntityResolver(
663                                                 osBaseDirname,
664                                                 m_oCache );
665         m_poSAXReader->setEntityResolver( m_poEntityResolver );
666     }
667     else
668     {
669         // Don't load schemas from any other source (e.g., from XML document's
670         // xsi:schemaLocation attributes).
671         //
672         m_poSAXReader->setFeature (XMLUni::fgXercesLoadSchema, false);
673         m_poSAXReader->setEntityResolver( this );
674     }
675 
676     m_fp = fp;
677     m_GMLInputSource = new GMLASInputSource(pszFilename, fp, false);
678 
679     return true;
680 }
681 
682 /************************************************************************/
683 /*                             IsArrayType()                            */
684 /************************************************************************/
685 
IsArrayType(OGRFieldType eType)686 static bool IsArrayType( OGRFieldType eType )
687 {
688     return eType == OFTIntegerList ||
689            eType == OFTInteger64List ||
690            eType == OFTRealList ||
691            eType == OFTStringList;
692 }
693 
694 /************************************************************************/
695 /*                                SetField()                            */
696 /************************************************************************/
697 
SetField(OGRFeature * poFeature,OGRGMLASLayer * poLayer,int nAttrIdx,const CPLString & osAttrValue)698 void GMLASReader::SetField( OGRFeature* poFeature,
699                             OGRGMLASLayer* poLayer,
700                             int nAttrIdx,
701                             const CPLString& osAttrValue )
702 {
703     const OGRFieldType eType(poFeature->GetFieldDefnRef(nAttrIdx)->GetType());
704     if( osAttrValue.empty() )
705     {
706         if( eType == OFTString &&
707             !poFeature->GetFieldDefnRef(nAttrIdx)->IsNullable() )
708         {
709             poFeature->SetField( nAttrIdx, "" );
710         }
711     }
712     else if( eType == OFTDate || eType == OFTDateTime )
713     {
714         OGRField sField;
715         if( OGRParseXMLDateTime(
716                 (m_bInitialPass) ? "1970-01-01T00:00:00" : osAttrValue.c_str(),
717                 &sField ) )
718         {
719             poFeature->SetField( nAttrIdx, &sField );
720         }
721     }
722     // Transform boolean values to something that OGR understands
723     else if( eType == OFTInteger &&
724              poFeature->GetFieldDefnRef(nAttrIdx)->GetSubType() == OFSTBoolean )
725     {
726         if( osAttrValue == "true" )
727             poFeature->SetField( nAttrIdx, TRUE );
728         else
729             poFeature->SetField( nAttrIdx, FALSE );
730     }
731     else if( eType == OFTBinary )
732     {
733         const int nFCFieldIdx =
734             poLayer->GetFCFieldIndexFromOGRFieldIdx(nAttrIdx);
735         if( nFCFieldIdx >= 0 )
736         {
737             const GMLASField& oField(
738                 poLayer->GetFeatureClass().GetFields()[nFCFieldIdx]);
739             if( m_bInitialPass )
740             {
741                 poFeature->SetField( nAttrIdx, 1, (GByte*)("X") );
742             }
743             else if( oField.GetType() == GMLAS_FT_BASE64BINARY )
744             {
745                 GByte* pabyBuffer = reinterpret_cast<GByte*>(
746                                                     CPLStrdup(osAttrValue));
747                 int nBytes = CPLBase64DecodeInPlace(pabyBuffer);
748                 poFeature->SetField( nAttrIdx, nBytes, pabyBuffer );
749                 CPLFree(pabyBuffer);
750             }
751             else
752             {
753                 int nBytes = 0;
754                 GByte* pabyBuffer = CPLHexToBinary( osAttrValue, &nBytes );
755                 poFeature->SetField( nAttrIdx, nBytes, pabyBuffer );
756                 CPLFree(pabyBuffer);
757             }
758         }
759     }
760     else if( IsArrayType(eType) )
761     {
762         const int nFCFieldIdx =
763             poLayer->GetFCFieldIndexFromOGRFieldIdx(nAttrIdx);
764         if( nFCFieldIdx >= 0 &&
765             poLayer->GetFeatureClass().GetFields()[nFCFieldIdx].IsList() )
766         {
767             char** papszTokens = CSLTokenizeString2( osAttrValue.c_str(), " ", 0 );
768             if( eType == OFTIntegerList &&
769                 poFeature->GetFieldDefnRef(nAttrIdx)->GetSubType() == OFSTBoolean )
770             {
771                 for( char** papszIter = papszTokens; *papszIter != nullptr; ++papszIter )
772                 {
773                     if( strcmp(*papszIter, "true") == 0 )
774                     {
775                         (*papszIter)[0] = '1';
776                         (*papszIter)[1] = '\0';
777                     }
778                     else if( strcmp(*papszIter, "false") == 0 )
779                     {
780                         (*papszIter)[0] = '0';
781                         (*papszIter)[1] = '\0';
782                     }
783                 }
784             }
785             poFeature->SetField( nAttrIdx, papszTokens );
786             CSLDestroy(papszTokens);
787         }
788         else
789         {
790             poFeature->SetField( nAttrIdx, osAttrValue.c_str() );
791         }
792     }
793     else
794     {
795         poFeature->SetField( nAttrIdx, osAttrValue.c_str() );
796     }
797 }
798 
799 /************************************************************************/
800 /*                          PushFeatureReady()                          */
801 /************************************************************************/
802 
PushFeatureReady(OGRFeature * poFeature,OGRGMLASLayer * poLayer)803 void GMLASReader::PushFeatureReady( OGRFeature* poFeature,
804                                     OGRGMLASLayer* poLayer )
805 {
806 #ifdef DEBUG_VERBOSE
807     CPLDebug("GMLAS", "PushFeatureReady(%p / %s / %s)",
808              poFeature, poFeature->GetDefnRef()->GetName(), poLayer->GetName());
809 #endif
810 
811     m_aoFeaturesReady.push_back(
812         std::pair<OGRFeature*, OGRGMLASLayer*>(poFeature, poLayer) );
813 }
814 
815 /************************************************************************/
816 /*                          CreateNewFeature                            */
817 /************************************************************************/
818 
CreateNewFeature(const CPLString & osLocalname)819 void GMLASReader::CreateNewFeature(const CPLString& osLocalname)
820 {
821     m_oCurCtxt.m_poFeature = new OGRFeature(
822                 m_oCurCtxt.m_poLayer->GetLayerDefn() );
823 #ifdef DEBUG_VERBOSE
824     CPLDebug("GMLAS", "CreateNewFeature(element=%s / layer=%s) = %p",
825              osLocalname.c_str(), m_oCurCtxt.m_poLayer->GetName(),
826              m_oCurCtxt.m_poFeature);
827 #endif
828     // Assign FID (1, ...). Only for OGR compliance, but definitely
829     // not a unique ID among datasets with the same schema
830     ++m_oMapGlobalCounter[m_oCurCtxt.m_poLayer];
831     const int nGlobalCounter =
832                     m_oMapGlobalCounter[m_oCurCtxt.m_poLayer];
833     m_oCurCtxt.m_poFeature->SetFID(nGlobalCounter);
834 
835     // Find parent ID
836     CPLString osParentId;
837     if( !m_aoStackContext.empty() &&
838         m_oCurCtxt.m_poLayer->GetParentIDFieldIdx() >= 0 )
839     {
840         CPLAssert(m_aoStackContext.back().
841                             m_poLayer->GetIDFieldIdx() >= 0 );
842         osParentId = m_aoStackContext.back().m_poFeature->
843             GetFieldAsString(
844             m_aoStackContext.back().m_poLayer->GetIDFieldIdx() );
845         m_oCurCtxt.m_poFeature->SetField(
846             m_oCurCtxt.m_poLayer->GetParentIDFieldIdx(),
847             osParentId.c_str() );
848     }
849 
850     // Should we generate a unique (child) ID from the parent ID ?
851     if( m_oCurCtxt.m_poLayer->IsGeneratedIDField() )
852     {
853         // Local IDs (ie related to a parent feature are fine, but when
854         // we might have cycles, that doesn't work anymore
855         /*
856         ++m_oCurCtxt.m_oMapCounter[m_oCurCtxt.m_poLayer];
857         const int nCounter =
858             m_oCurCtxt.m_oMapCounter[m_oCurCtxt.m_poLayer];*/
859         const int nCounter = nGlobalCounter;
860 
861         CPLString osGeneratedID = (osParentId.empty() ? m_osHash : osParentId) +
862                                    "_" + osLocalname +
863                                    CPLSPrintf("_%d", nCounter);
864         m_oCurCtxt.m_poFeature->SetField(
865                         m_oCurCtxt.m_poLayer->GetIDFieldIdx(),
866                         osGeneratedID.c_str() );
867     }
868 
869     m_nCurFieldIdx = -1;
870 }
871 
872 /************************************************************************/
873 /*                         AttachAsLastChild()                          */
874 /************************************************************************/
875 
876 /* Attach element as the last child of its parent */
AttachAsLastChild(CPLXMLNode * psNode)877 void GMLASReader::AttachAsLastChild(CPLXMLNode* psNode)
878 {
879     NodeLastChild& sNodeLastChild = m_apsXMLNodeStack.back();
880     CPLXMLNode* psLastChildParent = sNodeLastChild.psLastChild;
881 
882     if (psLastChildParent == nullptr)
883     {
884         CPLAssert( sNodeLastChild.psNode );
885         sNodeLastChild.psNode->psChild = psNode;
886     }
887     else
888     {
889         psLastChildParent->psNext = psNode;
890     }
891     sNodeLastChild.psLastChild = psNode;
892 }
893 
894 /************************************************************************/
895 /*                         BuildXMLBlobStartElement()                   */
896 /************************************************************************/
897 
BuildXMLBlobStartElement(const CPLString & osXPath,const Attributes & attrs)898 void GMLASReader::BuildXMLBlobStartElement(const CPLString& osXPath,
899                                            const  Attributes& attrs)
900 {
901     if( FillTextContent() )
902     {
903         m_osTextContent += "<";
904         m_osTextContent += osXPath;
905     }
906 
907     CPLXMLNode* psNode = nullptr;
908     if( m_nCurGeomFieldIdx >= 0 || m_nSWEDataArrayLevel >= 0 ||
909         m_nSWEDataRecordLevel >= 0 )
910     {
911         psNode = CPLCreateXMLNode( nullptr, CXT_Element, osXPath );
912         if( !m_apsXMLNodeStack.empty() )
913         {
914             AttachAsLastChild(psNode);
915         }
916     }
917 
918     CPLXMLNode* psLastChild = nullptr;
919     for(unsigned int i=0; i < attrs.getLength(); i++)
920     {
921         const CPLString& osAttrNSPrefix( m_osAttrNSPrefix =
922             m_oMapURIToPrefix[ transcode( attrs.getURI(i), m_osAttrNSUri ) ] );
923         const CPLString& osAttrLocalname(
924                         transcode(attrs.getLocalName(i), m_osAttrLocalName) );
925         const CPLString& osAttrValue(
926                                 transcode(attrs.getValue(i), m_osAttrValue) );
927         CPLString& osAttrXPath( m_osAttrXPath );
928         if( !osAttrNSPrefix.empty() )
929         {
930             osAttrXPath.reserve(
931                         osAttrNSPrefix.size() + 1 + osAttrLocalname.size() );
932             osAttrXPath = osAttrNSPrefix;
933             osAttrXPath += ":";
934             osAttrXPath += osAttrLocalname;
935         }
936         else
937         {
938             osAttrXPath = osAttrLocalname;
939         }
940 
941         if( psNode != nullptr )
942         {
943             CPLXMLNode* psAttrNode = CPLCreateXMLNode( nullptr, CXT_Attribute,
944                                                        osAttrXPath );
945             CPLCreateXMLNode(psAttrNode, CXT_Text, osAttrValue);
946 
947             if( psLastChild == nullptr )
948             {
949                 psNode->psChild = psAttrNode;
950             }
951             else
952             {
953                 psLastChild->psNext = psAttrNode;
954             }
955             psLastChild = psAttrNode;
956         }
957 
958         if( FillTextContent() )
959         {
960             m_osTextContent += " ";
961             m_osTextContent += osAttrXPath;
962             m_osTextContent += "=\"";
963             char* pszEscaped = CPLEscapeString( osAttrValue.c_str(),
964                                         static_cast<int>(osAttrValue.size()),
965                                         CPLES_XML );
966             m_osTextContent += pszEscaped;
967             CPLFree(pszEscaped);
968             m_osTextContent += '"';
969         }
970     }
971     if( FillTextContent() )
972         m_osTextContent += ">";
973 
974     if( psNode != nullptr )
975     {
976         /* Push the element on the stack */
977         NodeLastChild sNewNodeLastChild;
978         sNewNodeLastChild.psNode = psNode;
979         sNewNodeLastChild.psLastChild = psLastChild;
980         m_apsXMLNodeStack.push_back(sNewNodeLastChild);
981 #ifdef DEBUG_VERBOSE
982         CPLDebug("GMLAS", "m_apsXMLNodeStack.push_back()");
983 #endif
984     }
985 
986     if( m_osTextContent.size() > m_nMaxContentSize )
987     {
988         CPLError(CE_Failure, CPLE_OutOfMemory,
989                 "Too much data in a single element");
990         m_bParsingError = true;
991     }
992 }
993 
994 /************************************************************************/
995 /*                          GetLayerByXPath()                           */
996 /************************************************************************/
997 
GetLayerByXPath(const CPLString & osXPath)998 OGRGMLASLayer* GMLASReader::GetLayerByXPath( const CPLString& osXPath )
999 {
1000     for(size_t i = 0; i < m_papoLayers->size(); i++ )
1001     {
1002         if( (*m_papoLayers)[i]->GetFeatureClass().GetXPath() == osXPath )
1003         {
1004             return (*m_papoLayers)[i];
1005         }
1006     }
1007     return nullptr;
1008 }
1009 
1010 /************************************************************************/
1011 /*                            PushContext()                             */
1012 /************************************************************************/
1013 
PushContext(const Context & oContext)1014 void GMLASReader::PushContext( const Context& oContext )
1015 {
1016     m_aoStackContext.push_back( oContext );
1017 #ifdef DEBUG_VERBOSE
1018     CPLDebug("GMLAS", "Pushing new context:");
1019     oContext.Dump();
1020 #endif
1021 }
1022 
1023 /************************************************************************/
1024 /*                            PopContext()                              */
1025 /************************************************************************/
1026 
PopContext()1027 void GMLASReader::PopContext()
1028 {
1029 #ifdef DEBUG_VERBOSE
1030     if( !m_aoStackContext.empty() )
1031     {
1032         CPLDebug("GMLAS", "Popping up context:");
1033         m_aoStackContext.back().Dump();
1034     }
1035 #endif
1036     m_aoStackContext.pop_back();
1037 #ifdef DEBUG_VERBOSE
1038     if( !m_aoStackContext.empty() )
1039     {
1040         CPLDebug("GMLAS", "New top of stack is:");
1041         m_aoStackContext.back().Dump();
1042     }
1043 #endif
1044 }
1045 
1046 /************************************************************************/
1047 /*                             startElement()                           */
1048 /************************************************************************/
1049 
1050 /* <xs:group ref="somegroup" maxOccurs="unbounded"/> are particularly hard to
1051    deal with since we cannot easily know when the corresponding subfeature
1052    is exactly terminated.
1053 
1054    Let's consider:
1055 
1056         <xs:group name="somegroup">
1057             <xs:choice>
1058                 <xs:element name="first_elt_of_group" type="xs:string"/>
1059                 <xs:element name="second_elt_of_group" type="xs:string"/>
1060             </xs:choice>
1061         </xs:group>
1062 
1063         <xs:group name="another_group">
1064             <xs:choice>
1065                 <xs:element name="first_elt_of_another_group" type="xs:string"/>
1066             </xs:choice>
1067         </xs:group>
1068 
1069    There are different cases :
1070     *
1071               <first_elt_of_group>...</first_elt_of_group>
1072               <second_elt_of_group>...</first_elt_of_group>
1073               <first_elt_of_group>  <!-- we are here at startElement() -->
1074                 ...
1075               </first_elt_of_group>
1076 
1077     *
1078               <first_elt_of_group>...</first_elt_of_group>
1079               <first_elt_of_group>  <!-- we are here at startElement() -->
1080                 ...</first_elt_of_group>
1081 
1082     *
1083               <first_elt_of_group>...</first_elt_of_group>
1084               <first_elt_of_another_group>  <!-- we are here at startElement() -->
1085                 ...</first_elt_of_another_group>
1086 
1087     *
1088               <first_elt_of_group>...</first_elt_of_group>
1089               <some_other_elt>  <!-- we are here at startElement() -->
1090                 ...</some_other_elt>
1091 
1092     *
1093             <first_elt>...</first_elt>
1094             <second_elt><sub>...</sub></second_elt>
1095             <first_elt> <-- here -->
1096                 ...</first_elt>
1097     *
1098                 <first_elt_of_group>...</first_elt_of_group>
1099             </end_of_enclosing_element>   <!-- we are here at endElement() -->
1100 */
startElement(const XMLCh * const uri,const XMLCh * const localname,const XMLCh * const qname,const Attributes & attrs)1101 void GMLASReader::startElement(
1102             const   XMLCh* const    uri,
1103             const   XMLCh* const    localname,
1104             const   XMLCh* const
1105 #ifdef DEBUG_VERBOSE
1106                                     qname
1107 #endif
1108             , const   Attributes& attrs
1109         )
1110 {
1111     const CPLString& osLocalname( transcode(localname, m_osLocalname) );
1112     const CPLString& osNSURI( transcode(uri, m_osNSUri) );
1113     const CPLString& osNSPrefix( m_osNSPrefix = m_oMapURIToPrefix[osNSURI] );
1114     if( osNSPrefix.empty() )
1115         m_osXPath = osLocalname;
1116     else
1117     {
1118         m_osXPath.reserve( osNSPrefix.size() + 1 + osLocalname.size() );
1119         m_osXPath = osNSPrefix;
1120         m_osXPath += ":";
1121         m_osXPath += osLocalname;
1122     }
1123     const CPLString& osXPath( m_osXPath );
1124 #ifdef DEBUG_VERBOSE
1125     CPLDebug("GMLAS", "startElement(%s / %s)",
1126              transcode(qname).c_str(), osXPath.c_str());
1127 #endif
1128     m_anStackXPathLength.push_back(osXPath.size());
1129     if( !m_osCurXPath.empty() )
1130         m_osCurXPath += "/";
1131     m_osCurXPath += osXPath;
1132 
1133 #if 0
1134     CPLString osSubXPathBefore(m_osCurSubXPath);
1135 #endif
1136     if( !m_osCurSubXPath.empty() )
1137     {
1138         m_osCurSubXPath += "/";
1139         m_osCurSubXPath += osXPath;
1140     }
1141 
1142     if( m_bProcessSWEDataArray && m_nSWEDataArrayLevel < 0 &&
1143         m_nSWEDataRecordLevel < 0 && m_nCurGeomFieldIdx < 0 )
1144     {
1145         if( osNSURI == szSWE_URI &&
1146             (osLocalname == "DataArray" || osLocalname == "DataStream") )
1147         {
1148             if( m_nCurFieldIdx >= 0 )
1149             {
1150                 m_osSWEDataArrayParentField =
1151                     m_oCurCtxt.m_poFeature->
1152                                 GetFieldDefnRef(m_nCurFieldIdx)->GetNameRef();
1153             }
1154             else
1155             {
1156                 m_osSWEDataArrayParentField.clear();
1157             }
1158             m_nSWEDataArrayLevel = m_nLevel;
1159         }
1160     }
1161 
1162     // Deal with XML content
1163     if( m_bIsXMLBlob || m_nSWEDataArrayLevel >= 0 ||
1164         m_nSWEDataRecordLevel >= 0 )
1165     {
1166         BuildXMLBlobStartElement(osXPath, attrs);
1167     }
1168 
1169     if( m_bIsXMLBlob )
1170     {
1171         m_nLevel ++;
1172         return;
1173     }
1174 
1175     if( m_nLevel == m_nMaxLevel )
1176     {
1177         CPLError(CE_Failure, CPLE_AppDefined, "Too deeply nested XML content");
1178         m_bParsingError = true;
1179         return;
1180     }
1181 
1182     CPLAssert(m_aoFeaturesReady.empty());
1183 
1184     // Look which layer might match the current XPath
1185     for(size_t i = 0; i < m_papoLayers->size(); i++ )
1186     {
1187         const CPLString* posLayerXPath =
1188             &((*m_papoLayers)[i]->GetFeatureClass().GetXPath());
1189         if( (*m_papoLayers)[i]->GetFeatureClass().IsRepeatedSequence() )
1190         {
1191             size_t iPosExtra = posLayerXPath->find(szEXTRA_SUFFIX);
1192             if (iPosExtra != std::string::npos)
1193             {
1194                 m_osLayerXPath = *posLayerXPath;
1195                 m_osLayerXPath.resize(iPosExtra);
1196                 posLayerXPath = &m_osLayerXPath;
1197             }
1198         }
1199 
1200         const bool bIsGroup = (*m_papoLayers)[i]->GetFeatureClass().IsGroup();
1201 
1202         // Are we entering or staying in a group ?
1203         const bool bIsMatchingGroup =
1204             (bIsGroup &&
1205              (*m_papoLayers)[i]->GetOGRFieldIndexFromXPath(m_osCurSubXPath) != -1 );
1206 
1207         const bool bIsMatchingRepeatedSequence =
1208              ((*m_papoLayers)[i]->GetFeatureClass().IsRepeatedSequence()  &&
1209              m_oCurCtxt.m_poLayer != nullptr &&
1210              m_oCurCtxt.m_poLayer != (*m_papoLayers)[i] &&
1211              m_oCurCtxt.m_poLayer->GetFeatureClass().GetXPath() ==
1212                     *posLayerXPath &&
1213              (*m_papoLayers)[i]->GetOGRFieldIndexFromXPath(m_osCurSubXPath) >= 0);
1214 
1215         int nTmpIdx;
1216         if( // Case where we haven't yet entered the top-level element, which may
1217             // be in container elements
1218             (m_osCurSubXPath.empty() &&
1219              *posLayerXPath == osXPath && !bIsGroup) ||
1220 
1221             // Case where we are a sub-element of a top-level feature
1222             (!m_osCurSubXPath.empty() &&
1223              *posLayerXPath == m_osCurSubXPath && !bIsGroup) ||
1224 
1225             // Case where we are a sub-element of a (repeated) group of a
1226             // top-level feature
1227             bIsMatchingGroup ||
1228 
1229             // Needed to handle sequence_1_unbounded_non_simplifiable.subelement case of data/gmlas_test1.xml
1230             bIsMatchingRepeatedSequence ||
1231 
1232             // Case where we go back from a sub-element of a (repeated) group
1233             // of a top-level feature to a regular sub-element of that top-level
1234             // feature
1235             (m_oCurCtxt.m_poGroupLayer != nullptr &&
1236              ((nTmpIdx = (*m_papoLayers)[i]->GetOGRFieldIndexFromXPath(m_osCurSubXPath)) >= 0 ||
1237               nTmpIdx == IDX_COMPOUND_FOLDED)) )
1238         {
1239 #ifdef DEBUG_VERBOSE
1240             CPLDebug("GMLAS", "Matches layer %s (%s)",
1241                      (*m_papoLayers)[i]->GetName(),
1242                      (*m_papoLayers)[i]->GetFeatureClass().GetXPath().c_str());
1243 #endif
1244 
1245             if( (*m_papoLayers)[i]->GetParent() != nullptr &&
1246                 (*m_papoLayers)[i]->GetParent()->GetFeatureClass().IsRepeatedSequence() &&
1247                 m_oCurCtxt.m_poGroupLayer != (*m_papoLayers)[i]->GetParent() )
1248             {
1249                 // Yuck! Simulate top-level element of a group if we directly jump
1250                 // into a nested class of it !
1251                 /* Something like
1252                     <xs:group name="group">
1253                         <xs:sequence>
1254                             <xs:element name="optional_elt" type="xs:string" minOccurs="0"/>
1255                             <xs:element name="elt">
1256                                 <xs:complexType>
1257                                     <xs:sequence>
1258                                         <xs:element name="subelt"  type="xs:dateTime" maxOccurs="unbounded"/>
1259                                     </xs:sequence>
1260                                 </xs:complexType>
1261                             </xs:element>
1262                         </xs:sequence>
1263                     </xs:group>
1264 
1265                     <top_element>
1266                         <elt><subelt>...</subelt></elt>
1267                     </top_element>
1268                 */
1269                 m_oCurCtxt.m_poLayer = (*m_papoLayers)[i]->GetParent();
1270                 m_oCurCtxt.m_poGroupLayer = m_oCurCtxt.m_poLayer;
1271                 m_oCurCtxt.m_nLevel = m_nLevel;
1272                 m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
1273                 CreateNewFeature( m_oCurCtxt.m_poLayer->GetName() );
1274             }
1275 
1276             bool bPushNewState = true;
1277             if( bIsMatchingGroup )
1278             {
1279                 int nFieldIdx =
1280                     (*m_papoLayers)[i]->GetOGRFieldIndexFromXPath(m_osCurSubXPath);
1281                 bool bPushNewFeature = false;
1282                 if( m_oCurCtxt.m_poGroupLayer == nullptr )
1283                 {
1284                     m_oCurCtxt.m_poFeature = nullptr;
1285                 }
1286                 else if( nFieldIdx < 0 )
1287                 {
1288                     bPushNewState = false;
1289                 }
1290                 else if ( m_oCurCtxt.m_nGroupLayerLevel == m_nLevel &&
1291                           m_oCurCtxt.m_poGroupLayer != (*m_papoLayers)[i] )
1292                 {
1293 #ifdef DEBUG_VERBOSE
1294                     CPLDebug("GMLAS", "new feature: group case 1");
1295 #endif
1296                     /* Case like:
1297                             <first_elt_of_group>...</first_elt_of_group>
1298                             <first_elt_of_another_group>  <!-- we are here at startElement() -->
1299                                 ...</first_elt_of_group>
1300                     */
1301                     bPushNewFeature = true;
1302                 }
1303                 else if( m_oCurCtxt.m_nGroupLayerLevel == m_nLevel &&
1304                          m_oCurCtxt.m_poGroupLayer == (*m_papoLayers)[i] &&
1305                          nFieldIdx == m_oCurCtxt.m_nLastFieldIdxGroupLayer &&
1306                          !IsArrayType(m_oCurCtxt.m_poFeature->
1307                                         GetFieldDefnRef(nFieldIdx)->GetType()))
1308                 {
1309 #ifdef DEBUG_VERBOSE
1310                     CPLDebug("GMLAS", "new feature: group case 2");
1311 #endif
1312                     /* Case like:
1313                         <first_elt>...</first_elt>
1314                         <first_elt> <-- here -->
1315                     */
1316                     bPushNewFeature = true;
1317                 }
1318                 else if ( m_oCurCtxt.m_nGroupLayerLevel == m_nLevel &&
1319                           nFieldIdx < m_oCurCtxt.m_nLastFieldIdxGroupLayer )
1320                 {
1321 #ifdef DEBUG_VERBOSE
1322                     CPLDebug("GMLAS", "new feature: group case nFieldIdx < m_oCurCtxt.m_nLastFieldIdxGroupLayer" );
1323 #endif
1324                     /* Case like:
1325                             <first_elt_of_group>...</first_elt_of_group>
1326                             <second_elt_of_group>...</first_elt_of_group>
1327                             <first_elt_of_group>  <!-- we are here at startElement() -->
1328                                 ...
1329                             </first_elt_of_group>
1330                     */
1331                     bPushNewFeature = true;
1332                 }
1333                 else if ( m_oCurCtxt.m_nGroupLayerLevel == m_nLevel + 1 &&
1334                           m_oCurCtxt.m_poGroupLayer == (*m_papoLayers)[i] )
1335                 {
1336 #ifdef DEBUG_VERBOSE
1337                     CPLDebug("GMLAS", "new feature: group case 3");
1338 #endif
1339                     /* Case like:
1340                         <first_elt>...</first_elt>
1341                         <second_elt><sub>...</sub></second_elt>
1342                         <first_elt> <-- here -->
1343                             ...</first_elt>
1344                     */
1345                     bPushNewFeature = true;
1346                 }
1347                 if( bPushNewFeature )
1348                 {
1349                     CPLAssert( m_oCurCtxt.m_poFeature );
1350                     CPLAssert( m_oCurCtxt.m_poGroupLayer );
1351                     //CPLDebug("GMLAS", "Feature ready");
1352                     PushFeatureReady(m_oCurCtxt.m_poFeature,
1353                                      m_oCurCtxt.m_poGroupLayer);
1354                     m_oCurCtxt.m_poFeature = nullptr;
1355                     m_nCurFieldIdx = -1;
1356                 }
1357                 m_oCurCtxt.m_poLayer = (*m_papoLayers)[i];
1358                 m_oCurCtxt.m_poGroupLayer = (*m_papoLayers)[i];
1359                 m_oCurCtxt.m_nGroupLayerLevel = m_nLevel;
1360                 if( nFieldIdx >= 0 )
1361                     m_oCurCtxt.m_nLastFieldIdxGroupLayer = nFieldIdx;
1362             }
1363             else
1364             {
1365                 if( m_oCurCtxt.m_nGroupLayerLevel == m_nLevel &&
1366                     (*m_papoLayers)[i] == m_aoStackContext.back().m_poLayer )
1367                 {
1368                     // This is the case where we switch from an element that was
1369                     // in a group to a regular element of the same level
1370 
1371                     // Push group feature as ready
1372                     CPLAssert( m_oCurCtxt.m_poFeature );
1373 
1374                     //CPLDebug("GMLAS", "Feature ready");
1375                     PushFeatureReady(m_oCurCtxt.m_poFeature,
1376                                      m_oCurCtxt.m_poGroupLayer);
1377 
1378                     // Restore "top-level" context
1379                     CPLAssert( !m_aoStackContext.empty() );
1380                     m_oCurCtxt = m_aoStackContext.back();
1381                     bPushNewState = false;
1382                 }
1383                 else
1384                 {
1385                     if( m_oCurCtxt.m_poGroupLayer )
1386                     {
1387                         Context oContext;
1388                         oContext = m_oCurCtxt;
1389                         oContext.m_nLevel = -1;
1390                         oContext.Dump();
1391                         PushContext( oContext );
1392                     }
1393 
1394                     m_oCurCtxt.m_poFeature = nullptr;
1395                     m_oCurCtxt.m_poGroupLayer = nullptr;
1396                     m_oCurCtxt.m_nGroupLayerLevel = -1;
1397                     m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
1398                     m_oCurCtxt.m_poLayer = (*m_papoLayers)[i];
1399                     if( m_aoStackContext.empty() )
1400                         m_osCurSubXPath = osXPath;
1401                 }
1402             }
1403 
1404             if( m_oCurCtxt.m_poFeature == nullptr )
1405             {
1406                 CPLAssert( bPushNewState );
1407                 CreateNewFeature(osLocalname);
1408             }
1409 
1410             if( bPushNewState )
1411             {
1412                 Context oContext;
1413                 oContext = m_oCurCtxt;
1414                 oContext.m_nLevel = m_nLevel;
1415                 PushContext( oContext );
1416                 m_oCurCtxt.m_oMapCounter.clear();
1417             }
1418             break;
1419         }
1420     }
1421 
1422     if( m_oCurCtxt.m_poLayer )
1423     {
1424 #ifdef DEBUG_VERBOSE
1425         CPLDebug("GMLAS", "Current layer: %s", m_oCurCtxt.m_poLayer->GetName() );
1426 #endif
1427 
1428         bool bHasProcessedAttributes = false;
1429 
1430         // Find if we can match this element with one of our fields
1431         int idx = m_oCurCtxt.m_poLayer->
1432                             GetOGRFieldIndexFromXPath(m_osCurSubXPath);
1433         int geom_idx = m_oCurCtxt.m_poLayer->
1434                             GetOGRGeomFieldIndexFromXPath(m_osCurSubXPath);
1435 
1436         if( idx < 0 && idx != IDX_COMPOUND_FOLDED )
1437         {
1438             /* Special case for a layer that matches everything, as found */
1439             /* in swe:extension */
1440             idx = m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
1441               m_oCurCtxt.m_poLayer->GetFeatureClass().GetXPath() + szMATCH_ALL);
1442             if( idx >= 0 &&
1443                 m_oCurCtxt.m_poLayer->GetFeatureClass().GetFields().size() > 1 )
1444             {
1445                 // But only match this wildcard field if it is the only child
1446                 // of the feature class, otherwise that is going to prevent
1447                 // matching regular fields
1448                 // Practical case  the <any processContents="lax" minOccurs="0" maxOccurs="unbounded">
1449                 // declaratin of
1450                 // http://schemas.earthresourceml.org/earthresourceml-lite/1.0/erml-lite.xsd
1451                 // http://services.ga.gov.au/earthresource/ows?service=wfs&version=2.0.0&request=GetFeature&typenames=erl:CommodityResourceView&count=10
1452                 // FIXME: currently we will thus ignore those extra content
1453                 // See ogr_gmlas_any_field_at_end_of_declaration test case
1454                 idx = -1;
1455             }
1456         }
1457         if( idx < 0 && geom_idx < 0 && geom_idx != IDX_COMPOUND_FOLDED )
1458         {
1459             /* Special case for a layer that is a made of only a geometry */
1460             geom_idx = m_oCurCtxt.m_poLayer->GetOGRGeomFieldIndexFromXPath(
1461               m_oCurCtxt.m_poLayer->GetFeatureClass().GetXPath() + szMATCH_ALL);
1462         }
1463 
1464         if( idx >= 0 || geom_idx >= 0 )
1465         {
1466             // Sanity check. Shouldn't normally happen !
1467             if( m_oCurCtxt.m_poFeature == nullptr ||
1468                 m_oCurCtxt.m_poLayer->GetLayerDefn() !=
1469                                         m_oCurCtxt.m_poFeature->GetDefnRef() )
1470             {
1471                 CPLError(CE_Failure, CPLE_AppDefined,
1472                             "Inconsistent m_poLayer / m_poFeature state");
1473                 m_bParsingError = true;
1474                 return;
1475             }
1476 
1477             bool bPushNewFeature = false;
1478             const int nFCFieldIdx = (idx >= 0) ?
1479                 m_oCurCtxt.m_poLayer->GetFCFieldIndexFromOGRFieldIdx(idx) :
1480                 m_oCurCtxt.m_poLayer->GetFCFieldIndexFromOGRGeomFieldIdx(geom_idx);
1481 
1482             /* For cases like
1483                     <xs:element name="element_compound">
1484                         <xs:complexType>
1485                             <xs:sequence maxOccurs="unbounded">
1486                                 <xs:element name="subelement1" type="xs:string"/>
1487                                 <xs:element name="subelement2" type="xs:string"/>
1488                             </xs:sequence>
1489                         </xs:complexType>
1490                     </xs:element>
1491 
1492                     <element_compound>
1493                         <subelement1>a</subelement>
1494                         <subelement2>b</subelement>
1495                         <subelement1>c</subelement>
1496                         <subelement2>d</subelement>
1497                     </element_compound>
1498             */
1499 
1500             if( idx >= 0 && idx < m_nCurFieldIdx )
1501             {
1502 #ifdef DEBUG_VERBOSE
1503                 CPLDebug("GMLAS", "new feature: idx < m_nCurFieldIdx" );
1504 #endif
1505                 bPushNewFeature = true;
1506             }
1507 
1508             /* For cases like
1509                     <xs:element name="element_compound">
1510                         <xs:complexType>
1511                             <xs:sequence maxOccurs="unbounded">
1512                                 <xs:element name="subelement" type="xs:dateTime"/>
1513                             </xs:sequence>
1514                         </xs:complexType>
1515                     </xs:element>
1516 
1517                     <element_compound>
1518                         <subelement>2012-01-01T12:34:56Z</subelement>
1519                         <subelement>2012-01-02T12:34:56Z</subelement>
1520                     </element_compound>
1521             */
1522             else if( idx >= 0 && idx == m_nCurFieldIdx &&
1523                      !IsArrayType(m_oCurCtxt.m_poFeature->
1524                                 GetFieldDefnRef(m_nCurFieldIdx)->GetType()) &&
1525                      // Make sure this isn't a repeated geometry as well
1526                      !( geom_idx >= 0 && nFCFieldIdx >= 0 &&
1527                         m_oCurCtxt.m_poLayer->GetFeatureClass().GetFields()[
1528                                         nFCFieldIdx].GetMaxOccurs() > 1 ) )
1529             {
1530                 bPushNewFeature = true;
1531             }
1532 
1533             // Make sure we are in a repeated sequence, otherwise this is
1534             // invalid XML
1535             if( bPushNewFeature &&
1536                 !m_oCurCtxt.m_poLayer->GetFeatureClass().IsRepeatedSequence() &&
1537                 // Case of element within xs:choice
1538                 !(idx >= 0 && nFCFieldIdx >= 0 &&
1539                     m_oCurCtxt.m_poLayer->GetFeatureClass().
1540                         GetFields()[nFCFieldIdx].MayAppearOutOfOrder()) )
1541             {
1542                 bPushNewFeature = false;
1543                 CPLError(CE_Warning, CPLE_AppDefined,
1544                             "Unexpected element %s",
1545                             m_osCurSubXPath.c_str());
1546             }
1547 
1548             if( bPushNewFeature )
1549             {
1550                 //CPLDebug("GMLAS", "Feature ready");
1551                 PushFeatureReady(m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer);
1552                 Context oContext = m_aoStackContext.back();
1553                 m_aoStackContext.pop_back();
1554                 CreateNewFeature(osLocalname);
1555                 oContext.m_poFeature = m_oCurCtxt.m_poFeature;
1556                 m_aoStackContext.push_back( oContext );
1557                 m_oCurCtxt.m_oMapCounter.clear();
1558             }
1559 
1560             if( m_nCurFieldIdx != idx )
1561             {
1562                 m_osTextContentList.Clear();
1563                 m_nTextContentListEstimatedSize = 0;
1564             }
1565             m_nCurFieldIdx = idx;
1566             m_nCurGeomFieldIdx = geom_idx;
1567             m_nCurFieldLevel = m_nLevel + 1;
1568             m_osTextContent.clear();
1569             m_bIsXMLBlob = false;
1570             m_bIsXMLBlobIncludeUpper = false;
1571 
1572 #ifdef DEBUG_VERBOSE
1573             if( idx >= 0 )
1574             {
1575                 CPLDebug("GMLAS", "Matches field %s", m_oCurCtxt.m_poLayer->
1576                          GetLayerDefn()->GetFieldDefn(idx)->GetNameRef() );
1577             }
1578             if( geom_idx >= 0 )
1579             {
1580                 CPLDebug("GMLAS", "Matches geometry field %s", m_oCurCtxt.m_poLayer->
1581                          GetLayerDefn()->GetGeomFieldDefn(geom_idx)->GetNameRef() );
1582             }
1583 #endif
1584             if( nFCFieldIdx >= 0 )
1585             {
1586                 const GMLASField& oField(
1587                     m_oCurCtxt.m_poLayer->GetFeatureClass().GetFields()[
1588                                                                 nFCFieldIdx]);
1589                 if( m_nSWEDataArrayLevel < 0 && m_nSWEDataRecordLevel < 0 )
1590                 {
1591                     m_bIsXMLBlob = (oField.GetType() == GMLAS_FT_ANYTYPE ||
1592                                     m_nCurGeomFieldIdx != -1 );
1593                 }
1594                 m_bIsXMLBlobIncludeUpper = m_bIsXMLBlob &&
1595                                             oField.GetIncludeThisEltInBlob();
1596                 if( m_bIsXMLBlobIncludeUpper )
1597                 {
1598                     BuildXMLBlobStartElement(osXPath, attrs);
1599                     m_nLevel ++;
1600                     return;
1601                 }
1602 
1603                 // Figure out if it is an element that calls for a related
1604                 // top-level feature (but without junction table)
1605                 if( oField.GetCategory() ==
1606                                 GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK )
1607                 {
1608                     const CPLString& osNestedXPath(oField.GetRelatedClassXPath());
1609                     CPLAssert( !osNestedXPath.empty() );
1610                     OGRGMLASLayer* poSubLayer = GetLayerByXPath(osNestedXPath);
1611                     if( poSubLayer && m_nCurFieldIdx >= 0 )
1612                     {
1613                         int nOldCurFieldIdx = m_nCurFieldIdx;
1614                         OGRFeature* poOldCurFeature = m_oCurCtxt.m_poFeature;
1615                         OGRGMLASLayer* poOldLayer = m_oCurCtxt.m_poLayer;
1616                         m_oCurCtxt.m_poLayer = poSubLayer;
1617                         CreateNewFeature(osLocalname);
1618 
1619                         m_oCurCtxt.m_poGroupLayer = nullptr;
1620                         m_oCurCtxt.m_nGroupLayerLevel = -1;
1621                         m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
1622 
1623                         // Install new context
1624                         Context oContext;
1625                         oContext = m_oCurCtxt;
1626                         oContext.m_nLevel = m_nLevel;
1627                         oContext.m_osCurSubXPath = m_osCurSubXPath;
1628                         m_osCurSubXPath = osNestedXPath;
1629 #ifdef DEBUG_VERBOSE
1630                         CPLDebug("GMLAS",
1631                                  "Installing new m_osCurSubXPath from %s to %s",
1632                                  oContext.m_osCurSubXPath.c_str(),
1633                                  m_osCurSubXPath.c_str());
1634 #endif
1635                         PushContext( oContext );
1636                         m_oCurCtxt.m_oMapCounter.clear();
1637 
1638                         // Process attributes now because we might need to
1639                         // fetch the child id from them
1640                         ProcessAttributes(attrs);
1641                         bHasProcessedAttributes = true;
1642 
1643                         CPLString osChildId(
1644                             m_oCurCtxt.m_poFeature->GetFieldAsString(
1645                                     m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
1646                         SetField( poOldCurFeature,
1647                                   poOldLayer,
1648                                   nOldCurFieldIdx,
1649                                   osChildId );
1650 
1651                         if( m_bProcessSWEDataRecord && !m_bIsXMLBlob &&
1652                             m_nSWEDataArrayLevel < 0 &&
1653                             m_nSWEDataRecordLevel < 0 &&
1654                             osNestedXPath == "swe:DataRecord" )
1655                         {
1656                             m_nSWEDataRecordLevel = m_nLevel;
1657                             BuildXMLBlobStartElement(osXPath, attrs);
1658                         }
1659                     }
1660                 }
1661             }
1662         }
1663 
1664 #if 0
1665         // Case where we have an abstract type and don't know its realizations
1666         else if ( idx != IDX_COMPOUND_FOLDED &&
1667             (idx = m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
1668                                     osSubXPathBefore + "/" + "*")) >= 0 &&
1669             m_oCurCtxt.m_poGroupLayer == NULL )
1670         {
1671             m_nCurFieldIdx = idx;
1672             m_nCurFieldLevel = m_nLevel + 1;
1673             m_osTextContent.clear();
1674             m_bIsXMLBlob = true;
1675             m_bIsXMLBlobIncludeUpper = true;
1676             BuildXMLBlobStartElement(osNSPrefix, osLocalname, attrs);
1677             m_nLevel ++;
1678             return;
1679         }
1680 #endif
1681 
1682         else if( m_nLevel > m_aoStackContext.back().m_nLevel )
1683         {
1684             // Figure out if it is an element that calls from a related
1685             // top-level feature with a junction table
1686             const std::vector<GMLASField>& aoFields =
1687                     m_oCurCtxt.m_poLayer->GetFeatureClass().GetFields();
1688             for( size_t i = 0; i < aoFields.size(); ++i )
1689             {
1690                 if( aoFields[i].GetCategory() ==
1691                         GMLASField::PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE &&
1692                     aoFields[i].GetXPath() == m_osCurSubXPath )
1693                 {
1694                     const CPLString& osAbstractElementXPath(
1695                                         aoFields[i].GetAbstractElementXPath());
1696                     const CPLString& osNestedXPath(
1697                                         aoFields[i].GetRelatedClassXPath());
1698                     CPLAssert( !osAbstractElementXPath.empty() );
1699                     CPLAssert( !osNestedXPath.empty() );
1700 
1701                     OGRGMLASLayer* poJunctionLayer = GetLayerByXPath(
1702                         GMLASSchemaAnalyzer::BuildJunctionTableXPath(
1703                             osAbstractElementXPath, osNestedXPath));
1704                     OGRGMLASLayer* poSubLayer = GetLayerByXPath(osNestedXPath);
1705 
1706                     if( poSubLayer && poJunctionLayer )
1707                     {
1708                         CPLString osParentId(
1709                             m_oCurCtxt.m_poFeature->GetFieldAsString(
1710                                     m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
1711 
1712                         // Create child feature
1713                         m_oCurCtxt.m_poLayer = poSubLayer;
1714                         CreateNewFeature(osLocalname);
1715 
1716                         ++m_oMapGlobalCounter[poJunctionLayer];
1717                         const int nGlobalCounter =
1718                                         m_oMapGlobalCounter[poJunctionLayer];
1719 
1720                         ++m_oCurCtxt.m_oMapCounter[poJunctionLayer];
1721                         const int nCounter =
1722                             m_oCurCtxt.m_oMapCounter[poJunctionLayer];
1723 
1724                         m_oCurCtxt.m_poGroupLayer = nullptr;
1725                         m_oCurCtxt.m_nGroupLayerLevel = -1;
1726                         m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
1727 
1728                         // Install new context
1729                         Context oContext;
1730                         oContext = m_oCurCtxt;
1731                         oContext.m_nLevel = m_nLevel;
1732                         oContext.m_osCurSubXPath = m_osCurSubXPath;
1733                         m_osCurSubXPath = osNestedXPath;
1734 #ifdef DEBUG_VERBOSE
1735                         CPLDebug("GMLAS",
1736                                  "Installing new m_osCurSubXPath from %s to %s",
1737                                  oContext.m_osCurSubXPath.c_str(),
1738                                  m_osCurSubXPath.c_str());
1739 #endif
1740                         PushContext( oContext );
1741                         m_oCurCtxt.m_oMapCounter.clear();
1742 
1743                         // Process attributes now because we might need to
1744                         // fetch the child id from them
1745                         ProcessAttributes(attrs);
1746                         bHasProcessedAttributes = true;
1747 
1748                         CPLString osChildId(
1749                             m_oCurCtxt.m_poFeature->GetFieldAsString(
1750                                     m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
1751 
1752                         // Create junction feature
1753                         OGRFeature* poJunctionFeature =
1754                                 new OGRFeature(poJunctionLayer->GetLayerDefn());
1755                         poJunctionFeature->SetFID(nGlobalCounter);
1756                         poJunctionFeature->SetField(szOCCURRENCE, nCounter);
1757                         poJunctionFeature->SetField(szPARENT_PKID, osParentId);
1758                         poJunctionFeature->SetField(szCHILD_PKID, osChildId);
1759                         PushFeatureReady(poJunctionFeature, poJunctionLayer);
1760                     }
1761                     idx = IDX_COMPOUND_FOLDED;
1762 
1763                     break;
1764                 }
1765             }
1766 
1767             m_nCurFieldIdx = -1;
1768             m_nCurGeomFieldIdx = -1;
1769             if( idx != IDX_COMPOUND_FOLDED && m_nLevelSilentIgnoredXPath < 0 &&
1770 
1771                 // Detect if we are in a situation where elements like
1772                 // <foo xsi:nil="true"/> have no corresponding OGR field
1773                 // because of the use of remove_unused_fields=true
1774                 !( m_oCurCtxt.m_poLayer->
1775                             GetFCFieldIndexFromXPath(m_osCurSubXPath) >= 0 &&
1776                     attrs.getLength() == 1 &&
1777                     m_oMapURIToPrefix[ transcode( attrs.getURI(0) ) ] == szXSI_PREFIX &&
1778                     transcode(attrs.getLocalName(0)) == szNIL ) )
1779             {
1780                 CPLString osMatchedXPath;
1781                 if( m_oIgnoredXPathMatcher.MatchesRefXPath(
1782                                         m_osCurSubXPath, osMatchedXPath) )
1783                 {
1784                     if( m_oMapIgnoredXPathToWarn[osMatchedXPath] )
1785                     {
1786                         CPLError(CE_Warning, CPLE_AppDefined,
1787                                 "Element with xpath=%s found in document but "
1788                                 "ignored according to configuration",
1789                                 m_osCurSubXPath.c_str());
1790                     }
1791                     else
1792                     {
1793                         CPLDebug("GMLAS",
1794                                 "Element with xpath=%s found in document but "
1795                                 "ignored according to configuration",
1796                                 m_osCurSubXPath.c_str());
1797                     }
1798                     m_nLevelSilentIgnoredXPath = m_nLevel;
1799                 }
1800                 else
1801                 {
1802                     if( m_bWarnUnexpected )
1803                     {
1804                       CPLError(CE_Warning, CPLE_AppDefined,
1805                          "Unexpected element with xpath=%s (subxpath=%s) found",
1806                           m_osCurXPath.c_str(),
1807                           m_osCurSubXPath.c_str());
1808                     }
1809                     else
1810                     {
1811                        CPLDebug("GMLAS",
1812                          "Unexpected element with xpath=%s (subxpath=%s) found",
1813                           m_osCurXPath.c_str(),
1814                           m_osCurSubXPath.c_str());
1815                     }
1816                 }
1817             }
1818         }
1819         else
1820         {
1821             m_nCurFieldIdx = -1;
1822             m_nCurGeomFieldIdx = -1;
1823         }
1824 
1825         if( !bHasProcessedAttributes && m_nLevelSilentIgnoredXPath < 0 )
1826             ProcessAttributes(attrs);
1827     }
1828     else
1829     {
1830         m_nCurFieldIdx = -1;
1831         m_nCurGeomFieldIdx = -1;
1832     }
1833 
1834     m_nLevel ++;
1835 }
1836 
1837 /************************************************************************/
1838 /*                          ProcessAttributes()                         */
1839 /************************************************************************/
1840 
ProcessAttributes(const Attributes & attrs)1841 void GMLASReader::ProcessAttributes(const Attributes& attrs)
1842 {
1843     // Browse through attributes and match them with one of our fields
1844     const int nWildcardAttrIdx =
1845         m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(m_osCurSubXPath + "/" +
1846                                                         szAT_ANY_ATTR);
1847     json_object* poWildcard = nullptr;
1848 
1849     for(unsigned int i=0; i < attrs.getLength(); i++)
1850     {
1851         const CPLString& osAttrNSPrefix( m_osAttrNSPrefix =
1852             m_oMapURIToPrefix[ transcode( attrs.getURI(i), m_osAttrNSUri ) ] );
1853         const CPLString& osAttrLocalname(
1854                         transcode(attrs.getLocalName(i), m_osAttrLocalName) );
1855         const CPLString& osAttrValue(
1856                                 transcode(attrs.getValue(i), m_osAttrValue) );
1857         CPLString& osAttrXPath( m_osAttrXPath );
1858         if( !osAttrNSPrefix.empty() )
1859         {
1860             osAttrXPath.reserve( m_osCurSubXPath.size() + 2 +
1861                         osAttrNSPrefix.size() + 1 + osAttrLocalname.size() );
1862             osAttrXPath = m_osCurSubXPath;
1863             osAttrXPath += "/@";
1864             osAttrXPath += osAttrNSPrefix;
1865             osAttrXPath += ":";
1866             osAttrXPath += osAttrLocalname;
1867         }
1868         else
1869         {
1870             osAttrXPath.reserve( m_osCurSubXPath.size() + 2 +
1871                                  osAttrLocalname.size() );
1872             osAttrXPath = m_osCurSubXPath;
1873             osAttrXPath += "/@";
1874             osAttrXPath += osAttrLocalname;
1875         }
1876 
1877         //CPLDebug("GMLAS", "Attr %s=%s", osAttrXPath.c_str(), osAttrValue.c_str());
1878 
1879         const int nAttrIdx = m_oCurCtxt.m_poLayer->
1880                                     GetOGRFieldIndexFromXPath(osAttrXPath);
1881         int nFCIdx;
1882         if( nAttrIdx >= 0 )
1883         {
1884             const OGRFieldType eType(
1885                 m_oCurCtxt.m_poFeature->GetFieldDefnRef(nAttrIdx)->GetType());
1886             if( osAttrValue.empty() && eType == OFTString  )
1887             {
1888                 m_oCurCtxt.m_poFeature->SetField( nAttrIdx, "" );
1889             }
1890             else
1891             {
1892                 SetField( m_oCurCtxt.m_poFeature,
1893                           m_oCurCtxt.m_poLayer,
1894                           nAttrIdx, osAttrValue );
1895             }
1896 
1897             if( osAttrNSPrefix == szXLINK_PREFIX &&
1898                 osAttrLocalname == szHREF &&
1899                 !osAttrValue.empty() )
1900             {
1901                 ProcessXLinkHref( nAttrIdx, osAttrXPath, osAttrValue );
1902             }
1903 
1904             if( m_oXLinkResolver.GetConf().m_bResolveInternalXLinks &&
1905                 m_bInitialPass )
1906             {
1907                 nFCIdx = m_oCurCtxt.m_poLayer->
1908                         GetFCFieldIndexFromXPath(osAttrXPath);
1909                 if( nFCIdx >= 0 &&
1910                     m_oCurCtxt.m_poLayer->GetFeatureClass().
1911                         GetFields()[nFCIdx].GetType() == GMLAS_FT_ID )
1912                 {
1913                     // We don't check that there's no existing id in the map
1914                     // This is normally forbidden by the xs:ID rules
1915                     // If not respected by the document, this should not lead to
1916                     // crashes
1917                     m_oMapElementIdToLayer[ osAttrValue ] = m_oCurCtxt.m_poLayer;
1918 
1919                     if( m_oCurCtxt.m_poLayer->IsGeneratedIDField() )
1920                     {
1921                         CPLString osFeaturePKID(
1922                             m_oCurCtxt.m_poFeature->GetFieldAsString(
1923                                 m_oCurCtxt.m_poLayer->GetIDFieldIdx() ));
1924                         m_oMapElementIdToPKID[ osAttrValue ] = osFeaturePKID;
1925                     }
1926                 }
1927             }
1928         }
1929 
1930         else if( osAttrNSPrefix == szXSI_PREFIX &&
1931                  osAttrLocalname == szNIL )
1932         {
1933             if( osAttrValue == "true" )
1934             {
1935                 const int nMainAttrIdx = m_oCurCtxt.m_poLayer->
1936                                         GetOGRFieldIndexFromXPath(m_osCurSubXPath);
1937                 if( nMainAttrIdx >= 0 )
1938                 {
1939                     m_oCurCtxt.m_poFeature->SetFieldNull( nMainAttrIdx );
1940                 }
1941                 else
1942                 {
1943                     const int nHrefAttrIdx = m_oCurCtxt.m_poLayer->
1944                             GetOGRFieldIndexFromXPath(m_osCurSubXPath +
1945                                         "/@" + szXLINK_PREFIX + ":" + szHREF);
1946                     if( nHrefAttrIdx >= 0 )
1947                     {
1948                         m_oCurCtxt.m_poFeature->SetFieldNull( nHrefAttrIdx );
1949                     }
1950                 }
1951             }
1952         }
1953 
1954         else if( osAttrNSPrefix != szXMLNS_PREFIX &&
1955                  osAttrLocalname != szXMLNS_PREFIX &&
1956                     !(osAttrNSPrefix == szXSI_PREFIX &&
1957                         osAttrLocalname == szSCHEMA_LOCATION) &&
1958                     !(osAttrNSPrefix == szXSI_PREFIX &&
1959                         osAttrLocalname == szNO_NAMESPACE_SCHEMA_LOCATION) &&
1960                     // Do not warn about fixed attributes on geometry properties
1961                     !(m_nCurGeomFieldIdx >= 0 && (
1962                     (osAttrNSPrefix == szXLINK_PREFIX &&
1963                      osAttrLocalname == szTYPE) ||
1964                     (osAttrNSPrefix == "" && osAttrLocalname == szOWNS))) )
1965         {
1966             CPLString osMatchedXPath;
1967             if( nWildcardAttrIdx >= 0 )
1968             {
1969                 if( poWildcard == nullptr )
1970                     poWildcard = json_object_new_object();
1971                 CPLString osKey;
1972                 if( !osAttrNSPrefix.empty() )
1973                     osKey = osAttrNSPrefix + ":" + osAttrLocalname;
1974                 else
1975                     osKey = osAttrLocalname;
1976                 json_object_object_add(poWildcard,
1977                     osKey,
1978                     json_object_new_string(osAttrValue));
1979             }
1980             else if( m_bValidate &&
1981                      (nFCIdx = m_oCurCtxt.m_poLayer->
1982                         GetFCFieldIndexFromXPath(osAttrXPath)) >= 0 &&
1983                      !m_oCurCtxt.m_poLayer->GetFeatureClass().
1984                         GetFields()[nFCIdx].GetFixedValue().empty() )
1985             {
1986                 // In validation mode, fixed attributes not present in the
1987                 // document are still reported, which cause spurious warnings
1988             }
1989             else if( m_bValidate &&
1990                      (nFCIdx = m_oCurCtxt.m_poLayer->
1991                         GetFCFieldIndexFromXPath(osAttrXPath)) >= 0 &&
1992                      !m_oCurCtxt.m_poLayer->GetFeatureClass().
1993                         GetFields()[nFCIdx].GetDefaultValue().empty() &&
1994                      m_oCurCtxt.m_poLayer->GetFeatureClass().
1995                         GetFields()[nFCIdx].GetDefaultValue() == m_osAttrValue )
1996             {
1997                 // In validation mode, default attributes not present in the
1998                 // document are still reported, which cause spurious warnings
1999             }
2000             else if( m_oIgnoredXPathMatcher.MatchesRefXPath(
2001                                         osAttrXPath, osMatchedXPath) )
2002             {
2003                 if( m_oMapIgnoredXPathToWarn[osMatchedXPath] )
2004                 {
2005                     CPLError(CE_Warning, CPLE_AppDefined,
2006                             "Attribute with xpath=%s found in document but "
2007                             "ignored according to configuration",
2008                             osAttrXPath.c_str());
2009                 }
2010                 else
2011                 {
2012                     CPLDebug("GMLAS",
2013                             "Attribute with xpath=%s found in document but "
2014                             "ignored according to configuration",
2015                             osAttrXPath.c_str());
2016                 }
2017             }
2018             else
2019             {
2020                 if( m_bWarnUnexpected )
2021                 {
2022                     CPLError(CE_Warning, CPLE_AppDefined,
2023                              "Unexpected attribute with xpath=%s found",
2024                              osAttrXPath.c_str());
2025                 }
2026                 else
2027                 {
2028                     // Emit debug message if unexpected attribute
2029                     CPLDebug("GMLAS",
2030                                 "Unexpected attribute with xpath=%s found",
2031                             osAttrXPath.c_str());
2032                 }
2033             }
2034         }
2035     }
2036 
2037     // Store wildcard attributes
2038     if( poWildcard != nullptr )
2039     {
2040         SetField( m_oCurCtxt.m_poFeature,
2041                     m_oCurCtxt.m_poLayer,
2042                     nWildcardAttrIdx,
2043                     json_object_get_string(poWildcard) );
2044         json_object_put(poWildcard);
2045     }
2046 
2047     // Process fixed and default values, except when doing the initial scan
2048     // so as to avoid the bRemoveUnusedFields logic to be confused
2049     if( !m_bInitialPass )
2050     {
2051         const int nFieldCount = m_oCurCtxt.m_poFeature->GetFieldCount();
2052         const std::vector<GMLASField>& aoFields =
2053                         m_oCurCtxt.m_poLayer->GetFeatureClass().GetFields();
2054         for( int i=0; i < nFieldCount; i++ )
2055         {
2056             const int nFCIdx =
2057                     m_oCurCtxt.m_poLayer->GetFCFieldIndexFromOGRFieldIdx(i);
2058             if( nFCIdx >= 0 &&
2059                 aoFields[nFCIdx].GetXPath().find('@') != std::string::npos )
2060             {
2061                 // We process fixed as default. In theory, to be XSD compliant,
2062                 // the user shouldn't have put a different value than the fixed
2063                 // one, but just in case he did, then honour it instead of
2064                 // overwriting it.
2065                 CPLString osFixedDefaultValue = aoFields[nFCIdx].GetFixedValue();
2066                 if( osFixedDefaultValue.empty() )
2067                     osFixedDefaultValue = aoFields[nFCIdx].GetDefaultValue();
2068                 if( !osFixedDefaultValue.empty() &&
2069                     !m_oCurCtxt.m_poFeature->IsFieldSetAndNotNull(i) )
2070                 {
2071                     SetField( m_oCurCtxt.m_poFeature,
2072                                 m_oCurCtxt.m_poLayer,
2073                                 i, osFixedDefaultValue);
2074                 }
2075             }
2076         }
2077     }
2078 }
2079 
2080 /************************************************************************/
2081 /*                           ProcessXLinkHref()                         */
2082 /************************************************************************/
2083 
ProcessXLinkHref(int nAttrIdx,const CPLString & osAttrXPath,const CPLString & osAttrValue)2084 void GMLASReader::ProcessXLinkHref( int nAttrIdx,
2085                                     const CPLString& osAttrXPath,
2086                                     const CPLString& osAttrValue )
2087 {
2088     // If we are a xlink:href attribute, and that the link value is
2089     // a internal link, then find if we have
2090     // a field that does a relation to a targetElement
2091     if( osAttrValue[0] == '#' )
2092     {
2093         const int nAttrIdx2 =
2094             m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
2095                 GMLASField::MakePKIDFieldXPathFromXLinkHrefXPath(
2096                                                     osAttrXPath));
2097         if( nAttrIdx2 >= 0 )
2098         {
2099             SetField( m_oCurCtxt.m_poFeature,
2100                         m_oCurCtxt.m_poLayer,
2101                         nAttrIdx2, osAttrValue.substr(1) );
2102         }
2103         else if( m_oXLinkResolver.GetConf().m_bResolveInternalXLinks )
2104         {
2105             const CPLString osReferringField(
2106                 m_oCurCtxt.m_poLayer->GetLayerDefn()->
2107                     GetFieldDefn(nAttrIdx)->GetNameRef());
2108             const CPLString osId(osAttrValue.substr(1));
2109             if( m_bInitialPass )
2110             {
2111                 std::pair<OGRGMLASLayer*, CPLString> oReferringPair(
2112                     m_oCurCtxt.m_poLayer, osReferringField);
2113                 m_oMapFieldXPathToLinkValue[oReferringPair].push_back(osId);
2114             }
2115             else
2116             {
2117                 const auto oIter = m_oMapElementIdToLayer.find(osId);
2118                 if( oIter != m_oMapElementIdToLayer.end() )
2119                 {
2120                     OGRGMLASLayer* poTargetLayer = oIter->second;
2121                     const CPLString osLinkFieldXPath =
2122                         m_oCurCtxt.m_poLayer->GetXPathOfFieldLinkForAttrToOtherLayer(
2123                             osReferringField,
2124                             poTargetLayer->GetFeatureClass().GetXPath());
2125                     const int nLinkFieldOGRId =
2126                         m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
2127                             osLinkFieldXPath);
2128                     if( nLinkFieldOGRId >= 0 )
2129                     {
2130                         const auto oIter2 = m_oMapElementIdToPKID.find(osId);
2131                         if( oIter2 != m_oMapElementIdToPKID.end() )
2132                         {
2133                             m_oCurCtxt.m_poFeature->SetField(nLinkFieldOGRId,
2134                                                              oIter2->second);
2135                         }
2136                         else
2137                         {
2138                             m_oCurCtxt.m_poFeature->SetField(nLinkFieldOGRId,
2139                                                              osId);
2140                         }
2141                     }
2142                 }
2143             }
2144         }
2145     }
2146     else
2147     {
2148         const int nRuleIdx =
2149                         m_oXLinkResolver.GetMatchingResolutionRule(osAttrValue);
2150         if( nRuleIdx >= 0 )
2151         {
2152             const GMLASXLinkResolutionConf::URLSpecificResolution& oRule(
2153                     m_oXLinkResolver.GetConf().m_aoURLSpecificRules[nRuleIdx] );
2154             if( m_bInitialPass )
2155             {
2156                 m_oMapXLinkFields[m_oCurCtxt.m_poLayer][osAttrXPath].insert(
2157                                                             nRuleIdx );
2158             }
2159             else if( oRule.m_eResolutionMode ==
2160                         GMLASXLinkResolutionConf::RawContent )
2161             {
2162                 const int nAttrIdx2 =
2163                   m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
2164                     GMLASField::MakeXLinkRawContentFieldXPathFromXLinkHrefXPath(
2165                                                                     osAttrXPath));
2166                 CPLAssert( nAttrIdx2 >= 0 );
2167 
2168                 const CPLString osRawContent(
2169                     m_oXLinkResolver.GetRawContentForRule(osAttrValue, nRuleIdx));
2170                 if( !osRawContent.empty() )
2171                 {
2172                     SetField( m_oCurCtxt.m_poFeature,
2173                               m_oCurCtxt.m_poLayer,
2174                               nAttrIdx2,
2175                               osRawContent );
2176                 }
2177             }
2178             else if( oRule.m_eResolutionMode ==
2179                         GMLASXLinkResolutionConf::FieldsFromXPath )
2180             {
2181                 const CPLString osRawContent(
2182                     m_oXLinkResolver.GetRawContentForRule(osAttrValue, nRuleIdx));
2183                 if( !osRawContent.empty() )
2184                 {
2185                     CPLXMLNode* psNode = CPLParseXMLString( osRawContent );
2186                     if( psNode != nullptr )
2187                     {
2188                         std::vector<CPLString> aoXPaths;
2189                         std::map<CPLString, size_t> oMapFieldXPathToIdx;
2190                         for(size_t i=0; i < oRule.m_aoFields.size(); ++i )
2191                         {
2192                             const CPLString& osXPathRule(
2193                                             oRule.m_aoFields[i].m_osXPath);
2194                             aoXPaths.push_back(osXPathRule);
2195                             oMapFieldXPathToIdx[osXPathRule] = i;
2196                         }
2197                         GMLASXPathMatcher oMatcher;
2198                         oMatcher.SetRefXPaths(std::map<CPLString, CPLString>(),
2199                                               aoXPaths);
2200                         oMatcher.SetDocumentMapURIToPrefix(std::map<CPLString, CPLString>());
2201 
2202                         CPLXMLNode* psIter = psNode;
2203                         for( ; psIter != nullptr; psIter = psIter->psNext )
2204                         {
2205                             if( psIter->eType == CXT_Element &&
2206                                 psIter->pszValue[0] != '?' )
2207                             {
2208                                 ExploreXMLDoc( osAttrXPath,
2209                                                oRule,
2210                                                psIter,
2211                                                CPLString(),
2212                                                oMatcher,
2213                                                oMapFieldXPathToIdx );
2214                             }
2215                         }
2216                     }
2217                     CPLDestroyXMLNode(psNode);
2218                 }
2219             }
2220         }
2221         else if( m_oXLinkResolver.IsRawContentResolutionEnabled() )
2222         {
2223             const int nAttrIdx2 =
2224               m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
2225                 GMLASField::MakeXLinkRawContentFieldXPathFromXLinkHrefXPath(
2226                                                                 osAttrXPath));
2227             CPLAssert( nAttrIdx2 >= 0 );
2228 
2229             const CPLString osRawContent(
2230                 m_oXLinkResolver.GetRawContent(osAttrValue));
2231             if( !osRawContent.empty() )
2232             {
2233                 SetField( m_oCurCtxt.m_poFeature,
2234                         m_oCurCtxt.m_poLayer,
2235                         nAttrIdx2,
2236                         osRawContent );
2237             }
2238         }
2239     }
2240 }
2241 
2242 /************************************************************************/
2243 /*                            ExploreXMLDoc()                           */
2244 /************************************************************************/
2245 
ExploreXMLDoc(const CPLString & osAttrXPath,const GMLASXLinkResolutionConf::URLSpecificResolution & oRule,CPLXMLNode * psNode,const CPLString & osParentXPath,const GMLASXPathMatcher & oMatcher,const std::map<CPLString,size_t> & oMapFieldXPathToIdx)2246 void GMLASReader::ExploreXMLDoc( const CPLString& osAttrXPath,
2247                                  const GMLASXLinkResolutionConf::URLSpecificResolution& oRule,
2248                                  CPLXMLNode* psNode,
2249                                  const CPLString& osParentXPath,
2250                                  const GMLASXPathMatcher& oMatcher,
2251                                  const std::map<CPLString, size_t>&
2252                                                         oMapFieldXPathToIdx )
2253 {
2254     CPLString osXPath;
2255     if( osParentXPath.empty() )
2256         osXPath = psNode->pszValue;
2257     else if( psNode->eType == CXT_Element )
2258         osXPath = osParentXPath + "/" + psNode->pszValue;
2259     else
2260     {
2261         CPLAssert( psNode->eType == CXT_Attribute );
2262         osXPath = osParentXPath + "/@" + psNode->pszValue;
2263     }
2264 
2265     CPLString osMatchedXPathRule;
2266     if( oMatcher.MatchesRefXPath(osXPath, osMatchedXPathRule) )
2267     {
2268         const auto oIter = oMapFieldXPathToIdx.find(osMatchedXPathRule);
2269         CPLAssert( oIter != oMapFieldXPathToIdx.end() );
2270         const size_t nFieldRuleIdx = oIter->second;
2271         const CPLString osDerivedFieldXPath(
2272             GMLASField::MakeXLinkDerivedFieldXPathFromXLinkHrefXPath(
2273                 osAttrXPath, oRule.m_aoFields[nFieldRuleIdx].m_osName) );
2274         const int nAttrIdx =
2275               m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(osDerivedFieldXPath);
2276         CPLAssert( nAttrIdx >= 0 );
2277         CPLString osVal;
2278         if( psNode->eType == CXT_Element &&
2279             psNode->psChild != nullptr &&
2280             psNode->psChild->eType == CXT_Text &&
2281             psNode->psChild->psNext == nullptr )
2282         {
2283             osVal = psNode->psChild->pszValue;
2284         }
2285         else if( psNode->eType == CXT_Attribute )
2286         {
2287             osVal = psNode->psChild->pszValue;
2288         }
2289         else
2290         {
2291             char* pszContent = CPLSerializeXMLTree( psNode->psChild );
2292             osVal = pszContent;
2293             CPLFree(pszContent);
2294         }
2295         if( m_oCurCtxt.m_poFeature->IsFieldSetAndNotNull(nAttrIdx) &&
2296             m_oCurCtxt.m_poFeature->GetFieldDefnRef(nAttrIdx)->GetType() == OFTString )
2297         {
2298             osVal = m_oCurCtxt.m_poFeature->GetFieldAsString(nAttrIdx) +
2299                     CPLString(" ") + osVal;
2300         }
2301         SetField( m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer,
2302                     nAttrIdx, osVal );
2303     }
2304 
2305     CPLXMLNode* psIter = psNode->psChild;
2306     for( ; psIter != nullptr; psIter = psIter->psNext )
2307     {
2308         if( psIter->eType == CXT_Element || psIter->eType == CXT_Attribute )
2309         {
2310             ExploreXMLDoc( osAttrXPath, oRule, psIter, osXPath, oMatcher,
2311                         oMapFieldXPathToIdx );
2312         }
2313     }
2314 }
2315 
2316 /************************************************************************/
2317 /*                              endElement()                            */
2318 /************************************************************************/
2319 
endElement(const XMLCh * const uri,const XMLCh * const localname,const XMLCh * const qname)2320 void GMLASReader::endElement(
2321             const   XMLCh* const    uri,
2322             const   XMLCh* const    localname,
2323             const   XMLCh* const
2324 #ifdef DEBUG_VERBOSE
2325                                     qname
2326 #endif
2327         )
2328 {
2329     m_nLevel --;
2330 
2331 #ifdef DEBUG_VERBOSE
2332     CPLDebug("GMLAS", "m_nLevel = %d", m_nLevel);
2333 #endif
2334 
2335 #ifdef DEBUG_VERBOSE
2336     {
2337         const CPLString& osLocalname( transcode(localname, m_osLocalname) );
2338         const CPLString& osNSPrefix( m_osNSPrefix =
2339                                 m_oMapURIToPrefix[ transcode(uri, m_osNSUri) ] );
2340         if( osNSPrefix.empty() )
2341             m_osXPath = osLocalname;
2342         else
2343         {
2344             m_osXPath.reserve( osNSPrefix.size() + 1 + osLocalname.size() );
2345             m_osXPath = osNSPrefix;
2346             m_osXPath += ":";
2347             m_osXPath += osLocalname;
2348         }
2349     }
2350     CPLDebug("GMLAS", "endElement(%s / %s)",
2351              transcode(qname).c_str(), m_osXPath.c_str());
2352 #endif
2353 
2354     if( m_nLevelSilentIgnoredXPath == m_nLevel )
2355     {
2356         m_nLevelSilentIgnoredXPath = -1;
2357     }
2358 
2359     // Make sure to set field only if we are at the expected nesting level
2360     if( m_nCurFieldIdx >= 0 && m_nLevel == m_nCurFieldLevel - 1 )
2361     {
2362         const OGRFieldType eType(
2363             m_nCurFieldIdx >= 0 ?
2364                 m_oCurCtxt.m_poFeature->GetFieldDefnRef(m_nCurFieldIdx)->GetType() :
2365             OFTString );
2366 
2367         // Assign XML content to field value
2368         if( IsArrayType(eType) )
2369         {
2370             const int nFCFieldIdx =
2371                 m_oCurCtxt.m_poLayer->GetFCFieldIndexFromOGRFieldIdx(m_nCurFieldIdx);
2372             if( nFCFieldIdx >= 0 &&
2373                 m_oCurCtxt.m_poLayer->GetFeatureClass().GetFields()[nFCFieldIdx].IsList() )
2374             {
2375                 SetField( m_oCurCtxt.m_poFeature,
2376                           m_oCurCtxt.m_poLayer,
2377                           m_nCurFieldIdx, m_osTextContent );
2378             }
2379             else if( m_nTextContentListEstimatedSize > m_nMaxContentSize )
2380             {
2381                 CPLError(CE_Failure, CPLE_OutOfMemory,
2382                          "Too much repeated data in a single element");
2383                 m_bParsingError = true;
2384             }
2385             else
2386             {
2387                 // Transform boolean values to something that OGR understands
2388                 if( eType == OFTIntegerList &&
2389                     m_oCurCtxt.m_poFeature->GetFieldDefnRef(m_nCurFieldIdx)->
2390                                                 GetSubType() == OFSTBoolean )
2391                 {
2392                     if( m_osTextContent == "true" )
2393                         m_osTextContent = "1";
2394                     else
2395                         m_osTextContent = "0";
2396                 }
2397 
2398                 m_osTextContentList.AddString( m_osTextContent );
2399                 // 16 is an arbitrary number for the cost of a new entry in the
2400                 // string list
2401                 m_nTextContentListEstimatedSize += 16 + m_osTextContent.size();
2402                 m_oCurCtxt.m_poFeature->SetField( m_nCurFieldIdx,
2403                                         m_osTextContentList.List() );
2404             }
2405         }
2406         else
2407         {
2408             if( m_bIsXMLBlobIncludeUpper && FillTextContent() )
2409             {
2410                 const CPLString& osLocalname(
2411                                 transcode(localname, m_osLocalname) );
2412                 const CPLString& osNSPrefix(
2413                                 m_oMapURIToPrefix[ transcode(uri, m_osNSUri) ] );
2414 
2415                 m_osTextContent += "</";
2416                 if( !osNSPrefix.empty() )
2417                 {
2418                     m_osTextContent += osNSPrefix;
2419                     m_osTextContent += ":";
2420                 }
2421                 m_osTextContent += osLocalname;
2422                 m_osTextContent += ">";
2423             }
2424 
2425             SetField( m_oCurCtxt.m_poFeature,
2426                         m_oCurCtxt.m_poLayer,
2427                         m_nCurFieldIdx, m_osTextContent );
2428         }
2429     }
2430 
2431     // Make sure to set field only if we are at the expected nesting level
2432     if( m_nCurGeomFieldIdx >= 0 && m_nLevel == m_nCurFieldLevel - 1 )
2433     {
2434         if( !m_apsXMLNodeStack.empty() )
2435         {
2436             CPLAssert( m_apsXMLNodeStack.size() == 1 );
2437             CPLXMLNode* psRoot = m_apsXMLNodeStack[0].psNode;
2438             ProcessGeometry(psRoot);
2439             CPLDestroyXMLNode(psRoot);
2440             m_apsXMLNodeStack.clear();
2441         }
2442     }
2443 
2444     if( (m_nCurFieldIdx >= 0 || m_nCurGeomFieldIdx >= 0) &&
2445         m_nLevel == m_nCurFieldLevel - 1 )
2446     {
2447         m_bIsXMLBlob = false;
2448         m_bIsXMLBlobIncludeUpper = false;
2449     }
2450 
2451     if( m_bIsXMLBlob )
2452     {
2453         if( m_nCurGeomFieldIdx >= 0 )
2454         {
2455             if( m_apsXMLNodeStack.size() > 1 )
2456             {
2457 #ifdef DEBUG_VERBOSE
2458                 CPLDebug("GMLAS", "m_apsXMLNodeStack.pop_back()");
2459 #endif
2460                 m_apsXMLNodeStack.pop_back();
2461             }
2462         }
2463 
2464         if( FillTextContent() )
2465         {
2466             const CPLString& osLocalname(
2467                             transcode(localname, m_osLocalname) );
2468             const CPLString& osNSPrefix(
2469                             m_oMapURIToPrefix[ transcode(uri, m_osNSUri) ] );
2470 
2471             m_osTextContent += "</";
2472             if( !osNSPrefix.empty() )
2473             {
2474                 m_osTextContent += osNSPrefix;
2475                 m_osTextContent += ":";
2476             }
2477             m_osTextContent += osLocalname;
2478             m_osTextContent += ">";
2479 
2480             if( m_osTextContent.size() > m_nMaxContentSize )
2481             {
2482                 CPLError(CE_Failure, CPLE_OutOfMemory,
2483                         "Too much data in a single element");
2484                 m_bParsingError = true;
2485             }
2486         }
2487     }
2488     else
2489     {
2490         m_osTextContent.clear();
2491     }
2492 
2493     if( m_nSWEDataArrayLevel >= 0)
2494     {
2495         if( m_nLevel > m_nSWEDataArrayLevel )
2496         {
2497             CPLAssert( m_apsXMLNodeStack.size() > 1 );
2498             m_apsXMLNodeStack.pop_back();
2499         }
2500         else
2501         {
2502             CPLAssert( m_apsXMLNodeStack.size() == 1 );
2503             CPLXMLNode* psRoot = m_apsXMLNodeStack[0].psNode;
2504             ProcessSWEDataArray(psRoot);
2505             m_nSWEDataArrayLevel = -1;
2506             CPLDestroyXMLNode(psRoot);
2507             m_apsXMLNodeStack.clear();
2508         }
2509     }
2510 
2511     // The while and not just if is needed when a group is at the end of an
2512     // element
2513     while( !m_aoStackContext.empty() &&
2514            m_aoStackContext.back().m_nLevel >= m_nLevel )
2515     {
2516         std::map<OGRLayer*, int> oMapCounter = m_aoStackContext.back().m_oMapCounter;
2517         if( !m_aoStackContext.back().m_osCurSubXPath.empty() )
2518         {
2519 #ifdef DEBUG_VERBOSE
2520             CPLDebug("GMLAS", "Restoring m_osCurSubXPath from %s to %s",
2521                      m_osCurSubXPath.c_str(),
2522                      m_aoStackContext.back().m_osCurSubXPath.c_str());
2523 #endif
2524             m_osCurSubXPath = m_aoStackContext.back().m_osCurSubXPath;
2525         }
2526 
2527         if( m_oCurCtxt.m_poGroupLayer == m_oCurCtxt.m_poLayer )
2528         {
2529             PopContext();
2530             CPLAssert( !m_aoStackContext.empty() );
2531             m_oCurCtxt.m_poLayer = m_aoStackContext.back().m_poLayer;
2532         }
2533         else
2534         {
2535             if( m_oCurCtxt.m_poGroupLayer )
2536             {
2537                 /* Case like
2538                         <first_elt_of_group>...</first_elt_of_group>
2539                     </end_of_enclosing_element>   <!-- we are here at endElement() -->
2540                 */
2541 
2542                 //CPLDebug("GMLAS", "Feature ready");
2543                 PushFeatureReady(m_oCurCtxt.m_poFeature,
2544                                  m_oCurCtxt.m_poGroupLayer);
2545                 //CPLDebug("GMLAS", "Feature ready");
2546                 PushFeatureReady(m_aoStackContext.back().m_poFeature,
2547                                  m_aoStackContext.back().m_poLayer);
2548             }
2549             else
2550             {
2551                 //CPLDebug("GMLAS", "Feature ready");
2552                 PushFeatureReady(m_oCurCtxt.m_poFeature,
2553                                  m_oCurCtxt.m_poLayer);
2554             }
2555             PopContext();
2556             if( !m_aoStackContext.empty() )
2557             {
2558                 m_oCurCtxt = m_aoStackContext.back();
2559                 m_oCurCtxt.m_osCurSubXPath.clear();
2560                 if( m_oCurCtxt.m_nLevel < 0 )
2561                 {
2562                     PopContext();
2563                     CPLAssert( !m_aoStackContext.empty() );
2564                     m_oCurCtxt.m_poLayer = m_aoStackContext.back().m_poLayer;
2565                 }
2566             }
2567             else
2568             {
2569                 m_oCurCtxt.m_poFeature = nullptr;
2570                 m_oCurCtxt.m_poLayer = nullptr;
2571                 m_oCurCtxt.m_poGroupLayer = nullptr;
2572                 m_oCurCtxt.m_nGroupLayerLevel = -1;
2573                 m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
2574             }
2575             m_nCurFieldIdx = -1;
2576         }
2577         m_oCurCtxt.m_oMapCounter = oMapCounter;
2578 
2579 #ifdef DEBUG_VERBOSE
2580         CPLDebug("GMLAS", "m_oCurCtxt = ");
2581         m_oCurCtxt.Dump();
2582 #endif
2583     }
2584 
2585     size_t nLastXPathLength = m_anStackXPathLength.back();
2586     m_anStackXPathLength.pop_back();
2587     if( m_anStackXPathLength.empty())
2588         m_osCurXPath.clear();
2589     else
2590         m_osCurXPath.resize( m_osCurXPath.size() - 1 - nLastXPathLength);
2591 
2592     if( m_osCurSubXPath.size() >= 1 + nLastXPathLength )
2593         m_osCurSubXPath.resize( m_osCurSubXPath.size() - 1 - nLastXPathLength);
2594     else if( m_osCurSubXPath.size() == nLastXPathLength )
2595          m_osCurSubXPath.clear();
2596 
2597     if( m_nSWEDataRecordLevel >= 0)
2598     {
2599         if( m_nLevel > m_nSWEDataRecordLevel )
2600         {
2601             CPLAssert( m_apsXMLNodeStack.size() > 1 );
2602             m_apsXMLNodeStack.pop_back();
2603         }
2604         else
2605         {
2606             CPLAssert( m_apsXMLNodeStack.size() == 1 );
2607             CPLXMLNode* psRoot = m_apsXMLNodeStack[0].psNode;
2608             ProcessSWEDataRecord(psRoot);
2609             m_nSWEDataRecordLevel = -1;
2610             CPLDestroyXMLNode(psRoot);
2611             m_apsXMLNodeStack.clear();
2612         }
2613     }
2614 }
2615 
2616 /************************************************************************/
2617 /*                             SetSWEValue()                            */
2618 /************************************************************************/
2619 
SetSWEValue(OGRFeature * poFeature,int iField,CPLString & osValue)2620 static void SetSWEValue(OGRFeature* poFeature, int iField, CPLString& osValue)
2621 {
2622     if( !osValue.empty() )
2623     {
2624         OGRFieldDefn* poFieldDefn = poFeature->GetFieldDefnRef(iField);
2625         OGRFieldType eType(poFieldDefn->GetType());
2626         OGRFieldSubType eSubType(poFieldDefn->GetSubType());
2627         if( eType == OFTReal || eType == OFTInteger)
2628         {
2629             osValue.Trim();
2630             if( eSubType == OFSTBoolean )
2631             {
2632                 osValue = EQUAL(osValue, "1") ||
2633                           EQUAL(osValue, "True") ? "1" : "0";
2634             }
2635         }
2636         poFeature->SetField(iField, osValue.c_str());
2637     }
2638 }
2639 
2640 /************************************************************************/
2641 /*                              SkipSpace()                             */
2642 /************************************************************************/
2643 
SkipSpace(const char * pszValues,size_t i)2644 static size_t SkipSpace( const char* pszValues, size_t i )
2645 {
2646     while( isspace( static_cast<int>(pszValues[i]) ) )
2647         i ++;
2648     return i;
2649 }
2650 
2651 /************************************************************************/
2652 /*                         ProcessSWEDataArray()                        */
2653 /************************************************************************/
2654 
ProcessSWEDataArray(CPLXMLNode * psRoot)2655 void GMLASReader::ProcessSWEDataArray(CPLXMLNode* psRoot)
2656 {
2657     if( m_oCurCtxt.m_poLayer == nullptr )
2658         return;
2659 
2660     CPLStripXMLNamespace( psRoot, "swe", true );
2661     CPLXMLNode* psElementType = CPLGetXMLNode(psRoot, "elementType");
2662     if( psElementType == nullptr )
2663         return;
2664     CPLXMLNode* psDataRecord = CPLGetXMLNode(psElementType, "DataRecord");
2665     if( psDataRecord == nullptr )
2666         return;
2667     const char* pszValues = CPLGetXMLValue(psRoot, "values", nullptr);
2668     if( pszValues == nullptr )
2669         return;
2670     CPLXMLNode* psTextEncoding = CPLGetXMLNode(psRoot,
2671                                                "encoding.TextEncoding");
2672     if( psTextEncoding == nullptr )
2673         return;
2674     //CPLString osDecimalSeparator =
2675     //    CPLGetXMLValue(psTextEncoding, "decimalSeparator", ".");
2676     CPLString osBlockSeparator =
2677         CPLGetXMLValue(psTextEncoding, "blockSeparator", "");
2678     CPLString osTokenSeparator =
2679         CPLGetXMLValue(psTextEncoding, "tokenSeparator", "");
2680     if( osBlockSeparator.empty() || osTokenSeparator.empty() )
2681         return;
2682 
2683     if( m_bInitialPass )
2684     {
2685         CPLString osLayerName;
2686         osLayerName.Printf("DataArray_%d", m_nSWEDataArrayLayerIdx+1);
2687         const char* pszElementTypeName =
2688                                 CPLGetXMLValue(psElementType, "name", nullptr);
2689         if( pszElementTypeName != nullptr )
2690         {
2691             osLayerName += "_";
2692             osLayerName += pszElementTypeName;
2693         }
2694         osLayerName = osLayerName.tolower();
2695         OGRGMLASLayer* poLayer = new OGRGMLASLayer(osLayerName);
2696 
2697         // Register layer in _ogr_layers_metadata
2698         {
2699             OGRFeature* poLayerDescFeature =
2700                         new OGRFeature(m_poLayersMetadataLayer->GetLayerDefn());
2701             poLayerDescFeature->SetField( szLAYER_NAME, osLayerName );
2702             poLayerDescFeature->SetField( szLAYER_CATEGORY, szSWE_DATA_ARRAY );
2703 
2704             CPLString osFieldName(szPARENT_PREFIX);
2705             osFieldName += m_oCurCtxt.m_poLayer->GetLayerDefn()->GetFieldDefn(
2706                         m_oCurCtxt.m_poLayer->GetIDFieldIdx())->GetNameRef();
2707             poLayerDescFeature->SetField( szLAYER_PARENT_PKID_NAME,
2708                                           osFieldName.c_str() );
2709             CPL_IGNORE_RET_VAL(
2710                 m_poLayersMetadataLayer->CreateFeature(poLayerDescFeature));
2711             delete poLayerDescFeature;
2712         }
2713 
2714         // Register layer relationship in _ogr_layer_relationships
2715         {
2716             OGRFeature* poRelationshipsFeature =
2717                 new OGRFeature(m_poRelationshipsLayer->GetLayerDefn());
2718             poRelationshipsFeature->SetField( szPARENT_LAYER,
2719                                               m_oCurCtxt.m_poLayer->GetName() );
2720             poRelationshipsFeature->SetField( szPARENT_PKID,
2721                     m_oCurCtxt.m_poLayer->GetLayerDefn()->GetFieldDefn(
2722                         m_oCurCtxt.m_poLayer->GetIDFieldIdx())->GetNameRef() );
2723             if( !m_osSWEDataArrayParentField.empty() )
2724             {
2725                 poRelationshipsFeature->SetField( szPARENT_ELEMENT_NAME,
2726                                                   m_osSWEDataArrayParentField );
2727             }
2728             poRelationshipsFeature->SetField(szCHILD_LAYER,
2729                                              osLayerName);
2730             CPL_IGNORE_RET_VAL(m_poRelationshipsLayer->CreateFeature(
2731                                             poRelationshipsFeature));
2732             delete poRelationshipsFeature;
2733         }
2734 
2735         m_apoSWEDataArrayLayers.push_back(poLayer);
2736         poLayer->ProcessDataRecordOfDataArrayCreateFields(m_oCurCtxt.m_poLayer,
2737                                                           psDataRecord,
2738                                                           m_poFieldsMetadataLayer);
2739     }
2740     else
2741     {
2742         CPLAssert( m_nSWEDataArrayLayerIdx <
2743                     static_cast<int>(m_apoSWEDataArrayLayers.size()) );
2744         OGRGMLASLayer* poLayer =
2745                         m_apoSWEDataArrayLayers[m_nSWEDataArrayLayerIdx];
2746         // -1 because first field is parent id
2747         const int nFieldCount = poLayer->GetLayerDefn()->GetFieldCount() - 1;
2748         int nFID = 1;
2749         int iField = 0;
2750         const size_t nLen = strlen(pszValues);
2751         OGRFeature* poFeature = nullptr;
2752         const bool bSameSep = (osTokenSeparator == osBlockSeparator);
2753         size_t nLastValid = SkipSpace(pszValues, 0);
2754         size_t i = nLastValid;
2755         while( i < nLen )
2756         {
2757             if( poFeature == nullptr )
2758             {
2759                 poFeature = new OGRFeature( poLayer->GetLayerDefn() );
2760                 poFeature->SetFID( nFID );
2761                 poFeature->SetField( 0,
2762                     m_oCurCtxt.m_poFeature->GetFieldAsString(
2763                         m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
2764                 nFID ++;
2765                 iField = 0;
2766             }
2767             if( strncmp( pszValues + i, osTokenSeparator,
2768                          osTokenSeparator.size() ) == 0 )
2769             {
2770                 if( bSameSep && iField == nFieldCount )
2771                 {
2772                     PushFeatureReady( poFeature, poLayer );
2773                     poFeature = new OGRFeature( poLayer->GetLayerDefn() );
2774                     poFeature->SetFID( nFID );
2775                     poFeature->SetField( 0,
2776                         m_oCurCtxt.m_poFeature->GetFieldAsString(
2777                             m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
2778                     nFID ++;
2779                     iField = 0;
2780                 }
2781 
2782                 if( iField < nFieldCount )
2783                 {
2784                     CPLString osValue( pszValues + nLastValid,
2785                                        i - nLastValid );
2786                     // +1 because first field is parent id
2787                     SetSWEValue(poFeature, iField+1, osValue);
2788                     iField ++;
2789                 }
2790                 nLastValid = i + osTokenSeparator.size();
2791                 nLastValid = SkipSpace(pszValues, nLastValid);
2792                 i = nLastValid;
2793             }
2794             else if( strncmp( pszValues + i, osBlockSeparator,
2795                               osBlockSeparator.size() ) == 0 )
2796             {
2797                 if( iField < nFieldCount )
2798                 {
2799                     CPLString osValue( pszValues + nLastValid,
2800                                        i - nLastValid );
2801                     // +1 because first field is parent id
2802                     SetSWEValue(poFeature, iField+1, osValue);
2803                     iField ++;
2804                 }
2805                 PushFeatureReady( poFeature, poLayer );
2806                 poFeature = nullptr;
2807                 nLastValid = i + osBlockSeparator.size();
2808                 nLastValid = SkipSpace(pszValues, nLastValid);
2809                 i = nLastValid;
2810             }
2811             else
2812             {
2813                 i++;
2814             }
2815         }
2816         if( poFeature != nullptr )
2817         {
2818             if( iField < nFieldCount )
2819             {
2820                 CPLString osValue( pszValues + nLastValid,
2821                                     nLen - nLastValid );
2822                 // +1 because first field is parent id
2823                 SetSWEValue(poFeature, iField+1, osValue);
2824                 //iField ++;
2825             }
2826             PushFeatureReady( poFeature, poLayer );
2827         }
2828     }
2829     m_nSWEDataArrayLayerIdx ++;
2830 }
2831 
2832 
2833 /************************************************************************/
2834 /*                        ProcessSWEDataRecord()                        */
2835 /************************************************************************/
2836 
ProcessSWEDataRecord(CPLXMLNode * psRoot)2837 void GMLASReader::ProcessSWEDataRecord(CPLXMLNode* psRoot)
2838 {
2839     CPLStripXMLNamespace( psRoot, "swe", true );
2840     if( m_bInitialPass )
2841     {
2842         // Collect existing live features of this layer, so that we can
2843         // patch them
2844         std::vector<OGRFeature*> apoFeatures;
2845         apoFeatures.push_back(m_oCurCtxt.m_poFeature);
2846         for(size_t i = 0; i < m_aoFeaturesReady.size(); ++i )
2847         {
2848             if( m_aoFeaturesReady[i].second == m_oCurCtxt.m_poLayer )
2849                 apoFeatures.push_back(m_aoFeaturesReady[i].first);
2850         }
2851         m_oCurCtxt.m_poLayer->ProcessDataRecordCreateFields(
2852             psRoot, apoFeatures, m_poFieldsMetadataLayer);
2853     }
2854     else
2855     {
2856         m_oCurCtxt.m_poLayer->ProcessDataRecordFillFeature(
2857             psRoot, m_oCurCtxt.m_poFeature);
2858     }
2859 }
2860 
2861 /************************************************************************/
2862 /*                            GMLASGetSRSName()                         */
2863 /************************************************************************/
2864 
GMLASGetSRSName(CPLXMLNode * psNode)2865 static const char* GMLASGetSRSName(CPLXMLNode* psNode)
2866 {
2867     const char* pszSRSName = CPLGetXMLValue(psNode, szSRS_NAME, nullptr);
2868     if( pszSRSName == nullptr )
2869     {
2870         // Case of a gml:Point where the srsName is on the gml:pos
2871         pszSRSName = CPLGetXMLValue(psNode, "gml:pos.srsName", nullptr);
2872     }
2873     return pszSRSName;
2874 }
2875 
2876 /************************************************************************/
2877 /*                            ProcessGeometry()                         */
2878 /************************************************************************/
2879 
ProcessGeometry(CPLXMLNode * psRoot)2880 void GMLASReader::ProcessGeometry(CPLXMLNode* psRoot)
2881 {
2882     OGRGeomFieldDefn* poGeomFieldDefn =
2883         m_oCurCtxt.m_poFeature->GetGeomFieldDefnRef(
2884                                     m_nCurGeomFieldIdx);
2885 
2886     if( m_bInitialPass )
2887     {
2888         const char* pszSRSName = GMLASGetSRSName(psRoot);
2889         if( pszSRSName != nullptr )
2890         {
2891             // If we are doing a first pass, store the SRS of the geometry
2892             // column
2893             if( !m_oSetGeomFieldsWithUnknownSRS.empty() &&
2894                  m_oSetGeomFieldsWithUnknownSRS.find(poGeomFieldDefn) !=
2895                         m_oSetGeomFieldsWithUnknownSRS.end() )
2896             {
2897                 OGRSpatialReference* poSRS =
2898                                 new OGRSpatialReference();
2899                 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
2900 
2901                 if( poSRS->SetFromUserInput( pszSRSName ) == OGRERR_NONE )
2902                 {
2903                     m_oMapGeomFieldDefnToSRSName[poGeomFieldDefn] = pszSRSName;
2904                     poGeomFieldDefn->SetSpatialRef(poSRS);
2905                 }
2906                 poSRS->Release();
2907                 m_oSetGeomFieldsWithUnknownSRS.erase(poGeomFieldDefn);
2908             }
2909         }
2910         return;
2911     }
2912 
2913 #ifdef DEBUG_VERBOSE
2914     {
2915         char* pszXML = CPLSerializeXMLTree(psRoot);
2916         CPLDebug("GML", "geometry = %s", pszXML);
2917         CPLFree(pszXML);
2918     }
2919 #endif
2920 
2921     OGRGeometry* poGeom = reinterpret_cast<OGRGeometry*>
2922                     (OGR_G_CreateFromGMLTree( psRoot ));
2923     if( poGeom != nullptr )
2924     {
2925         const char* pszSRSName = GMLASGetSRSName(psRoot);
2926 
2927         bool bSwapXY = false;
2928         if( pszSRSName != nullptr )
2929         {
2930             // Check if the srsName indicates unusual axis order,
2931             // and if so swap x and y coordinates.
2932             const auto oIter = m_oMapSRSNameToInvertedAxis.find(pszSRSName);
2933             if( oIter == m_oMapSRSNameToInvertedAxis.end() )
2934             {
2935                 OGRSpatialReference oSRS;
2936                 oSRS.SetFromUserInput( pszSRSName );
2937                 bSwapXY = !STARTS_WITH_CI(pszSRSName, "EPSG:") &&
2938                     (CPL_TO_BOOL(oSRS.EPSGTreatsAsLatLong()) ||
2939                      CPL_TO_BOOL(oSRS.EPSGTreatsAsNorthingEasting()));
2940                 m_oMapSRSNameToInvertedAxis[ pszSRSName ] = bSwapXY;
2941             }
2942             else
2943             {
2944                 bSwapXY = oIter->second;
2945             }
2946         }
2947         if( (bSwapXY && m_eSwapCoordinates == GMLAS_SWAP_AUTO) ||
2948             m_eSwapCoordinates == GMLAS_SWAP_YES )
2949         {
2950             poGeom->swapXY();
2951         }
2952 
2953         // Do we need to do reprojection ?
2954         if( pszSRSName != nullptr && poGeomFieldDefn->GetSpatialRef() != nullptr &&
2955             m_oMapGeomFieldDefnToSRSName[poGeomFieldDefn] != pszSRSName )
2956         {
2957             bool bReprojectionOK = false;
2958             OGRSpatialReference oSRS;
2959             if( oSRS.SetFromUserInput( pszSRSName ) == OGRERR_NONE )
2960             {
2961                 OGRCoordinateTransformation* poCT =
2962                     OGRCreateCoordinateTransformation( &oSRS,
2963                                             poGeomFieldDefn->GetSpatialRef() );
2964                 if( poCT != nullptr )
2965                 {
2966                     bReprojectionOK = (poGeom->transform( poCT ) == OGRERR_NONE);
2967                     delete poCT;
2968                 }
2969             }
2970             if( !bReprojectionOK )
2971             {
2972                 CPLError(CE_Warning, CPLE_AppDefined,
2973                          "Reprojection from %s to %s failed",
2974                          pszSRSName,
2975                          m_oMapGeomFieldDefnToSRSName[poGeomFieldDefn].c_str());
2976                 delete poGeom;
2977                 poGeom = nullptr;
2978             }
2979 #ifdef DEBUG_VERBOSE
2980             else
2981             {
2982                 CPLDebug("GMLAS", "Reprojected geometry from %s to %s",
2983                          pszSRSName,
2984                          m_oMapGeomFieldDefnToSRSName[poGeomFieldDefn].c_str());
2985             }
2986 #endif
2987         }
2988 
2989         if( poGeom != nullptr )
2990         {
2991             // Deal with possibly repeated geometries by building
2992             // a geometry collection. We could also create a
2993             // nested table, but that would probably be less
2994             // convenient to use.
2995             OGRGeometry* poPrevGeom = m_oCurCtxt.m_poFeature->
2996                                     StealGeometry(m_nCurGeomFieldIdx);
2997             if( poPrevGeom != nullptr )
2998             {
2999                 if( poPrevGeom->getGeometryType() ==
3000                                         wkbGeometryCollection )
3001                 {
3002                     poPrevGeom->toGeometryCollection()->
3003                                         addGeometryDirectly(poGeom);
3004                     poGeom = poPrevGeom;
3005                 }
3006                 else
3007                 {
3008                     OGRGeometryCollection* poGC =
3009                                     new OGRGeometryCollection();
3010                     poGC->addGeometryDirectly(poPrevGeom);
3011                     poGC->addGeometryDirectly(poGeom);
3012                     poGeom = poGC;
3013                 }
3014             }
3015             poGeom->assignSpatialReference(
3016                                 poGeomFieldDefn->GetSpatialRef());
3017             m_oCurCtxt.m_poFeature->SetGeomFieldDirectly(
3018                 m_nCurGeomFieldIdx, poGeom );
3019         }
3020     }
3021     else
3022     {
3023         char* pszXML = CPLSerializeXMLTree(psRoot);
3024         CPLDebug("GMLAS", "Non-recognized geometry: %s",
3025                     pszXML);
3026         CPLFree(pszXML);
3027     }
3028 }
3029 
3030 /************************************************************************/
3031 /*                              characters()                            */
3032 /************************************************************************/
3033 
characters(const XMLCh * const chars,const XMLSize_t length)3034 void GMLASReader::characters( const XMLCh *const chars,
3035                               const XMLSize_t length )
3036 {
3037     bool bTextMemberUpdated = false;
3038     if( ((m_bIsXMLBlob && m_nCurGeomFieldIdx >= 0 && !m_bInitialPass) ||
3039         m_nSWEDataArrayLevel >= 0 || m_nSWEDataRecordLevel >= 0) &&
3040         // Check the stack is not empty in case of space chars before the
3041         // starting node
3042         !m_apsXMLNodeStack.empty() )
3043     {
3044         bTextMemberUpdated = true;
3045         const CPLString& osText( transcode(chars, m_osText,
3046                                            static_cast<int>(length) ) );
3047 
3048         // Merge content in current text node if it exists
3049         NodeLastChild& sNodeLastChild = m_apsXMLNodeStack.back();
3050         if( sNodeLastChild.psLastChild != nullptr &&
3051             sNodeLastChild.psLastChild->eType == CXT_Text )
3052         {
3053             CPLXMLNode* psNode = sNodeLastChild.psLastChild;
3054             const size_t nOldLength = strlen(psNode->pszValue);
3055             char* pszNewValue = reinterpret_cast<char*>(VSIRealloc(
3056                     psNode->pszValue, nOldLength + osText.size() + 1));
3057             if( pszNewValue )
3058             {
3059                 psNode->pszValue = pszNewValue;
3060                 memcpy( pszNewValue + nOldLength, osText.c_str(),
3061                         osText.size() + 1);
3062             }
3063             else
3064             {
3065                 CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
3066                 m_bParsingError = true;
3067             }
3068         }
3069         // Otherwise create a new text node
3070         else
3071         {
3072             CPLXMLNode* psNode = reinterpret_cast<CPLXMLNode*>
3073                                         ( CPLMalloc(sizeof(CPLXMLNode)) );
3074             psNode->eType = CXT_Text;
3075             psNode->pszValue = reinterpret_cast<char*>
3076                                         ( CPLMalloc( osText.size() + 1 ) );
3077             memcpy(psNode->pszValue, osText.c_str(), osText.size() + 1);
3078             psNode->psNext = nullptr;
3079             psNode->psChild = nullptr;
3080             AttachAsLastChild( psNode );
3081         }
3082     }
3083 
3084     if( !FillTextContent() )
3085     {
3086         m_osTextContent = "1"; // dummy
3087         return;
3088     }
3089 
3090     if( m_bIsXMLBlob )
3091     {
3092         if( m_nCurFieldIdx >= 0 )
3093         {
3094             const CPLString& osText( bTextMemberUpdated ? m_osText:
3095                     transcode(chars, m_osText, static_cast<int>(length) ) );
3096 
3097             char* pszEscaped = CPLEscapeString( osText.c_str(),
3098                                             static_cast<int>(osText.size()),
3099                                             CPLES_XML );
3100             try
3101             {
3102                 m_osTextContent += pszEscaped;
3103             }
3104             catch( const std::bad_alloc& )
3105             {
3106                 CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
3107                 m_bParsingError = true;
3108             }
3109             CPLFree(pszEscaped);
3110         }
3111     }
3112     // Make sure to set content only if we are at the expected nesting level
3113     else if( m_nLevel == m_nCurFieldLevel )
3114     {
3115         const CPLString& osText(
3116                         transcode(chars, m_osText, static_cast<int>(length) ) );
3117         try
3118         {
3119             m_osTextContent += osText;
3120         }
3121         catch( const std::bad_alloc& )
3122         {
3123             CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
3124             m_bParsingError = true;
3125         }
3126     }
3127 
3128     if( m_osTextContent.size() > m_nMaxContentSize )
3129     {
3130         CPLError(CE_Failure, CPLE_OutOfMemory,
3131                  "Too much data in a single element");
3132         m_bParsingError = true;
3133     }
3134 }
3135 
3136 /************************************************************************/
3137 /*                            GetNextFeature()                          */
3138 /************************************************************************/
3139 
GetNextFeature(OGRGMLASLayer ** ppoBelongingLayer,GDALProgressFunc pfnProgress,void * pProgressData)3140 OGRFeature* GMLASReader::GetNextFeature( OGRGMLASLayer** ppoBelongingLayer,
3141                                          GDALProgressFunc pfnProgress,
3142                                          void* pProgressData )
3143 {
3144     while( !m_aoFeaturesReady.empty() )
3145     {
3146         OGRFeature* m_poFeatureReady = m_aoFeaturesReady[0].first;
3147         OGRGMLASLayer* m_poFeatureReadyLayer = m_aoFeaturesReady[0].second;
3148         m_aoFeaturesReady.erase( m_aoFeaturesReady.begin() );
3149 
3150         if( m_poLayerOfInterest == nullptr ||
3151             m_poLayerOfInterest == m_poFeatureReadyLayer )
3152         {
3153             if( ppoBelongingLayer )
3154                 *ppoBelongingLayer = m_poFeatureReadyLayer;
3155             return m_poFeatureReady;
3156         }
3157         delete m_poFeatureReady;
3158     }
3159 
3160     if( m_bEOF )
3161         return nullptr;
3162 
3163     try
3164     {
3165         if( m_bFirstIteration )
3166         {
3167             m_bFirstIteration = false;
3168             if( !m_poSAXReader->parseFirst( *m_GMLInputSource, m_oToFill ) )
3169             {
3170                 m_bParsingError = true;
3171                 m_bEOF = true;
3172                 return nullptr;
3173             }
3174         }
3175 
3176         vsi_l_offset nLastOffset = VSIFTellL(m_fp);
3177         while( m_poSAXReader->parseNext( m_oToFill ) )
3178         {
3179             if( pfnProgress && VSIFTellL(m_fp) - nLastOffset > 100 * 1024 )
3180             {
3181                 nLastOffset = VSIFTellL(m_fp);
3182                 double dfPct = -1;
3183                 if( m_nFileSize )
3184                     dfPct = 1.0 * nLastOffset / m_nFileSize;
3185                 if( !pfnProgress( dfPct, "", pProgressData ) )
3186                 {
3187                     m_bInterrupted = true;
3188                     break;
3189                 }
3190             }
3191             if( m_bParsingError )
3192                 break;
3193 
3194             while( !m_aoFeaturesReady.empty() )
3195             {
3196                 OGRFeature* m_poFeatureReady = m_aoFeaturesReady[0].first;
3197                 OGRGMLASLayer* m_poFeatureReadyLayer =
3198                                                m_aoFeaturesReady[0].second;
3199                 m_aoFeaturesReady.erase( m_aoFeaturesReady.begin() );
3200 
3201                 if( m_poLayerOfInterest == nullptr ||
3202                     m_poLayerOfInterest == m_poFeatureReadyLayer )
3203                 {
3204                     if( ppoBelongingLayer )
3205                         *ppoBelongingLayer = m_poFeatureReadyLayer;
3206 
3207                     if( pfnProgress )
3208                     {
3209                         nLastOffset = VSIFTellL(m_fp);
3210                         double dfPct = -1;
3211                         if( m_nFileSize )
3212                             dfPct = 1.0 * nLastOffset / m_nFileSize;
3213                         if( !pfnProgress( dfPct, "", pProgressData ) )
3214                         {
3215                             delete m_poFeatureReady;
3216                             m_bInterrupted = true;
3217                             m_bEOF = true;
3218                             return nullptr;
3219                         }
3220                     }
3221 
3222                     return m_poFeatureReady;
3223                 }
3224                 delete m_poFeatureReady;
3225             }
3226         }
3227 
3228         m_bEOF = true;
3229     }
3230     catch (const XMLException& toCatch)
3231     {
3232         CPLError(CE_Failure, CPLE_AppDefined, "%s",
3233                  transcode( toCatch.getMessage() ).c_str() );
3234         m_bParsingError = true;
3235         m_bEOF = true;
3236     }
3237     catch (const SAXException& toCatch)
3238     {
3239         CPLError(CE_Failure, CPLE_AppDefined, "%s",
3240                  transcode( toCatch.getMessage() ).c_str() );
3241         m_bParsingError = true;
3242         m_bEOF = true;
3243     }
3244 
3245     return nullptr;
3246 }
3247 
3248 /************************************************************************/
3249 /*                              RunFirstPass()                          */
3250 /************************************************************************/
3251 
RunFirstPass(GDALProgressFunc pfnProgress,void * pProgressData,bool bRemoveUnusedLayers,bool bRemoveUnusedFields,bool bProcessSWEDataArray,OGRLayer * poFieldsMetadataLayer,OGRLayer * poLayersMetadataLayer,OGRLayer * poRelationshipsLayer,std::set<CPLString> & aoSetRemovedLayerNames)3252 bool GMLASReader::RunFirstPass(GDALProgressFunc pfnProgress,
3253                                void* pProgressData,
3254                                bool bRemoveUnusedLayers,
3255                                bool bRemoveUnusedFields,
3256                                bool bProcessSWEDataArray,
3257                                OGRLayer* poFieldsMetadataLayer,
3258                                OGRLayer* poLayersMetadataLayer,
3259                                OGRLayer* poRelationshipsLayer,
3260                                std::set<CPLString>& aoSetRemovedLayerNames)
3261 {
3262     m_bInitialPass = true;
3263     m_bProcessSWEDataArray = bProcessSWEDataArray;
3264     m_poFieldsMetadataLayer = poFieldsMetadataLayer;
3265     m_poLayersMetadataLayer = poLayersMetadataLayer;
3266     m_poRelationshipsLayer = poRelationshipsLayer;
3267 
3268     // Store in m_oSetGeomFieldsWithUnknownSRS the geometry fields
3269     std::set<OGRGMLASLayer*> oSetUnreferencedLayers;
3270     std::map<OGRGMLASLayer*, std::set<CPLString> > oMapUnusedFields;
3271     for(size_t i=0; i < m_papoLayers->size(); i++ )
3272     {
3273         OGRGMLASLayer* poLayer = (*m_papoLayers)[i];
3274         OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
3275         oSetUnreferencedLayers.insert( poLayer );
3276         for(int j=0; j< poFDefn->GetGeomFieldCount(); j++ )
3277         {
3278             m_oSetGeomFieldsWithUnknownSRS.insert(
3279                                             poFDefn->GetGeomFieldDefn(j));
3280         }
3281         for(int j=0; j< poFDefn->GetFieldCount(); j++ )
3282         {
3283             oMapUnusedFields[poLayer].insert(
3284                 poFDefn->GetFieldDefn(j)->GetNameRef());
3285         }
3286     }
3287 
3288     CPLDebug("GMLAS", "Start of first pass");
3289 
3290     // Do we need to do a full scan of the file ?
3291     const bool bHasURLSpecificRules =
3292                 !m_oXLinkResolver.GetConf().m_aoURLSpecificRules.empty();
3293     const bool bDoFullPass =
3294         (m_bValidate ||
3295          bRemoveUnusedLayers ||
3296          bRemoveUnusedFields ||
3297          bHasURLSpecificRules ||
3298          bProcessSWEDataArray ||
3299          m_oXLinkResolver.GetConf().m_bResolveInternalXLinks);
3300 
3301     // Loop on features until we have determined the SRS of all geometry
3302     // columns, or potentially on the whole file for the above reasons
3303     OGRGMLASLayer* poLayer;
3304     OGRFeature* poFeature;
3305     while( (bDoFullPass || !m_oSetGeomFieldsWithUnknownSRS.empty() ) &&
3306            (poFeature = GetNextFeature(&poLayer, pfnProgress, pProgressData)) != nullptr )
3307     {
3308         if( bRemoveUnusedLayers )
3309             oSetUnreferencedLayers.erase( poLayer );
3310         if( bRemoveUnusedFields )
3311         {
3312             std::set<CPLString>& oSetUnusedFields = oMapUnusedFields[poLayer];
3313             OGRFeatureDefn* poFDefn = poLayer->GetLayerDefn();
3314             int nFieldCount = poFDefn->GetFieldCount();
3315             for(int j=0; j< nFieldCount; j++ )
3316             {
3317                 if( poFeature->IsFieldSetAndNotNull(j) )
3318                     oSetUnusedFields.erase(poFDefn->GetFieldDefn(j)->GetNameRef());
3319             }
3320         }
3321         delete poFeature;
3322     }
3323 
3324     CPLDebug("GMLAS", "End of first pass");
3325 
3326     ProcessInternalXLinkFirstPass(bRemoveUnusedFields, oMapUnusedFields);
3327 
3328     if( bRemoveUnusedLayers )
3329     {
3330         std::vector<OGRGMLASLayer*> apoNewLayers;
3331         for(size_t i=0; i < m_papoLayers->size(); i++ )
3332         {
3333             poLayer = (*m_papoLayers)[i];
3334             if( oSetUnreferencedLayers.find( poLayer ) ==
3335                     oSetUnreferencedLayers.end() )
3336             {
3337                 apoNewLayers.push_back( poLayer );
3338             }
3339             else
3340             {
3341                 aoSetRemovedLayerNames.insert( poLayer->GetName() );
3342                 delete poLayer;
3343             }
3344         }
3345         *m_papoLayers = apoNewLayers;
3346     }
3347     if( bRemoveUnusedFields )
3348     {
3349         for(size_t i=0; i < m_papoLayers->size(); i++ )
3350         {
3351             poLayer = (*m_papoLayers)[i];
3352             for( const auto& oIter: oMapUnusedFields[poLayer] )
3353             {
3354                 poLayer->RemoveField(
3355                     poLayer->GetLayerDefn()->GetFieldIndex(oIter) );
3356             }
3357 
3358             // We need to run this again since we may have delete the
3359             // element that holds attributes, like in
3360             // <foo xsi:nil="true" nilReason="unknown"/> where foo will be
3361             // eliminated, but foo_nilReason kept.
3362             poLayer->CreateCompoundFoldedMappings();
3363         }
3364     }
3365 
3366     // Add fields coming from matching URL specific rules
3367     if( bHasURLSpecificRules )
3368     {
3369         CreateFieldsForURLSpecificRules();
3370     }
3371 
3372     // Clear the set even if we didn't manage to determine all the SRS
3373     m_oSetGeomFieldsWithUnknownSRS.clear();
3374 
3375     return !m_bInterrupted;
3376 }
3377 
3378 /************************************************************************/
3379 /*                    ProcessInternalXLinkFirstPass()                   */
3380 /************************************************************************/
3381 
ProcessInternalXLinkFirstPass(bool bRemoveUnusedFields,std::map<OGRGMLASLayer *,std::set<CPLString>> & oMapUnusedFields)3382 void GMLASReader::ProcessInternalXLinkFirstPass(
3383     bool bRemoveUnusedFields,
3384     std::map<OGRGMLASLayer*, std::set<CPLString> >&oMapUnusedFields)
3385 {
3386     for( const auto& oIter: m_oMapFieldXPathToLinkValue )
3387     {
3388         OGRGMLASLayer* poReferringLayer = oIter.first.first;
3389         const CPLString& osReferringField = oIter.first.second;
3390         const std::vector<CPLString>& aosLinks = oIter.second;
3391         std::set<OGRGMLASLayer*> oSetTargetLayers;
3392         for( size_t i = 0; i < aosLinks.size(); i++ )
3393         {
3394             const auto oIter2 = m_oMapElementIdToLayer.find(aosLinks[i]);
3395             if( oIter2 == m_oMapElementIdToLayer.end() )
3396             {
3397                 CPLError(CE_Warning, CPLE_AppDefined,
3398                          "%s:%s = '#%s' has no corresponding target "
3399                          "element in this document",
3400                          poReferringLayer->GetName(),
3401                          osReferringField.c_str(),
3402                          aosLinks[i].c_str());
3403             }
3404             else if( oSetTargetLayers.find(oIter2->second) ==
3405                                                     oSetTargetLayers.end() )
3406             {
3407                 OGRGMLASLayer* poTargetLayer = oIter2->second;
3408                 oSetTargetLayers.insert(poTargetLayer);
3409                 CPLString osLinkFieldName =
3410                     poReferringLayer->CreateLinkForAttrToOtherLayer(
3411                         osReferringField,
3412                         poTargetLayer->GetFeatureClass().GetXPath());
3413                 if( bRemoveUnusedFields )
3414                 {
3415                     oMapUnusedFields[poReferringLayer].erase(osLinkFieldName);
3416                 }
3417             }
3418         }
3419     }
3420 }
3421 
3422 /************************************************************************/
3423 /*                    CreateFieldsForURLSpecificRules()                 */
3424 /************************************************************************/
3425 
CreateFieldsForURLSpecificRules()3426 void GMLASReader::CreateFieldsForURLSpecificRules()
3427 {
3428     for( const auto& oIter: m_oMapXLinkFields )
3429     {
3430         OGRGMLASLayer* poLayer = oIter.first;
3431         const auto& oMap2 = oIter.second;
3432         for( const auto& oIter2: oMap2 )
3433         {
3434             const CPLString& osFieldXPath(oIter2.first);
3435             // Note that CreateFieldsForURLSpecificRule() running on a previous
3436             // iteration will have inserted new OGR fields, so we really need
3437             // to compute that index now.
3438             const int nFieldIdx = poLayer->GetOGRFieldIndexFromXPath(osFieldXPath);
3439             CPLAssert(nFieldIdx >= 0);
3440             int nInsertFieldIdx = nFieldIdx + 1;
3441             const auto& oSetRuleIndex = oIter2.second;
3442             for( const auto& nRuleIdx: oSetRuleIndex )
3443             {
3444                 const GMLASXLinkResolutionConf::URLSpecificResolution& oRule =
3445                     m_oXLinkResolver.GetConf().m_aoURLSpecificRules[nRuleIdx];
3446                 CreateFieldsForURLSpecificRule( poLayer, nFieldIdx,
3447                                                 osFieldXPath,
3448                                                 nInsertFieldIdx,
3449                                                 oRule );
3450             }
3451         }
3452     }
3453 }
3454 
3455 /************************************************************************/
3456 /*                    CreateFieldsForURLSpecificRule()                  */
3457 /************************************************************************/
3458 
CreateFieldsForURLSpecificRule(OGRGMLASLayer * poLayer,int nFieldIdx,const CPLString & osFieldXPath,int & nInsertFieldIdx,const GMLASXLinkResolutionConf::URLSpecificResolution & oRule)3459 void GMLASReader::CreateFieldsForURLSpecificRule(
3460                 OGRGMLASLayer* poLayer,
3461                 int nFieldIdx,
3462                 const CPLString& osFieldXPath,
3463                 int& nInsertFieldIdx,
3464                 const GMLASXLinkResolutionConf::URLSpecificResolution& oRule )
3465 {
3466     if( oRule.m_eResolutionMode ==
3467                         GMLASXLinkResolutionConf::RawContent )
3468     {
3469         const CPLString osRawContentXPath(
3470             GMLASField::MakeXLinkRawContentFieldXPathFromXLinkHrefXPath(
3471                                                                 osFieldXPath) );
3472         if( poLayer->GetOGRFieldIndexFromXPath( osRawContentXPath ) < 0 )
3473         {
3474             const CPLString osOGRFieldName(
3475                 poLayer->GetLayerDefn()->
3476                         GetFieldDefn(nFieldIdx)->GetNameRef() );
3477             CPLString osRawContentFieldname(osOGRFieldName);
3478             size_t nPos = osRawContentFieldname.find("_href");
3479             if( nPos != std::string::npos )
3480                 osRawContentFieldname.resize(nPos);
3481             osRawContentFieldname += "_rawcontent";
3482             OGRFieldDefn oFieldDefnRaw( osRawContentFieldname,
3483                                         OFTString );
3484             poLayer->InsertNewField( nInsertFieldIdx,
3485                                         oFieldDefnRaw,
3486                                         osRawContentXPath );
3487             nInsertFieldIdx ++;
3488         }
3489     }
3490     else if ( oRule.m_eResolutionMode ==
3491                     GMLASXLinkResolutionConf::FieldsFromXPath )
3492     {
3493         for( size_t i=0; i < oRule.m_aoFields.size(); ++i )
3494         {
3495             const CPLString osDerivedFieldXPath(
3496                 GMLASField::MakeXLinkDerivedFieldXPathFromXLinkHrefXPath(
3497                     osFieldXPath, oRule.m_aoFields[i].m_osName) );
3498             if( poLayer->GetOGRFieldIndexFromXPath( osDerivedFieldXPath ) < 0 )
3499             {
3500                 const CPLString osOGRFieldName(
3501                     poLayer->GetLayerDefn()->
3502                             GetFieldDefn(nFieldIdx)->GetNameRef() );
3503                 CPLString osNewFieldname(osOGRFieldName);
3504                 size_t nPos = osNewFieldname.find("_href");
3505                 if( nPos != std::string::npos )
3506                     osNewFieldname.resize(nPos);
3507                 osNewFieldname += "_" + oRule.m_aoFields[i].m_osName;
3508 
3509                 OGRFieldType eType = OFTString;
3510                 const CPLString& osType(oRule.m_aoFields[i].m_osType);
3511                 if( osType == "integer" )
3512                     eType = OFTInteger;
3513                 else if( osType == "long" )
3514                     eType = OFTInteger64;
3515                 else if( osType == "double" )
3516                     eType = OFTReal;
3517                 else if( osType == "dateTime" )
3518                     eType = OFTDateTime;
3519 
3520                 OGRFieldDefn oFieldDefnRaw( osNewFieldname,
3521                                             eType );
3522                 poLayer->InsertNewField( nInsertFieldIdx,
3523                                             oFieldDefnRaw,
3524                                             osDerivedFieldXPath );
3525                 nInsertFieldIdx ++;
3526             }
3527         }
3528     }
3529 }
3530