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 582 : GMLExpatHandler::GMLExpatHandler(GMLReader *poReader, XML_Parser oParser)
248 : : GMLHandler(poReader), m_oParser(oParser), m_bStopParsing(false),
249 582 : m_nDataHandlerCounter(0)
250 : {
251 582 : }
252 :
253 : /************************************************************************/
254 : /* GMLExpatHandler::DealWithError() */
255 : /************************************************************************/
256 :
257 367353 : void GMLExpatHandler::DealWithError(OGRErr eErr)
258 : {
259 367353 : 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 367353 : }
267 :
268 : /************************************************************************/
269 : /* startElementCbk() */
270 : /************************************************************************/
271 :
272 60783 : void XMLCALL GMLExpatHandler::startElementCbk(void *pUserData,
273 : const char *pszName,
274 : const char **ppszAttr)
275 :
276 : {
277 60783 : GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
278 60783 : if (pThis->m_bStopParsing)
279 0 : return;
280 :
281 60783 : const char *pszIter = pszName;
282 60783 : char ch = '\0';
283 797059 : while ((ch = *pszIter) != '\0')
284 : {
285 736276 : if (ch == ':')
286 47613 : pszName = pszIter + 1;
287 736276 : pszIter++;
288 : }
289 :
290 60783 : pThis->DealWithError(pThis->GMLHandler::startElement(
291 60783 : pszName, static_cast<int>(pszIter - pszName), ppszAttr));
292 : }
293 :
294 : /************************************************************************/
295 : /* endElementCbk() */
296 : /************************************************************************/
297 60665 : void XMLCALL GMLExpatHandler::endElementCbk(void *pUserData,
298 : const char * /* pszName */)
299 : {
300 60665 : GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
301 60665 : if (pThis->m_bStopParsing)
302 1 : return;
303 :
304 60664 : pThis->DealWithError(pThis->GMLHandler::endElement());
305 : }
306 :
307 : /************************************************************************/
308 : /* dataHandlerCbk() */
309 : /************************************************************************/
310 :
311 245907 : void XMLCALL GMLExpatHandler::dataHandlerCbk(void *pUserData, const char *data,
312 : int nLen)
313 :
314 : {
315 245907 : GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
316 245907 : if (pThis->m_bStopParsing)
317 0 : return;
318 :
319 245907 : 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 245907 : 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 245906 : pThis->DealWithError(pThis->GMLHandler::dataHandler(data, nLen));
337 : }
338 :
339 : /************************************************************************/
340 : /* GetFID() */
341 : /************************************************************************/
342 :
343 3276 : const char *GMLExpatHandler::GetFID(void *attr)
344 : {
345 3276 : const char *const *papszIter = static_cast<const char *const *>(attr);
346 3294 : while (*papszIter)
347 : {
348 1780 : if (strcmp(*papszIter, "fid") == 0 || strcmp(*papszIter, "gml:id") == 0)
349 : {
350 1762 : return papszIter[1];
351 : }
352 18 : papszIter += 2;
353 : }
354 1514 : return nullptr;
355 : }
356 :
357 : /************************************************************************/
358 : /* AddAttributes() */
359 : /************************************************************************/
360 :
361 34048 : CPLXMLNode *GMLExpatHandler::AddAttributes(CPLXMLNode *psNode, void *attr)
362 : {
363 34048 : const char **papszIter = static_cast<const char **>(attr);
364 :
365 34048 : CPLXMLNode *psLastChild = nullptr;
366 :
367 49152 : while (*papszIter)
368 : {
369 : CPLXMLNode *psChild =
370 15104 : CPLCreateXMLNode(nullptr, CXT_Attribute, papszIter[0]);
371 15104 : CPLCreateXMLNode(psChild, CXT_Text, papszIter[1]);
372 :
373 15104 : if (psLastChild == nullptr)
374 14075 : psNode->psChild = psChild;
375 : else
376 1029 : psLastChild->psNext = psChild;
377 15104 : psLastChild = psChild;
378 :
379 15104 : papszIter += 2;
380 : }
381 :
382 34048 : return psLastChild;
383 : }
384 :
385 : /************************************************************************/
386 : /* GetAttributeValue() */
387 : /************************************************************************/
388 :
389 11091 : char *GMLExpatHandler::GetAttributeValue(void *attr,
390 : const char *pszAttributeName)
391 : {
392 11091 : const char *const *papszIter = static_cast<const char *const *>(attr);
393 11705 : while (*papszIter)
394 : {
395 861 : if (strcmp(*papszIter, pszAttributeName) == 0)
396 : {
397 247 : return CPLStrdup(papszIter[1]);
398 : }
399 614 : papszIter += 2;
400 : }
401 10844 : 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 13828 : char *GMLExpatHandler::GetAttributeByIdx(void *attr, unsigned int idx,
411 : char **ppszKey)
412 : {
413 13828 : const char *const *papszIter = static_cast<const char *const *>(attr);
414 13828 : if (papszIter[2 * idx] == nullptr)
415 : {
416 13208 : *ppszKey = nullptr;
417 13208 : return nullptr;
418 : }
419 620 : *ppszKey = CPLStrdup(papszIter[2 * idx]);
420 620 : 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 106 : bool OGRGMLIsGeometryElement(const char *pszElement)
460 : {
461 3060 : for (const auto &pszGMLElement : apszGMLGeometryElements)
462 : {
463 2955 : if (strcmp(pszElement, pszGMLElement) == 0)
464 1 : return true;
465 : }
466 105 : return false;
467 : }
468 :
469 : struct _GeometryNamesStruct
470 : {
471 : unsigned long nHash;
472 : const char *pszName;
473 : };
474 :
475 : /************************************************************************/
476 : /* GMLHandler() */
477 : /************************************************************************/
478 :
479 584 : GMLHandler::GMLHandler(GMLReader *poReader)
480 : : pasGeometryNames(static_cast<GeometryNamesStruct *>(
481 1168 : CPLMalloc(GML_GEOMETRY_TYPE_COUNT * sizeof(GeometryNamesStruct)))),
482 : m_nSRSDimensionIfMissing(
483 584 : atoi(CPLGetConfigOption("GML_SRS_DIMENSION_IF_MISSING", "0"))),
484 1168 : m_poReader(poReader), eAppSchemaType(APPSCHEMA_GENERIC), nStackDepth(0)
485 : {
486 16936 : for (int i = 0; i < GML_GEOMETRY_TYPE_COUNT; i++)
487 : {
488 16352 : pasGeometryNames[i].pszName = apszGMLGeometryElements[i];
489 32704 : pasGeometryNames[i].nHash =
490 16352 : CPLHashSetHashStr(pasGeometryNames[i].pszName);
491 : }
492 584 : std::sort(pasGeometryNames, pasGeometryNames + GML_GEOMETRY_TYPE_COUNT,
493 99280 : [](const GeometryNamesStruct &a, const GeometryNamesStruct &b)
494 99280 : { return a.nHash < b.nHash; });
495 :
496 584 : stateStack[0] = STATE_TOP;
497 584 : }
498 :
499 : /************************************************************************/
500 : /* ~GMLHandler() */
501 : /************************************************************************/
502 :
503 584 : GMLHandler::~GMLHandler()
504 :
505 : {
506 584 : if (apsXMLNode.size() >= 2 && apsXMLNode[1].psNode != nullptr)
507 4 : CPLDestroyXMLNode(apsXMLNode[1].psNode);
508 :
509 584 : CPLFree(m_pszCurField);
510 584 : CPLFree(m_pszGeometry);
511 584 : CPLFree(m_pszCityGMLGenericAttrName);
512 584 : CPLFree(m_pszHref);
513 584 : CPLFree(m_pszUom);
514 584 : CPLFree(m_pszValue);
515 584 : CPLFree(m_pszKieli);
516 584 : CPLFree(pasGeometryNames);
517 584 : }
518 :
519 : /************************************************************************/
520 : /* startElement() */
521 : /************************************************************************/
522 :
523 60802 : OGRErr GMLHandler::startElement(const char *pszName, int nLenName, void *attr)
524 : {
525 : OGRErr eRet;
526 60802 : switch (stateStack[nStackDepth])
527 : {
528 582 : case STATE_TOP:
529 582 : eRet = startElementTop(pszName, nLenName, attr);
530 582 : break;
531 7142 : case STATE_DEFAULT:
532 7142 : eRet = startElementDefault(pszName, nLenName, attr);
533 7142 : break;
534 13336 : case STATE_FEATURE:
535 13336 : eRet = startElementFeatureAttribute(pszName, nLenName, attr);
536 13336 : break;
537 3400 : case STATE_PROPERTY:
538 3400 : eRet = startElementFeatureAttribute(pszName, nLenName, attr);
539 3400 : break;
540 165 : case STATE_FEATUREPROPERTY:
541 165 : eRet = startElementFeatureProperty(pszName, nLenName, attr);
542 165 : break;
543 29862 : case STATE_GEOMETRY:
544 29862 : eRet = startElementGeometry(pszName, nLenName, attr);
545 29862 : break;
546 4003 : case STATE_IGNORED_FEATURE:
547 4003 : eRet = OGRERR_NONE;
548 4003 : break;
549 1180 : case STATE_BOUNDED_BY:
550 1180 : eRet = startElementBoundedBy(pszName, nLenName, attr);
551 1180 : 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 60802 : m_nDepth++;
563 60802 : 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 60802 : return eRet;
583 : }
584 :
585 : /************************************************************************/
586 : /* endElement() */
587 : /************************************************************************/
588 :
589 60679 : OGRErr GMLHandler::endElement()
590 : {
591 60679 : m_nDepth--;
592 60679 : switch (stateStack[nStackDepth])
593 : {
594 0 : case STATE_TOP:
595 0 : return OGRERR_NONE;
596 : break;
597 3927 : case STATE_DEFAULT:
598 3927 : return endElementDefault();
599 : break;
600 5934 : case STATE_FEATURE:
601 5934 : return endElementFeature();
602 : break;
603 10641 : case STATE_PROPERTY:
604 10641 : return endElementAttribute();
605 : break;
606 189 : case STATE_FEATUREPROPERTY:
607 189 : return endElementFeatureProperty();
608 : break;
609 32929 : case STATE_GEOMETRY:
610 32929 : return endElementGeometry();
611 : break;
612 4348 : case STATE_IGNORED_FEATURE:
613 4348 : return endElementIgnoredFeature();
614 : break;
615 1461 : case STATE_BOUNDED_BY:
616 1461 : return endElementBoundedBy();
617 : break;
618 1166 : case STATE_BOUNDED_BY_IN_FEATURE:
619 1166 : return endElementBoundedByInFeature();
620 : break;
621 84 : case STATE_CITYGML_ATTRIBUTE:
622 84 : return endElementCityGMLGenericAttr();
623 : break;
624 0 : default:
625 0 : return OGRERR_NONE;
626 : break;
627 : }
628 : }
629 :
630 : /************************************************************************/
631 : /* dataHandler() */
632 : /************************************************************************/
633 :
634 246811 : OGRErr GMLHandler::dataHandler(const char *data, int nLen)
635 : {
636 246811 : switch (stateStack[nStackDepth])
637 : {
638 0 : case STATE_TOP:
639 0 : return OGRERR_NONE;
640 : break;
641 102832 : case STATE_DEFAULT:
642 102832 : return OGRERR_NONE;
643 : break;
644 28411 : case STATE_FEATURE:
645 28411 : return OGRERR_NONE;
646 : break;
647 14288 : case STATE_PROPERTY:
648 14288 : return dataHandlerAttribute(data, nLen);
649 : break;
650 625 : case STATE_FEATUREPROPERTY:
651 625 : return OGRERR_NONE;
652 : break;
653 92255 : case STATE_GEOMETRY:
654 92255 : return dataHandlerGeometry(data, nLen);
655 : break;
656 3295 : case STATE_IGNORED_FEATURE:
657 3295 : return OGRERR_NONE;
658 : break;
659 2553 : case STATE_BOUNDED_BY:
660 2553 : return OGRERR_NONE;
661 : break;
662 2414 : case STATE_BOUNDED_BY_IN_FEATURE:
663 2414 : return dataHandlerGeometry(data, nLen);
664 : break;
665 138 : case STATE_CITYGML_ATTRIBUTE:
666 138 : return dataHandlerAttribute(data, nLen);
667 : break;
668 0 : default:
669 0 : return OGRERR_NONE;
670 : break;
671 : }
672 : }
673 :
674 : #define PUSH_STATE(val) \
675 : do \
676 : { \
677 : nStackDepth++; \
678 : CPLAssert(nStackDepth < STACK_SIZE); \
679 : stateStack[nStackDepth] = val; \
680 : } while (false)
681 : #define POP_STATE() nStackDepth--
682 :
683 : /************************************************************************/
684 : /* startElementBoundedBy() */
685 : /************************************************************************/
686 :
687 1180 : OGRErr GMLHandler::startElementBoundedBy(const char *pszName, int /*nLenName*/,
688 : void *attr)
689 : {
690 1180 : if (m_nDepth == 2 && strcmp(pszName, "Envelope") == 0)
691 : {
692 136 : char *pszGlobalSRSName = GetAttributeValue(attr, "srsName");
693 136 : m_poReader->SetGlobalSRSName(pszGlobalSRSName);
694 136 : CPLFree(pszGlobalSRSName);
695 :
696 136 : if (m_nSRSDimensionIfMissing == 0)
697 : {
698 : char *pszGlobalSRSDimension =
699 132 : GetAttributeValue(attr, "srsDimension");
700 132 : if (pszGlobalSRSDimension)
701 15 : m_nSRSDimensionIfMissing = atoi(pszGlobalSRSDimension);
702 132 : CPLFree(pszGlobalSRSDimension);
703 : }
704 : }
705 :
706 1180 : return OGRERR_NONE;
707 : }
708 :
709 : /************************************************************************/
710 : /* startElementGeometry() */
711 : /************************************************************************/
712 :
713 34051 : OGRErr GMLHandler::startElementGeometry(const char *pszName, int nLenName,
714 : void *attr)
715 : {
716 35141 : if (stateStack[nStackDepth] == STATE_BOUNDED_BY_IN_FEATURE &&
717 1090 : apsXMLNode.empty())
718 : {
719 1 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid <boundedBy> construct");
720 1 : return OGRERR_FAILURE;
721 : }
722 :
723 : /* Create new XML Element */
724 : CPLXMLNode *psCurNode =
725 34050 : static_cast<CPLXMLNode *>(CPLCalloc(sizeof(CPLXMLNode), 1));
726 34050 : psCurNode->eType = CXT_Element;
727 34050 : psCurNode->pszValue = static_cast<char *>(CPLMalloc(nLenName + 1));
728 34050 : memcpy(psCurNode->pszValue, pszName, nLenName + 1);
729 :
730 : /* Attach element as the last child of its parent */
731 34050 : NodeLastChild &sNodeLastChild = apsXMLNode.back();
732 34050 : CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
733 :
734 34050 : if (psLastChildParent == nullptr)
735 : {
736 18142 : CPLXMLNode *psParent = sNodeLastChild.psNode;
737 18142 : if (psParent)
738 14965 : psParent->psChild = psCurNode;
739 : }
740 : else
741 : {
742 15908 : psLastChildParent->psNext = psCurNode;
743 : }
744 34050 : sNodeLastChild.psLastChild = psCurNode;
745 :
746 : /* Add attributes to the element */
747 34050 : CPLXMLNode *psLastChildCurNode = AddAttributes(psCurNode, attr);
748 49157 : for (CPLXMLNode *psIter = psCurNode->psChild; psIter;
749 15107 : psIter = psIter->psNext)
750 : {
751 15107 : if (psIter->eType == CXT_Attribute &&
752 15107 : strcmp(psIter->pszValue, "xlink:href") == 0 &&
753 1090 : psIter->psChild->pszValue && psIter->psChild->pszValue[0] == '#')
754 : {
755 1090 : m_oMapElementToSubstitute[psIter->psChild->pszValue + 1] =
756 : psCurNode;
757 : }
758 : }
759 :
760 : /* Some CityGML lack a srsDimension="3" in posList, such as in */
761 : /* http://www.citygml.org/fileadmin/count.php?f=fileadmin%2Fcitygml%2Fdocs%2FFrankfurt_Street_Setting_LOD3.zip
762 : */
763 : /* So we have to add it manually */
764 71491 : if (strcmp(pszName, "posList") == 0 &&
765 34930 : CPLGetXMLValue(psCurNode, "srsDimension", nullptr) == nullptr &&
766 880 : m_nSRSDimensionIfMissing != 0)
767 : {
768 : CPLXMLNode *psChild =
769 118 : CPLCreateXMLNode(nullptr, CXT_Attribute, "srsDimension");
770 118 : CPLCreateXMLNode(psChild, CXT_Text,
771 118 : (m_nSRSDimensionIfMissing == 3) ? "3" : "2");
772 :
773 118 : if (psLastChildCurNode == nullptr)
774 118 : psCurNode->psChild = psChild;
775 : else
776 0 : psLastChildCurNode->psNext = psChild;
777 118 : psLastChildCurNode = psChild;
778 : }
779 :
780 : /* Push the element on the stack */
781 : NodeLastChild sNewNodeLastChild;
782 34050 : sNewNodeLastChild.psNode = psCurNode;
783 34050 : sNewNodeLastChild.psLastChild = psLastChildCurNode;
784 34050 : apsXMLNode.push_back(sNewNodeLastChild);
785 :
786 34050 : if (m_pszGeometry)
787 : {
788 0 : CPLFree(m_pszGeometry);
789 0 : m_pszGeometry = nullptr;
790 0 : m_nGeomAlloc = 0;
791 0 : m_nGeomLen = 0;
792 : }
793 :
794 34050 : return OGRERR_NONE;
795 : }
796 :
797 : /************************************************************************/
798 : /* startElementCityGMLGenericAttr() */
799 : /************************************************************************/
800 :
801 42 : OGRErr GMLHandler::startElementCityGMLGenericAttr(const char *pszName,
802 : int /*nLenName*/,
803 : void * /*attr*/)
804 : {
805 42 : if (strcmp(pszName, "value") == 0)
806 : {
807 42 : if (m_pszCurField)
808 : {
809 0 : CPLFree(m_pszCurField);
810 0 : m_pszCurField = nullptr;
811 0 : m_nCurFieldLen = 0;
812 0 : m_nCurFieldAlloc = 0;
813 : }
814 42 : m_bInCurField = true;
815 : }
816 :
817 42 : return OGRERR_NONE;
818 : }
819 :
820 : /************************************************************************/
821 : /* DealWithAttributes() */
822 : /************************************************************************/
823 :
824 13217 : void GMLHandler::DealWithAttributes(const char *pszName, int nLenName,
825 : void *attr)
826 : {
827 13217 : GMLReadState *poState = m_poReader->GetState();
828 13217 : GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
829 :
830 13217 : for (unsigned int idx = 0; true; idx++)
831 : {
832 13837 : char *pszAttrKey = nullptr;
833 :
834 13837 : char *pszAttrVal = GetAttributeByIdx(attr, idx, &pszAttrKey);
835 13837 : if (pszAttrVal == nullptr)
836 13217 : break;
837 :
838 620 : int nAttrIndex = 0;
839 620 : const char *pszAttrKeyNoNS = strchr(pszAttrKey, ':');
840 620 : if (pszAttrKeyNoNS != nullptr)
841 147 : pszAttrKeyNoNS++;
842 :
843 : /* If attribute is referenced by the .gfs */
844 767 : if (poClass->IsSchemaLocked() &&
845 48 : ((pszAttrKeyNoNS != nullptr &&
846 48 : (nAttrIndex = m_poReader->GetAttributeElementIndex(
847 114 : pszName, nLenName, pszAttrKeyNoNS)) != -1) ||
848 114 : ((nAttrIndex = m_poReader->GetAttributeElementIndex(
849 : pszName, nLenName, pszAttrKey)) != -1)))
850 : {
851 67 : nAttrIndex = FindRealPropertyByCheckingConditions(nAttrIndex, attr);
852 67 : if (nAttrIndex >= 0)
853 : {
854 65 : m_poReader->SetFeaturePropertyDirectly(nullptr, pszAttrVal,
855 : nAttrIndex);
856 65 : pszAttrVal = nullptr;
857 : }
858 : }
859 :
860 : /* Hard-coded historic cases */
861 553 : else if (strcmp(pszAttrKey, "xlink:href") == 0)
862 : {
863 14 : if ((m_bReportHref || m_poReader->ReportAllAttributes()) &&
864 7 : m_bInCurField)
865 : {
866 4 : CPLFree(m_pszHref);
867 4 : m_pszHref = pszAttrVal;
868 4 : pszAttrVal = nullptr;
869 : }
870 9 : else if ((!poClass->IsSchemaLocked() &&
871 9 : (m_bReportHref || m_poReader->ReportAllAttributes())) ||
872 3 : (poClass->IsSchemaLocked() &&
873 3 : (nAttrIndex = m_poReader->GetAttributeElementIndex(
874 6 : (std::string(pszName) + "_href").c_str(),
875 : nLenName + 5)) != -1))
876 : {
877 3 : poState->PushPath(pszName, nLenName);
878 3 : CPLString osPropNameHref = poState->osPath + "_href";
879 3 : poState->PopPath();
880 3 : m_poReader->SetFeaturePropertyDirectly(osPropNameHref,
881 : pszAttrVal, nAttrIndex);
882 3 : pszAttrVal = nullptr;
883 : }
884 : }
885 546 : else if (strcmp(pszAttrKey, "uom") == 0)
886 : {
887 20 : CPLFree(m_pszUom);
888 20 : m_pszUom = pszAttrVal;
889 20 : pszAttrVal = nullptr;
890 : }
891 526 : else if (strcmp(pszAttrKey, "value") == 0)
892 : {
893 10 : CPLFree(m_pszValue);
894 10 : m_pszValue = pszAttrVal;
895 10 : pszAttrVal = nullptr;
896 : }
897 : else /* Get language in 'kieli' attribute of 'teksti' element */
898 516 : if (eAppSchemaType == APPSCHEMA_MTKGML && nLenName == 6 &&
899 7 : strcmp(pszName, "teksti") == 0 &&
900 7 : strcmp(pszAttrKey, "kieli") == 0)
901 : {
902 7 : CPLFree(m_pszKieli);
903 7 : m_pszKieli = pszAttrVal;
904 7 : pszAttrVal = nullptr;
905 : }
906 :
907 : /* Should we report all attributes ? */
908 517 : else if (m_poReader->ReportAllAttributes() &&
909 8 : !poClass->IsSchemaLocked())
910 : {
911 8 : poState->PushPath(pszName, nLenName);
912 8 : CPLString osPropName = poState->osPath;
913 8 : poState->PopPath();
914 :
915 8 : m_poReader->SetFeaturePropertyDirectly(
916 : CPLSPrintf("%s@%s", osPropName.c_str(),
917 : pszAttrKeyNoNS ? pszAttrKeyNoNS : pszAttrKey),
918 : pszAttrVal, -1);
919 8 : pszAttrVal = nullptr;
920 : }
921 :
922 620 : CPLFree(pszAttrKey);
923 620 : CPLFree(pszAttrVal);
924 620 : }
925 :
926 : #if 0
927 : if( poClass->IsSchemaLocked() )
928 : {
929 : poState->PushPath( pszName, nLenName );
930 : CPLString osPath = poState->osPath;
931 : poState->PopPath();
932 : /* Find fields that match an attribute that is missing */
933 : for(int i=0; i < poClass->GetPropertyCount(); i++ )
934 : {
935 : GMLPropertyDefn* poProp = poClass->GetProperty(i);
936 : const char* pszSrcElement = poProp->GetSrcElement();
937 : if( poProp->GetType() == OFTStringList &&
938 : poProp->GetSrcElementLen() > osPath.size() &&
939 : strncmp(pszSrcElement, osPath, osPath.size()) == 0 &&
940 : pszSrcElement[osPath.size()] == '@' )
941 : {
942 : char* pszAttrVal = GetAttributeValue(attr, pszSrcElement + osPath.size() + 1);
943 : if( pszAttrVal == NULL )
944 : {
945 : const char* pszCond = poProp->GetCondition();
946 : if( pszCond == NULL || IsConditionMatched(pszCond, attr) )
947 : {
948 : m_poReader->SetFeaturePropertyDirectly( NULL, CPLStrdup(""), i );
949 : }
950 : }
951 : else
952 : CPLFree(pszAttrVal);
953 : }
954 : }
955 : }
956 : #endif
957 13217 : }
958 :
959 : /************************************************************************/
960 : /* IsConditionMatched() */
961 : /************************************************************************/
962 :
963 : /* FIXME! 'and' / 'or' operators are evaluated left to right, without */
964 : /* and precedence rules between them ! */
965 :
966 15 : bool GMLHandler::IsConditionMatched(const char *pszCondition, void *attr)
967 : {
968 15 : if (pszCondition == nullptr)
969 0 : return true;
970 :
971 15 : bool bSyntaxError = false;
972 30 : CPLString osCondAttr, osCondVal;
973 15 : const char *pszIter = pszCondition;
974 15 : bool bOpEqual = true;
975 19 : while (*pszIter == ' ')
976 4 : pszIter++;
977 15 : if (*pszIter != '@')
978 0 : bSyntaxError = true;
979 : else
980 : {
981 15 : pszIter++;
982 75 : while (*pszIter != '\0' && *pszIter != ' ' && *pszIter != '!' &&
983 65 : *pszIter != '=')
984 : {
985 60 : osCondAttr += *pszIter;
986 60 : pszIter++;
987 : }
988 15 : while (*pszIter == ' ')
989 0 : pszIter++;
990 :
991 15 : if (*pszIter == '!')
992 : {
993 10 : bOpEqual = false;
994 10 : pszIter++;
995 : }
996 :
997 15 : if (*pszIter != '=')
998 0 : bSyntaxError = true;
999 : else
1000 : {
1001 15 : pszIter++;
1002 15 : while (*pszIter == ' ')
1003 0 : pszIter++;
1004 15 : if (*pszIter != '\'')
1005 0 : bSyntaxError = true;
1006 : else
1007 : {
1008 15 : pszIter++;
1009 45 : while (*pszIter != '\0' && *pszIter != '\'')
1010 : {
1011 30 : osCondVal += *pszIter;
1012 30 : pszIter++;
1013 : }
1014 15 : if (*pszIter != '\'')
1015 0 : bSyntaxError = true;
1016 : else
1017 : {
1018 15 : pszIter++;
1019 21 : while (*pszIter == ' ')
1020 6 : pszIter++;
1021 : }
1022 : }
1023 : }
1024 : }
1025 :
1026 15 : if (bSyntaxError)
1027 : {
1028 0 : CPLError(CE_Failure, CPLE_NotSupported,
1029 : "Invalid condition : %s. Must be of the form "
1030 : "@attrname[!]='attrvalue' [and|or other_cond]*. "
1031 : "'and' and 'or' operators cannot be mixed",
1032 : pszCondition);
1033 0 : return false;
1034 : }
1035 :
1036 15 : char *pszVal = GetAttributeValue(attr, osCondAttr);
1037 15 : if (pszVal == nullptr)
1038 0 : pszVal = CPLStrdup("");
1039 28 : const bool bCondMet = (bOpEqual && strcmp(pszVal, osCondVal) == 0) ||
1040 13 : (!bOpEqual && strcmp(pszVal, osCondVal) != 0);
1041 15 : CPLFree(pszVal);
1042 15 : if (*pszIter == '\0')
1043 9 : return bCondMet;
1044 :
1045 6 : if (STARTS_WITH(pszIter, "and"))
1046 : {
1047 6 : pszIter += 3;
1048 6 : if (!bCondMet)
1049 2 : return false;
1050 4 : return IsConditionMatched(pszIter, attr);
1051 : }
1052 :
1053 0 : if (STARTS_WITH(pszIter, "or"))
1054 : {
1055 0 : pszIter += 2;
1056 0 : if (bCondMet)
1057 0 : return true;
1058 0 : return IsConditionMatched(pszIter, attr);
1059 : }
1060 :
1061 0 : CPLError(
1062 : CE_Failure, CPLE_NotSupported,
1063 : "Invalid condition : %s. Must be of the form @attrname[!]='attrvalue' "
1064 : "[and|or other_cond]*. 'and' and 'or' operators cannot be mixed",
1065 : pszCondition);
1066 0 : return false;
1067 : }
1068 :
1069 : /************************************************************************/
1070 : /* FindRealPropertyByCheckingConditions() */
1071 : /************************************************************************/
1072 :
1073 3041 : int GMLHandler::FindRealPropertyByCheckingConditions(int nIdx, void *attr)
1074 : {
1075 3041 : GMLReadState *poState = m_poReader->GetState();
1076 3041 : GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
1077 :
1078 3041 : GMLPropertyDefn *poProp = poClass->GetProperty(nIdx);
1079 3041 : const char *pszCond = poProp->GetCondition();
1080 3041 : if (pszCond != nullptr && !IsConditionMatched(pszCond, attr))
1081 : {
1082 : /* try other attributes with same source element, but with different */
1083 : /* condition */
1084 4 : const char *pszSrcElement = poProp->GetSrcElement();
1085 4 : nIdx = -1;
1086 11 : for (int i = m_nAttributeIndex + 1; i < poClass->GetPropertyCount();
1087 : i++)
1088 : {
1089 9 : poProp = poClass->GetProperty(i);
1090 9 : if (strcmp(poProp->GetSrcElement(), pszSrcElement) == 0)
1091 : {
1092 5 : pszCond = poProp->GetCondition();
1093 5 : if (IsConditionMatched(pszCond, attr))
1094 : {
1095 2 : nIdx = i;
1096 2 : break;
1097 : }
1098 : }
1099 : }
1100 : }
1101 3041 : return nIdx;
1102 : }
1103 :
1104 : /************************************************************************/
1105 : /* startElementFeatureAttribute() */
1106 : /************************************************************************/
1107 :
1108 16736 : OGRErr GMLHandler::startElementFeatureAttribute(const char *pszName,
1109 : int nLenName, void *attr)
1110 : {
1111 : /* Reset flag */
1112 16736 : m_bInCurField = false;
1113 :
1114 16736 : GMLReadState *poState = m_poReader->GetState();
1115 :
1116 : /* -------------------------------------------------------------------- */
1117 : /* If we are collecting geometry, or if we determine this is a */
1118 : /* geometry element then append to the geometry info. */
1119 : /* -------------------------------------------------------------------- */
1120 16736 : if (IsGeometryElement(pszName))
1121 : {
1122 : bool bReadGeometry;
1123 :
1124 : /* If the <GeometryElementPath> is defined in the .gfs, use it */
1125 : /* to read the appropriate geometry element */
1126 3104 : GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
1127 3104 : m_nGeometryPropertyIndex = 0;
1128 4387 : if (poClass->IsSchemaLocked() &&
1129 1283 : poClass->GetGeometryPropertyCount() == 0)
1130 : {
1131 0 : bReadGeometry = false;
1132 : }
1133 4387 : else if (poClass->IsSchemaLocked() &&
1134 4387 : poClass->GetGeometryPropertyCount() == 1 &&
1135 1121 : poClass->GetGeometryProperty(0)->GetSrcElement()[0] == '\0')
1136 : {
1137 70 : bReadGeometry = true;
1138 : }
1139 4247 : else if (poClass->IsSchemaLocked() &&
1140 1213 : poClass->GetGeometryPropertyCount() > 0)
1141 : {
1142 1213 : m_nGeometryPropertyIndex =
1143 1213 : poClass->GetGeometryPropertyIndexBySrcElement(
1144 : poState->osPath.c_str());
1145 1213 : bReadGeometry = (m_nGeometryPropertyIndex >= 0);
1146 : }
1147 1821 : else if (m_poReader->FetchAllGeometries())
1148 : {
1149 0 : bReadGeometry = true;
1150 : }
1151 1821 : else if (!poClass->IsSchemaLocked() && m_poReader->IsWFSJointLayer())
1152 : {
1153 12 : m_nGeometryPropertyIndex =
1154 12 : poClass->GetGeometryPropertyIndexBySrcElement(
1155 : poState->osPath.c_str());
1156 12 : if (m_nGeometryPropertyIndex < 0)
1157 : {
1158 4 : const char *pszElement = poState->osPath.c_str();
1159 4 : CPLString osFieldName;
1160 : /* Strip member| prefix. Should always be true normally */
1161 4 : if (STARTS_WITH(pszElement, "member|"))
1162 4 : osFieldName = pszElement + strlen("member|");
1163 :
1164 : /* Replace layer|property by layer_property */
1165 4 : size_t iPos = osFieldName.find('|');
1166 4 : if (iPos != std::string::npos)
1167 4 : osFieldName[iPos] = '.';
1168 :
1169 8 : poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
1170 4 : osFieldName, poState->osPath.c_str(), wkbUnknown, -1,
1171 8 : true));
1172 4 : m_nGeometryPropertyIndex = poClass->GetGeometryPropertyCount();
1173 : }
1174 12 : bReadGeometry = true;
1175 : }
1176 : else
1177 : {
1178 : /* AIXM special case: for RouteSegment, we only want to read Curve
1179 : * geometries */
1180 : /* not 'start' and 'end' geometries */
1181 1821 : if (eAppSchemaType == APPSCHEMA_AIXM &&
1182 12 : strcmp(poState->m_poFeature->GetClass()->GetName(),
1183 : "RouteSegment") == 0)
1184 0 : bReadGeometry = strcmp(pszName, "Curve") == 0;
1185 :
1186 : // AIXM special case: we want to read both horizontalProjection_location and
1187 : // horizontalProjection_linearExtent
1188 1821 : else if (eAppSchemaType == APPSCHEMA_AIXM &&
1189 12 : STARTS_WITH(poState->m_poFeature->GetClass()->GetName(),
1190 : "horizontalProjection_") == 0)
1191 12 : bReadGeometry =
1192 12 : STARTS_WITH(pszName, "horizontalProjection_") == 0;
1193 :
1194 : /* For Inspire objects : the "main" geometry is in a <geometry>
1195 : * element */
1196 1797 : else if (m_bAlreadyFoundGeometry)
1197 2 : bReadGeometry = false;
1198 1795 : else if (strcmp(poState->osPath.c_str(), "geometry") == 0)
1199 : {
1200 8 : m_bAlreadyFoundGeometry = true;
1201 8 : bReadGeometry = true;
1202 8 : m_nGeometryPropertyIndex =
1203 8 : poClass->GetGeometryPropertyIndexBySrcElement(
1204 : poState->osPath.c_str());
1205 8 : if (m_nGeometryPropertyIndex < 0)
1206 : {
1207 6 : poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
1208 3 : "geometry", poState->osPath.c_str(), wkbUnknown, -1,
1209 3 : true));
1210 3 : m_nGeometryPropertyIndex =
1211 3 : poClass->GetGeometryPropertyCount();
1212 : }
1213 : }
1214 : else
1215 : {
1216 3574 : if (!poClass->IsSchemaLocked() &&
1217 1787 : poClass->IsConsistentSingleGeomElemPath())
1218 : {
1219 : const std::string &osGeomElemPath =
1220 1777 : poClass->GetSingleGeomElemPath();
1221 1777 : if (osGeomElemPath.empty())
1222 : {
1223 413 : poClass->SetSingleGeomElemPath(poState->osPath);
1224 : }
1225 1364 : else if (poState->osPath != osGeomElemPath)
1226 : {
1227 5 : poClass->SetConsistentSingleGeomElemPath(false);
1228 5 : poClass->SetSingleGeomElemPath(std::string());
1229 : }
1230 : }
1231 1787 : bReadGeometry = true;
1232 : }
1233 : }
1234 3104 : if (bReadGeometry)
1235 : {
1236 3099 : m_nGeometryDepth = m_nDepth;
1237 :
1238 3099 : CPLAssert(apsXMLNode.empty());
1239 :
1240 : NodeLastChild sNodeLastChild;
1241 3099 : sNodeLastChild.psNode = nullptr;
1242 3099 : sNodeLastChild.psLastChild = nullptr;
1243 3099 : apsXMLNode.push_back(sNodeLastChild);
1244 :
1245 3099 : PUSH_STATE(STATE_GEOMETRY);
1246 :
1247 3099 : return startElementGeometry(pszName, nLenName, attr);
1248 : }
1249 : }
1250 13632 : else if (
1251 12275 : (nLenName == 9 || nLenName == 8) &&
1252 2532 : (strcmp(pszName, "boundedBy") == 0 ||
1253 27273 : strcmp(pszName, "boundary") == 0) &&
1254 : // We ignore the UseBBOX() flag for CityGML, since CityGML
1255 : // has elements like bldg:boundedBy or 'boundary', which are not a simple
1256 : // rectangular bbox. This is needed to read properly
1257 : // autotest/ogr/data/gml/citygml_lod2_713_5322.xml
1258 : // (this is a workaround of not being namespace aware)
1259 967 : (eAppSchemaType == APPSCHEMA_CITYGML || m_poReader->UseBBOX()))
1260 : {
1261 78 : m_inBoundedByDepth = m_nDepth;
1262 :
1263 78 : CPLAssert(apsXMLNode.empty());
1264 :
1265 : NodeLastChild sNodeLastChild;
1266 78 : sNodeLastChild.psNode = nullptr;
1267 78 : sNodeLastChild.psLastChild = nullptr;
1268 78 : apsXMLNode.push_back(sNodeLastChild);
1269 :
1270 78 : PUSH_STATE(STATE_BOUNDED_BY_IN_FEATURE);
1271 :
1272 78 : return OGRERR_NONE;
1273 : }
1274 :
1275 : /* -------------------------------------------------------------------- */
1276 : /* Is it a CityGML generic attribute ? */
1277 : /* -------------------------------------------------------------------- */
1278 13690 : else if (eAppSchemaType == APPSCHEMA_CITYGML &&
1279 136 : m_poReader->IsCityGMLGenericAttributeElement(pszName, attr))
1280 : {
1281 42 : CPLFree(m_pszCityGMLGenericAttrName);
1282 42 : m_pszCityGMLGenericAttrName = GetAttributeValue(attr, "name");
1283 42 : m_inCityGMLGenericAttrDepth = m_nDepth;
1284 :
1285 42 : PUSH_STATE(STATE_CITYGML_ATTRIBUTE);
1286 :
1287 42 : return OGRERR_NONE;
1288 : }
1289 :
1290 13512 : else if (m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 1)
1291 : {
1292 : }
1293 :
1294 13388 : else if (m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 2)
1295 : {
1296 124 : const char *pszFID = GetFID(attr);
1297 124 : if (pszFID)
1298 : {
1299 124 : poState->PushPath(pszName, nLenName);
1300 248 : CPLString osPropPath = poState->osPath + "@id";
1301 124 : poState->PopPath();
1302 124 : m_poReader->SetFeaturePropertyDirectly(osPropPath,
1303 : CPLStrdup(pszFID), -1);
1304 : }
1305 : }
1306 :
1307 : /* -------------------------------------------------------------------- */
1308 : /* If it is (or at least potentially is) a simple attribute, */
1309 : /* then start collecting it. */
1310 : /* -------------------------------------------------------------------- */
1311 13264 : else if ((m_nAttributeIndex = m_poReader->GetAttributeElementIndex(
1312 13264 : pszName, nLenName)) != -1)
1313 : {
1314 10726 : GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
1315 16721 : if (poClass->IsSchemaLocked() &&
1316 2998 : (poClass->GetProperty(m_nAttributeIndex)->GetType() ==
1317 2997 : GMLPT_FeatureProperty ||
1318 2997 : poClass->GetProperty(m_nAttributeIndex)->GetType() ==
1319 : GMLPT_FeaturePropertyList))
1320 : {
1321 24 : m_nAttributeDepth = m_nDepth;
1322 24 : PUSH_STATE(STATE_FEATUREPROPERTY);
1323 : }
1324 : else
1325 : {
1326 : /* Is this a property with a condition on an attribute value ? */
1327 10702 : if (poClass->IsSchemaLocked())
1328 : {
1329 2974 : m_nAttributeIndex = FindRealPropertyByCheckingConditions(
1330 : m_nAttributeIndex, attr);
1331 : }
1332 :
1333 10702 : if (m_nAttributeIndex >= 0)
1334 : {
1335 10702 : if (m_pszCurField)
1336 : {
1337 1277 : CPLFree(m_pszCurField);
1338 1277 : m_pszCurField = nullptr;
1339 1277 : m_nCurFieldLen = 0;
1340 1277 : m_nCurFieldAlloc = 0;
1341 : }
1342 10702 : m_bInCurField = true;
1343 :
1344 10702 : char *pszXSINil = GetAttributeValue(attr, "xsi:nil");
1345 10702 : if (pszXSINil)
1346 : {
1347 23 : if (EQUAL(pszXSINil, "true"))
1348 23 : m_poReader->SetFeaturePropertyDirectly(
1349 : pszName, CPLStrdup(OGR_GML_NULL), -1);
1350 23 : CPLFree(pszXSINil);
1351 : }
1352 : else
1353 : {
1354 10679 : DealWithAttributes(pszName, nLenName, attr);
1355 : }
1356 :
1357 10702 : if (stateStack[nStackDepth] != STATE_PROPERTY)
1358 : {
1359 8802 : m_nAttributeDepth = m_nDepth;
1360 8802 : PUSH_STATE(STATE_PROPERTY);
1361 : }
1362 : }
1363 : /*else
1364 : {
1365 : DealWithAttributes(pszName, nLenName, attr);
1366 : }*/
1367 : }
1368 : }
1369 : else
1370 : {
1371 2538 : DealWithAttributes(pszName, nLenName, attr);
1372 : }
1373 :
1374 13517 : poState->PushPath(pszName, nLenName);
1375 :
1376 13517 : return OGRERR_NONE;
1377 : }
1378 :
1379 : /************************************************************************/
1380 : /* startElementTop() */
1381 : /************************************************************************/
1382 :
1383 582 : OGRErr GMLHandler::startElementTop(const char *pszName, int /*nLenName*/,
1384 : void *attr)
1385 : {
1386 582 : if (strcmp(pszName, "CityModel") == 0)
1387 : {
1388 10 : eAppSchemaType = APPSCHEMA_CITYGML;
1389 : // Default to 3D geometries for CityGML (#6989)
1390 10 : if (m_nSRSDimensionIfMissing == 0)
1391 10 : m_nSRSDimensionIfMissing = 3;
1392 : }
1393 572 : else if (strcmp(pszName, "AIXMBasicMessage") == 0)
1394 : {
1395 7 : eAppSchemaType = APPSCHEMA_AIXM;
1396 7 : m_bReportHref = true;
1397 : }
1398 565 : else if (strcmp(pszName, "Maastotiedot") == 0)
1399 : {
1400 8 : eAppSchemaType = APPSCHEMA_MTKGML;
1401 :
1402 8 : char *pszSRSName = GetAttributeValue(attr, "srsName");
1403 8 : m_poReader->SetGlobalSRSName(pszSRSName);
1404 8 : CPLFree(pszSRSName);
1405 :
1406 8 : m_bReportHref = true;
1407 :
1408 : /* the schemas of MTKGML don't have (string) width, so don't set it */
1409 8 : m_poReader->SetWidthFlag(false);
1410 : }
1411 :
1412 582 : stateStack[0] = STATE_DEFAULT;
1413 :
1414 582 : return OGRERR_NONE;
1415 : }
1416 :
1417 : /************************************************************************/
1418 : /* startElementDefault() */
1419 : /************************************************************************/
1420 :
1421 7142 : OGRErr GMLHandler::startElementDefault(const char *pszName, int nLenName,
1422 : void *attr)
1423 :
1424 : {
1425 : /* -------------------------------------------------------------------- */
1426 : /* Is it a feature? If so push a whole new state, and return. */
1427 : /* -------------------------------------------------------------------- */
1428 : int nClassIndex;
1429 7142 : const char *pszFilteredClassName = nullptr;
1430 :
1431 7142 : if (nLenName == 9 && strcmp(pszName, "boundedBy") == 0)
1432 : {
1433 281 : m_inBoundedByDepth = m_nDepth;
1434 :
1435 281 : PUSH_STATE(STATE_BOUNDED_BY);
1436 :
1437 281 : return OGRERR_NONE;
1438 : }
1439 :
1440 7682 : else if (m_poReader->ShouldLookForClassAtAnyLevel() &&
1441 821 : (pszFilteredClassName = m_poReader->GetFilteredClassName()) !=
1442 : nullptr)
1443 : {
1444 821 : if (strcmp(pszName, pszFilteredClassName) == 0)
1445 : {
1446 49 : m_poReader->PushFeature(pszName, GetFID(attr),
1447 49 : m_poReader->GetFilteredClassIndex());
1448 :
1449 49 : m_nDepthFeature = m_nDepth;
1450 :
1451 49 : PUSH_STATE(STATE_FEATURE);
1452 :
1453 49 : return OGRERR_NONE;
1454 : }
1455 : }
1456 :
1457 : /* WFS 2.0 GetFeature documents have a wfs:FeatureCollection */
1458 : /* as a wfs:member of the top wfs:FeatureCollection. We don't want this */
1459 : /* wfs:FeatureCollection to be recognized as a feature */
1460 6068 : else if ((!(nLenName == static_cast<int>(strlen("FeatureCollection")) &&
1461 12066 : strcmp(pszName, "FeatureCollection") == 0)) &&
1462 6026 : (nClassIndex = m_poReader->GetFeatureElementIndex(
1463 : pszName, nLenName, eAppSchemaType)) != -1)
1464 : {
1465 3447 : m_bAlreadyFoundGeometry = false;
1466 :
1467 3447 : pszFilteredClassName = m_poReader->GetFilteredClassName();
1468 3447 : if (pszFilteredClassName != nullptr &&
1469 377 : strcmp(pszName, pszFilteredClassName) != 0)
1470 : {
1471 345 : m_nDepthFeature = m_nDepth;
1472 :
1473 345 : PUSH_STATE(STATE_IGNORED_FEATURE);
1474 :
1475 345 : return OGRERR_NONE;
1476 : }
1477 : else
1478 : {
1479 3102 : if (eAppSchemaType == APPSCHEMA_MTKGML)
1480 : {
1481 22 : m_poReader->PushFeature(pszName, nullptr, nClassIndex);
1482 :
1483 22 : char *pszGID = GetAttributeValue(attr, "gid");
1484 22 : if (pszGID)
1485 21 : m_poReader->SetFeaturePropertyDirectly("gid", pszGID, -1,
1486 : GMLPT_String);
1487 : }
1488 : else
1489 3080 : m_poReader->PushFeature(pszName, GetFID(attr), nClassIndex);
1490 :
1491 3102 : m_nDepthFeature = m_nDepth;
1492 :
1493 3102 : PUSH_STATE(STATE_FEATURE);
1494 :
1495 3102 : return OGRERR_NONE;
1496 : }
1497 : }
1498 :
1499 : /* -------------------------------------------------------------------- */
1500 : /* Push the element onto the current state's path. */
1501 : /* -------------------------------------------------------------------- */
1502 3365 : m_poReader->GetState()->PushPath(pszName, nLenName);
1503 :
1504 3365 : return OGRERR_NONE;
1505 : }
1506 :
1507 : /************************************************************************/
1508 : /* endElementIgnoredFeature() */
1509 : /************************************************************************/
1510 :
1511 4348 : OGRErr GMLHandler::endElementIgnoredFeature()
1512 :
1513 : {
1514 4348 : if (m_nDepth == m_nDepthFeature)
1515 : {
1516 345 : POP_STATE();
1517 : }
1518 4348 : return OGRERR_NONE;
1519 : }
1520 :
1521 : /************************************************************************/
1522 : /* endElementBoundedBy() */
1523 : /************************************************************************/
1524 1461 : OGRErr GMLHandler::endElementBoundedBy()
1525 :
1526 : {
1527 1461 : if (m_inBoundedByDepth == m_nDepth)
1528 : {
1529 281 : POP_STATE();
1530 : }
1531 :
1532 1461 : return OGRERR_NONE;
1533 : }
1534 :
1535 : /************************************************************************/
1536 : /* endElementBoundedByInFeature() */
1537 : /************************************************************************/
1538 1166 : OGRErr GMLHandler::endElementBoundedByInFeature()
1539 :
1540 : {
1541 1166 : if (m_nDepth > m_inBoundedByDepth)
1542 : {
1543 1089 : if (m_nDepth == m_inBoundedByDepth + 1)
1544 : {
1545 78 : m_nGeometryDepth = m_nDepth;
1546 : }
1547 1089 : return endElementGeometry();
1548 : }
1549 : else
1550 : {
1551 77 : POP_STATE();
1552 77 : if (apsXMLNode.size() >= 2 && apsXMLNode[1].psNode != nullptr)
1553 0 : CPLDestroyXMLNode(apsXMLNode[1].psNode);
1554 77 : apsXMLNode.clear();
1555 77 : return OGRERR_NONE;
1556 : }
1557 : }
1558 :
1559 : /************************************************************************/
1560 : /* ParseAIXMElevationProperties() */
1561 : /************************************************************************/
1562 :
1563 10 : void GMLHandler::ParseAIXMElevationProperties(const CPLXMLNode *psGML)
1564 : {
1565 10 : if (const char *pszElevation = CPLGetXMLValue(psGML, "elevation", nullptr))
1566 : {
1567 7 : m_poReader->SetFeaturePropertyDirectly("elevation",
1568 : CPLStrdup(pszElevation), -1);
1569 : const char *pszElevationUnit =
1570 7 : CPLGetXMLValue(psGML, "elevation.uom", nullptr);
1571 7 : if (pszElevationUnit)
1572 : {
1573 5 : m_poReader->SetFeaturePropertyDirectly(
1574 : "elevation_uom", CPLStrdup(pszElevationUnit), -1);
1575 : }
1576 : }
1577 :
1578 10 : if (const char *pszGeoidUndulation =
1579 10 : CPLGetXMLValue(psGML, "geoidUndulation", nullptr))
1580 : {
1581 0 : m_poReader->SetFeaturePropertyDirectly(
1582 : "geoidUndulation", CPLStrdup(pszGeoidUndulation), -1);
1583 : const char *pszGeoidUndulationUnit =
1584 0 : CPLGetXMLValue(psGML, "geoidUndulation.uom", nullptr);
1585 0 : if (pszGeoidUndulationUnit)
1586 : {
1587 0 : m_poReader->SetFeaturePropertyDirectly(
1588 : "geoidUndulation_uom", CPLStrdup(pszGeoidUndulationUnit), -1);
1589 : }
1590 : }
1591 :
1592 10 : if (const char *pszVerticalDatum =
1593 10 : CPLGetXMLValue(psGML, "verticalDatum", nullptr))
1594 : {
1595 3 : m_poReader->SetFeaturePropertyDirectly("verticalDatum",
1596 : CPLStrdup(pszVerticalDatum), -1);
1597 : }
1598 :
1599 10 : if (const char *pszVerticalAccuracy =
1600 10 : CPLGetXMLValue(psGML, "verticalAccuracy", nullptr))
1601 : {
1602 5 : m_poReader->SetFeaturePropertyDirectly(
1603 : "verticalAccuracy", CPLStrdup(pszVerticalAccuracy), -1);
1604 : const char *pszVerticalAccuracyUnit =
1605 5 : CPLGetXMLValue(psGML, "verticalAccuracy.uom", nullptr);
1606 5 : if (pszVerticalAccuracyUnit)
1607 : {
1608 3 : m_poReader->SetFeaturePropertyDirectly(
1609 : "verticalAccuracy_uom", CPLStrdup(pszVerticalAccuracyUnit), -1);
1610 : }
1611 : }
1612 10 : }
1613 :
1614 : /************************************************************************/
1615 : /* ParseAIXMElevationPoint() */
1616 : /************************************************************************/
1617 :
1618 2 : CPLXMLNode *GMLHandler::ParseAIXMElevationPoint(CPLXMLNode *psGML)
1619 : {
1620 2 : ParseAIXMElevationProperties(psGML);
1621 :
1622 2 : const char *pszPos = CPLGetXMLValue(psGML, "pos", nullptr);
1623 2 : const char *pszCoordinates = CPLGetXMLValue(psGML, "coordinates", nullptr);
1624 2 : if (pszPos != nullptr || pszCoordinates != nullptr)
1625 : {
1626 2 : CPLFree(psGML->pszValue);
1627 2 : psGML->pszValue = CPLStrdup("gml:Point");
1628 : }
1629 : else
1630 : {
1631 0 : CPLDestroyXMLNode(psGML);
1632 0 : psGML = nullptr;
1633 : }
1634 :
1635 2 : return psGML;
1636 : }
1637 :
1638 : /************************************************************************/
1639 : /* endElementGeometry() */
1640 : /************************************************************************/
1641 34018 : OGRErr GMLHandler::endElementGeometry()
1642 :
1643 : {
1644 34018 : if (m_nGeomLen)
1645 : {
1646 : CPLXMLNode *psNode =
1647 5099 : static_cast<CPLXMLNode *>(CPLCalloc(sizeof(CPLXMLNode), 1));
1648 5099 : psNode->eType = CXT_Text;
1649 5099 : psNode->pszValue = m_pszGeometry;
1650 :
1651 5099 : NodeLastChild &sNodeLastChild = apsXMLNode.back();
1652 5099 : CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
1653 5099 : if (psLastChildParent == nullptr)
1654 : {
1655 2393 : CPLXMLNode *psParent = sNodeLastChild.psNode;
1656 2393 : if (psParent)
1657 2393 : psParent->psChild = psNode;
1658 : }
1659 : else
1660 2706 : psLastChildParent->psNext = psNode;
1661 5099 : sNodeLastChild.psLastChild = psNode;
1662 :
1663 5099 : m_pszGeometry = nullptr;
1664 5099 : m_nGeomAlloc = 0;
1665 5099 : m_nGeomLen = 0;
1666 : }
1667 :
1668 34018 : CPLXMLNode *psThisNode = apsXMLNode.back().psNode;
1669 34018 : CPLXMLNode *psThisNodeChild = psThisNode->psChild;
1670 40981 : if (!m_oMapElementToSubstitute.empty() && psThisNodeChild &&
1671 6963 : psThisNodeChild->eType == CXT_Attribute &&
1672 42097 : strcmp(psThisNodeChild->pszValue, "gml:id") == 0 &&
1673 1116 : psThisNodeChild->psChild->pszValue)
1674 : {
1675 : auto oIter =
1676 1116 : m_oMapElementToSubstitute.find(psThisNodeChild->psChild->pszValue);
1677 1116 : if (oIter != m_oMapElementToSubstitute.end())
1678 : {
1679 165 : auto psLastChild = oIter->second->psChild;
1680 165 : if (psLastChild)
1681 : {
1682 : // CPLDebug("GML", "Substitution of xlink:href=\"#%s\" with actual content", psThisNodeChild->psChild->pszValue);
1683 165 : CPLXMLNode *psAfter = psThisNode->psNext;
1684 165 : psThisNode->psNext = nullptr;
1685 : // We can patch oIter->second as it stored as it in the current
1686 : // GMLFeature.
1687 : // Of course that would no longer be the case in case of
1688 : // cross-references between different GMLFeature, hence we clear
1689 : // m_oMapElementToSubstitute at the end of the current feature.
1690 254 : while (psLastChild->psNext)
1691 89 : psLastChild = psLastChild->psNext;
1692 165 : if (psLastChild == psThisNode)
1693 : {
1694 : /* Can happen in situations like:
1695 : <foo xlink:href="#X">
1696 : <bar gml:id="X"/>
1697 : </foo>
1698 : Do not attempt substitution as that would cause a memory
1699 : leak.
1700 : */
1701 : }
1702 : else
1703 : {
1704 164 : psLastChild->psNext = CPLCloneXMLTree(psThisNode);
1705 : }
1706 165 : psThisNode->psNext = psAfter;
1707 : }
1708 : }
1709 : }
1710 :
1711 34018 : if (m_nDepth == m_nGeometryDepth)
1712 : {
1713 3173 : m_nGeometryDepth = 0;
1714 :
1715 3173 : CPLAssert(apsXMLNode.size() == 2);
1716 3173 : CPLXMLNode *psInterestNode = apsXMLNode.back().psNode;
1717 :
1718 : /*char* pszXML = CPLSerializeXMLTree(psInterestNode);
1719 : CPLDebug("GML", "geometry = %s", pszXML);
1720 : CPLFree(pszXML);*/
1721 :
1722 3173 : apsXMLNode.pop_back();
1723 :
1724 : /* AIXM ElevatedPoint. We want to parse this */
1725 : /* a bit specially because ElevatedPoint is aixm: stuff and */
1726 : /* the srsDimension of the <gml:pos> can be set to true although */
1727 : /* they are only 2 coordinates in practice */
1728 3173 : if (eAppSchemaType == APPSCHEMA_AIXM && psInterestNode != nullptr &&
1729 12 : strcmp(psInterestNode->pszValue, "ElevatedPoint") == 0)
1730 : {
1731 2 : psInterestNode = ParseAIXMElevationPoint(psInterestNode);
1732 : }
1733 3171 : else if (eAppSchemaType == APPSCHEMA_AIXM &&
1734 10 : psInterestNode != nullptr &&
1735 10 : (strcmp(psInterestNode->pszValue, "ElevatedCurve") == 0 ||
1736 2 : strcmp(psInterestNode->pszValue, "ElevateSurface") == 0))
1737 : {
1738 8 : ParseAIXMElevationProperties(psInterestNode);
1739 : }
1740 3163 : else if (eAppSchemaType == APPSCHEMA_MTKGML &&
1741 : psInterestNode != nullptr)
1742 : {
1743 36 : if (strcmp(psInterestNode->pszValue, "Murtoviiva") == 0)
1744 : {
1745 7 : CPLFree(psInterestNode->pszValue);
1746 7 : psInterestNode->pszValue = CPLStrdup("gml:LineString");
1747 : }
1748 29 : else if (strcmp(psInterestNode->pszValue, "Alue") == 0)
1749 : {
1750 7 : CPLFree(psInterestNode->pszValue);
1751 7 : psInterestNode->pszValue = CPLStrdup("gml:Polygon");
1752 : }
1753 22 : else if (strcmp(psInterestNode->pszValue, "Piste") == 0)
1754 : {
1755 21 : CPLFree(psInterestNode->pszValue);
1756 21 : psInterestNode->pszValue = CPLStrdup("gml:Point");
1757 : }
1758 : }
1759 3127 : else if (psInterestNode != nullptr &&
1760 3127 : strcmp(psInterestNode->pszValue, "BoundingBox") == 0)
1761 : {
1762 27 : CPLFree(psInterestNode->pszValue);
1763 27 : psInterestNode->pszValue = CPLStrdup("Envelope");
1764 30 : for (CPLXMLNode *psChild = psInterestNode->psChild; psChild;
1765 3 : psChild = psChild->psNext)
1766 : {
1767 29 : if (psChild->eType == CXT_Attribute &&
1768 27 : strcmp(psChild->pszValue, "crs") == 0)
1769 : {
1770 26 : CPLFree(psChild->pszValue);
1771 26 : psChild->pszValue = CPLStrdup("srsName");
1772 26 : break;
1773 : }
1774 : }
1775 : }
1776 :
1777 3173 : GMLFeature *poGMLFeature = m_poReader->GetState()->m_poFeature;
1778 3173 : if (stateStack[nStackDepth] == STATE_BOUNDED_BY_IN_FEATURE)
1779 : {
1780 78 : if (eAppSchemaType == APPSCHEMA_CITYGML)
1781 58 : CPLDestroyXMLNode(psInterestNode);
1782 : else
1783 20 : poGMLFeature->SetBoundedByGeometry(psInterestNode);
1784 : }
1785 : else
1786 : {
1787 3095 : if (m_poReader->FetchAllGeometries())
1788 0 : poGMLFeature->AddGeometry(psInterestNode);
1789 : else
1790 : {
1791 3095 : GMLFeatureClass *poClass = poGMLFeature->GetClass();
1792 3095 : if (poClass->GetGeometryPropertyCount() > 1)
1793 : {
1794 172 : if (poGMLFeature->GetGeometryRef(m_nGeometryPropertyIndex))
1795 : {
1796 : // If we have already a geometry, setting a new one
1797 : // will invalidate nodes potentially stored in
1798 : // m_oMapElementToSubstitute, so clear it
1799 0 : m_oMapElementToSubstitute.clear();
1800 : }
1801 172 : poGMLFeature->SetGeometryDirectly(m_nGeometryPropertyIndex,
1802 : psInterestNode);
1803 : }
1804 : else
1805 : {
1806 2923 : if (poGMLFeature->GetGeometryRef(0))
1807 : {
1808 : // If we have already a geometry, setting a new one
1809 : // will invalidate nodes potentially stored in
1810 : // m_oMapElementToSubstitute, so clear it
1811 21 : m_oMapElementToSubstitute.clear();
1812 : }
1813 2923 : poGMLFeature->SetGeometryDirectly(psInterestNode);
1814 : }
1815 : }
1816 :
1817 3095 : POP_STATE();
1818 : }
1819 : }
1820 :
1821 34018 : apsXMLNode.pop_back();
1822 :
1823 34018 : return OGRERR_NONE;
1824 : }
1825 :
1826 : /************************************************************************/
1827 : /* endElementCityGMLGenericAttr() */
1828 : /************************************************************************/
1829 84 : OGRErr GMLHandler::endElementCityGMLGenericAttr()
1830 :
1831 : {
1832 84 : if (m_pszCityGMLGenericAttrName != nullptr && m_bInCurField)
1833 : {
1834 42 : if (m_pszCurField != nullptr)
1835 : {
1836 42 : m_poReader->SetFeaturePropertyDirectly(m_pszCityGMLGenericAttrName,
1837 : m_pszCurField, -1);
1838 : }
1839 42 : m_pszCurField = nullptr;
1840 42 : m_nCurFieldLen = 0;
1841 42 : m_nCurFieldAlloc = 0;
1842 42 : m_bInCurField = false;
1843 42 : CPLFree(m_pszCityGMLGenericAttrName);
1844 42 : m_pszCityGMLGenericAttrName = nullptr;
1845 : }
1846 :
1847 84 : if (m_inCityGMLGenericAttrDepth == m_nDepth)
1848 : {
1849 42 : POP_STATE();
1850 : }
1851 :
1852 84 : return OGRERR_NONE;
1853 : }
1854 :
1855 : /************************************************************************/
1856 : /* endElementAttribute() */
1857 : /************************************************************************/
1858 10641 : OGRErr GMLHandler::endElementAttribute()
1859 :
1860 : {
1861 10641 : GMLReadState *poState = m_poReader->GetState();
1862 :
1863 10641 : if (m_bInCurField)
1864 : {
1865 7898 : if (m_pszCurField == nullptr && m_poReader->IsEmptyAsNull())
1866 : {
1867 70 : if (m_pszValue != nullptr)
1868 : {
1869 10 : m_poReader->SetFeaturePropertyDirectly(poState->osPath.c_str(),
1870 : m_pszValue, -1);
1871 10 : m_pszValue = nullptr;
1872 : }
1873 : }
1874 : else
1875 : {
1876 7828 : m_poReader->SetFeaturePropertyDirectly(
1877 : poState->osPath.c_str(),
1878 7828 : m_pszCurField ? m_pszCurField : CPLStrdup(""),
1879 : m_nAttributeIndex);
1880 7828 : m_pszCurField = nullptr;
1881 : }
1882 :
1883 7898 : if (m_pszHref != nullptr)
1884 : {
1885 4 : CPLString osPropNameHref = poState->osPath + "_href";
1886 4 : m_poReader->SetFeaturePropertyDirectly(osPropNameHref, m_pszHref,
1887 : -1);
1888 4 : m_pszHref = nullptr;
1889 : }
1890 :
1891 7898 : if (m_pszUom != nullptr)
1892 : {
1893 20 : CPLString osPropNameUom = poState->osPath + "_uom";
1894 20 : m_poReader->SetFeaturePropertyDirectly(osPropNameUom, m_pszUom, -1);
1895 20 : m_pszUom = nullptr;
1896 : }
1897 :
1898 7898 : if (m_pszKieli != nullptr)
1899 : {
1900 7 : CPLString osPropName = poState->osPath + "_kieli";
1901 7 : m_poReader->SetFeaturePropertyDirectly(osPropName, m_pszKieli, -1);
1902 7 : m_pszKieli = nullptr;
1903 : }
1904 :
1905 7898 : m_nCurFieldLen = 0;
1906 7898 : m_nCurFieldAlloc = 0;
1907 7898 : m_bInCurField = false;
1908 7898 : m_nAttributeIndex = -1;
1909 :
1910 7898 : CPLFree(m_pszValue);
1911 7898 : m_pszValue = nullptr;
1912 : }
1913 :
1914 10641 : poState->PopPath();
1915 :
1916 10641 : if (m_nAttributeDepth == m_nDepth)
1917 : {
1918 8799 : POP_STATE();
1919 : }
1920 :
1921 10641 : return OGRERR_NONE;
1922 : }
1923 :
1924 : /************************************************************************/
1925 : /* startElementFeatureProperty() */
1926 : /************************************************************************/
1927 :
1928 165 : OGRErr GMLHandler::startElementFeatureProperty(const char * /*pszName*/,
1929 : int /*nLenName*/, void *attr)
1930 : {
1931 165 : if (m_nDepth == m_nAttributeDepth + 1)
1932 : {
1933 24 : const char *pszGMLId = GetFID(attr);
1934 24 : if (pszGMLId != nullptr)
1935 : {
1936 24 : m_poReader->SetFeaturePropertyDirectly(
1937 : nullptr, CPLStrdup(CPLSPrintf("#%s", pszGMLId)),
1938 : m_nAttributeIndex);
1939 : }
1940 : }
1941 :
1942 165 : return OGRERR_NONE;
1943 : }
1944 :
1945 : /************************************************************************/
1946 : /* endElementFeatureProperty() */
1947 : /************************************************************************/
1948 :
1949 189 : OGRErr GMLHandler::endElementFeatureProperty()
1950 :
1951 : {
1952 189 : if (m_nDepth == m_nAttributeDepth)
1953 : {
1954 24 : GMLReadState *poState = m_poReader->GetState();
1955 24 : poState->PopPath();
1956 :
1957 24 : POP_STATE();
1958 : }
1959 189 : return OGRERR_NONE;
1960 : }
1961 :
1962 : /************************************************************************/
1963 : /* endElementFeature() */
1964 : /************************************************************************/
1965 5934 : OGRErr GMLHandler::endElementFeature()
1966 :
1967 : {
1968 : /* -------------------------------------------------------------------- */
1969 : /* If we are collecting a feature, and this element tag matches */
1970 : /* element name for the class, then we have finished the */
1971 : /* feature, and we pop the feature read state. */
1972 : /* -------------------------------------------------------------------- */
1973 5934 : if (m_nDepth == m_nDepthFeature)
1974 : {
1975 3145 : m_oMapElementToSubstitute.clear();
1976 3145 : m_poReader->PopState();
1977 :
1978 3145 : POP_STATE();
1979 : }
1980 :
1981 : /* -------------------------------------------------------------------- */
1982 : /* Otherwise, we just pop the element off the local read states */
1983 : /* element stack. */
1984 : /* -------------------------------------------------------------------- */
1985 : else
1986 : {
1987 2789 : m_poReader->GetState()->PopPath();
1988 : }
1989 :
1990 5934 : return OGRERR_NONE;
1991 : }
1992 :
1993 : /************************************************************************/
1994 : /* endElementDefault() */
1995 : /************************************************************************/
1996 3927 : OGRErr GMLHandler::endElementDefault()
1997 :
1998 : {
1999 3927 : if (m_nDepth > 0)
2000 3356 : m_poReader->GetState()->PopPath();
2001 :
2002 3927 : return OGRERR_NONE;
2003 : }
2004 :
2005 : /************************************************************************/
2006 : /* dataHandlerAttribute() */
2007 : /************************************************************************/
2008 :
2009 14426 : OGRErr GMLHandler::dataHandlerAttribute(const char *data, int nLen)
2010 :
2011 : {
2012 14426 : if (!m_bInCurField)
2013 3476 : return OGRERR_NONE;
2014 :
2015 10950 : int nIter = 0;
2016 :
2017 : // Ignore white space.
2018 10950 : if (m_nCurFieldLen == 0)
2019 : {
2020 25579 : while (nIter < nLen)
2021 : {
2022 22501 : const char ch = data[nIter];
2023 22501 : if (!(ch == ' ' || ch == 10 || ch == 13 || ch == '\t'))
2024 7870 : break;
2025 14631 : nIter++;
2026 : }
2027 : }
2028 :
2029 10950 : const int nCharsLen = nLen - nIter;
2030 :
2031 10950 : if (nCharsLen > INT_MAX - static_cast<int>(m_nCurFieldLen) - 1)
2032 : {
2033 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2034 : "Too much data in a single element");
2035 0 : return OGRERR_NOT_ENOUGH_MEMORY;
2036 : }
2037 10950 : if (m_nCurFieldLen + nCharsLen + 1 > m_nCurFieldAlloc)
2038 : {
2039 9208 : if (m_nCurFieldAlloc < INT_MAX - m_nCurFieldAlloc / 3 - nCharsLen - 1)
2040 9208 : m_nCurFieldAlloc =
2041 9208 : m_nCurFieldAlloc + m_nCurFieldAlloc / 3 + nCharsLen + 1;
2042 : else
2043 0 : m_nCurFieldAlloc = m_nCurFieldLen + nCharsLen + 1;
2044 : char *pszNewCurField = static_cast<char *>(
2045 9208 : VSI_REALLOC_VERBOSE(m_pszCurField, m_nCurFieldAlloc));
2046 9208 : if (pszNewCurField == nullptr)
2047 : {
2048 0 : return OGRERR_NOT_ENOUGH_MEMORY;
2049 : }
2050 9208 : m_pszCurField = pszNewCurField;
2051 : }
2052 10950 : memcpy(m_pszCurField + m_nCurFieldLen, data + nIter, nCharsLen);
2053 10950 : m_nCurFieldLen += nCharsLen;
2054 10950 : m_pszCurField[m_nCurFieldLen] = '\0';
2055 :
2056 10950 : return OGRERR_NONE;
2057 : }
2058 :
2059 : /************************************************************************/
2060 : /* dataHandlerGeometry() */
2061 : /************************************************************************/
2062 :
2063 94669 : OGRErr GMLHandler::dataHandlerGeometry(const char *data, int nLen)
2064 :
2065 : {
2066 94669 : int nIter = 0;
2067 :
2068 : // Ignore white space
2069 94669 : if (m_nGeomLen == 0)
2070 : {
2071 737628 : while (nIter < nLen)
2072 : {
2073 648097 : char ch = data[nIter];
2074 648097 : if (!(ch == ' ' || ch == 10 || ch == 13 || ch == '\t'))
2075 5099 : break;
2076 642998 : nIter++;
2077 : }
2078 : }
2079 :
2080 94669 : const int nCharsLen = nLen - nIter;
2081 94669 : if (nCharsLen)
2082 : {
2083 5138 : if (nCharsLen > INT_MAX - static_cast<int>(m_nGeomLen) - 1)
2084 : {
2085 0 : CPLError(CE_Failure, CPLE_OutOfMemory,
2086 : "Too much data in a single element");
2087 0 : return OGRERR_NOT_ENOUGH_MEMORY;
2088 : }
2089 5138 : if (m_nGeomLen + nCharsLen + 1 > m_nGeomAlloc)
2090 : {
2091 5133 : if (m_nGeomAlloc < INT_MAX - m_nGeomAlloc / 3 - nCharsLen - 1)
2092 5133 : m_nGeomAlloc = m_nGeomAlloc + m_nGeomAlloc / 3 + nCharsLen + 1;
2093 : else
2094 0 : m_nGeomAlloc = m_nGeomAlloc + nCharsLen + 1;
2095 : char *pszNewGeometry = static_cast<char *>(
2096 5133 : VSI_REALLOC_VERBOSE(m_pszGeometry, m_nGeomAlloc));
2097 5133 : if (pszNewGeometry == nullptr)
2098 : {
2099 0 : return OGRERR_NOT_ENOUGH_MEMORY;
2100 : }
2101 5133 : m_pszGeometry = pszNewGeometry;
2102 : }
2103 5138 : memcpy(m_pszGeometry + m_nGeomLen, data + nIter, nCharsLen);
2104 5138 : m_nGeomLen += nCharsLen;
2105 5138 : m_pszGeometry[m_nGeomLen] = '\0';
2106 : }
2107 :
2108 94669 : return OGRERR_NONE;
2109 : }
2110 :
2111 : /************************************************************************/
2112 : /* IsGeometryElement() */
2113 : /************************************************************************/
2114 :
2115 16736 : bool GMLHandler::IsGeometryElement(const char *pszElement)
2116 :
2117 : {
2118 16736 : int nFirst = 0;
2119 16736 : int nLast = GML_GEOMETRY_TYPE_COUNT - 1;
2120 16736 : unsigned long nHash = CPLHashSetHashStr(pszElement);
2121 60124 : do
2122 : {
2123 76860 : const int nMiddle = (nFirst + nLast) / 2;
2124 76860 : if (nHash == pasGeometryNames[nMiddle].nHash)
2125 3057 : return strcmp(pszElement, pasGeometryNames[nMiddle].pszName) == 0;
2126 73803 : if (nHash < pasGeometryNames[nMiddle].nHash)
2127 46630 : nLast = nMiddle - 1;
2128 : else
2129 27173 : nFirst = nMiddle + 1;
2130 73803 : } while (nFirst <= nLast);
2131 :
2132 13679 : if (eAppSchemaType == APPSCHEMA_AIXM &&
2133 265 : (strcmp(pszElement, "ElevatedPoint") == 0 ||
2134 263 : strcmp(pszElement, "ElevatedCurve") == 0 ||
2135 255 : strcmp(pszElement, "ElevatedSurface") == 0))
2136 : {
2137 12 : return true;
2138 : }
2139 :
2140 13667 : if (eAppSchemaType == APPSCHEMA_MTKGML &&
2141 77 : (strcmp(pszElement, "Piste") == 0 || strcmp(pszElement, "Alue") == 0 ||
2142 49 : strcmp(pszElement, "Murtoviiva") == 0))
2143 35 : return true;
2144 :
2145 13632 : return false;
2146 : }
|