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