/src/gdal/ogr/ogrsf_frmts/jml/ogrjmllayer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: JML Translator |
4 | | * Purpose: Implements OGRJMLLayer class. |
5 | | * |
6 | | ****************************************************************************** |
7 | | * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com> |
8 | | * |
9 | | * SPDX-License-Identifier: MIT |
10 | | ****************************************************************************/ |
11 | | |
12 | | #include "cpl_conv.h" |
13 | | #include "ogr_jml.h" |
14 | | #include "ogr_p.h" |
15 | | |
16 | | #ifdef HAVE_EXPAT |
17 | | |
18 | | constexpr int PARSER_BUF_SIZE = 8192; |
19 | | |
20 | | /************************************************************************/ |
21 | | /* OGRJMLLayer() */ |
22 | | /************************************************************************/ |
23 | | |
24 | | OGRJMLLayer::OGRJMLLayer(const char *pszLayerName, OGRJMLDataset *poDSIn, |
25 | | VSILFILE *fpIn) |
26 | 1.01k | : m_poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn(pszLayerName)), |
27 | 1.01k | nNextFID(0), fp(fpIn), bHasReadSchema(false), oParser(nullptr), |
28 | 1.01k | currentDepth(0), bStopParsing(false), nWithoutEventCounter(0), |
29 | 1.01k | nDataHandlerCounter(0), bAccumulateElementValue(false), |
30 | 1.01k | pszElementValue(static_cast<char *>(CPLCalloc(1024, 1))), |
31 | 1.01k | nElementValueLen(0), nElementValueAlloc(1024), poFeature(nullptr), |
32 | 1.01k | ppoFeatureTab(nullptr), nFeatureTabLength(0), nFeatureTabIndex(0), |
33 | 1.01k | bSchemaFinished(false), nJCSGMLInputTemplateDepth(0), |
34 | 1.01k | nCollectionElementDepth(0), nFeatureCollectionDepth(0), |
35 | 1.01k | nFeatureElementDepth(0), nGeometryElementDepth(0), nColumnDepth(0), |
36 | 1.01k | nNameDepth(0), nTypeDepth(0), nAttributeElementDepth(0), iAttr(-1), |
37 | 1.01k | iRGBField(-1) |
38 | 1.01k | { |
39 | 1.01k | SetDescription(poFeatureDefn->GetName()); |
40 | 1.01k | poFeatureDefn->Reference(); |
41 | 1.01k | } |
42 | | |
43 | | /************************************************************************/ |
44 | | /* ~OGRJMLLayer() */ |
45 | | /************************************************************************/ |
46 | | |
47 | | OGRJMLLayer::~OGRJMLLayer() |
48 | | |
49 | 1.01k | { |
50 | 1.01k | if (oParser) |
51 | 778 | XML_ParserFree(oParser); |
52 | 1.01k | poFeatureDefn->Release(); |
53 | | |
54 | 1.01k | CPLFree(pszElementValue); |
55 | | |
56 | 1.01k | for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++) |
57 | 0 | delete ppoFeatureTab[i]; |
58 | 1.01k | CPLFree(ppoFeatureTab); |
59 | | |
60 | 1.01k | if (poFeature) |
61 | 0 | delete poFeature; |
62 | 1.01k | } |
63 | | |
64 | | /************************************************************************/ |
65 | | /* GetLayerDefn() */ |
66 | | /************************************************************************/ |
67 | | |
68 | | OGRFeatureDefn *OGRJMLLayer::GetLayerDefn() |
69 | 3.74k | { |
70 | 3.74k | if (!bHasReadSchema) |
71 | 778 | LoadSchema(); |
72 | | |
73 | 3.74k | return poFeatureDefn; |
74 | 3.74k | } |
75 | | |
76 | | static void XMLCALL startElementCbk(void *pUserData, const char *pszName, |
77 | | const char **ppszAttr) |
78 | 1.04k | { |
79 | 1.04k | static_cast<OGRJMLLayer *>(pUserData)->startElementCbk(pszName, ppszAttr); |
80 | 1.04k | } |
81 | | |
82 | | static void XMLCALL endElementCbk(void *pUserData, const char *pszName) |
83 | 984 | { |
84 | 984 | static_cast<OGRJMLLayer *>(pUserData)->endElementCbk(pszName); |
85 | 984 | } |
86 | | |
87 | | static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen) |
88 | 25.0k | { |
89 | 25.0k | static_cast<OGRJMLLayer *>(pUserData)->dataHandlerCbk(data, nLen); |
90 | 25.0k | } |
91 | | |
92 | | /************************************************************************/ |
93 | | /* ResetReading() */ |
94 | | /************************************************************************/ |
95 | | |
96 | | void OGRJMLLayer::ResetReading() |
97 | | |
98 | 779 | { |
99 | 779 | nNextFID = 0; |
100 | | |
101 | 779 | VSIFSeekL(fp, 0, SEEK_SET); |
102 | 779 | VSIFClearErrL(fp); |
103 | 779 | if (oParser) |
104 | 1 | XML_ParserFree(oParser); |
105 | | |
106 | 779 | oParser = OGRCreateExpatXMLParser(); |
107 | 779 | XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk); |
108 | 779 | XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk); |
109 | 779 | XML_SetUserData(oParser, this); |
110 | | |
111 | 779 | for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++) |
112 | 0 | delete ppoFeatureTab[i]; |
113 | 779 | nFeatureTabIndex = 0; |
114 | 779 | nFeatureTabLength = 0; |
115 | 779 | delete poFeature; |
116 | 779 | poFeature = nullptr; |
117 | | |
118 | 779 | currentDepth = 0; |
119 | | |
120 | 779 | nCollectionElementDepth = 0; |
121 | 779 | nFeatureElementDepth = 0; |
122 | 779 | nGeometryElementDepth = 0; |
123 | 779 | nAttributeElementDepth = 0; |
124 | 779 | iAttr = -1; |
125 | | |
126 | 779 | bAccumulateElementValue = false; |
127 | 779 | nElementValueLen = 0; |
128 | 779 | pszElementValue[0] = '\0'; |
129 | 779 | } |
130 | | |
131 | | /************************************************************************/ |
132 | | /* startElementCbk() */ |
133 | | /************************************************************************/ |
134 | | |
135 | | void OGRJMLLayer::startElementCbk(const char *pszName, const char **ppszAttr) |
136 | 1.04k | { |
137 | 1.04k | if (bStopParsing) |
138 | 0 | return; |
139 | | |
140 | 1.04k | nWithoutEventCounter = 0; |
141 | | |
142 | 1.04k | if (nFeatureElementDepth > 0 && nAttributeElementDepth == 0 && |
143 | 1.04k | nGeometryElementDepth == 0 && osGeometryElement.compare(pszName) == 0) |
144 | 0 | { |
145 | 0 | nGeometryElementDepth = currentDepth; |
146 | 0 | bAccumulateElementValue = true; |
147 | 0 | } |
148 | 1.04k | else if (nFeatureElementDepth > 0 && nAttributeElementDepth == 0 && |
149 | 1.04k | nGeometryElementDepth == 0) |
150 | 0 | { |
151 | | /* We assume that attributes are present in the order they are */ |
152 | | /* declared, so as a first guess, we can try the aoColumns[iAttr + 1] */ |
153 | 0 | int i = (iAttr + 1 < poFeatureDefn->GetFieldCount()) ? -1 : 0; |
154 | 0 | for (; i < static_cast<int>(aoColumns.size()); i++) |
155 | 0 | { |
156 | 0 | const OGRJMLColumn &oColumn = |
157 | 0 | (i < 0) ? aoColumns[iAttr + 1] : aoColumns[i]; |
158 | 0 | if (oColumn.osElementName != pszName) |
159 | 0 | continue; |
160 | | |
161 | 0 | if (oColumn.bIsBody) |
162 | 0 | { |
163 | 0 | if (!oColumn.osAttributeName.empty() && ppszAttr != nullptr && |
164 | 0 | ppszAttr[0] != nullptr && ppszAttr[1] != nullptr && |
165 | 0 | oColumn.osAttributeName.compare(ppszAttr[0]) == 0 && |
166 | 0 | oColumn.osAttributeValue.compare(ppszAttr[1]) == 0) |
167 | 0 | { |
168 | | /* <osElementName |
169 | | * osAttributeName="osAttributeValue">value</osElementName> |
170 | | */ |
171 | |
|
172 | 0 | bAccumulateElementValue = true; |
173 | 0 | nAttributeElementDepth = currentDepth; |
174 | 0 | iAttr = (i < 0) ? iAttr + 1 : i; |
175 | 0 | break; |
176 | 0 | } |
177 | 0 | else if (oColumn.osAttributeName.empty()) |
178 | 0 | { |
179 | | /* <osElementName>value</osElementName> */ |
180 | |
|
181 | 0 | bAccumulateElementValue = true; |
182 | 0 | nAttributeElementDepth = currentDepth; |
183 | 0 | iAttr = (i < 0) ? iAttr + 1 : i; |
184 | 0 | break; |
185 | 0 | } |
186 | 0 | } |
187 | 0 | else if (!oColumn.osAttributeName.empty() && ppszAttr != nullptr && |
188 | 0 | ppszAttr[0] != nullptr && ppszAttr[1] != nullptr && |
189 | 0 | oColumn.osAttributeName.compare(ppszAttr[0]) == 0) |
190 | 0 | { |
191 | | /* <osElementName osAttributeName="value"></osElementName> */ |
192 | |
|
193 | 0 | AddStringToElementValue(ppszAttr[1], (int)strlen(ppszAttr[1])); |
194 | |
|
195 | 0 | nAttributeElementDepth = currentDepth; |
196 | 0 | iAttr = (i < 0) ? iAttr + 1 : i; |
197 | 0 | break; |
198 | 0 | } |
199 | 0 | } |
200 | 0 | } |
201 | 1.04k | else if (nGeometryElementDepth > 0) |
202 | 0 | { |
203 | 0 | AddStringToElementValue("<", 1); |
204 | 0 | AddStringToElementValue(pszName, (int)strlen(pszName)); |
205 | |
|
206 | 0 | const char **papszIter = ppszAttr; |
207 | 0 | while (papszIter && *papszIter != nullptr) |
208 | 0 | { |
209 | 0 | AddStringToElementValue(" ", 1); |
210 | 0 | AddStringToElementValue(papszIter[0], (int)strlen(papszIter[0])); |
211 | 0 | AddStringToElementValue("=\"", 2); |
212 | 0 | AddStringToElementValue(papszIter[1], (int)strlen(papszIter[1])); |
213 | 0 | AddStringToElementValue("\"", 1); |
214 | 0 | papszIter += 2; |
215 | 0 | } |
216 | |
|
217 | 0 | AddStringToElementValue(">", 1); |
218 | 0 | } |
219 | 1.04k | else if (nFeatureCollectionDepth > 0 && nFeatureElementDepth == 0 && |
220 | 1.04k | osFeatureElement.compare(pszName) == 0) |
221 | 8 | { |
222 | 8 | nFeatureElementDepth = currentDepth; |
223 | 8 | poFeature = new OGRFeature(poFeatureDefn); |
224 | 8 | } |
225 | 1.04k | else if (nFeatureCollectionDepth == 0 && |
226 | 1.04k | osCollectionElement.compare(pszName) == 0) |
227 | 1 | { |
228 | 1 | nFeatureCollectionDepth = currentDepth; |
229 | 1 | } |
230 | | |
231 | 1.04k | currentDepth++; |
232 | 1.04k | } |
233 | | |
234 | | /************************************************************************/ |
235 | | /* StopAccumulate() */ |
236 | | /************************************************************************/ |
237 | | |
238 | | void OGRJMLLayer::StopAccumulate() |
239 | 1.18k | { |
240 | 1.18k | bAccumulateElementValue = false; |
241 | 1.18k | nElementValueLen = 0; |
242 | 1.18k | pszElementValue[0] = '\0'; |
243 | 1.18k | } |
244 | | |
245 | | /************************************************************************/ |
246 | | /* endElementCbk() */ |
247 | | /************************************************************************/ |
248 | | |
249 | | void OGRJMLLayer::endElementCbk(const char *pszName) |
250 | 984 | { |
251 | 984 | if (bStopParsing) |
252 | 0 | return; |
253 | | |
254 | 984 | nWithoutEventCounter = 0; |
255 | | |
256 | 984 | currentDepth--; |
257 | | |
258 | 984 | if (nAttributeElementDepth == currentDepth) |
259 | 0 | { |
260 | 0 | if (nElementValueLen) |
261 | 0 | poFeature->SetField(iAttr, pszElementValue); |
262 | 0 | else if (iAttr >= 0) |
263 | 0 | poFeature->SetFieldNull(iAttr); |
264 | 0 | nAttributeElementDepth = 0; |
265 | 0 | StopAccumulate(); |
266 | 0 | } |
267 | 984 | else if (nGeometryElementDepth > 0 && currentDepth > nGeometryElementDepth) |
268 | 0 | { |
269 | 0 | AddStringToElementValue("</", 2); |
270 | 0 | AddStringToElementValue(pszName, static_cast<int>(strlen(pszName))); |
271 | 0 | AddStringToElementValue(">", 1); |
272 | 0 | } |
273 | 984 | else if (nGeometryElementDepth == currentDepth) |
274 | 0 | { |
275 | 0 | if (nElementValueLen) |
276 | 0 | { |
277 | 0 | OGRGeometry *poGeom = |
278 | 0 | OGRGeometry::FromHandle(OGR_G_CreateFromGML(pszElementValue)); |
279 | 0 | if (poGeom != nullptr && |
280 | 0 | poGeom->getGeometryType() == wkbGeometryCollection && |
281 | 0 | poGeom->IsEmpty()) |
282 | 0 | { |
283 | 0 | delete poGeom; |
284 | 0 | } |
285 | 0 | else |
286 | 0 | poFeature->SetGeometryDirectly(poGeom); |
287 | 0 | } |
288 | |
|
289 | 0 | nGeometryElementDepth = 0; |
290 | 0 | StopAccumulate(); |
291 | 0 | } |
292 | 984 | else if (nFeatureElementDepth == currentDepth) |
293 | 8 | { |
294 | | /* Builds a style string from R_G_B if we don't already have a */ |
295 | | /* style string */ |
296 | 8 | OGRGeometry *poGeom = poFeature->GetGeometryRef(); |
297 | 8 | unsigned int R = 0; |
298 | 8 | unsigned int G = 0; |
299 | 8 | unsigned int B = 0; |
300 | 8 | if (iRGBField >= 0 && poFeature->IsFieldSetAndNotNull(iRGBField) && |
301 | 8 | poFeature->GetStyleString() == nullptr && poGeom != nullptr && |
302 | 8 | sscanf(poFeature->GetFieldAsString(iRGBField), "%02X%02X%02X", &R, |
303 | 0 | &G, &B) == 3) |
304 | 0 | { |
305 | 0 | const OGRwkbGeometryType eGeomType = |
306 | 0 | wkbFlatten(poGeom->getGeometryType()); |
307 | 0 | if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint || |
308 | 0 | eGeomType == wkbLineString || eGeomType == wkbMultiLineString) |
309 | 0 | { |
310 | 0 | poFeature->SetStyleString( |
311 | 0 | CPLSPrintf("PEN(c:#%02X%02X%02X)", R, G, B)); |
312 | 0 | } |
313 | 0 | else if (eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon) |
314 | 0 | { |
315 | 0 | poFeature->SetStyleString( |
316 | 0 | CPLSPrintf("BRUSH(fc:#%02X%02X%02X)", R, G, B)); |
317 | 0 | } |
318 | 0 | } |
319 | | |
320 | 8 | poFeature->SetFID(nNextFID++); |
321 | | |
322 | 8 | if ((m_poFilterGeom == nullptr || FilterGeometry(poGeom)) && |
323 | 8 | (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature))) |
324 | 8 | { |
325 | 8 | ppoFeatureTab = static_cast<OGRFeature **>(CPLRealloc( |
326 | 8 | ppoFeatureTab, sizeof(OGRFeature *) * (nFeatureTabLength + 1))); |
327 | 8 | ppoFeatureTab[nFeatureTabLength] = poFeature; |
328 | 8 | nFeatureTabLength++; |
329 | 8 | } |
330 | 0 | else |
331 | 0 | { |
332 | 0 | delete poFeature; |
333 | 0 | } |
334 | 8 | poFeature = nullptr; |
335 | 8 | iAttr = -1; |
336 | | |
337 | 8 | nFeatureElementDepth = 0; |
338 | 8 | } |
339 | 976 | else if (nFeatureCollectionDepth == currentDepth) |
340 | 0 | { |
341 | 0 | nFeatureCollectionDepth = 0; |
342 | 0 | } |
343 | 984 | } |
344 | | |
345 | | /************************************************************************/ |
346 | | /* AddStringToElementValue() */ |
347 | | /************************************************************************/ |
348 | | |
349 | | void OGRJMLLayer::AddStringToElementValue(const char *data, int nLen) |
350 | 1.59k | { |
351 | 1.59k | if (nLen > INT_MAX - nElementValueLen - 1 - 1000) |
352 | 0 | { |
353 | 0 | CPLError(CE_Failure, CPLE_OutOfMemory, |
354 | 0 | "Too much data in a single element"); |
355 | 0 | XML_StopParser(oParser, XML_FALSE); |
356 | 0 | bStopParsing = true; |
357 | 0 | return; |
358 | 0 | } |
359 | 1.59k | if (nElementValueLen + nLen + 1 > nElementValueAlloc) |
360 | 0 | { |
361 | 0 | char *pszNewElementValue = static_cast<char *>(VSI_REALLOC_VERBOSE( |
362 | 0 | pszElementValue, nElementValueLen + nLen + 1 + 1000)); |
363 | 0 | if (pszNewElementValue == nullptr) |
364 | 0 | { |
365 | 0 | XML_StopParser(oParser, XML_FALSE); |
366 | 0 | bStopParsing = true; |
367 | 0 | return; |
368 | 0 | } |
369 | 0 | nElementValueAlloc = nElementValueLen + nLen + 1 + 1000; |
370 | 0 | pszElementValue = pszNewElementValue; |
371 | 0 | } |
372 | 1.59k | memcpy(pszElementValue + nElementValueLen, data, nLen); |
373 | 1.59k | nElementValueLen += nLen; |
374 | 1.59k | pszElementValue[nElementValueLen] = '\0'; |
375 | 1.59k | } |
376 | | |
377 | | /************************************************************************/ |
378 | | /* dataHandlerCbk() */ |
379 | | /************************************************************************/ |
380 | | |
381 | | void OGRJMLLayer::dataHandlerCbk(const char *data, int nLen) |
382 | 25.0k | { |
383 | 25.0k | if (bStopParsing) |
384 | 0 | return; |
385 | | |
386 | 25.0k | nDataHandlerCounter++; |
387 | 25.0k | if (nDataHandlerCounter >= PARSER_BUF_SIZE) |
388 | 0 | { |
389 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
390 | 0 | "File probably corrupted (million laugh pattern)"); |
391 | 0 | XML_StopParser(oParser, XML_FALSE); |
392 | 0 | bStopParsing = true; |
393 | 0 | return; |
394 | 0 | } |
395 | | |
396 | 25.0k | nWithoutEventCounter = 0; |
397 | | |
398 | 25.0k | if (bAccumulateElementValue) |
399 | 1.59k | { |
400 | 1.59k | AddStringToElementValue(data, nLen); |
401 | 1.59k | } |
402 | 25.0k | } |
403 | | |
404 | | /************************************************************************/ |
405 | | /* GetNextFeature() */ |
406 | | /************************************************************************/ |
407 | | |
408 | | OGRFeature *OGRJMLLayer::GetNextFeature() |
409 | 750 | { |
410 | 750 | if (!bHasReadSchema) |
411 | 0 | LoadSchema(); |
412 | | |
413 | 750 | if (bStopParsing) |
414 | 742 | return nullptr; |
415 | | |
416 | 8 | if (nFeatureTabIndex < nFeatureTabLength) |
417 | 3 | { |
418 | 3 | return ppoFeatureTab[nFeatureTabIndex++]; |
419 | 3 | } |
420 | | |
421 | 5 | if (VSIFEofL(fp) || VSIFErrorL(fp)) |
422 | 0 | return nullptr; |
423 | | |
424 | 5 | std::vector<char> aBuf(PARSER_BUF_SIZE); |
425 | | |
426 | 5 | nFeatureTabLength = 0; |
427 | 5 | nFeatureTabIndex = 0; |
428 | | |
429 | 5 | nWithoutEventCounter = 0; |
430 | | |
431 | 5 | int nDone = 0; |
432 | 5 | do |
433 | 6 | { |
434 | 6 | nDataHandlerCounter = 0; |
435 | 6 | unsigned int nLen = |
436 | 6 | (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fp); |
437 | 6 | nDone = (nLen < aBuf.size()); |
438 | 6 | if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR) |
439 | 1 | { |
440 | 1 | CPLError(CE_Failure, CPLE_AppDefined, |
441 | 1 | "XML parsing of JML file failed : %s " |
442 | 1 | "at line %d, column %d", |
443 | 1 | XML_ErrorString(XML_GetErrorCode(oParser)), |
444 | 1 | (int)XML_GetCurrentLineNumber(oParser), |
445 | 1 | (int)XML_GetCurrentColumnNumber(oParser)); |
446 | 1 | bStopParsing = true; |
447 | 1 | } |
448 | 6 | nWithoutEventCounter++; |
449 | 6 | } while (!nDone && !bStopParsing && nFeatureTabLength == 0 && |
450 | 6 | nWithoutEventCounter < 10); |
451 | | |
452 | 5 | if (nWithoutEventCounter == 10) |
453 | 0 | { |
454 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
455 | 0 | "Too much data inside one element. File probably corrupted"); |
456 | 0 | bStopParsing = true; |
457 | 0 | } |
458 | | |
459 | 5 | return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : nullptr; |
460 | 5 | } |
461 | | |
462 | | static void XMLCALL startElementLoadSchemaCbk(void *pUserData, |
463 | | const char *pszName, |
464 | | const char **ppszAttr) |
465 | 36.3k | { |
466 | 36.3k | static_cast<OGRJMLLayer *>(pUserData)->startElementLoadSchemaCbk(pszName, |
467 | 36.3k | ppszAttr); |
468 | 36.3k | } |
469 | | |
470 | | static void XMLCALL endElementLoadSchemaCbk(void *pUserData, |
471 | | const char *pszName) |
472 | 32.8k | { |
473 | 32.8k | static_cast<OGRJMLLayer *>(pUserData)->endElementLoadSchemaCbk(pszName); |
474 | 32.8k | } |
475 | | |
476 | | /************************************************************************/ |
477 | | /* LoadSchema() */ |
478 | | /************************************************************************/ |
479 | | |
480 | | /** This function parses the beginning of the file to detect the fields */ |
481 | | void OGRJMLLayer::LoadSchema() |
482 | 778 | { |
483 | 778 | if (bHasReadSchema) |
484 | 0 | return; |
485 | | |
486 | 778 | bHasReadSchema = true; |
487 | | |
488 | 778 | oParser = OGRCreateExpatXMLParser(); |
489 | 778 | XML_SetElementHandler(oParser, ::startElementLoadSchemaCbk, |
490 | 778 | ::endElementLoadSchemaCbk); |
491 | 778 | XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk); |
492 | 778 | XML_SetUserData(oParser, this); |
493 | | |
494 | 778 | VSIFSeekL(fp, 0, SEEK_SET); |
495 | | |
496 | 778 | std::vector<char> aBuf(PARSER_BUF_SIZE); |
497 | 778 | int nDone = 0; |
498 | 778 | do |
499 | 1.19k | { |
500 | 1.19k | nDataHandlerCounter = 0; |
501 | 1.19k | const unsigned int nLen = static_cast<unsigned int>( |
502 | 1.19k | VSIFReadL(aBuf.data(), 1, aBuf.size(), fp)); |
503 | 1.19k | nDone = (nLen < aBuf.size()); |
504 | 1.19k | if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR) |
505 | 777 | { |
506 | 777 | CPLError(CE_Failure, CPLE_AppDefined, |
507 | 777 | "XML parsing of JML file failed : %s at line %d, " |
508 | 777 | "column %d", |
509 | 777 | XML_ErrorString(XML_GetErrorCode(oParser)), |
510 | 777 | static_cast<int>(XML_GetCurrentLineNumber(oParser)), |
511 | 777 | static_cast<int>(XML_GetCurrentColumnNumber(oParser))); |
512 | 777 | bStopParsing = true; |
513 | 777 | } |
514 | 1.19k | nWithoutEventCounter++; |
515 | 1.19k | } while (!nDone && !bStopParsing && !bSchemaFinished && |
516 | 1.19k | nWithoutEventCounter < 10); |
517 | | |
518 | 778 | XML_ParserFree(oParser); |
519 | 778 | oParser = nullptr; |
520 | | |
521 | 778 | if (nWithoutEventCounter == 10) |
522 | 0 | { |
523 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
524 | 0 | "Too much data inside one element. File probably corrupted"); |
525 | 0 | bStopParsing = true; |
526 | 0 | } |
527 | | |
528 | 778 | if (osCollectionElement.empty() || osFeatureElement.empty() || |
529 | 778 | osGeometryElement.empty()) |
530 | 719 | { |
531 | 719 | CPLError(CE_Failure, CPLE_AppDefined, |
532 | 719 | "Missing CollectionElement, FeatureElement or " |
533 | 719 | "GeometryElement"); |
534 | 719 | bStopParsing = true; |
535 | 719 | } |
536 | | |
537 | 778 | if (!osSRSName.empty()) |
538 | 0 | { |
539 | 0 | if (osSRSName.find("http://www.opengis.net/gml/srs/epsg.xml#") == 0) |
540 | 0 | { |
541 | 0 | OGRSpatialReference *poSRS = new OGRSpatialReference(); |
542 | 0 | poSRS->importFromEPSG(atoi( |
543 | 0 | osSRSName |
544 | 0 | .substr(strlen("http://www.opengis.net/gml/srs/epsg.xml#")) |
545 | 0 | .c_str())); |
546 | 0 | poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER); |
547 | 0 | poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS); |
548 | 0 | poSRS->Release(); |
549 | 0 | } |
550 | 0 | } |
551 | | |
552 | 778 | nJCSGMLInputTemplateDepth = 0; |
553 | 778 | nCollectionElementDepth = 0; |
554 | 778 | nFeatureCollectionDepth = 0; |
555 | 778 | nFeatureElementDepth = 0; |
556 | 778 | nGeometryElementDepth = 0; |
557 | 778 | nColumnDepth = 0; |
558 | 778 | nNameDepth = 0; |
559 | 778 | nTypeDepth = 0; |
560 | 778 | nAttributeElementDepth = 0; |
561 | | |
562 | 778 | ResetReading(); |
563 | 778 | } |
564 | | |
565 | | /************************************************************************/ |
566 | | /* startElementLoadSchemaCbk() */ |
567 | | /************************************************************************/ |
568 | | |
569 | | void OGRJMLLayer::startElementLoadSchemaCbk(const char *pszName, |
570 | | const char **ppszAttr) |
571 | 36.3k | { |
572 | 36.3k | if (bStopParsing) |
573 | 0 | return; |
574 | | |
575 | 36.3k | nWithoutEventCounter = 0; |
576 | | |
577 | 36.3k | if (nJCSGMLInputTemplateDepth == 0 && |
578 | 36.3k | strcmp(pszName, "JCSGMLInputTemplate") == 0) |
579 | 91 | nJCSGMLInputTemplateDepth = currentDepth; |
580 | 36.2k | else if (nJCSGMLInputTemplateDepth > 0) |
581 | 3.00k | { |
582 | 3.00k | if (nCollectionElementDepth == 0 && |
583 | 3.00k | strcmp(pszName, "CollectionElement") == 0) |
584 | 140 | { |
585 | 140 | nCollectionElementDepth = currentDepth; |
586 | 140 | bAccumulateElementValue = true; |
587 | 140 | } |
588 | 2.86k | else if (nFeatureElementDepth == 0 && |
589 | 2.86k | strcmp(pszName, "FeatureElement") == 0) |
590 | 137 | { |
591 | 137 | nFeatureElementDepth = currentDepth; |
592 | 137 | bAccumulateElementValue = true; |
593 | 137 | } |
594 | 2.72k | else if (nGeometryElementDepth == 0 && |
595 | 2.72k | strcmp(pszName, "GeometryElement") == 0) |
596 | 124 | { |
597 | 124 | nGeometryElementDepth = currentDepth; |
598 | 124 | bAccumulateElementValue = true; |
599 | 124 | } |
600 | 2.60k | else if (nColumnDepth == 0 && strcmp(pszName, "column") == 0) |
601 | 263 | { |
602 | 263 | nColumnDepth = currentDepth; |
603 | 263 | oCurColumn.osName = ""; |
604 | 263 | oCurColumn.osType = ""; |
605 | 263 | oCurColumn.osElementName = ""; |
606 | 263 | oCurColumn.osAttributeName = ""; |
607 | 263 | oCurColumn.osAttributeValue = ""; |
608 | 263 | oCurColumn.bIsBody = false; |
609 | 263 | } |
610 | 2.33k | else if (nColumnDepth > 0) |
611 | 2.13k | { |
612 | 2.13k | if (nNameDepth == 0 && strcmp(pszName, "name") == 0) |
613 | 425 | { |
614 | 425 | nNameDepth = currentDepth; |
615 | 425 | bAccumulateElementValue = true; |
616 | 425 | } |
617 | 1.71k | else if (nTypeDepth == 0 && strcmp(pszName, "type") == 0) |
618 | 435 | { |
619 | 435 | nTypeDepth = currentDepth; |
620 | 435 | bAccumulateElementValue = true; |
621 | 435 | } |
622 | 1.27k | else if (strcmp(pszName, "valueElement") == 0) |
623 | 340 | { |
624 | 340 | const char **papszIter = ppszAttr; |
625 | 1.26k | while (papszIter && *papszIter != nullptr) |
626 | 924 | { |
627 | 924 | if (strcmp(*papszIter, "elementName") == 0) |
628 | 338 | oCurColumn.osElementName = papszIter[1]; |
629 | 586 | else if (strcmp(*papszIter, "attributeName") == 0) |
630 | 282 | oCurColumn.osAttributeName = papszIter[1]; |
631 | 304 | else if (strcmp(*papszIter, "attributeValue") == 0) |
632 | 271 | oCurColumn.osAttributeValue = papszIter[1]; |
633 | 924 | papszIter += 2; |
634 | 924 | } |
635 | 340 | } |
636 | 936 | else if (strcmp(pszName, "valueLocation") == 0) |
637 | 316 | { |
638 | 316 | const char **papszIter = ppszAttr; |
639 | 645 | while (papszIter && *papszIter != nullptr) |
640 | 329 | { |
641 | 329 | if (strcmp(*papszIter, "position") == 0) |
642 | 236 | oCurColumn.bIsBody = strcmp(papszIter[1], "body") == 0; |
643 | 93 | else if (strcmp(*papszIter, "attributeName") == 0) |
644 | 14 | oCurColumn.osAttributeName = papszIter[1]; |
645 | 329 | papszIter += 2; |
646 | 329 | } |
647 | 316 | } |
648 | 2.13k | } |
649 | 3.00k | } |
650 | 33.2k | else if (nFeatureCollectionDepth == 0 && |
651 | 33.2k | osCollectionElement.compare(pszName) == 0) |
652 | 5 | { |
653 | 5 | nFeatureCollectionDepth = currentDepth; |
654 | 5 | } |
655 | 33.2k | else if (nFeatureCollectionDepth > 0 && |
656 | 33.2k | currentDepth == nFeatureCollectionDepth + 2 && |
657 | 33.2k | strcmp(pszName, "gml:Box") == 0) |
658 | 0 | { |
659 | 0 | const char **papszIter = ppszAttr; |
660 | 0 | while (papszIter && *papszIter != nullptr) |
661 | 0 | { |
662 | 0 | if (strcmp(*papszIter, "srsName") == 0) |
663 | 0 | osSRSName = papszIter[1]; |
664 | 0 | papszIter += 2; |
665 | 0 | } |
666 | 0 | bSchemaFinished = true; |
667 | 0 | } |
668 | 33.2k | else if (nFeatureCollectionDepth >= 0 && |
669 | 33.2k | currentDepth >= nFeatureCollectionDepth + 1 && |
670 | 33.2k | osFeatureElement.compare(pszName) == 0) |
671 | 8 | { |
672 | 8 | bSchemaFinished = true; |
673 | 8 | } |
674 | | |
675 | 36.3k | currentDepth++; |
676 | 36.3k | } |
677 | | |
678 | | /************************************************************************/ |
679 | | /* endElementLoadSchemaCbk() */ |
680 | | /************************************************************************/ |
681 | | |
682 | | void OGRJMLLayer::endElementLoadSchemaCbk(const char * /* pszName */) |
683 | 32.8k | { |
684 | 32.8k | if (bStopParsing) |
685 | 0 | return; |
686 | | |
687 | 32.8k | nWithoutEventCounter = 0; |
688 | | |
689 | 32.8k | currentDepth--; |
690 | | |
691 | 32.8k | if (nJCSGMLInputTemplateDepth == currentDepth) |
692 | 12 | { |
693 | 12 | nJCSGMLInputTemplateDepth = 0; |
694 | 12 | } |
695 | 32.8k | else if (nCollectionElementDepth == currentDepth) |
696 | 130 | { |
697 | 130 | nCollectionElementDepth = 0; |
698 | 130 | osCollectionElement = pszElementValue; |
699 | | #ifdef DEBUG_VERBOSE |
700 | | CPLDebug("JML", "osCollectionElement = %s", |
701 | | osCollectionElement.c_str()); |
702 | | #endif |
703 | 130 | StopAccumulate(); |
704 | 130 | } |
705 | 32.7k | else if (nFeatureElementDepth == currentDepth) |
706 | 124 | { |
707 | 124 | nFeatureElementDepth = 0; |
708 | 124 | osFeatureElement = pszElementValue; |
709 | | #ifdef DEBUG_VERBOSE |
710 | | CPLDebug("JML", "osFeatureElement = %s", osFeatureElement.c_str()); |
711 | | #endif |
712 | 124 | StopAccumulate(); |
713 | 124 | } |
714 | 32.6k | else if (nGeometryElementDepth == currentDepth) |
715 | 108 | { |
716 | 108 | nGeometryElementDepth = 0; |
717 | 108 | osGeometryElement = pszElementValue; |
718 | | #ifdef DEBUG_VERBOSE |
719 | | CPLDebug("JML", "osGeometryElement = %s", osGeometryElement.c_str()); |
720 | | #endif |
721 | 108 | StopAccumulate(); |
722 | 108 | } |
723 | 32.5k | else if (nColumnDepth == currentDepth) |
724 | 205 | { |
725 | 205 | bool bIsOK = true; |
726 | 205 | if (oCurColumn.osName.empty()) |
727 | 33 | bIsOK = false; |
728 | 205 | if (oCurColumn.osType.empty()) |
729 | 46 | bIsOK = false; |
730 | 205 | if (oCurColumn.osElementName.empty()) |
731 | 60 | bIsOK = false; |
732 | 205 | if (oCurColumn.bIsBody) |
733 | 97 | { |
734 | 97 | if (oCurColumn.osAttributeName.empty() && |
735 | 97 | !oCurColumn.osAttributeValue.empty()) |
736 | 2 | bIsOK = false; |
737 | 97 | if (!oCurColumn.osAttributeName.empty() && |
738 | 97 | oCurColumn.osAttributeValue.empty()) |
739 | 5 | bIsOK = false; |
740 | | /* Only 2 valid possibilities : */ |
741 | | /* <osElementName |
742 | | * osAttributeName="osAttributeValue">value</osElementName> */ |
743 | | /* <osElementName>value</osElementName> */ |
744 | 97 | } |
745 | 108 | else |
746 | 108 | { |
747 | | /* <osElementName osAttributeName="value"></osElementName> */ |
748 | 108 | if (oCurColumn.osAttributeName.empty()) |
749 | 65 | bIsOK = false; |
750 | 108 | if (!oCurColumn.osAttributeValue.empty()) |
751 | 38 | bIsOK = false; |
752 | 108 | } |
753 | | |
754 | 205 | if (bIsOK) |
755 | 87 | { |
756 | 87 | OGRFieldType eType = OFTString; |
757 | 87 | if (EQUAL(oCurColumn.osType, "INTEGER")) |
758 | 3 | eType = OFTInteger; |
759 | 84 | else if (EQUAL(oCurColumn.osType, "DOUBLE")) |
760 | 0 | eType = OFTReal; |
761 | 84 | else if (EQUAL(oCurColumn.osType, "DATE")) |
762 | 14 | eType = OFTDateTime; |
763 | 87 | OGRFieldDefn oField(oCurColumn.osName, eType); |
764 | | |
765 | 87 | if (oCurColumn.osName == "R_G_B" && eType == OFTString) |
766 | 7 | iRGBField = poFeatureDefn->GetFieldCount(); |
767 | | |
768 | 87 | poFeatureDefn->AddFieldDefn(&oField); |
769 | 87 | aoColumns.push_back(oCurColumn); |
770 | 87 | } |
771 | 118 | else |
772 | 118 | { |
773 | 118 | CPLDebug("JML", |
774 | 118 | "Invalid column definition: name = %s, type = %s, " |
775 | 118 | "elementName = %s, attributeName = %s, " |
776 | 118 | "attributeValue = %s, bIsBody = %d", |
777 | 118 | oCurColumn.osName.c_str(), oCurColumn.osType.c_str(), |
778 | 118 | oCurColumn.osElementName.c_str(), |
779 | 118 | oCurColumn.osAttributeName.c_str(), |
780 | 118 | oCurColumn.osAttributeValue.c_str(), |
781 | 118 | static_cast<int>(oCurColumn.bIsBody)); |
782 | 118 | } |
783 | | |
784 | 205 | nColumnDepth = 0; |
785 | 205 | } |
786 | 32.3k | else if (nNameDepth == currentDepth) |
787 | 401 | { |
788 | 401 | nNameDepth = 0; |
789 | 401 | oCurColumn.osName = pszElementValue; |
790 | | #ifdef DEBUG_VERBOSE |
791 | | CPLDebug("JML", "oCurColumn.osName = %s", oCurColumn.osName.c_str()); |
792 | | #endif |
793 | 401 | StopAccumulate(); |
794 | 401 | } |
795 | 31.9k | else if (nTypeDepth == currentDepth) |
796 | 420 | { |
797 | 420 | nTypeDepth = 0; |
798 | 420 | oCurColumn.osType = pszElementValue; |
799 | | #ifdef DEBUG_VERBOSE |
800 | | CPLDebug("JML", "oCurColumn.osType = %s", oCurColumn.osType.c_str()); |
801 | | #endif |
802 | 420 | StopAccumulate(); |
803 | 420 | } |
804 | 32.8k | } |
805 | | |
806 | | /************************************************************************/ |
807 | | /* TestCapability() */ |
808 | | /************************************************************************/ |
809 | | |
810 | | int OGRJMLLayer::TestCapability(const char *pszCap) |
811 | | |
812 | 1 | { |
813 | 1 | if (EQUAL(pszCap, OLCStringsAsUTF8)) |
814 | 0 | return true; |
815 | 1 | else if (EQUAL(pszCap, OLCZGeometries)) |
816 | 0 | return true; |
817 | | |
818 | 1 | return false; |
819 | 1 | } |
820 | | |
821 | | #endif /* HAVE_EXPAT */ |