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