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