Line data Source code
1 : /**********************************************************************
2 : *
3 : * Project: GML Reader
4 : * Purpose: Implementation of GMLHandler class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : **********************************************************************
8 : * Copyright (c) 2002, Frank Warmerdam
9 : * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "gmlreader.h"
16 : #include "gmlreaderp.h"
17 :
18 : #include <algorithm>
19 : #include <climits>
20 : #include <cstddef>
21 : #include <cstdlib>
22 : #include <cstring>
23 : #include <memory>
24 : #include <string>
25 : #include <vector>
26 :
27 : #include "cpl_conv.h"
28 : #include "cpl_error.h"
29 : #include "cpl_hash_set.h"
30 : #include "cpl_minixml.h"
31 : #include "cpl_string.h"
32 : #include "cpl_vsi.h"
33 : #ifdef HAVE_EXPAT
34 : #include "expat.h"
35 : #include "expat_external.h"
36 : #endif
37 : #include "ogr_core.h"
38 : #ifdef HAVE_XERCES
39 : #include "ogr_xerces.h"
40 : #endif
41 :
42 : #ifdef HAVE_XERCES
43 :
44 : /************************************************************************/
45 : /* GMLXercesHandler() */
46 : /************************************************************************/
47 :
48 2 : GMLXercesHandler::GMLXercesHandler(GMLReader *poReader)
49 2 : : GMLHandler(poReader), m_nEntityCounter(0)
50 : {
51 2 : }
52 :
53 : /************************************************************************/
54 : /* GMLXercesHandlerDealWithError() */
55 : /************************************************************************/
56 :
57 939 : static void GMLXercesHandlerDealWithError(OGRErr eErr)
58 : {
59 939 : if (eErr == OGRERR_NOT_ENOUGH_MEMORY)
60 : {
61 0 : throw SAXNotSupportedException("Out of memory");
62 : }
63 939 : else if (eErr != OGRERR_NONE)
64 : {
65 0 : throw SAXNotSupportedException("Other error during parsing");
66 : }
67 939 : }
68 :
69 : /************************************************************************/
70 : /* startElement() */
71 : /************************************************************************/
72 :
73 19 : void GMLXercesHandler::startElement(const XMLCh *const /*uri*/,
74 : const XMLCh *const localname,
75 : const XMLCh *const /*qname*/,
76 : const Attributes &attrs)
77 : {
78 19 : m_nEntityCounter = 0;
79 :
80 19 : transcode(localname, m_osElement);
81 :
82 19 : GMLXercesHandlerDealWithError(GMLHandler::startElement(
83 19 : m_osElement.c_str(), static_cast<int>(m_osElement.size()),
84 : const_cast<Attributes *>(&attrs)));
85 19 : }
86 :
87 : /************************************************************************/
88 : /* endElement() */
89 : /************************************************************************/
90 15 : void GMLXercesHandler::endElement(const XMLCh *const /*uri*/,
91 : const XMLCh *const /*localname*/,
92 : const XMLCh *const /*qname */)
93 : {
94 15 : m_nEntityCounter = 0;
95 :
96 15 : GMLXercesHandlerDealWithError(GMLHandler::endElement());
97 15 : }
98 :
99 : /************************************************************************/
100 : /* characters() */
101 : /************************************************************************/
102 :
103 905 : void GMLXercesHandler::characters(const XMLCh *const chars_in,
104 : const XMLSize_t length)
105 :
106 : {
107 905 : transcode(chars_in, m_osCharacters, static_cast<int>(length));
108 905 : GMLXercesHandlerDealWithError(GMLHandler::dataHandler(
109 905 : m_osCharacters.c_str(), static_cast<int>(m_osCharacters.size())));
110 905 : }
111 :
112 : /************************************************************************/
113 : /* fatalError() */
114 : /************************************************************************/
115 :
116 0 : void GMLXercesHandler::fatalError(const SAXParseException &exception)
117 :
118 : {
119 0 : CPLString osMsg;
120 0 : transcode(exception.getMessage(), osMsg);
121 0 : CPLError(CE_Failure, CPLE_AppDefined,
122 : "XML Parsing Error: %s at line %d, column %d\n", osMsg.c_str(),
123 0 : static_cast<int>(exception.getLineNumber()),
124 0 : static_cast<int>(exception.getColumnNumber()));
125 0 : }
126 :
127 : /************************************************************************/
128 : /* startEntity() */
129 : /************************************************************************/
130 :
131 1001 : void GMLXercesHandler::startEntity(const XMLCh *const /* name */)
132 : {
133 1001 : m_nEntityCounter++;
134 1001 : if (m_nEntityCounter > 1000 && !m_poReader->HasStoppedParsing())
135 : {
136 : throw SAXNotSupportedException(
137 1 : "File probably corrupted (million laugh pattern)");
138 : }
139 1000 : }
140 :
141 : /************************************************************************/
142 : /* GetFID() */
143 : /************************************************************************/
144 :
145 1 : const char *GMLXercesHandler::GetFID(void *attr)
146 : {
147 1 : const Attributes *attrs = static_cast<const Attributes *>(attr);
148 1 : const XMLCh achFID[] = {'f', 'i', 'd', '\0'};
149 1 : int nFIDIndex = attrs->getIndex(achFID);
150 1 : if (nFIDIndex != -1)
151 : {
152 1 : transcode(attrs->getValue(nFIDIndex), m_osFID);
153 1 : return m_osFID.c_str();
154 : }
155 : else
156 : {
157 0 : const XMLCh achGMLID[] = {'g', 'm', 'l', ':', 'i', 'd', '\0'};
158 0 : nFIDIndex = attrs->getIndex(achGMLID);
159 0 : if (nFIDIndex != -1)
160 : {
161 0 : transcode(attrs->getValue(nFIDIndex), m_osFID);
162 0 : return m_osFID.c_str();
163 : }
164 : }
165 :
166 0 : m_osFID.resize(0);
167 0 : return nullptr;
168 : }
169 :
170 : /************************************************************************/
171 : /* AddAttributes() */
172 : /************************************************************************/
173 :
174 2 : CPLXMLNode *GMLXercesHandler::AddAttributes(CPLXMLNode *psNode, void *attr)
175 : {
176 2 : const Attributes *attrs = static_cast<const Attributes *>(attr);
177 :
178 2 : CPLXMLNode *psLastChild = nullptr;
179 :
180 5 : for (unsigned int i = 0; i < attrs->getLength(); i++)
181 : {
182 3 : transcode(attrs->getQName(i), m_osAttrName);
183 3 : transcode(attrs->getValue(i), m_osAttrValue);
184 :
185 : CPLXMLNode *psChild =
186 3 : CPLCreateXMLNode(nullptr, CXT_Attribute, m_osAttrName.c_str());
187 3 : CPLCreateXMLNode(psChild, CXT_Text, m_osAttrValue.c_str());
188 :
189 3 : if (psLastChild == nullptr)
190 1 : psNode->psChild = psChild;
191 : else
192 2 : psLastChild->psNext = psChild;
193 3 : psLastChild = psChild;
194 : }
195 :
196 2 : return psLastChild;
197 : }
198 :
199 : /************************************************************************/
200 : /* GetAttributeValue() */
201 : /************************************************************************/
202 :
203 8 : char *GMLXercesHandler::GetAttributeValue(void *attr,
204 : const char *pszAttributeName)
205 : {
206 8 : const Attributes *attrs = static_cast<const Attributes *>(attr);
207 8 : for (unsigned int i = 0; i < attrs->getLength(); i++)
208 : {
209 0 : transcode(attrs->getQName(i), m_osAttrName);
210 0 : if (m_osAttrName == pszAttributeName)
211 : {
212 0 : transcode(attrs->getValue(i), m_osAttrValue);
213 0 : return CPLStrdup(m_osAttrValue);
214 : }
215 : }
216 8 : return nullptr;
217 : }
218 :
219 : /************************************************************************/
220 : /* GetAttributeByIdx() */
221 : /************************************************************************/
222 :
223 9 : char *GMLXercesHandler::GetAttributeByIdx(void *attr, unsigned int idx,
224 : char **ppszKey)
225 : {
226 9 : const Attributes *attrs = static_cast<const Attributes *>(attr);
227 9 : if (idx >= attrs->getLength())
228 : {
229 9 : *ppszKey = nullptr;
230 9 : return nullptr;
231 : }
232 0 : transcode(attrs->getQName(idx), m_osAttrName);
233 0 : transcode(attrs->getValue(idx), m_osAttrValue);
234 :
235 0 : *ppszKey = CPLStrdup(m_osAttrName);
236 0 : return CPLStrdup(m_osAttrValue);
237 : }
238 :
239 : #endif
240 :
241 : #ifdef HAVE_EXPAT
242 :
243 : /************************************************************************/
244 : /* GMLExpatHandler() */
245 : /************************************************************************/
246 :
247 604 : GMLExpatHandler::GMLExpatHandler(GMLReader *poReader, XML_Parser oParser)
248 : : GMLHandler(poReader), m_oParser(oParser), m_bStopParsing(false),
249 604 : m_nDataHandlerCounter(0)
250 : {
251 604 : }
252 :
253 : /************************************************************************/
254 : /* GMLExpatHandler::DealWithError() */
255 : /************************************************************************/
256 :
257 368803 : void GMLExpatHandler::DealWithError(OGRErr eErr)
258 : {
259 368803 : if (eErr != OGRERR_NONE)
260 : {
261 2 : m_bStopParsing = true;
262 2 : XML_StopParser(m_oParser, static_cast<XML_Bool>(false));
263 2 : if (eErr == OGRERR_NOT_ENOUGH_MEMORY)
264 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
265 : }
266 368803 : }
267 :
268 : /************************************************************************/
269 : /* startElementCbk() */
270 : /************************************************************************/
271 :
272 61116 : void XMLCALL GMLExpatHandler::startElementCbk(void *pUserData,
273 : const char *pszName,
274 : const char **ppszAttr)
275 :
276 : {
277 61116 : GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
278 61116 : if (pThis->m_bStopParsing)
279 0 : return;
280 :
281 61116 : const char *pszIter = pszName;
282 61116 : char ch = '\0';
283 801424 : while ((ch = *pszIter) != '\0')
284 : {
285 740308 : if (ch == ':')
286 47932 : pszName = pszIter + 1;
287 740308 : pszIter++;
288 : }
289 :
290 61116 : pThis->DealWithError(pThis->GMLHandler::startElement(
291 61116 : pszName, static_cast<int>(pszIter - pszName), ppszAttr));
292 : }
293 :
294 : /************************************************************************/
295 : /* endElementCbk() */
296 : /************************************************************************/
297 60989 : void XMLCALL GMLExpatHandler::endElementCbk(void *pUserData,
298 : const char * /* pszName */)
299 : {
300 60989 : GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
301 60989 : if (pThis->m_bStopParsing)
302 1 : return;
303 :
304 60988 : pThis->DealWithError(pThis->GMLHandler::endElement());
305 : }
306 :
307 : /************************************************************************/
308 : /* dataHandlerCbk() */
309 : /************************************************************************/
310 :
311 246700 : void XMLCALL GMLExpatHandler::dataHandlerCbk(void *pUserData, const char *data,
312 : int nLen)
313 :
314 : {
315 246700 : GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
316 246700 : if (pThis->m_bStopParsing)
317 0 : return;
318 :
319 246700 : pThis->m_nDataHandlerCounter++;
320 : // The size of the buffer that is fetched and that Expat parses is
321 : // PARSER_BUF_SIZE bytes. If the dataHandlerCbk() callback is called
322 : // more than PARSER_BUF_SIZE times, this means that one byte in the
323 : // file expands to more XML text fragments, which is the sign of a
324 : // likely abuse of <!ENTITY>
325 : // Note: the counter is zeroed by ResetDataHandlerCounter() before each
326 : // new XML parsing.
327 246700 : if (pThis->m_nDataHandlerCounter >= PARSER_BUF_SIZE)
328 : {
329 1 : CPLError(CE_Failure, CPLE_AppDefined,
330 : "File probably corrupted (million laugh pattern)");
331 1 : pThis->m_bStopParsing = true;
332 1 : XML_StopParser(pThis->m_oParser, static_cast<XML_Bool>(false));
333 1 : return;
334 : }
335 :
336 246699 : pThis->DealWithError(pThis->GMLHandler::dataHandler(data, nLen));
337 : }
338 :
339 : /************************************************************************/
340 : /* GetFID() */
341 : /************************************************************************/
342 :
343 3337 : const char *GMLExpatHandler::GetFID(void *attr)
344 : {
345 3337 : const char *const *papszIter = static_cast<const char *const *>(attr);
346 3355 : while (*papszIter)
347 : {
348 1840 : if (strcmp(*papszIter, "fid") == 0 || strcmp(*papszIter, "gml:id") == 0)
349 : {
350 1822 : return papszIter[1];
351 : }
352 18 : papszIter += 2;
353 : }
354 1515 : return nullptr;
355 : }
356 :
357 : /************************************************************************/
358 : /* AddAttributes() */
359 : /************************************************************************/
360 :
361 34117 : CPLXMLNode *GMLExpatHandler::AddAttributes(CPLXMLNode *psNode, void *attr)
362 : {
363 34117 : const char **papszIter = static_cast<const char **>(attr);
364 :
365 34117 : CPLXMLNode *psLastChild = nullptr;
366 :
367 49234 : while (*papszIter)
368 : {
369 : CPLXMLNode *psChild =
370 15117 : CPLCreateXMLNode(nullptr, CXT_Attribute, papszIter[0]);
371 15117 : CPLCreateXMLNode(psChild, CXT_Text, papszIter[1]);
372 :
373 15117 : if (psLastChild == nullptr)
374 14083 : psNode->psChild = psChild;
375 : else
376 1034 : psLastChild->psNext = psChild;
377 15117 : psLastChild = psChild;
378 :
379 15117 : papszIter += 2;
380 : }
381 :
382 34117 : return psLastChild;
383 : }
384 :
385 : /************************************************************************/
386 : /* GetAttributeValue() */
387 : /************************************************************************/
388 :
389 11175 : char *GMLExpatHandler::GetAttributeValue(void *attr,
390 : const char *pszAttributeName)
391 : {
392 11175 : const char *const *papszIter = static_cast<const char *const *>(attr);
393 11789 : while (*papszIter)
394 : {
395 861 : if (strcmp(*papszIter, pszAttributeName) == 0)
396 : {
397 247 : return CPLStrdup(papszIter[1]);
398 : }
399 614 : papszIter += 2;
400 : }
401 10928 : return nullptr;
402 : }
403 :
404 : /************************************************************************/
405 : /* GetAttributeByIdx() */
406 : /************************************************************************/
407 :
408 : // CAUTION: should be called with increasing idx starting from 0 and
409 : // no attempt to read beyond end of list.
410 13927 : char *GMLExpatHandler::GetAttributeByIdx(void *attr, unsigned int idx,
411 : char **ppszKey)
412 : {
413 13927 : const char *const *papszIter = static_cast<const char *const *>(attr);
414 13927 : if (papszIter[2 * idx] == nullptr)
415 : {
416 13305 : *ppszKey = nullptr;
417 13305 : return nullptr;
418 : }
419 622 : *ppszKey = CPLStrdup(papszIter[2 * idx]);
420 622 : return CPLStrdup(papszIter[2 * idx + 1]);
421 : }
422 :
423 : #endif
424 :
425 : static const char *const apszGMLGeometryElements[] = {
426 : "BoundingBox", /* ows:BoundingBox */
427 : "CompositeCurve",
428 : "CompositeSurface",
429 : "Shell", /* CityGML 3 */
430 : "Curve",
431 : "GeometryCollection", /* OGR < 1.8.0 bug... */
432 : "LineString",
433 : "MultiCurve",
434 : "MultiGeometry",
435 : "MultiLineString",
436 : "MultiPoint",
437 : "MultiPolygon",
438 : "MultiSurface",
439 : "Point",
440 : "Polygon",
441 : "PolygonPatch",
442 : "PolyhedralSurface",
443 : "SimplePolygon", /* GML 3.3 compact encoding */
444 : "SimpleRectangle", /* GML 3.3 compact encoding */
445 : "SimpleTriangle", /* GML 3.3 compact encoding */
446 : "SimpleMultiPoint", /* GML 3.3 compact encoding */
447 : "Solid",
448 : "Surface",
449 : "Tin",
450 : "TopoCurve",
451 : "TopoSurface",
452 : "Triangle",
453 : "TriangulatedSurface"};
454 :
455 : #define GML_GEOMETRY_TYPE_COUNT \
456 : static_cast<int>(sizeof(apszGMLGeometryElements) / \
457 : sizeof(apszGMLGeometryElements[0]))
458 :
459 108 : bool OGRGMLIsGeometryElement(const char *pszElement)
460 : {
461 3118 : for (const auto &pszGMLElement : apszGMLGeometryElements)
462 : {
463 3011 : if (strcmp(pszElement, pszGMLElement) == 0)
464 1 : return true;
465 : }
466 107 : return false;
467 : }
468 :
469 : struct _GeometryNamesStruct
470 : {
471 : unsigned long nHash;
472 : const char *pszName;
473 : };
474 :
475 : /************************************************************************/
476 : /* GMLHandler() */
477 : /************************************************************************/
478 :
479 606 : GMLHandler::GMLHandler(GMLReader *poReader)
480 : : pasGeometryNames(static_cast<GeometryNamesStruct *>(
481 1212 : CPLMalloc(GML_GEOMETRY_TYPE_COUNT * sizeof(GeometryNamesStruct)))),
482 : m_nSRSDimensionIfMissing(
483 606 : atoi(CPLGetConfigOption("GML_SRS_DIMENSION_IF_MISSING", "0"))),
484 1212 : m_poReader(poReader), eAppSchemaType(APPSCHEMA_GENERIC), nStackDepth(0)
485 : {
486 17574 : for (int i = 0; i < GML_GEOMETRY_TYPE_COUNT; i++)
487 : {
488 16968 : pasGeometryNames[i].pszName = apszGMLGeometryElements[i];
489 33936 : pasGeometryNames[i].nHash =
490 16968 : CPLHashSetHashStr(pasGeometryNames[i].pszName);
491 : }
492 606 : std::sort(pasGeometryNames, pasGeometryNames + GML_GEOMETRY_TYPE_COUNT,
493 103020 : [](const GeometryNamesStruct &a, const GeometryNamesStruct &b)
494 103020 : { return a.nHash < b.nHash; });
495 :
496 606 : stateStack[0] = STATE_TOP;
497 606 : }
498 :
499 : /************************************************************************/
500 : /* ~GMLHandler() */
501 : /************************************************************************/
502 :
503 606 : GMLHandler::~GMLHandler()
504 :
505 : {
506 606 : if (apsXMLNode.size() >= 2 && apsXMLNode[1].psNode != nullptr)
507 5 : CPLDestroyXMLNode(apsXMLNode[1].psNode);
508 :
509 606 : CPLFree(m_pszCurField);
510 606 : CPLFree(m_pszGeometry);
511 606 : CPLFree(m_pszCityGMLGenericAttrName);
512 606 : CPLFree(m_pszHref);
513 606 : CPLFree(m_pszUom);
514 606 : CPLFree(m_pszValue);
515 606 : CPLFree(m_pszKieli);
516 606 : CPLFree(pasGeometryNames);
517 606 : }
518 :
519 : /************************************************************************/
520 : /* startElement() */
521 : /************************************************************************/
522 :
523 61135 : OGRErr GMLHandler::startElement(const char *pszName, int nLenName, void *attr)
524 : {
525 : OGRErr eRet;
526 61135 : switch (stateStack[nStackDepth])
527 : {
528 604 : case STATE_TOP:
529 604 : eRet = startElementTop(pszName, nLenName, attr);
530 604 : break;
531 7267 : case STATE_DEFAULT:
532 7267 : eRet = startElementDefault(pszName, nLenName, attr);
533 7267 : break;
534 13444 : case STATE_FEATURE:
535 13444 : eRet = startElementFeatureAttribute(pszName, nLenName, attr);
536 13444 : break;
537 3426 : case STATE_PROPERTY:
538 3426 : eRet = startElementFeatureAttribute(pszName, nLenName, attr);
539 3426 : break;
540 165 : case STATE_FEATUREPROPERTY:
541 165 : eRet = startElementFeatureProperty(pszName, nLenName, attr);
542 165 : break;
543 29900 : case STATE_GEOMETRY:
544 29900 : eRet = startElementGeometry(pszName, nLenName, attr);
545 29900 : break;
546 4003 : case STATE_IGNORED_FEATURE:
547 4003 : eRet = OGRERR_NONE;
548 4003 : break;
549 1194 : case STATE_BOUNDED_BY:
550 1194 : eRet = startElementBoundedBy(pszName, nLenName, attr);
551 1194 : break;
552 1090 : case STATE_BOUNDED_BY_IN_FEATURE:
553 1090 : eRet = startElementGeometry(pszName, nLenName, attr);
554 1090 : break;
555 42 : case STATE_CITYGML_ATTRIBUTE:
556 42 : eRet = startElementCityGMLGenericAttr(pszName, nLenName, attr);
557 42 : break;
558 0 : default:
559 0 : eRet = OGRERR_NONE;
560 0 : break;
561 : }
562 61135 : m_nDepth++;
563 61135 : if (m_nDepth == 64)
564 : {
565 : // Avoid performance issues on files like
566 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=21737
567 3 : if (m_nUnlimitedDepth < 0)
568 : {
569 3 : m_nUnlimitedDepth = EQUAL(
570 : CPLGetConfigOption("OGR_GML_NESTING_LEVEL", ""), "UNLIMITED");
571 : }
572 3 : if (!m_nUnlimitedDepth)
573 : {
574 1 : CPLError(CE_Failure, CPLE_NotSupported,
575 : "Too deep XML nesting level (%d). "
576 : "Set the OGR_GML_NESTING_LEVEL configuration option to "
577 : "UNLIMITED to remove that limitation.",
578 : m_nDepth);
579 1 : eRet = OGRERR_FAILURE;
580 : }
581 : }
582 61135 : return eRet;
583 : }
584 :
585 : /************************************************************************/
586 : /* endElement() */
587 : /************************************************************************/
588 :
589 61003 : OGRErr GMLHandler::endElement()
590 : {
591 61003 : m_nDepth--;
592 61003 : switch (stateStack[nStackDepth])
593 : {
594 0 : case STATE_TOP:
595 0 : break;
596 4009 : case STATE_DEFAULT:
597 4009 : return endElementDefault();
598 6007 : case STATE_FEATURE:
599 6007 : return endElementFeature();
600 10730 : case STATE_PROPERTY:
601 10730 : return endElementAttribute();
602 189 : case STATE_FEATUREPROPERTY:
603 189 : return endElementFeatureProperty();
604 32993 : case STATE_GEOMETRY:
605 32993 : return endElementGeometry();
606 4348 : case STATE_IGNORED_FEATURE:
607 4348 : return endElementIgnoredFeature();
608 1477 : case STATE_BOUNDED_BY:
609 1477 : return endElementBoundedBy();
610 1166 : case STATE_BOUNDED_BY_IN_FEATURE:
611 1166 : return endElementBoundedByInFeature();
612 84 : case STATE_CITYGML_ATTRIBUTE:
613 84 : return endElementCityGMLGenericAttr();
614 0 : default:
615 0 : break;
616 : }
617 0 : return OGRERR_NONE;
618 : }
619 :
620 : /************************************************************************/
621 : /* dataHandler() */
622 : /************************************************************************/
623 :
624 247604 : OGRErr GMLHandler::dataHandler(const char *data, int nLen)
625 : {
626 247604 : switch (stateStack[nStackDepth])
627 : {
628 131875 : case STATE_TOP:
629 : case STATE_DEFAULT:
630 : case STATE_FEATURE:
631 131875 : break;
632 14365 : case STATE_PROPERTY:
633 14365 : return dataHandlerAttribute(data, nLen);
634 625 : case STATE_FEATUREPROPERTY:
635 625 : break;
636 92311 : case STATE_GEOMETRY:
637 92311 : return dataHandlerGeometry(data, nLen);
638 5876 : case STATE_IGNORED_FEATURE:
639 : case STATE_BOUNDED_BY:
640 5876 : break;
641 2414 : case STATE_BOUNDED_BY_IN_FEATURE:
642 2414 : return dataHandlerGeometry(data, nLen);
643 138 : case STATE_CITYGML_ATTRIBUTE:
644 138 : return dataHandlerAttribute(data, nLen);
645 0 : default:
646 0 : break;
647 : }
648 138376 : return OGRERR_NONE;
649 : }
650 :
651 : #define PUSH_STATE(val) \
652 : do \
653 : { \
654 : nStackDepth++; \
655 : CPLAssert(nStackDepth < STACK_SIZE); \
656 : stateStack[nStackDepth] = val; \
657 : } while (false)
658 : #define POP_STATE() nStackDepth--
659 :
660 : /************************************************************************/
661 : /* startElementBoundedBy() */
662 : /************************************************************************/
663 :
664 1194 : OGRErr GMLHandler::startElementBoundedBy(const char *pszName, int /*nLenName*/,
665 : void *attr)
666 : {
667 1194 : if (m_nDepth == 2 && strcmp(pszName, "Envelope") == 0)
668 : {
669 136 : char *pszGlobalSRSName = GetAttributeValue(attr, "srsName");
670 136 : m_poReader->SetGlobalSRSName(pszGlobalSRSName);
671 136 : CPLFree(pszGlobalSRSName);
672 :
673 136 : if (m_nSRSDimensionIfMissing == 0)
674 : {
675 : char *pszGlobalSRSDimension =
676 132 : GetAttributeValue(attr, "srsDimension");
677 132 : if (pszGlobalSRSDimension)
678 15 : m_nSRSDimensionIfMissing = atoi(pszGlobalSRSDimension);
679 132 : CPLFree(pszGlobalSRSDimension);
680 : }
681 : }
682 :
683 1194 : return OGRERR_NONE;
684 : }
685 :
686 : /************************************************************************/
687 : /* startElementGeometry() */
688 : /************************************************************************/
689 :
690 34120 : OGRErr GMLHandler::startElementGeometry(const char *pszName, int nLenName,
691 : void *attr)
692 : {
693 35210 : if (stateStack[nStackDepth] == STATE_BOUNDED_BY_IN_FEATURE &&
694 1090 : apsXMLNode.empty())
695 : {
696 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid <boundedBy> construct");
697 1 : return OGRERR_FAILURE;
698 : }
699 :
700 : /* Create new XML Element */
701 : CPLXMLNode *psCurNode =
702 34119 : static_cast<CPLXMLNode *>(CPLCalloc(sizeof(CPLXMLNode), 1));
703 34119 : psCurNode->eType = CXT_Element;
704 34119 : psCurNode->pszValue = static_cast<char *>(CPLMalloc(nLenName + 1));
705 34119 : memcpy(psCurNode->pszValue, pszName, nLenName + 1);
706 :
707 : /* Attach element as the last child of its parent */
708 34119 : NodeLastChild &sNodeLastChild = apsXMLNode.back();
709 34119 : CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
710 :
711 34119 : if (psLastChildParent == nullptr)
712 : {
713 18204 : CPLXMLNode *psParent = sNodeLastChild.psNode;
714 18204 : if (psParent)
715 14996 : psParent->psChild = psCurNode;
716 : }
717 : else
718 : {
719 15915 : psLastChildParent->psNext = psCurNode;
720 : }
721 34119 : sNodeLastChild.psLastChild = psCurNode;
722 :
723 : /* Add attributes to the element */
724 34119 : CPLXMLNode *psLastChildCurNode = AddAttributes(psCurNode, attr);
725 49239 : for (CPLXMLNode *psIter = psCurNode->psChild; psIter;
726 15120 : psIter = psIter->psNext)
727 : {
728 15120 : if (psIter->eType == CXT_Attribute &&
729 15120 : strcmp(psIter->pszValue, "xlink:href") == 0 &&
730 1091 : psIter->psChild->pszValue && psIter->psChild->pszValue[0] == '#')
731 : {
732 1091 : m_oMapElementToSubstitute[psIter->psChild->pszValue + 1] =
733 : psCurNode;
734 : }
735 : }
736 :
737 : /* Some CityGML lack a srsDimension="3" in posList, such as in */
738 : /* http://www.citygml.org/fileadmin/count.php?f=fileadmin%2Fcitygml%2Fdocs%2FFrankfurt_Street_Setting_LOD3.zip
739 : */
740 : /* So we have to add it manually */
741 71646 : if (strcmp(pszName, "posList") == 0 &&
742 35015 : CPLGetXMLValue(psCurNode, "srsDimension", nullptr) == nullptr &&
743 896 : m_nSRSDimensionIfMissing != 0)
744 : {
745 : CPLXMLNode *psChild =
746 118 : CPLCreateXMLNode(nullptr, CXT_Attribute, "srsDimension");
747 118 : CPLCreateXMLNode(psChild, CXT_Text,
748 118 : (m_nSRSDimensionIfMissing == 3) ? "3" : "2");
749 :
750 118 : if (psLastChildCurNode == nullptr)
751 118 : psCurNode->psChild = psChild;
752 : else
753 0 : psLastChildCurNode->psNext = psChild;
754 118 : psLastChildCurNode = psChild;
755 : }
756 :
757 : /* Push the element on the stack */
758 : NodeLastChild sNewNodeLastChild;
759 34119 : sNewNodeLastChild.psNode = psCurNode;
760 34119 : sNewNodeLastChild.psLastChild = psLastChildCurNode;
761 34119 : apsXMLNode.push_back(sNewNodeLastChild);
762 :
763 34119 : if (m_pszGeometry)
764 : {
765 0 : CPLFree(m_pszGeometry);
766 0 : m_pszGeometry = nullptr;
767 0 : m_nGeomAlloc = 0;
768 0 : m_nGeomLen = 0;
769 : }
770 :
771 34119 : return OGRERR_NONE;
772 : }
773 :
774 : /************************************************************************/
775 : /* startElementCityGMLGenericAttr() */
776 : /************************************************************************/
777 :
778 42 : OGRErr GMLHandler::startElementCityGMLGenericAttr(const char *pszName,
779 : int /*nLenName*/,
780 : void * /*attr*/)
781 : {
782 42 : if (strcmp(pszName, "value") == 0)
783 : {
784 42 : if (m_pszCurField)
785 : {
786 0 : CPLFree(m_pszCurField);
787 0 : m_pszCurField = nullptr;
788 0 : m_nCurFieldLen = 0;
789 0 : m_nCurFieldAlloc = 0;
790 : }
791 42 : m_bInCurField = true;
792 : }
793 :
794 42 : return OGRERR_NONE;
795 : }
796 :
797 : /************************************************************************/
798 : /* DealWithAttributes() */
799 : /************************************************************************/
800 :
801 13314 : void GMLHandler::DealWithAttributes(const char *pszName, int nLenName,
802 : void *attr)
803 : {
804 13314 : GMLReadState *poState = m_poReader->GetState();
805 13314 : GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
806 :
807 13314 : for (unsigned int idx = 0; true; idx++)
808 : {
809 13936 : char *pszAttrKey = nullptr;
810 :
811 13936 : char *pszAttrVal = GetAttributeByIdx(attr, idx, &pszAttrKey);
812 13936 : if (pszAttrVal == nullptr)
813 13314 : break;
814 :
815 622 : int nAttrIndex = 0;
816 622 : const char *pszAttrKeyNoNS = strchr(pszAttrKey, ':');
817 622 : if (pszAttrKeyNoNS != nullptr)
818 149 : pszAttrKeyNoNS++;
819 :
820 : /* If attribute is referenced by the .gfs */
821 771 : if (poClass->IsSchemaLocked() &&
822 50 : ((pszAttrKeyNoNS != nullptr &&
823 50 : (nAttrIndex = m_poReader->GetAttributeElementIndex(
824 116 : pszName, nLenName, pszAttrKeyNoNS)) != -1) ||
825 116 : ((nAttrIndex = m_poReader->GetAttributeElementIndex(
826 : pszName, nLenName, pszAttrKey)) != -1)))
827 : {
828 67 : nAttrIndex = FindRealPropertyByCheckingConditions(nAttrIndex, attr);
829 67 : if (nAttrIndex >= 0)
830 : {
831 65 : m_poReader->SetFeaturePropertyDirectly(nullptr, pszAttrVal,
832 : nAttrIndex);
833 65 : pszAttrVal = nullptr;
834 : }
835 : }
836 :
837 : /* Hard-coded historic cases */
838 555 : else if (strcmp(pszAttrKey, "xlink:href") == 0)
839 : {
840 14 : if ((m_bReportHref || m_poReader->ReportAllAttributes()) &&
841 7 : m_bInCurField)
842 : {
843 4 : CPLFree(m_pszHref);
844 4 : m_pszHref = pszAttrVal;
845 4 : pszAttrVal = nullptr;
846 : }
847 9 : else if ((!poClass->IsSchemaLocked() &&
848 9 : (m_bReportHref || m_poReader->ReportAllAttributes())) ||
849 3 : (poClass->IsSchemaLocked() &&
850 3 : (nAttrIndex = m_poReader->GetAttributeElementIndex(
851 6 : (std::string(pszName) + "_href").c_str(),
852 : nLenName + 5)) != -1))
853 : {
854 3 : poState->PushPath(pszName, nLenName);
855 3 : CPLString osPropNameHref = poState->osPath + "_href";
856 3 : poState->PopPath();
857 3 : m_poReader->SetFeaturePropertyDirectly(osPropNameHref,
858 : pszAttrVal, nAttrIndex);
859 3 : pszAttrVal = nullptr;
860 : }
861 : }
862 548 : else if (strcmp(pszAttrKey, "uom") == 0)
863 : {
864 20 : CPLFree(m_pszUom);
865 20 : m_pszUom = pszAttrVal;
866 20 : pszAttrVal = nullptr;
867 : }
868 528 : else if (strcmp(pszAttrKey, "value") == 0)
869 : {
870 10 : CPLFree(m_pszValue);
871 10 : m_pszValue = pszAttrVal;
872 10 : pszAttrVal = nullptr;
873 : }
874 : else /* Get language in 'kieli' attribute of 'teksti' element */
875 518 : if (eAppSchemaType == APPSCHEMA_MTKGML && nLenName == 6 &&
876 7 : strcmp(pszName, "teksti") == 0 &&
877 7 : strcmp(pszAttrKey, "kieli") == 0)
878 : {
879 7 : CPLFree(m_pszKieli);
880 7 : m_pszKieli = pszAttrVal;
881 7 : pszAttrVal = nullptr;
882 : }
883 :
884 : /* Should we report all attributes ? */
885 519 : else if (m_poReader->ReportAllAttributes() &&
886 8 : !poClass->IsSchemaLocked())
887 : {
888 8 : poState->PushPath(pszName, nLenName);
889 8 : CPLString osPropName = poState->osPath;
890 8 : poState->PopPath();
891 :
892 8 : m_poReader->SetFeaturePropertyDirectly(
893 : CPLSPrintf("%s@%s", osPropName.c_str(),
894 : pszAttrKeyNoNS ? pszAttrKeyNoNS : pszAttrKey),
895 : pszAttrVal, -1);
896 8 : pszAttrVal = nullptr;
897 : }
898 :
899 622 : CPLFree(pszAttrKey);
900 622 : CPLFree(pszAttrVal);
901 622 : }
902 :
903 : #if 0
904 : if( poClass->IsSchemaLocked() )
905 : {
906 : poState->PushPath( pszName, nLenName );
907 : CPLString osPath = poState->osPath;
908 : poState->PopPath();
909 : /* Find fields that match an attribute that is missing */
910 : for(int i=0; i < poClass->GetPropertyCount(); i++ )
911 : {
912 : GMLPropertyDefn* poProp = poClass->GetProperty(i);
913 : const char* pszSrcElement = poProp->GetSrcElement();
914 : if( poProp->GetType() == OFTStringList &&
915 : poProp->GetSrcElementLen() > osPath.size() &&
916 : strncmp(pszSrcElement, osPath, osPath.size()) == 0 &&
917 : pszSrcElement[osPath.size()] == '@' )
918 : {
919 : char* pszAttrVal = GetAttributeValue(attr, pszSrcElement + osPath.size() + 1);
920 : if( pszAttrVal == NULL )
921 : {
922 : const char* pszCond = poProp->GetCondition();
923 : if( pszCond == NULL || IsConditionMatched(pszCond, attr) )
924 : {
925 : m_poReader->SetFeaturePropertyDirectly( NULL, CPLStrdup(""), i );
926 : }
927 : }
928 : else
929 : CPLFree(pszAttrVal);
930 : }
931 : }
932 : }
933 : #endif
934 13314 : }
935 :
936 : /************************************************************************/
937 : /* IsConditionMatched() */
938 : /************************************************************************/
939 :
940 : /* FIXME! 'and' / 'or' operators are evaluated left to right, without */
941 : /* and precedence rules between them ! */
942 :
943 15 : bool GMLHandler::IsConditionMatched(const char *pszCondition, void *attr)
944 : {
945 15 : if (pszCondition == nullptr)
946 0 : return true;
947 :
948 15 : bool bSyntaxError = false;
949 30 : CPLString osCondAttr, osCondVal;
950 15 : const char *pszIter = pszCondition;
951 15 : bool bOpEqual = true;
952 19 : while (*pszIter == ' ')
953 4 : pszIter++;
954 15 : if (*pszIter != '@')
955 0 : bSyntaxError = true;
956 : else
957 : {
958 15 : pszIter++;
959 75 : while (*pszIter != '\0' && *pszIter != ' ' && *pszIter != '!' &&
960 65 : *pszIter != '=')
961 : {
962 60 : osCondAttr += *pszIter;
963 60 : pszIter++;
964 : }
965 15 : while (*pszIter == ' ')
966 0 : pszIter++;
967 :
968 15 : if (*pszIter == '!')
969 : {
970 10 : bOpEqual = false;
971 10 : pszIter++;
972 : }
973 :
974 15 : if (*pszIter != '=')
975 0 : bSyntaxError = true;
976 : else
977 : {
978 15 : pszIter++;
979 15 : while (*pszIter == ' ')
980 0 : pszIter++;
981 15 : if (*pszIter != '\'')
982 0 : bSyntaxError = true;
983 : else
984 : {
985 15 : pszIter++;
986 45 : while (*pszIter != '\0' && *pszIter != '\'')
987 : {
988 30 : osCondVal += *pszIter;
989 30 : pszIter++;
990 : }
991 15 : if (*pszIter != '\'')
992 0 : bSyntaxError = true;
993 : else
994 : {
995 15 : pszIter++;
996 21 : while (*pszIter == ' ')
997 6 : pszIter++;
998 : }
999 : }
1000 : }
1001 : }
1002 :
1003 15 : if (bSyntaxError)
1004 : {
1005 0 : CPLError(CE_Failure, CPLE_NotSupported,
1006 : "Invalid condition : %s. Must be of the form "
1007 : "@attrname[!]='attrvalue' [and|or other_cond]*. "
1008 : "'and' and 'or' operators cannot be mixed",
1009 : pszCondition);
1010 0 : return false;
1011 : }
1012 :
1013 15 : char *pszVal = GetAttributeValue(attr, osCondAttr);
1014 15 : if (pszVal == nullptr)
1015 0 : pszVal = CPLStrdup("");
1016 28 : const bool bCondMet = (bOpEqual && strcmp(pszVal, osCondVal) == 0) ||
1017 13 : (!bOpEqual && strcmp(pszVal, osCondVal) != 0);
1018 15 : CPLFree(pszVal);
1019 15 : if (*pszIter == '\0')
1020 9 : return bCondMet;
1021 :
1022 6 : if (STARTS_WITH(pszIter, "and"))
1023 : {
1024 6 : pszIter += 3;
1025 6 : if (!bCondMet)
1026 2 : return false;
1027 4 : return IsConditionMatched(pszIter, attr);
1028 : }
1029 :
1030 0 : if (STARTS_WITH(pszIter, "or"))
1031 : {
1032 0 : pszIter += 2;
1033 0 : if (bCondMet)
1034 0 : return true;
1035 0 : return IsConditionMatched(pszIter, attr);
1036 : }
1037 :
1038 0 : CPLError(
1039 : CE_Failure, CPLE_NotSupported,
1040 : "Invalid condition : %s. Must be of the form @attrname[!]='attrvalue' "
1041 : "[and|or other_cond]*. 'and' and 'or' operators cannot be mixed",
1042 : pszCondition);
1043 0 : return false;
1044 : }
1045 :
1046 : /************************************************************************/
1047 : /* FindRealPropertyByCheckingConditions() */
1048 : /************************************************************************/
1049 :
1050 3086 : int GMLHandler::FindRealPropertyByCheckingConditions(int nIdx, void *attr)
1051 : {
1052 3086 : GMLReadState *poState = m_poReader->GetState();
1053 3086 : GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
1054 :
1055 3086 : GMLPropertyDefn *poProp = poClass->GetProperty(nIdx);
1056 3086 : const char *pszCond = poProp->GetCondition();
1057 3086 : if (pszCond != nullptr && !IsConditionMatched(pszCond, attr))
1058 : {
1059 : /* try other attributes with same source element, but with different */
1060 : /* condition */
1061 4 : const char *pszSrcElement = poProp->GetSrcElement();
1062 4 : nIdx = -1;
1063 11 : for (int i = m_nAttributeIndex + 1; i < poClass->GetPropertyCount();
1064 : i++)
1065 : {
1066 9 : poProp = poClass->GetProperty(i);
1067 9 : if (strcmp(poProp->GetSrcElement(), pszSrcElement) == 0)
1068 : {
1069 5 : pszCond = poProp->GetCondition();
1070 5 : if (IsConditionMatched(pszCond, attr))
1071 : {
1072 2 : nIdx = i;
1073 2 : break;
1074 : }
1075 : }
1076 : }
1077 : }
1078 3086 : return nIdx;
1079 : }
1080 :
1081 : /************************************************************************/
1082 : /* startElementFeatureAttribute() */
1083 : /************************************************************************/
1084 :
1085 16870 : OGRErr GMLHandler::startElementFeatureAttribute(const char *pszName,
1086 : int nLenName, void *attr)
1087 : {
1088 :
1089 16870 : GMLReadState *poState = m_poReader->GetState();
1090 :
1091 19699 : if (m_bInCurField && m_nAttributeIndex >= 0 &&
1092 19699 : std::string_view(pszName, nLenName) == "timePosition")
1093 : {
1094 6 : poState->PushPath(pszName, nLenName);
1095 6 : return OGRERR_NONE;
1096 : }
1097 :
1098 : /* Reset flag */
1099 16864 : m_bInCurField = false;
1100 :
1101 : /* -------------------------------------------------------------------- */
1102 : /* If we are collecting geometry, or if we determine this is a */
1103 : /* geometry element then append to the geometry info. */
1104 : /* -------------------------------------------------------------------- */
1105 16864 : if (IsGeometryElement(pszName))
1106 : {
1107 : bool bReadGeometry;
1108 :
1109 : /* If the <GeometryElementPath> is defined in the .gfs, use it */
1110 : /* to read the appropriate geometry element */
1111 3135 : GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
1112 3135 : m_nGeometryPropertyIndex = 0;
1113 4429 : if (poClass->IsSchemaLocked() &&
1114 1294 : poClass->GetGeometryPropertyCount() == 0)
1115 : {
1116 0 : bReadGeometry = false;
1117 : }
1118 4429 : else if (poClass->IsSchemaLocked() &&
1119 4429 : poClass->GetGeometryPropertyCount() == 1 &&
1120 1132 : poClass->GetGeometryProperty(0)->GetSrcElement()[0] == '\0')
1121 : {
1122 70 : bReadGeometry = true;
1123 : }
1124 4289 : else if (poClass->IsSchemaLocked() &&
1125 1224 : poClass->GetGeometryPropertyCount() > 0)
1126 : {
1127 1224 : m_nGeometryPropertyIndex =
1128 1224 : poClass->GetGeometryPropertyIndexBySrcElement(
1129 : poState->osPath.c_str());
1130 1224 : bReadGeometry = (m_nGeometryPropertyIndex >= 0);
1131 : }
1132 1841 : else if (m_poReader->FetchAllGeometries())
1133 : {
1134 0 : bReadGeometry = true;
1135 : }
1136 1841 : else if (!poClass->IsSchemaLocked() && m_poReader->IsWFSJointLayer())
1137 : {
1138 12 : m_nGeometryPropertyIndex =
1139 12 : poClass->GetGeometryPropertyIndexBySrcElement(
1140 : poState->osPath.c_str());
1141 12 : if (m_nGeometryPropertyIndex < 0)
1142 : {
1143 4 : const char *pszElement = poState->osPath.c_str();
1144 4 : CPLString osFieldName;
1145 : /* Strip member| prefix. Should always be true normally */
1146 4 : if (STARTS_WITH(pszElement, "member|"))
1147 4 : osFieldName = pszElement + strlen("member|");
1148 :
1149 : /* Replace layer|property by layer_property */
1150 4 : size_t iPos = osFieldName.find('|');
1151 4 : if (iPos != std::string::npos)
1152 4 : osFieldName[iPos] = '.';
1153 :
1154 8 : poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
1155 4 : osFieldName, poState->osPath.c_str(), wkbUnknown, -1,
1156 8 : true));
1157 4 : m_nGeometryPropertyIndex = poClass->GetGeometryPropertyCount();
1158 : }
1159 12 : bReadGeometry = true;
1160 : }
1161 : else
1162 : {
1163 : /* AIXM special case: for RouteSegment, we only want to read Curve
1164 : * geometries */
1165 : /* not 'start' and 'end' geometries */
1166 1841 : if (eAppSchemaType == APPSCHEMA_AIXM &&
1167 12 : strcmp(poState->m_poFeature->GetClass()->GetName(),
1168 : "RouteSegment") == 0)
1169 0 : bReadGeometry = strcmp(pszName, "Curve") == 0;
1170 :
1171 : // AIXM special case: we want to read both horizontalProjection_location and
1172 : // horizontalProjection_linearExtent
1173 1841 : else if (eAppSchemaType == APPSCHEMA_AIXM &&
1174 12 : STARTS_WITH(poState->m_poFeature->GetClass()->GetName(),
1175 : "horizontalProjection_") == 0)
1176 12 : bReadGeometry =
1177 12 : STARTS_WITH(pszName, "horizontalProjection_") == 0;
1178 :
1179 : /* For Inspire objects : the "main" geometry is in a <geometry>
1180 : * element */
1181 1817 : else if (m_bAlreadyFoundGeometry)
1182 2 : bReadGeometry = false;
1183 1815 : else if (strcmp(poState->osPath.c_str(), "geometry") == 0)
1184 : {
1185 8 : m_bAlreadyFoundGeometry = true;
1186 8 : bReadGeometry = true;
1187 8 : m_nGeometryPropertyIndex =
1188 8 : poClass->GetGeometryPropertyIndexBySrcElement(
1189 : poState->osPath.c_str());
1190 8 : if (m_nGeometryPropertyIndex < 0)
1191 : {
1192 6 : poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
1193 3 : "geometry", poState->osPath.c_str(), wkbUnknown, -1,
1194 3 : true));
1195 3 : m_nGeometryPropertyIndex =
1196 3 : poClass->GetGeometryPropertyCount();
1197 : }
1198 : }
1199 : else
1200 : {
1201 3614 : if (!poClass->IsSchemaLocked() &&
1202 1807 : poClass->IsConsistentSingleGeomElemPath())
1203 : {
1204 1797 : m_osLastGeomPathInCurFeature = poState->osPath;
1205 : }
1206 1807 : bReadGeometry = true;
1207 : }
1208 : }
1209 3135 : if (bReadGeometry)
1210 : {
1211 3130 : m_nGeometryDepth = m_nDepth;
1212 :
1213 3130 : CPLAssert(apsXMLNode.empty());
1214 :
1215 : NodeLastChild sNodeLastChild;
1216 3130 : sNodeLastChild.psNode = nullptr;
1217 3130 : sNodeLastChild.psLastChild = nullptr;
1218 3130 : apsXMLNode.push_back(sNodeLastChild);
1219 :
1220 3130 : PUSH_STATE(STATE_GEOMETRY);
1221 :
1222 3130 : return startElementGeometry(pszName, nLenName, attr);
1223 : }
1224 : }
1225 13729 : else if (
1226 12364 : (nLenName == 9 || nLenName == 8) &&
1227 2548 : (strcmp(pszName, "boundedBy") == 0 ||
1228 27467 : strcmp(pszName, "boundary") == 0) &&
1229 : // We ignore the UseBBOX() flag for CityGML, since CityGML
1230 : // has elements like bldg:boundedBy or 'boundary', which are not a simple
1231 : // rectangular bbox. This is needed to read properly
1232 : // autotest/ogr/data/gml/citygml_lod2_713_5322.xml
1233 : // (this is a workaround of not being namespace aware)
1234 967 : (eAppSchemaType == APPSCHEMA_CITYGML || m_poReader->UseBBOX()))
1235 : {
1236 78 : m_inBoundedByDepth = m_nDepth;
1237 :
1238 78 : CPLAssert(apsXMLNode.empty());
1239 :
1240 : NodeLastChild sNodeLastChild;
1241 78 : sNodeLastChild.psNode = nullptr;
1242 78 : sNodeLastChild.psLastChild = nullptr;
1243 78 : apsXMLNode.push_back(sNodeLastChild);
1244 :
1245 78 : PUSH_STATE(STATE_BOUNDED_BY_IN_FEATURE);
1246 :
1247 78 : return OGRERR_NONE;
1248 : }
1249 :
1250 : /* -------------------------------------------------------------------- */
1251 : /* Is it a CityGML generic attribute ? */
1252 : /* -------------------------------------------------------------------- */
1253 13787 : else if (eAppSchemaType == APPSCHEMA_CITYGML &&
1254 136 : m_poReader->IsCityGMLGenericAttributeElement(pszName, attr))
1255 : {
1256 42 : CPLFree(m_pszCityGMLGenericAttrName);
1257 42 : m_pszCityGMLGenericAttrName = GetAttributeValue(attr, "name");
1258 42 : m_inCityGMLGenericAttrDepth = m_nDepth;
1259 :
1260 42 : PUSH_STATE(STATE_CITYGML_ATTRIBUTE);
1261 :
1262 42 : return OGRERR_NONE;
1263 : }
1264 :
1265 13609 : else if (m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 1)
1266 : {
1267 : }
1268 :
1269 13485 : else if (m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 2)
1270 : {
1271 124 : const char *pszFID = GetFID(attr);
1272 124 : if (pszFID)
1273 : {
1274 124 : poState->PushPath(pszName, nLenName);
1275 248 : CPLString osPropPath = poState->osPath + "@id";
1276 124 : poState->PopPath();
1277 124 : m_poReader->SetFeaturePropertyDirectly(osPropPath,
1278 : CPLStrdup(pszFID), -1);
1279 : }
1280 : }
1281 :
1282 : /* -------------------------------------------------------------------- */
1283 : /* If it is (or at least potentially is) a simple attribute, */
1284 : /* then start collecting it. */
1285 : /* -------------------------------------------------------------------- */
1286 13361 : else if ((m_nAttributeIndex = m_poReader->GetAttributeElementIndex(
1287 13361 : pszName, nLenName)) != -1)
1288 : {
1289 10810 : GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
1290 16895 : if (poClass->IsSchemaLocked() &&
1291 3043 : (poClass->GetProperty(m_nAttributeIndex)->GetType() ==
1292 3042 : GMLPT_FeatureProperty ||
1293 3042 : poClass->GetProperty(m_nAttributeIndex)->GetType() ==
1294 : GMLPT_FeaturePropertyList))
1295 : {
1296 24 : m_nAttributeDepth = m_nDepth;
1297 24 : PUSH_STATE(STATE_FEATUREPROPERTY);
1298 : }
1299 : else
1300 : {
1301 : /* Is this a property with a condition on an attribute value ? */
1302 10786 : if (poClass->IsSchemaLocked())
1303 : {
1304 3019 : m_nAttributeIndex = FindRealPropertyByCheckingConditions(
1305 : m_nAttributeIndex, attr);
1306 : }
1307 :
1308 10786 : if (m_nAttributeIndex >= 0)
1309 : {
1310 10786 : if (m_pszCurField)
1311 : {
1312 1277 : CPLFree(m_pszCurField);
1313 1277 : m_pszCurField = nullptr;
1314 1277 : m_nCurFieldLen = 0;
1315 1277 : m_nCurFieldAlloc = 0;
1316 : }
1317 10786 : m_bInCurField = true;
1318 :
1319 10786 : char *pszXSINil = GetAttributeValue(attr, "xsi:nil");
1320 10786 : if (pszXSINil)
1321 : {
1322 23 : if (EQUAL(pszXSINil, "true"))
1323 23 : m_poReader->SetFeaturePropertyDirectly(
1324 : pszName, CPLStrdup(OGR_GML_NULL), -1);
1325 23 : CPLFree(pszXSINil);
1326 : }
1327 : else
1328 : {
1329 10763 : DealWithAttributes(pszName, nLenName, attr);
1330 : }
1331 :
1332 10786 : if (stateStack[nStackDepth] != STATE_PROPERTY)
1333 : {
1334 8886 : m_nAttributeDepth = m_nDepth;
1335 8886 : PUSH_STATE(STATE_PROPERTY);
1336 : }
1337 : }
1338 : /*else
1339 : {
1340 : DealWithAttributes(pszName, nLenName, attr);
1341 : }*/
1342 : }
1343 : }
1344 : else
1345 : {
1346 2551 : DealWithAttributes(pszName, nLenName, attr);
1347 : }
1348 :
1349 13614 : poState->PushPath(pszName, nLenName);
1350 :
1351 13614 : return OGRERR_NONE;
1352 : }
1353 :
1354 : /************************************************************************/
1355 : /* startElementTop() */
1356 : /************************************************************************/
1357 :
1358 604 : OGRErr GMLHandler::startElementTop(const char *pszName, int /*nLenName*/,
1359 : void *attr)
1360 : {
1361 604 : if (strcmp(pszName, "CityModel") == 0)
1362 : {
1363 10 : eAppSchemaType = APPSCHEMA_CITYGML;
1364 : // Default to 3D geometries for CityGML (#6989)
1365 10 : if (m_nSRSDimensionIfMissing == 0)
1366 10 : m_nSRSDimensionIfMissing = 3;
1367 : }
1368 594 : else if (strcmp(pszName, "AIXMBasicMessage") == 0)
1369 : {
1370 7 : eAppSchemaType = APPSCHEMA_AIXM;
1371 7 : m_bReportHref = true;
1372 : }
1373 587 : else if (strcmp(pszName, "Maastotiedot") == 0)
1374 : {
1375 8 : eAppSchemaType = APPSCHEMA_MTKGML;
1376 :
1377 8 : char *pszSRSName = GetAttributeValue(attr, "srsName");
1378 8 : m_poReader->SetGlobalSRSName(pszSRSName);
1379 8 : CPLFree(pszSRSName);
1380 :
1381 8 : m_bReportHref = true;
1382 :
1383 : /* the schemas of MTKGML don't have (string) width, so don't set it */
1384 8 : m_poReader->SetWidthFlag(false);
1385 : }
1386 :
1387 604 : stateStack[0] = STATE_DEFAULT;
1388 :
1389 604 : return OGRERR_NONE;
1390 : }
1391 :
1392 : /************************************************************************/
1393 : /* startElementDefault() */
1394 : /************************************************************************/
1395 :
1396 7267 : OGRErr GMLHandler::startElementDefault(const char *pszName, int nLenName,
1397 : void *attr)
1398 :
1399 : {
1400 : /* -------------------------------------------------------------------- */
1401 : /* Is it a feature? If so push a whole new state, and return. */
1402 : /* -------------------------------------------------------------------- */
1403 : int nClassIndex;
1404 7267 : const char *pszFilteredClassName = nullptr;
1405 :
1406 7267 : if (nLenName == 9 && strcmp(pszName, "boundedBy") == 0)
1407 : {
1408 283 : m_inBoundedByDepth = m_nDepth;
1409 :
1410 283 : PUSH_STATE(STATE_BOUNDED_BY);
1411 :
1412 283 : return OGRERR_NONE;
1413 : }
1414 :
1415 7805 : else if (m_poReader->ShouldLookForClassAtAnyLevel() &&
1416 821 : (pszFilteredClassName = m_poReader->GetFilteredClassName()) !=
1417 : nullptr)
1418 : {
1419 821 : if (strcmp(pszName, pszFilteredClassName) == 0)
1420 : {
1421 49 : m_poReader->PushFeature(pszName, GetFID(attr),
1422 49 : m_poReader->GetFilteredClassIndex());
1423 :
1424 49 : m_nDepthFeature = m_nDepth;
1425 :
1426 49 : PUSH_STATE(STATE_FEATURE);
1427 :
1428 49 : return OGRERR_NONE;
1429 : }
1430 : }
1431 :
1432 : /* WFS 2.0 GetFeature documents have a wfs:FeatureCollection */
1433 : /* as a wfs:member of the top wfs:FeatureCollection. We don't want this */
1434 : /* wfs:FeatureCollection to be recognized as a feature */
1435 6191 : else if ((!(nLenName == static_cast<int>(strlen("FeatureCollection")) &&
1436 12312 : strcmp(pszName, "FeatureCollection") == 0)) &&
1437 6149 : (nClassIndex = m_poReader->GetFeatureElementIndex(
1438 : pszName, nLenName, eAppSchemaType)) != -1)
1439 : {
1440 3508 : m_bAlreadyFoundGeometry = false;
1441 :
1442 3508 : pszFilteredClassName = m_poReader->GetFilteredClassName();
1443 3508 : if (pszFilteredClassName != nullptr &&
1444 377 : strcmp(pszName, pszFilteredClassName) != 0)
1445 : {
1446 345 : m_nDepthFeature = m_nDepth;
1447 :
1448 345 : PUSH_STATE(STATE_IGNORED_FEATURE);
1449 :
1450 345 : return OGRERR_NONE;
1451 : }
1452 : else
1453 : {
1454 3163 : if (eAppSchemaType == APPSCHEMA_MTKGML)
1455 : {
1456 22 : m_poReader->PushFeature(pszName, nullptr, nClassIndex);
1457 :
1458 22 : char *pszGID = GetAttributeValue(attr, "gid");
1459 22 : if (pszGID)
1460 21 : m_poReader->SetFeaturePropertyDirectly("gid", pszGID, -1,
1461 : GMLPT_String);
1462 : }
1463 : else
1464 3141 : m_poReader->PushFeature(pszName, GetFID(attr), nClassIndex);
1465 :
1466 3163 : m_nDepthFeature = m_nDepth;
1467 :
1468 3163 : PUSH_STATE(STATE_FEATURE);
1469 :
1470 3163 : return OGRERR_NONE;
1471 : }
1472 : }
1473 :
1474 : /* -------------------------------------------------------------------- */
1475 : /* Push the element onto the current state's path. */
1476 : /* -------------------------------------------------------------------- */
1477 3427 : m_poReader->GetState()->PushPath(pszName, nLenName);
1478 :
1479 3427 : return OGRERR_NONE;
1480 : }
1481 :
1482 : /************************************************************************/
1483 : /* endElementIgnoredFeature() */
1484 : /************************************************************************/
1485 :
1486 4348 : OGRErr GMLHandler::endElementIgnoredFeature()
1487 :
1488 : {
1489 4348 : if (m_nDepth == m_nDepthFeature)
1490 : {
1491 345 : POP_STATE();
1492 : }
1493 4348 : return OGRERR_NONE;
1494 : }
1495 :
1496 : /************************************************************************/
1497 : /* endElementBoundedBy() */
1498 : /************************************************************************/
1499 1477 : OGRErr GMLHandler::endElementBoundedBy()
1500 :
1501 : {
1502 1477 : if (m_inBoundedByDepth == m_nDepth)
1503 : {
1504 283 : POP_STATE();
1505 : }
1506 :
1507 1477 : return OGRERR_NONE;
1508 : }
1509 :
1510 : /************************************************************************/
1511 : /* endElementBoundedByInFeature() */
1512 : /************************************************************************/
1513 1166 : OGRErr GMLHandler::endElementBoundedByInFeature()
1514 :
1515 : {
1516 1166 : if (m_nDepth > m_inBoundedByDepth)
1517 : {
1518 1089 : if (m_nDepth == m_inBoundedByDepth + 1)
1519 : {
1520 78 : m_nGeometryDepth = m_nDepth;
1521 : }
1522 1089 : return endElementGeometry();
1523 : }
1524 : else
1525 : {
1526 77 : POP_STATE();
1527 77 : if (apsXMLNode.size() >= 2 && apsXMLNode[1].psNode != nullptr)
1528 0 : CPLDestroyXMLNode(apsXMLNode[1].psNode);
1529 77 : apsXMLNode.clear();
1530 77 : return OGRERR_NONE;
1531 : }
1532 : }
1533 :
1534 : /************************************************************************/
1535 : /* ParseAIXMElevationProperties() */
1536 : /************************************************************************/
1537 :
1538 10 : void GMLHandler::ParseAIXMElevationProperties(const CPLXMLNode *psGML)
1539 : {
1540 10 : if (const char *pszElevation = CPLGetXMLValue(psGML, "elevation", nullptr))
1541 : {
1542 7 : m_poReader->SetFeaturePropertyDirectly("elevation",
1543 : CPLStrdup(pszElevation), -1);
1544 : const char *pszElevationUnit =
1545 7 : CPLGetXMLValue(psGML, "elevation.uom", nullptr);
1546 7 : if (pszElevationUnit)
1547 : {
1548 5 : m_poReader->SetFeaturePropertyDirectly(
1549 : "elevation_uom", CPLStrdup(pszElevationUnit), -1);
1550 : }
1551 : }
1552 :
1553 10 : if (const char *pszGeoidUndulation =
1554 10 : CPLGetXMLValue(psGML, "geoidUndulation", nullptr))
1555 : {
1556 0 : m_poReader->SetFeaturePropertyDirectly(
1557 : "geoidUndulation", CPLStrdup(pszGeoidUndulation), -1);
1558 : const char *pszGeoidUndulationUnit =
1559 0 : CPLGetXMLValue(psGML, "geoidUndulation.uom", nullptr);
1560 0 : if (pszGeoidUndulationUnit)
1561 : {
1562 0 : m_poReader->SetFeaturePropertyDirectly(
1563 : "geoidUndulation_uom", CPLStrdup(pszGeoidUndulationUnit), -1);
1564 : }
1565 : }
1566 :
1567 10 : if (const char *pszVerticalDatum =
1568 10 : CPLGetXMLValue(psGML, "verticalDatum", nullptr))
1569 : {
1570 3 : m_poReader->SetFeaturePropertyDirectly("verticalDatum",
1571 : CPLStrdup(pszVerticalDatum), -1);
1572 : }
1573 :
1574 10 : if (const char *pszVerticalAccuracy =
1575 10 : CPLGetXMLValue(psGML, "verticalAccuracy", nullptr))
1576 : {
1577 5 : m_poReader->SetFeaturePropertyDirectly(
1578 : "verticalAccuracy", CPLStrdup(pszVerticalAccuracy), -1);
1579 : const char *pszVerticalAccuracyUnit =
1580 5 : CPLGetXMLValue(psGML, "verticalAccuracy.uom", nullptr);
1581 5 : if (pszVerticalAccuracyUnit)
1582 : {
1583 3 : m_poReader->SetFeaturePropertyDirectly(
1584 : "verticalAccuracy_uom", CPLStrdup(pszVerticalAccuracyUnit), -1);
1585 : }
1586 : }
1587 10 : }
1588 :
1589 : /************************************************************************/
1590 : /* ParseAIXMElevationPoint() */
1591 : /************************************************************************/
1592 :
1593 2 : CPLXMLNode *GMLHandler::ParseAIXMElevationPoint(CPLXMLNode *psGML)
1594 : {
1595 2 : ParseAIXMElevationProperties(psGML);
1596 :
1597 2 : const char *pszPos = CPLGetXMLValue(psGML, "pos", nullptr);
1598 2 : const char *pszCoordinates = CPLGetXMLValue(psGML, "coordinates", nullptr);
1599 2 : if (pszPos != nullptr || pszCoordinates != nullptr)
1600 : {
1601 2 : CPLFree(psGML->pszValue);
1602 2 : psGML->pszValue = CPLStrdup("gml:Point");
1603 : }
1604 : else
1605 : {
1606 0 : CPLDestroyXMLNode(psGML);
1607 0 : psGML = nullptr;
1608 : }
1609 :
1610 2 : return psGML;
1611 : }
1612 :
1613 : /************************************************************************/
1614 : /* nodeHasChild() */
1615 : /************************************************************************/
1616 :
1617 735 : static bool nodeHasChild(const CPLXMLNode *psHaystack,
1618 : const CPLXMLNode *psNeedle)
1619 : {
1620 735 : if (psHaystack == psNeedle)
1621 2 : return true;
1622 1299 : for (const CPLXMLNode *psChild = psHaystack->psChild; psChild;
1623 566 : psChild = psChild->psNext)
1624 : {
1625 569 : if (nodeHasChild(psChild, psNeedle))
1626 3 : return true;
1627 : }
1628 730 : return false;
1629 : }
1630 :
1631 : /************************************************************************/
1632 : /* endElementGeometry() */
1633 : /************************************************************************/
1634 34082 : OGRErr GMLHandler::endElementGeometry()
1635 :
1636 : {
1637 34082 : if (m_nGeomLen)
1638 : {
1639 : CPLXMLNode *psNode =
1640 5130 : static_cast<CPLXMLNode *>(CPLCalloc(sizeof(CPLXMLNode), 1));
1641 5130 : psNode->eType = CXT_Text;
1642 5130 : psNode->pszValue = m_pszGeometry;
1643 :
1644 5130 : NodeLastChild &sNodeLastChild = apsXMLNode.back();
1645 5130 : CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
1646 5130 : if (psLastChildParent == nullptr)
1647 : {
1648 2423 : CPLXMLNode *psParent = sNodeLastChild.psNode;
1649 2423 : if (psParent)
1650 2423 : psParent->psChild = psNode;
1651 : }
1652 : else
1653 2707 : psLastChildParent->psNext = psNode;
1654 5130 : sNodeLastChild.psLastChild = psNode;
1655 :
1656 5130 : m_pszGeometry = nullptr;
1657 5130 : m_nGeomAlloc = 0;
1658 5130 : m_nGeomLen = 0;
1659 : }
1660 :
1661 34082 : CPLXMLNode *psThisNode = apsXMLNode.back().psNode;
1662 34082 : CPLXMLNode *psThisNodeChild = psThisNode->psChild;
1663 41049 : if (!m_oMapElementToSubstitute.empty() && psThisNodeChild &&
1664 6967 : psThisNodeChild->eType == CXT_Attribute &&
1665 42166 : strcmp(psThisNodeChild->pszValue, "gml:id") == 0 &&
1666 1117 : psThisNodeChild->psChild->pszValue)
1667 : {
1668 : auto oIter =
1669 1117 : m_oMapElementToSubstitute.find(psThisNodeChild->psChild->pszValue);
1670 1117 : if (oIter != m_oMapElementToSubstitute.end())
1671 : {
1672 166 : const auto psElementToSubstitute = oIter->second;
1673 166 : if (nodeHasChild(psElementToSubstitute, psThisNode))
1674 : {
1675 2 : CPLDebug("GML", "href #%s referencing one of its subnodes",
1676 2 : psThisNodeChild->psChild->pszValue);
1677 : /* Can happen in situations like:
1678 : <foo xlink:href="#X">
1679 : <bar gml:id="X"/>
1680 : </foo>
1681 : Do not attempt substitution as that would cause a memory
1682 : leak.
1683 : */
1684 : }
1685 : else
1686 : {
1687 164 : auto psLastChild = oIter->second->psChild;
1688 164 : if (psLastChild)
1689 : {
1690 : // CPLDebug("GML", "Substitution of xlink:href=\"#%s\" with actual content", psThisNodeChild->psChild->pszValue);
1691 164 : CPLXMLNode *psAfter = psThisNode->psNext;
1692 164 : psThisNode->psNext = nullptr;
1693 : // We can patch oIter->second as it stored as it in the current
1694 : // GMLFeature.
1695 : // Of course that would no longer be the case in case of
1696 : // cross-references between different GMLFeature, hence we clear
1697 : // m_oMapElementToSubstitute at the end of the current feature.
1698 252 : while (psLastChild->psNext)
1699 88 : psLastChild = psLastChild->psNext;
1700 164 : psLastChild->psNext = CPLCloneXMLTree(psThisNode);
1701 164 : psThisNode->psNext = psAfter;
1702 : }
1703 : }
1704 : }
1705 : }
1706 :
1707 34082 : if (m_nDepth == m_nGeometryDepth)
1708 : {
1709 3203 : m_nGeometryDepth = 0;
1710 :
1711 3203 : CPLAssert(apsXMLNode.size() == 2);
1712 3203 : CPLXMLNode *psInterestNode = apsXMLNode.back().psNode;
1713 :
1714 : /*char* pszXML = CPLSerializeXMLTree(psInterestNode);
1715 : CPLDebug("GML", "geometry = %s", pszXML);
1716 : CPLFree(pszXML);*/
1717 :
1718 3203 : apsXMLNode.pop_back();
1719 :
1720 : /* AIXM ElevatedPoint. We want to parse this */
1721 : /* a bit specially because ElevatedPoint is aixm: stuff and */
1722 : /* the srsDimension of the <gml:pos> can be set to true although */
1723 : /* they are only 2 coordinates in practice */
1724 3203 : if (eAppSchemaType == APPSCHEMA_AIXM && psInterestNode != nullptr &&
1725 12 : strcmp(psInterestNode->pszValue, "ElevatedPoint") == 0)
1726 : {
1727 2 : psInterestNode = ParseAIXMElevationPoint(psInterestNode);
1728 : }
1729 3201 : else if (eAppSchemaType == APPSCHEMA_AIXM &&
1730 10 : psInterestNode != nullptr &&
1731 10 : (strcmp(psInterestNode->pszValue, "ElevatedCurve") == 0 ||
1732 2 : strcmp(psInterestNode->pszValue, "ElevateSurface") == 0))
1733 : {
1734 8 : ParseAIXMElevationProperties(psInterestNode);
1735 : }
1736 3193 : else if (eAppSchemaType == APPSCHEMA_MTKGML &&
1737 : psInterestNode != nullptr)
1738 : {
1739 36 : if (strcmp(psInterestNode->pszValue, "Murtoviiva") == 0)
1740 : {
1741 7 : CPLFree(psInterestNode->pszValue);
1742 7 : psInterestNode->pszValue = CPLStrdup("gml:LineString");
1743 : }
1744 29 : else if (strcmp(psInterestNode->pszValue, "Alue") == 0)
1745 : {
1746 7 : CPLFree(psInterestNode->pszValue);
1747 7 : psInterestNode->pszValue = CPLStrdup("gml:Polygon");
1748 : }
1749 22 : else if (strcmp(psInterestNode->pszValue, "Piste") == 0)
1750 : {
1751 21 : CPLFree(psInterestNode->pszValue);
1752 21 : psInterestNode->pszValue = CPLStrdup("gml:Point");
1753 : }
1754 : }
1755 3157 : else if (psInterestNode != nullptr &&
1756 3157 : strcmp(psInterestNode->pszValue, "BoundingBox") == 0)
1757 : {
1758 27 : CPLFree(psInterestNode->pszValue);
1759 27 : psInterestNode->pszValue = CPLStrdup("Envelope");
1760 30 : for (CPLXMLNode *psChild = psInterestNode->psChild; psChild;
1761 3 : psChild = psChild->psNext)
1762 : {
1763 29 : if (psChild->eType == CXT_Attribute &&
1764 27 : strcmp(psChild->pszValue, "crs") == 0)
1765 : {
1766 26 : CPLFree(psChild->pszValue);
1767 26 : psChild->pszValue = CPLStrdup("srsName");
1768 26 : break;
1769 : }
1770 : }
1771 : }
1772 :
1773 3203 : GMLFeature *poGMLFeature = m_poReader->GetState()->m_poFeature;
1774 3203 : if (stateStack[nStackDepth] == STATE_BOUNDED_BY_IN_FEATURE)
1775 : {
1776 78 : if (eAppSchemaType == APPSCHEMA_CITYGML)
1777 58 : CPLDestroyXMLNode(psInterestNode);
1778 : else
1779 20 : poGMLFeature->SetBoundedByGeometry(psInterestNode);
1780 : }
1781 : else
1782 : {
1783 3125 : if (m_poReader->FetchAllGeometries())
1784 0 : poGMLFeature->AddGeometry(psInterestNode);
1785 : else
1786 : {
1787 3125 : GMLFeatureClass *poClass = poGMLFeature->GetClass();
1788 3125 : if (poClass->GetGeometryPropertyCount() > 1)
1789 : {
1790 172 : if (poGMLFeature->GetGeometryRef(m_nGeometryPropertyIndex))
1791 : {
1792 : // If we have already a geometry, setting a new one
1793 : // will invalidate nodes potentially stored in
1794 : // m_oMapElementToSubstitute, so clear it
1795 0 : m_oMapElementToSubstitute.clear();
1796 : }
1797 172 : poGMLFeature->SetGeometryDirectly(m_nGeometryPropertyIndex,
1798 : psInterestNode);
1799 : }
1800 : else
1801 : {
1802 2953 : if (poGMLFeature->GetGeometryRef(0))
1803 : {
1804 : // If we have already a geometry, setting a new one
1805 : // will invalidate nodes potentially stored in
1806 : // m_oMapElementToSubstitute, so clear it
1807 29 : m_oMapElementToSubstitute.clear();
1808 : }
1809 2953 : poGMLFeature->SetGeometryDirectly(psInterestNode);
1810 : }
1811 : }
1812 :
1813 3125 : POP_STATE();
1814 : }
1815 : }
1816 :
1817 34082 : apsXMLNode.pop_back();
1818 :
1819 34082 : return OGRERR_NONE;
1820 : }
1821 :
1822 : /************************************************************************/
1823 : /* endElementCityGMLGenericAttr() */
1824 : /************************************************************************/
1825 84 : OGRErr GMLHandler::endElementCityGMLGenericAttr()
1826 :
1827 : {
1828 84 : if (m_pszCityGMLGenericAttrName != nullptr && m_bInCurField)
1829 : {
1830 42 : if (m_pszCurField != nullptr)
1831 : {
1832 42 : m_poReader->SetFeaturePropertyDirectly(m_pszCityGMLGenericAttrName,
1833 : m_pszCurField, -1);
1834 : }
1835 42 : m_pszCurField = nullptr;
1836 42 : m_nCurFieldLen = 0;
1837 42 : m_nCurFieldAlloc = 0;
1838 42 : m_bInCurField = false;
1839 42 : CPLFree(m_pszCityGMLGenericAttrName);
1840 42 : m_pszCityGMLGenericAttrName = nullptr;
1841 : }
1842 :
1843 84 : if (m_inCityGMLGenericAttrDepth == m_nDepth)
1844 : {
1845 42 : POP_STATE();
1846 : }
1847 :
1848 84 : return OGRERR_NONE;
1849 : }
1850 :
1851 : /************************************************************************/
1852 : /* endElementAttribute() */
1853 : /************************************************************************/
1854 10730 : OGRErr GMLHandler::endElementAttribute()
1855 :
1856 : {
1857 10730 : GMLReadState *poState = m_poReader->GetState();
1858 :
1859 10730 : if (m_bInCurField)
1860 : {
1861 7962 : if (m_pszCurField == nullptr && m_poReader->IsEmptyAsNull())
1862 : {
1863 70 : if (m_pszValue != nullptr)
1864 : {
1865 10 : m_poReader->SetFeaturePropertyDirectly(poState->osPath.c_str(),
1866 : m_pszValue, -1);
1867 10 : m_pszValue = nullptr;
1868 : }
1869 : }
1870 : else
1871 : {
1872 7892 : m_poReader->SetFeaturePropertyDirectly(
1873 : poState->osPath.c_str(),
1874 7892 : m_pszCurField ? m_pszCurField : CPLStrdup(""),
1875 : m_nAttributeIndex);
1876 7892 : m_pszCurField = nullptr;
1877 : }
1878 :
1879 7962 : if (m_pszHref != nullptr)
1880 : {
1881 4 : CPLString osPropNameHref = poState->osPath + "_href";
1882 4 : m_poReader->SetFeaturePropertyDirectly(osPropNameHref, m_pszHref,
1883 : -1);
1884 4 : m_pszHref = nullptr;
1885 : }
1886 :
1887 7962 : if (m_pszUom != nullptr)
1888 : {
1889 20 : CPLString osPropNameUom = poState->osPath + "_uom";
1890 20 : m_poReader->SetFeaturePropertyDirectly(osPropNameUom, m_pszUom, -1);
1891 20 : m_pszUom = nullptr;
1892 : }
1893 :
1894 7962 : if (m_pszKieli != nullptr)
1895 : {
1896 7 : CPLString osPropName = poState->osPath + "_kieli";
1897 7 : m_poReader->SetFeaturePropertyDirectly(osPropName, m_pszKieli, -1);
1898 7 : m_pszKieli = nullptr;
1899 : }
1900 :
1901 7962 : m_nCurFieldLen = 0;
1902 7962 : m_nCurFieldAlloc = 0;
1903 7962 : m_bInCurField = false;
1904 7962 : m_nAttributeIndex = -1;
1905 :
1906 7962 : CPLFree(m_pszValue);
1907 7962 : m_pszValue = nullptr;
1908 : }
1909 :
1910 10730 : poState->PopPath();
1911 :
1912 10730 : if (m_nAttributeDepth == m_nDepth)
1913 : {
1914 8882 : POP_STATE();
1915 : }
1916 :
1917 10730 : return OGRERR_NONE;
1918 : }
1919 :
1920 : /************************************************************************/
1921 : /* startElementFeatureProperty() */
1922 : /************************************************************************/
1923 :
1924 165 : OGRErr GMLHandler::startElementFeatureProperty(const char * /*pszName*/,
1925 : int /*nLenName*/, void *attr)
1926 : {
1927 165 : if (m_nDepth == m_nAttributeDepth + 1)
1928 : {
1929 24 : const char *pszGMLId = GetFID(attr);
1930 24 : if (pszGMLId != nullptr)
1931 : {
1932 24 : m_poReader->SetFeaturePropertyDirectly(
1933 : nullptr, CPLStrdup(CPLSPrintf("#%s", pszGMLId)),
1934 : m_nAttributeIndex);
1935 : }
1936 : }
1937 :
1938 165 : return OGRERR_NONE;
1939 : }
1940 :
1941 : /************************************************************************/
1942 : /* endElementFeatureProperty() */
1943 : /************************************************************************/
1944 :
1945 189 : OGRErr GMLHandler::endElementFeatureProperty()
1946 :
1947 : {
1948 189 : if (m_nDepth == m_nAttributeDepth)
1949 : {
1950 24 : GMLReadState *poState = m_poReader->GetState();
1951 24 : poState->PopPath();
1952 :
1953 24 : POP_STATE();
1954 : }
1955 189 : return OGRERR_NONE;
1956 : }
1957 :
1958 : /************************************************************************/
1959 : /* endElementFeature() */
1960 : /************************************************************************/
1961 6007 : OGRErr GMLHandler::endElementFeature()
1962 :
1963 : {
1964 : /* -------------------------------------------------------------------- */
1965 : /* If we are collecting a feature, and this element tag matches */
1966 : /* element name for the class, then we have finished the */
1967 : /* feature, and we pop the feature read state. */
1968 : /* -------------------------------------------------------------------- */
1969 6007 : if (m_nDepth == m_nDepthFeature)
1970 : {
1971 3205 : GMLReadState *poState = m_poReader->GetState();
1972 3205 : GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
1973 5119 : if (!poClass->IsSchemaLocked() &&
1974 1914 : poClass->IsConsistentSingleGeomElemPath())
1975 : {
1976 : const std::string &osGeomElemPath =
1977 1906 : poClass->GetSingleGeomElemPath();
1978 1906 : if (osGeomElemPath.empty())
1979 : {
1980 548 : poClass->SetSingleGeomElemPath(m_osLastGeomPathInCurFeature);
1981 : }
1982 1358 : else if (m_osLastGeomPathInCurFeature != osGeomElemPath)
1983 : {
1984 4 : poClass->SetConsistentSingleGeomElemPath(false);
1985 4 : poClass->SetSingleGeomElemPath(std::string());
1986 : }
1987 : }
1988 :
1989 3205 : m_osLastGeomPathInCurFeature.clear();
1990 3205 : m_oMapElementToSubstitute.clear();
1991 3205 : m_poReader->PopState();
1992 :
1993 3205 : POP_STATE();
1994 : }
1995 :
1996 : /* -------------------------------------------------------------------- */
1997 : /* Otherwise, we just pop the element off the local read states */
1998 : /* element stack. */
1999 : /* -------------------------------------------------------------------- */
2000 : else
2001 : {
2002 2802 : m_poReader->GetState()->PopPath();
2003 : }
2004 :
2005 6007 : return OGRERR_NONE;
2006 : }
2007 :
2008 : /************************************************************************/
2009 : /* endElementDefault() */
2010 : /************************************************************************/
2011 4009 : OGRErr GMLHandler::endElementDefault()
2012 :
2013 : {
2014 4009 : if (m_nDepth > 0)
2015 3417 : m_poReader->GetState()->PopPath();
2016 :
2017 4009 : return OGRERR_NONE;
2018 : }
2019 :
2020 : /************************************************************************/
2021 : /* dataHandlerAttribute() */
2022 : /************************************************************************/
2023 :
2024 14503 : OGRErr GMLHandler::dataHandlerAttribute(const char *data, int nLen)
2025 :
2026 : {
2027 14503 : if (!m_bInCurField)
2028 3482 : return OGRERR_NONE;
2029 :
2030 11021 : int nIter = 0;
2031 :
2032 : // Ignore white space.
2033 11021 : if (m_nCurFieldLen == 0)
2034 : {
2035 25702 : while (nIter < nLen)
2036 : {
2037 22617 : const char ch = data[nIter];
2038 22617 : if (!(ch == ' ' || ch == 10 || ch == 13 || ch == '\t'))
2039 7934 : break;
2040 14683 : nIter++;
2041 : }
2042 : }
2043 :
2044 11021 : const int nCharsLen = nLen - nIter;
2045 :
2046 11021 : if (nCharsLen > INT_MAX - static_cast<int>(m_nCurFieldLen) - 1)
2047 : {
2048 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2049 : "Too much data in a single element");
2050 0 : return OGRERR_NOT_ENOUGH_MEMORY;
2051 : }
2052 11021 : if (m_nCurFieldLen + nCharsLen + 1 > m_nCurFieldAlloc)
2053 : {
2054 9276 : if (m_nCurFieldAlloc < INT_MAX - m_nCurFieldAlloc / 3 - nCharsLen - 1)
2055 9276 : m_nCurFieldAlloc =
2056 9276 : m_nCurFieldAlloc + m_nCurFieldAlloc / 3 + nCharsLen + 1;
2057 : else
2058 0 : m_nCurFieldAlloc = m_nCurFieldLen + nCharsLen + 1;
2059 : char *pszNewCurField = static_cast<char *>(
2060 9276 : VSI_REALLOC_VERBOSE(m_pszCurField, m_nCurFieldAlloc));
2061 9276 : if (pszNewCurField == nullptr)
2062 : {
2063 0 : return OGRERR_NOT_ENOUGH_MEMORY;
2064 : }
2065 9276 : m_pszCurField = pszNewCurField;
2066 : }
2067 11021 : memcpy(m_pszCurField + m_nCurFieldLen, data + nIter, nCharsLen);
2068 11021 : m_nCurFieldLen += nCharsLen;
2069 11021 : m_pszCurField[m_nCurFieldLen] = '\0';
2070 :
2071 11021 : return OGRERR_NONE;
2072 : }
2073 :
2074 : /************************************************************************/
2075 : /* dataHandlerGeometry() */
2076 : /************************************************************************/
2077 :
2078 94725 : OGRErr GMLHandler::dataHandlerGeometry(const char *data, int nLen)
2079 :
2080 : {
2081 94725 : int nIter = 0;
2082 :
2083 : // Ignore white space
2084 94725 : if (m_nGeomLen == 0)
2085 : {
2086 737811 : while (nIter < nLen)
2087 : {
2088 648255 : char ch = data[nIter];
2089 648255 : if (!(ch == ' ' || ch == 10 || ch == 13 || ch == '\t'))
2090 5130 : break;
2091 643125 : nIter++;
2092 : }
2093 : }
2094 :
2095 94725 : const int nCharsLen = nLen - nIter;
2096 94725 : if (nCharsLen)
2097 : {
2098 5169 : if (nCharsLen > INT_MAX - static_cast<int>(m_nGeomLen) - 1)
2099 : {
2100 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2101 : "Too much data in a single element");
2102 0 : return OGRERR_NOT_ENOUGH_MEMORY;
2103 : }
2104 5169 : if (m_nGeomLen + nCharsLen + 1 > m_nGeomAlloc)
2105 : {
2106 5164 : if (m_nGeomAlloc < INT_MAX - m_nGeomAlloc / 3 - nCharsLen - 1)
2107 5164 : m_nGeomAlloc = m_nGeomAlloc + m_nGeomAlloc / 3 + nCharsLen + 1;
2108 : else
2109 0 : m_nGeomAlloc = m_nGeomAlloc + nCharsLen + 1;
2110 : char *pszNewGeometry = static_cast<char *>(
2111 5164 : VSI_REALLOC_VERBOSE(m_pszGeometry, m_nGeomAlloc));
2112 5164 : if (pszNewGeometry == nullptr)
2113 : {
2114 0 : return OGRERR_NOT_ENOUGH_MEMORY;
2115 : }
2116 5164 : m_pszGeometry = pszNewGeometry;
2117 : }
2118 5169 : memcpy(m_pszGeometry + m_nGeomLen, data + nIter, nCharsLen);
2119 5169 : m_nGeomLen += nCharsLen;
2120 5169 : m_pszGeometry[m_nGeomLen] = '\0';
2121 : }
2122 :
2123 94725 : return OGRERR_NONE;
2124 : }
2125 :
2126 : /************************************************************************/
2127 : /* IsGeometryElement() */
2128 : /************************************************************************/
2129 :
2130 16864 : bool GMLHandler::IsGeometryElement(const char *pszElement)
2131 :
2132 : {
2133 16864 : int nFirst = 0;
2134 16864 : int nLast = GML_GEOMETRY_TYPE_COUNT - 1;
2135 16864 : unsigned long nHash = CPLHashSetHashStr(pszElement);
2136 60565 : do
2137 : {
2138 77429 : const int nMiddle = (nFirst + nLast) / 2;
2139 77429 : if (nHash == pasGeometryNames[nMiddle].nHash)
2140 3088 : return strcmp(pszElement, pasGeometryNames[nMiddle].pszName) == 0;
2141 74341 : if (nHash < pasGeometryNames[nMiddle].nHash)
2142 46993 : nLast = nMiddle - 1;
2143 : else
2144 27348 : nFirst = nMiddle + 1;
2145 74341 : } while (nFirst <= nLast);
2146 :
2147 13776 : if (eAppSchemaType == APPSCHEMA_AIXM &&
2148 265 : (strcmp(pszElement, "ElevatedPoint") == 0 ||
2149 263 : strcmp(pszElement, "ElevatedCurve") == 0 ||
2150 255 : strcmp(pszElement, "ElevatedSurface") == 0))
2151 : {
2152 12 : return true;
2153 : }
2154 :
2155 13764 : if (eAppSchemaType == APPSCHEMA_MTKGML &&
2156 77 : (strcmp(pszElement, "Piste") == 0 || strcmp(pszElement, "Alue") == 0 ||
2157 49 : strcmp(pszElement, "Murtoviiva") == 0))
2158 35 : return true;
2159 :
2160 13729 : return false;
2161 : }
|