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