Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: NAS Reader
4 : * Purpose: Implementation of NASReader class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2002, Frank Warmerdam
9 : * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "gmlreaderp.h"
31 : #include "gmlreader.h"
32 :
33 : #include <algorithm>
34 :
35 : #include "cpl_conv.h"
36 : #include "cpl_error.h"
37 : #include "cpl_multiproc.h"
38 : #include "cpl_string.h"
39 : #include "gmlutils.h"
40 : #include "ogr_geometry.h"
41 :
42 : /************************************************************************/
43 : /* ==================================================================== */
44 : /* With XERCES Library */
45 : /* ==================================================================== */
46 : /************************************************************************/
47 :
48 : #include "nasreaderp.h"
49 :
50 : /************************************************************************/
51 : /* CreateNASReader() */
52 : /************************************************************************/
53 :
54 3 : IGMLReader *CreateNASReader()
55 :
56 : {
57 3 : return new NASReader();
58 : }
59 :
60 : /************************************************************************/
61 : /* NASReader() */
62 : /************************************************************************/
63 :
64 3 : NASReader::NASReader()
65 : : m_bClassListLocked(false), m_nClassCount(0), m_papoClass(nullptr),
66 : m_pszFilename(nullptr), m_poNASHandler(nullptr), m_poSAXReader(nullptr),
67 : m_bReadStarted(false), m_bXercesInitialized(false), m_poState(nullptr),
68 : m_poCompleteFeature(nullptr), m_fp(nullptr), m_GMLInputSource(nullptr),
69 3 : m_pszFilteredClassName(nullptr)
70 : {
71 3 : }
72 :
73 : /************************************************************************/
74 : /* ~NASReader() */
75 : /************************************************************************/
76 :
77 6 : NASReader::~NASReader()
78 :
79 : {
80 3 : NASReader::ClearClasses();
81 :
82 3 : CPLFree(m_pszFilename);
83 :
84 3 : CleanupParser();
85 :
86 3 : if (m_fp)
87 3 : VSIFCloseL(m_fp);
88 :
89 3 : if (m_bXercesInitialized)
90 3 : OGRDeinitializeXerces();
91 :
92 3 : CPLFree(m_pszFilteredClassName);
93 6 : }
94 :
95 : /************************************************************************/
96 : /* SetSourceFile() */
97 : /************************************************************************/
98 :
99 3 : void NASReader::SetSourceFile(const char *pszFilename)
100 :
101 : {
102 3 : CPLFree(m_pszFilename);
103 3 : m_pszFilename = CPLStrdup(pszFilename);
104 3 : }
105 :
106 : /************************************************************************/
107 : /* GetSourceFileName() */
108 : /************************************************************************/
109 :
110 0 : const char *NASReader::GetSourceFileName()
111 : {
112 0 : return m_pszFilename;
113 : }
114 :
115 : /************************************************************************/
116 : /* SetupParser() */
117 : /************************************************************************/
118 :
119 6 : bool NASReader::SetupParser()
120 :
121 : {
122 6 : if (m_fp == nullptr)
123 3 : m_fp = VSIFOpenL(m_pszFilename, "rb");
124 6 : if (m_fp == nullptr)
125 0 : return false;
126 6 : VSIFSeekL(m_fp, 0, SEEK_SET);
127 :
128 6 : if (!m_bXercesInitialized)
129 : {
130 3 : if (!OGRInitializeXerces())
131 0 : return false;
132 3 : m_bXercesInitialized = true;
133 : }
134 :
135 : // Cleanup any old parser.
136 6 : if (m_poSAXReader != nullptr)
137 0 : CleanupParser();
138 :
139 : // Create and initialize parser.
140 6 : XMLCh *xmlUriValid = nullptr;
141 6 : XMLCh *xmlUriNS = nullptr;
142 :
143 : try
144 : {
145 6 : m_poSAXReader = XMLReaderFactory::createXMLReader();
146 :
147 6 : m_poNASHandler = new NASHandler(this);
148 :
149 6 : m_poSAXReader->setContentHandler(m_poNASHandler);
150 6 : m_poSAXReader->setErrorHandler(m_poNASHandler);
151 6 : m_poSAXReader->setLexicalHandler(m_poNASHandler);
152 6 : m_poSAXReader->setEntityResolver(m_poNASHandler);
153 6 : m_poSAXReader->setDTDHandler(m_poNASHandler);
154 6 : m_poSAXReader->setFeature(
155 6 : XMLUni::fgXercesDisableDefaultEntityResolution, true);
156 :
157 6 : xmlUriValid =
158 6 : XMLString::transcode("http://xml.org/sax/features/validation");
159 6 : xmlUriNS =
160 6 : XMLString::transcode("http://xml.org/sax/features/namespaces");
161 :
162 : #if (OGR_GML_VALIDATION)
163 : m_poSAXReader->setFeature(xmlUriValid, true);
164 : m_poSAXReader->setFeature(xmlUriNS, true);
165 :
166 : m_poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
167 : m_poSAXReader->setFeature(XMLUni::fgXercesSchema, true);
168 :
169 : // m_poSAXReader->setDoSchema(true);
170 : // m_poSAXReader->setValidationSchemaFullChecking(true);
171 : #else
172 6 : m_poSAXReader->setFeature(XMLUni::fgSAX2CoreValidation, false);
173 :
174 6 : m_poSAXReader->setFeature(XMLUni::fgXercesSchema, false);
175 :
176 : #endif
177 6 : XMLString::release(&xmlUriValid);
178 6 : XMLString::release(&xmlUriNS);
179 : }
180 0 : catch (...)
181 : {
182 0 : XMLString::release(&xmlUriValid);
183 0 : XMLString::release(&xmlUriNS);
184 :
185 0 : CPLError(CE_Warning, CPLE_AppDefined,
186 : "NAS: Exception initializing Xerces based GML reader.\n");
187 0 : return false;
188 : }
189 :
190 6 : m_bReadStarted = false;
191 :
192 : // Push an empty state.
193 6 : PushState(new GMLReadState());
194 :
195 6 : if (m_GMLInputSource == nullptr)
196 : {
197 6 : m_GMLInputSource = OGRCreateXercesInputSource(m_fp);
198 : }
199 :
200 6 : return true;
201 : }
202 :
203 : /************************************************************************/
204 : /* CleanupParser() */
205 : /************************************************************************/
206 :
207 12 : void NASReader::CleanupParser()
208 :
209 : {
210 12 : if (m_poSAXReader == nullptr)
211 6 : return;
212 :
213 12 : while (m_poState)
214 6 : PopState();
215 :
216 6 : delete m_poSAXReader;
217 6 : m_poSAXReader = nullptr;
218 :
219 6 : delete m_poNASHandler;
220 6 : m_poNASHandler = nullptr;
221 :
222 6 : delete m_poCompleteFeature;
223 6 : m_poCompleteFeature = nullptr;
224 :
225 6 : OGRDestroyXercesInputSource(m_GMLInputSource);
226 6 : m_GMLInputSource = nullptr;
227 :
228 6 : m_bReadStarted = false;
229 : }
230 :
231 : /************************************************************************/
232 : /* NextFeature() */
233 : /************************************************************************/
234 :
235 11 : GMLFeature *NASReader::NextFeature()
236 :
237 : {
238 11 : GMLFeature *poReturn = nullptr;
239 :
240 : try
241 : {
242 11 : if (!m_bReadStarted)
243 : {
244 6 : if (m_poSAXReader == nullptr)
245 3 : SetupParser();
246 :
247 6 : if (m_poSAXReader == nullptr)
248 0 : return nullptr;
249 :
250 6 : if (!m_poSAXReader->parseFirst(*m_GMLInputSource, m_oToFill))
251 0 : return nullptr;
252 6 : m_bReadStarted = true;
253 : }
254 :
255 1726 : while (m_poCompleteFeature == nullptr && !m_bStopParsing &&
256 1707 : m_poSAXReader->parseNext(m_oToFill))
257 : {
258 : }
259 :
260 11 : poReturn = m_poCompleteFeature;
261 11 : m_poCompleteFeature = nullptr;
262 : }
263 0 : catch (const XMLException &toCatch)
264 : {
265 0 : m_bStopParsing = true;
266 0 : CPLDebug("NAS", "Error during NextFeature()! Message:\n%s",
267 0 : transcode(toCatch.getMessage()).c_str());
268 : }
269 :
270 11 : return poReturn;
271 : }
272 :
273 : /************************************************************************/
274 : /* PushFeature() */
275 : /* */
276 : /* Create a feature based on the named element. If the */
277 : /* corresponding feature class doesn't exist yet, then create */
278 : /* it now. A new GMLReadState will be created for the feature, */
279 : /* and it will be placed within that state. The state is */
280 : /* pushed onto the readstate stack. */
281 : /************************************************************************/
282 :
283 8 : void NASReader::PushFeature(const char *pszElement, const Attributes &attrs)
284 :
285 : {
286 : /* -------------------------------------------------------------------- */
287 : /* Find the class of this element. */
288 : /* -------------------------------------------------------------------- */
289 8 : int iClass = 0;
290 10 : for (; iClass < GetClassCount(); iClass++)
291 : {
292 7 : if (strcmp(pszElement, GetClass(iClass)->GetElementName()) == 0)
293 5 : break;
294 : }
295 :
296 : /* -------------------------------------------------------------------- */
297 : /* Create a new feature class for this element, if there is no */
298 : /* existing class for it. */
299 : /* -------------------------------------------------------------------- */
300 8 : if (iClass == GetClassCount())
301 : {
302 3 : CPLAssert(!IsClassListLocked());
303 :
304 3 : GMLFeatureClass *poNewClass = new GMLFeatureClass(pszElement);
305 :
306 3 : if (EQUAL(pszElement, "Delete"))
307 : {
308 : const struct
309 : {
310 : const char *pszName;
311 : GMLPropertyType eType;
312 : int width;
313 2 : } types[] = {
314 : {"typeName", GMLPT_String, -1},
315 : {"FeatureId", GMLPT_String, -1},
316 : {"context", GMLPT_String, -1},
317 : {"safeToIgnore", GMLPT_String, -1},
318 : {"replacedBy", GMLPT_String, -1},
319 : {"anlass", GMLPT_StringList, -1},
320 : {"endet", GMLPT_String, 20},
321 : {"ignored", GMLPT_String, -1},
322 : };
323 :
324 18 : for (unsigned int i = 0; i < CPL_ARRAYSIZE(types); i++)
325 : {
326 : GMLPropertyDefn *poPDefn =
327 16 : new GMLPropertyDefn(types[i].pszName, types[i].pszName);
328 :
329 16 : poPDefn->SetType(types[i].eType);
330 16 : if (types[i].width > 0)
331 2 : poPDefn->SetWidth(types[i].width);
332 :
333 16 : poNewClass->AddProperty(poPDefn);
334 : }
335 : }
336 :
337 3 : iClass = AddClass(poNewClass);
338 : }
339 :
340 : /* -------------------------------------------------------------------- */
341 : /* Create a feature of this feature class. */
342 : /* -------------------------------------------------------------------- */
343 8 : GMLFeature *poFeature = new GMLFeature(GetClass(iClass));
344 :
345 : /* -------------------------------------------------------------------- */
346 : /* Create and push a new read state. */
347 : /* -------------------------------------------------------------------- */
348 8 : GMLReadState *poState = new GMLReadState();
349 8 : poState->m_poFeature = poFeature;
350 8 : PushState(poState);
351 :
352 : /* -------------------------------------------------------------------- */
353 : /* Check for gml:id, and if found push it as an attribute named */
354 : /* gml_id. */
355 : /* -------------------------------------------------------------------- */
356 8 : const XMLCh achGmlId[] = {'g', 'm', 'l', ':', 'i', 'd', 0};
357 8 : int nFIDIndex = attrs.getIndex(achGmlId);
358 8 : if (nFIDIndex != -1)
359 : {
360 2 : char *pszFID = CPLStrdup(transcode(attrs.getValue(nFIDIndex)));
361 2 : SetFeaturePropertyDirectly("gml_id", pszFID);
362 : }
363 8 : }
364 :
365 : /************************************************************************/
366 : /* IsFeatureElement() */
367 : /* */
368 : /* Based on context and the element name, is this element a new */
369 : /* GML feature element? */
370 : /************************************************************************/
371 :
372 151 : bool NASReader::IsFeatureElement(const char *pszElement)
373 :
374 : {
375 151 : CPLAssert(m_poState != nullptr);
376 :
377 151 : const char *pszLast = m_poState->GetLastComponent();
378 151 : const int nLen = static_cast<int>(strlen(pszLast));
379 :
380 : // There seem to be two major NAS classes of feature identifiers
381 : // -- either a wfs:Insert or a gml:featureMember/wfs:member
382 :
383 151 : if ((nLen < 6 || !EQUAL(pszLast + nLen - 6, "Insert")) &&
384 151 : (nLen < 13 || !EQUAL(pszLast + nLen - 13, "featureMember")) &&
385 151 : (nLen < 6 || !EQUAL(pszLast + nLen - 6, "member")) &&
386 141 : (nLen < 7 || !EQUAL(pszLast + nLen - 7, "Replace")))
387 146 : return false;
388 :
389 : // If the class list isn't locked, any element that is a featureMember
390 : // will do.
391 5 : if (EQUAL(pszElement, "Filter"))
392 2 : return false;
393 :
394 3 : if (!IsClassListLocked())
395 3 : return true;
396 :
397 0 : if (EQUAL(pszElement, "Delete"))
398 0 : return false;
399 :
400 : // otherwise, find a class with the desired element name.
401 0 : for (int i = 0; i < GetClassCount(); i++)
402 : {
403 0 : if (EQUAL(pszElement, GetClass(i)->GetElementName()))
404 0 : return true;
405 : }
406 :
407 0 : return false;
408 : }
409 :
410 : /************************************************************************/
411 : /* IsAttributeElement() */
412 : /************************************************************************/
413 :
414 106 : bool NASReader::IsAttributeElement(const char *pszElement,
415 : const Attributes &attrs)
416 :
417 : {
418 106 : if (m_poState->m_poFeature == nullptr)
419 0 : return false;
420 :
421 106 : GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
422 :
423 : // If the schema is not yet locked, then any simple element
424 : // is potentially an attribute.
425 106 : if (!poClass->IsSchemaLocked())
426 106 : return true;
427 :
428 : // Otherwise build the path to this element into a single string
429 : // and compare against known attributes.
430 0 : CPLString osElemPath;
431 :
432 0 : if (m_poState->m_nPathLength == 0)
433 0 : osElemPath = pszElement;
434 : else
435 : {
436 0 : osElemPath = m_poState->osPath;
437 0 : osElemPath += "|";
438 0 : osElemPath += pszElement;
439 : }
440 :
441 0 : if (poClass->GetPropertyIndexBySrcElement(
442 0 : osElemPath.c_str(), static_cast<int>(osElemPath.size())) >= 0)
443 0 : return true;
444 :
445 0 : for (unsigned int idx = 0; idx < attrs.getLength(); ++idx)
446 : {
447 0 : CPLString osAttrName = transcode(attrs.getQName(idx));
448 0 : CPLString osAttrPath;
449 :
450 0 : const char *pszName = strchr(osAttrName.c_str(), ':');
451 0 : if (pszName)
452 : {
453 0 : osAttrPath = osElemPath + "@" + (pszName + 1);
454 0 : if (poClass->GetPropertyIndexBySrcElement(
455 0 : osAttrPath.c_str(), static_cast<int>(osAttrPath.size())) >=
456 : 0)
457 0 : return true;
458 : }
459 :
460 0 : osAttrPath = osElemPath + "@" + osAttrName;
461 0 : if (poClass->GetPropertyIndexBySrcElement(
462 0 : osAttrPath.c_str(), static_cast<int>(osAttrPath.size())) >= 0)
463 0 : return true;
464 : }
465 :
466 0 : return false;
467 : }
468 :
469 : /************************************************************************/
470 : /* PopState() */
471 : /************************************************************************/
472 :
473 14 : void NASReader::PopState()
474 :
475 : {
476 14 : if (m_poState != nullptr)
477 : {
478 14 : if (m_poState->m_poFeature != nullptr && m_poCompleteFeature == nullptr)
479 : {
480 8 : m_poCompleteFeature = m_poState->m_poFeature;
481 8 : m_poState->m_poFeature = nullptr;
482 : }
483 6 : else if (m_poState->m_poFeature != nullptr)
484 : {
485 0 : delete m_poState->m_poFeature;
486 0 : m_poState->m_poFeature = nullptr;
487 : }
488 :
489 14 : GMLReadState *poParent = m_poState->m_poParentState;
490 :
491 14 : delete m_poState;
492 14 : m_poState = poParent;
493 : }
494 14 : }
495 :
496 : /************************************************************************/
497 : /* PushState() */
498 : /************************************************************************/
499 :
500 14 : void NASReader::PushState(GMLReadState *poState)
501 :
502 : {
503 14 : poState->m_poParentState = m_poState;
504 14 : m_poState = poState;
505 14 : }
506 :
507 : /************************************************************************/
508 : /* GetClass() */
509 : /************************************************************************/
510 :
511 21 : GMLFeatureClass *NASReader::GetClass(int iClass) const
512 :
513 : {
514 21 : if (iClass < 0 || iClass >= m_nClassCount)
515 0 : return nullptr;
516 :
517 21 : return m_papoClass[iClass];
518 : }
519 :
520 : /************************************************************************/
521 : /* GetClass() */
522 : /************************************************************************/
523 :
524 6 : GMLFeatureClass *NASReader::GetClass(const char *pszName) const
525 :
526 : {
527 8 : for (int iClass = 0; iClass < m_nClassCount; iClass++)
528 : {
529 5 : if (strcmp(m_papoClass[iClass]->GetName(), pszName) == 0)
530 3 : return m_papoClass[iClass];
531 : }
532 :
533 3 : return nullptr;
534 : }
535 :
536 : /************************************************************************/
537 : /* AddClass() */
538 : /************************************************************************/
539 :
540 3 : int NASReader::AddClass(GMLFeatureClass *poNewClass)
541 :
542 : {
543 3 : CPLAssert(poNewClass != nullptr &&
544 : GetClass(poNewClass->GetName()) == nullptr);
545 :
546 3 : m_nClassCount++;
547 3 : m_papoClass = static_cast<GMLFeatureClass **>(
548 3 : CPLRealloc(m_papoClass, sizeof(void *) * m_nClassCount));
549 :
550 : // keep delete the last entry
551 4 : if (m_nClassCount > 1 &&
552 1 : EQUAL(m_papoClass[m_nClassCount - 2]->GetName(), "Delete"))
553 : {
554 0 : m_papoClass[m_nClassCount - 1] = m_papoClass[m_nClassCount - 2];
555 0 : m_papoClass[m_nClassCount - 2] = poNewClass;
556 0 : return m_nClassCount - 2;
557 : }
558 : else
559 : {
560 3 : m_papoClass[m_nClassCount - 1] = poNewClass;
561 3 : return m_nClassCount - 1;
562 : }
563 : }
564 :
565 : /************************************************************************/
566 : /* ClearClasses() */
567 : /************************************************************************/
568 :
569 3 : void NASReader::ClearClasses()
570 :
571 : {
572 3 : CPLDebug("NAS", "Clearing classes.");
573 :
574 6 : for (int i = 0; i < m_nClassCount; i++)
575 3 : delete m_papoClass[i];
576 3 : CPLFree(m_papoClass);
577 :
578 3 : m_nClassCount = 0;
579 3 : m_papoClass = nullptr;
580 3 : }
581 :
582 : /************************************************************************/
583 : /* SetFeaturePropertyDirectly() */
584 : /* */
585 : /* Set the property value on the current feature, adding the */
586 : /* property name to the GMLFeatureClass if required. */
587 : /* The pszValue ownership is passed to this function. */
588 : /************************************************************************/
589 :
590 82 : void NASReader::SetFeaturePropertyDirectly(const char *pszElement,
591 : char *pszValue)
592 :
593 : {
594 82 : GMLFeature *poFeature = GetState()->m_poFeature;
595 :
596 82 : CPLAssert(poFeature != nullptr);
597 :
598 : /* -------------------------------------------------------------------- */
599 : /* Does this property exist in the feature class? If not, add */
600 : /* it. */
601 : /* -------------------------------------------------------------------- */
602 82 : GMLFeatureClass *poClass = poFeature->GetClass();
603 164 : int iProperty = poClass->GetPropertyIndexBySrcElement(
604 82 : pszElement, static_cast<int>(strlen(pszElement)));
605 :
606 82 : if (iProperty < 0)
607 : {
608 26 : if (poClass->IsSchemaLocked())
609 : {
610 : // CPLDebug("NAS", "Encountered property %s missing from class %s schema [%s].", pszElement, poClass->GetName(), pszValue);
611 0 : CPLFree(pszValue);
612 0 : return;
613 : }
614 :
615 26 : iProperty = poClass->GetPropertyCount();
616 :
617 52 : CPLString osFieldName;
618 :
619 26 : if (strchr(pszElement, '|') == nullptr)
620 11 : osFieldName = pszElement;
621 : else
622 : {
623 15 : osFieldName = strrchr(pszElement, '|') + 1;
624 15 : if (poClass->GetPropertyIndex(osFieldName) != -1)
625 2 : osFieldName = pszElement;
626 : }
627 :
628 : // Does this conflict with an existing property name?
629 26 : while (poClass->GetProperty(osFieldName) != nullptr)
630 : {
631 0 : osFieldName += "_";
632 : }
633 :
634 26 : GMLPropertyDefn *poPDefn = new GMLPropertyDefn(osFieldName, pszElement);
635 :
636 26 : if (EQUAL(CPLGetConfigOption("GML_FIELDTYPES", ""), "ALWAYS_STRING"))
637 0 : poPDefn->SetType(GMLPT_String);
638 :
639 26 : poClass->AddProperty(poPDefn);
640 : }
641 :
642 82 : if (GMLPropertyDefn::IsSimpleType(
643 : poClass->GetProperty(iProperty)->GetType()))
644 : {
645 44 : const GMLProperty *poProp = poFeature->GetProperty(iProperty);
646 44 : if (poProp && poProp->nSubProperties > 0)
647 : {
648 2 : int iId = poClass->GetPropertyIndex("gml_id");
649 2 : const GMLProperty *poIdProp = poFeature->GetProperty(iId);
650 :
651 4 : CPLError(CE_Warning, CPLE_AppDefined,
652 : "NAS: Overwriting existing property %s.%s of value '%s' "
653 : "with '%s' (gml_id: %s; type:%d).",
654 : poClass->GetName(), pszElement,
655 2 : poProp->papszSubProperties[0], pszValue,
656 2 : poIdProp && poIdProp->nSubProperties > 0 &&
657 2 : poIdProp->papszSubProperties &&
658 2 : poIdProp->papszSubProperties[0]
659 2 : ? poIdProp->papszSubProperties[0]
660 : : "(null)",
661 2 : poClass->GetProperty(iProperty)->GetType());
662 : }
663 : }
664 :
665 : /* -------------------------------------------------------------------- */
666 : /* Set the property */
667 : /* -------------------------------------------------------------------- */
668 82 : poFeature->SetPropertyDirectly(iProperty, pszValue);
669 :
670 : /* -------------------------------------------------------------------- */
671 : /* Do we need to update the property type? */
672 : /* -------------------------------------------------------------------- */
673 82 : if (!poClass->IsSchemaLocked())
674 : {
675 82 : auto poClassProperty = poClass->GetProperty(iProperty);
676 82 : if (poClassProperty)
677 : {
678 : // coverity[dereference]
679 82 : poClassProperty->AnalysePropertyValue(
680 : poFeature->GetProperty(iProperty));
681 : }
682 : else
683 : {
684 0 : CPLAssert(false);
685 : }
686 : }
687 : }
688 :
689 : /************************************************************************/
690 : /* LoadClasses() */
691 : /************************************************************************/
692 :
693 0 : bool NASReader::LoadClasses(const char *pszFile)
694 :
695 : {
696 : // Add logic later to determine reasonable default schema file.
697 0 : if (pszFile == nullptr)
698 0 : return false;
699 :
700 0 : CPLDebug("NAS", "Loading classes from %s", pszFile);
701 :
702 : /* -------------------------------------------------------------------- */
703 : /* Load the raw XML file. */
704 : /* -------------------------------------------------------------------- */
705 0 : VSILFILE *fp = VSIFOpenL(pszFile, "rb");
706 :
707 0 : if (fp == nullptr)
708 : {
709 0 : CPLError(CE_Failure, CPLE_OpenFailed, "NAS: Failed to open file %s.",
710 : pszFile);
711 0 : return false;
712 : }
713 :
714 0 : VSIFSeekL(fp, 0, SEEK_END);
715 0 : int nLength = static_cast<int>(VSIFTellL(fp));
716 0 : VSIFSeekL(fp, 0, SEEK_SET);
717 :
718 0 : char *pszWholeText = static_cast<char *>(VSIMalloc(nLength + 1));
719 0 : if (pszWholeText == nullptr)
720 : {
721 0 : CPLError(CE_Failure, CPLE_AppDefined,
722 : "NAS: Failed to allocate %d byte buffer for %s,\n"
723 : "is this really a GMLFeatureClassList file?",
724 : nLength, pszFile);
725 0 : VSIFCloseL(fp);
726 0 : return false;
727 : }
728 :
729 0 : if (VSIFReadL(pszWholeText, nLength, 1, fp) != 1)
730 : {
731 0 : VSIFree(pszWholeText);
732 0 : VSIFCloseL(fp);
733 0 : CPLError(CE_Failure, CPLE_AppDefined, "NAS: Read failed on %s.",
734 : pszFile);
735 0 : return false;
736 : }
737 0 : pszWholeText[nLength] = '\0';
738 :
739 0 : VSIFCloseL(fp);
740 :
741 0 : if (strstr(pszWholeText, "<GMLFeatureClassList") == nullptr)
742 : {
743 0 : VSIFree(pszWholeText);
744 0 : CPLError(CE_Failure, CPLE_AppDefined,
745 : "NAS: File %s does not contain a GMLFeatureClassList tree.",
746 : pszFile);
747 0 : return false;
748 : }
749 :
750 : /* -------------------------------------------------------------------- */
751 : /* Convert to XML parse tree. */
752 : /* -------------------------------------------------------------------- */
753 0 : CPLXMLTreeCloser psRoot(CPLParseXMLString(pszWholeText));
754 0 : VSIFree(pszWholeText);
755 :
756 : // We assume parser will report errors via CPL.
757 0 : if (psRoot.get() == nullptr)
758 0 : return false;
759 :
760 0 : if (psRoot->eType != CXT_Element ||
761 0 : !EQUAL(psRoot->pszValue, "GMLFeatureClassList"))
762 : {
763 0 : CPLError(CE_Failure, CPLE_AppDefined,
764 : "NAS: File %s is not a GMLFeatureClassList document.",
765 : pszFile);
766 0 : return false;
767 : }
768 :
769 : /* -------------------------------------------------------------------- */
770 : /* Extract feature classes for all definitions found. */
771 : /* -------------------------------------------------------------------- */
772 0 : for (CPLXMLNode *psThis = psRoot->psChild; psThis != nullptr;
773 0 : psThis = psThis->psNext)
774 : {
775 0 : if (psThis->eType == CXT_Element &&
776 0 : EQUAL(psThis->pszValue, "GMLFeatureClass"))
777 : {
778 0 : GMLFeatureClass *poClass = new GMLFeatureClass();
779 :
780 0 : if (!poClass->InitializeFromXML(psThis))
781 : {
782 0 : delete poClass;
783 0 : return false;
784 : }
785 :
786 0 : poClass->SetSchemaLocked(true);
787 :
788 0 : AddClass(poClass);
789 : }
790 : }
791 :
792 0 : SetClassListLocked(true);
793 :
794 0 : return true;
795 : }
796 :
797 : /************************************************************************/
798 : /* SaveClasses() */
799 : /************************************************************************/
800 :
801 2 : bool NASReader::SaveClasses(const char *pszFile)
802 :
803 : {
804 : // Add logic later to determine reasonable default schema file.
805 2 : if (pszFile == nullptr)
806 0 : return false;
807 :
808 : /* -------------------------------------------------------------------- */
809 : /* Create in memory schema tree. */
810 : /* -------------------------------------------------------------------- */
811 : CPLXMLNode *psRoot =
812 2 : CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClassList");
813 :
814 5 : for (int iClass = 0; iClass < GetClassCount(); iClass++)
815 : {
816 3 : GMLFeatureClass *poClass = GetClass(iClass);
817 :
818 3 : CPLAddXMLChild(psRoot, poClass->SerializeToXML());
819 : }
820 :
821 : /* -------------------------------------------------------------------- */
822 : /* Serialize to disk. */
823 : /* -------------------------------------------------------------------- */
824 2 : char *pszWholeText = CPLSerializeXMLTree(psRoot);
825 :
826 2 : CPLDestroyXMLNode(psRoot);
827 :
828 2 : VSILFILE *fp = VSIFOpenL(pszFile, "wb");
829 :
830 2 : bool bSuccess = true;
831 2 : if (fp == nullptr)
832 0 : bSuccess = false;
833 2 : else if (VSIFWriteL(pszWholeText, strlen(pszWholeText), 1, fp) != 1)
834 : {
835 0 : VSIFCloseL(fp);
836 0 : bSuccess = false;
837 : }
838 : else
839 : {
840 2 : if (VSIFWriteL(pszWholeText, strlen(pszWholeText), 1, fp) != 1)
841 0 : bSuccess = false;
842 2 : VSIFCloseL(fp);
843 : }
844 :
845 2 : CPLFree(pszWholeText);
846 :
847 2 : return bSuccess;
848 : }
849 :
850 : /************************************************************************/
851 : /* PrescanForSchema() */
852 : /* */
853 : /* For now we use a pretty dumb approach of just doing a normal */
854 : /* scan of the whole file, building up the schema information. */
855 : /* Eventually we hope to do a more efficient scan when just */
856 : /* looking for schema information. */
857 : /************************************************************************/
858 :
859 3 : bool NASReader::PrescanForSchema(bool bGetExtents, bool /*bOnlyDetectSRS*/)
860 : {
861 3 : if (m_pszFilename == nullptr)
862 0 : return false;
863 :
864 3 : CPLDebug("NAS", "Prescanning %s.", m_pszFilename);
865 :
866 3 : SetClassListLocked(false);
867 :
868 3 : if (!SetupParser())
869 0 : return false;
870 :
871 3 : std::string osWork;
872 :
873 3 : GMLFeature *poFeature = nullptr;
874 8 : while ((poFeature = NextFeature()) != nullptr)
875 : {
876 5 : GMLFeatureClass *poClass = poFeature->GetClass();
877 :
878 5 : if (poClass->GetFeatureCount() == -1)
879 3 : poClass->SetFeatureCount(1);
880 : else
881 2 : poClass->SetFeatureCount(poClass->GetFeatureCount() + 1);
882 :
883 5 : if (bGetExtents)
884 : {
885 5 : OGRGeometry *poGeometry = nullptr;
886 :
887 : const CPLXMLNode *const *papsGeometry =
888 5 : poFeature->GetGeometryList();
889 5 : if (papsGeometry[0] != nullptr)
890 : {
891 : poGeometry =
892 1 : (OGRGeometry *)OGR_G_CreateFromGMLTree(papsGeometry[0]);
893 1 : poGeometry = ConvertGeometry(poGeometry);
894 : }
895 :
896 5 : if (poGeometry != nullptr)
897 : {
898 1 : OGREnvelope sEnvelope;
899 :
900 1 : if (poClass->GetGeometryPropertyCount() == 0)
901 2 : poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
902 1 : "", "", wkbUnknown, -1, true));
903 :
904 : OGRwkbGeometryType eGType =
905 : (OGRwkbGeometryType)poClass->GetGeometryProperty(0)
906 1 : ->GetType();
907 :
908 : // Merge SRSName into layer.
909 : const char *pszSRSName =
910 1 : GML_ExtractSrsNameFromGeometry(papsGeometry, osWork, false);
911 : // if (pszSRSName != NULL)
912 : // m_bCanUseGlobalSRSName = FALSE;
913 1 : poClass->MergeSRSName(pszSRSName);
914 :
915 : // Merge geometry type into layer.
916 1 : if (poClass->GetFeatureCount() == 1 && eGType == wkbUnknown)
917 1 : eGType = wkbNone;
918 :
919 2 : poClass->GetGeometryProperty(0)->SetType(
920 : OGRMergeGeometryTypesEx(
921 1 : eGType, poGeometry->getGeometryType(), TRUE));
922 :
923 : // merge extents.
924 1 : poGeometry->getEnvelope(&sEnvelope);
925 1 : delete poGeometry;
926 1 : double dfXMin = 0.0;
927 1 : double dfXMax = 0.0;
928 1 : double dfYMin = 0.0;
929 1 : double dfYMax = 0.0;
930 1 : if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
931 : {
932 0 : dfXMin = std::min(dfXMin, sEnvelope.MinX);
933 0 : dfXMax = std::max(dfXMax, sEnvelope.MaxX);
934 0 : dfYMin = std::min(dfYMin, sEnvelope.MinY);
935 0 : dfYMax = std::max(dfYMax, sEnvelope.MaxY);
936 : }
937 : else
938 : {
939 1 : dfXMin = sEnvelope.MinX;
940 1 : dfXMax = sEnvelope.MaxX;
941 1 : dfYMin = sEnvelope.MinY;
942 1 : dfYMax = sEnvelope.MaxY;
943 : }
944 :
945 1 : poClass->SetExtents(dfXMin, dfXMax, dfYMin, dfYMax);
946 : }
947 : else
948 : {
949 4 : if (poClass->GetGeometryPropertyCount() == 1 &&
950 0 : poClass->GetGeometryProperty(0)->GetType() ==
951 4 : (int)wkbUnknown &&
952 0 : poClass->GetFeatureCount() == 1)
953 : {
954 0 : poClass->ClearGeometryProperties();
955 : }
956 : }
957 : }
958 :
959 5 : delete poFeature;
960 : }
961 :
962 3 : CleanupParser();
963 :
964 : // Skip empty classes
965 3 : int j = 0;
966 6 : for (int i = 0, n = m_nClassCount; i < n; i++)
967 : {
968 3 : if (m_papoClass[i]->GetFeatureCount() > 0)
969 : {
970 3 : m_papoClass[j++] = m_papoClass[i];
971 3 : continue;
972 : }
973 :
974 0 : CPLDebug("NAS", "Skipping empty layer %s.", m_papoClass[i]->GetName());
975 :
976 0 : delete m_papoClass[i];
977 0 : m_papoClass[i] = nullptr;
978 : }
979 :
980 3 : m_nClassCount = j;
981 :
982 3 : CPLDebug("NAS", "%d remaining classes after prescan.", m_nClassCount);
983 :
984 6 : for (int i = 0; i < m_nClassCount; i++)
985 : {
986 3 : CPLDebug("NAS", "%s: " CPL_FRMT_GIB " features.",
987 3 : m_papoClass[i]->GetName(), m_papoClass[i]->GetFeatureCount());
988 : }
989 :
990 3 : return GetClassCount() > 0;
991 : }
992 :
993 : /************************************************************************/
994 : /* ResetReading() */
995 : /************************************************************************/
996 :
997 6 : void NASReader::ResetReading()
998 :
999 : {
1000 6 : CleanupParser();
1001 6 : SetFilteredClassName(nullptr);
1002 6 : }
1003 :
1004 : /************************************************************************/
1005 : /* GetAttributeElementIndex() */
1006 : /************************************************************************/
1007 :
1008 16 : int NASReader::GetAttributeElementIndex(const char *pszElement, int nLen,
1009 : const char *pszAttrKey)
1010 :
1011 : {
1012 16 : GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
1013 :
1014 : // Otherwise build the path to this element into a single string
1015 : // and compare against known attributes.
1016 16 : if (m_poState->m_nPathLength == 0)
1017 : {
1018 16 : if (pszAttrKey == nullptr)
1019 0 : return poClass->GetPropertyIndexBySrcElement(pszElement, nLen);
1020 : else
1021 : {
1022 32 : CPLString osElemPath;
1023 16 : int nFullLen = nLen + 1 + static_cast<int>(strlen(pszAttrKey));
1024 16 : osElemPath.reserve(nFullLen);
1025 16 : osElemPath.assign(pszElement, nLen);
1026 16 : osElemPath.append(1, '@');
1027 16 : osElemPath.append(pszAttrKey);
1028 16 : return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
1029 16 : nFullLen);
1030 : }
1031 : }
1032 : else
1033 : {
1034 0 : int nFullLen = nLen + static_cast<int>(m_poState->osPath.size()) + 1;
1035 0 : if (pszAttrKey != nullptr)
1036 0 : nFullLen += 1 + static_cast<int>(strlen(pszAttrKey));
1037 :
1038 0 : CPLString osElemPath;
1039 0 : osElemPath.reserve(nFullLen);
1040 0 : osElemPath.assign(m_poState->osPath);
1041 0 : osElemPath.append(1, '|');
1042 0 : osElemPath.append(pszElement, nLen);
1043 0 : if (pszAttrKey != nullptr)
1044 : {
1045 0 : osElemPath.append(1, '@');
1046 0 : osElemPath.append(pszAttrKey);
1047 : }
1048 0 : return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
1049 0 : nFullLen);
1050 : }
1051 :
1052 : return -1;
1053 : }
1054 :
1055 : /************************************************************************/
1056 : /* DealWithAttributes() */
1057 : /************************************************************************/
1058 :
1059 106 : void NASReader::DealWithAttributes(const char *pszName, int nLenName,
1060 : const Attributes &attrs)
1061 :
1062 : {
1063 106 : GMLFeature *poFeature = GetState()->m_poFeature;
1064 106 : CPLAssert(poFeature != nullptr);
1065 :
1066 116 : for (unsigned int idx = 0; idx < attrs.getLength(); ++idx)
1067 : {
1068 20 : CPLString osAttrKey = transcode(attrs.getQName(idx));
1069 20 : CPLString osAttrVal = transcode(attrs.getValue(idx));
1070 :
1071 10 : int nAttrIndex = 0;
1072 10 : const char *pszAttrKeyNoNS = strchr(osAttrKey, ':');
1073 10 : if (pszAttrKeyNoNS)
1074 6 : pszAttrKeyNoNS++;
1075 :
1076 16 : if ((pszAttrKeyNoNS &&
1077 6 : (nAttrIndex = GetAttributeElementIndex(pszName, nLenName,
1078 20 : pszAttrKeyNoNS)) != -1) ||
1079 10 : ((nAttrIndex = GetAttributeElementIndex(pszName, nLenName,
1080 : osAttrKey)) != -1))
1081 : {
1082 0 : const char *pszAttrVal = osAttrVal;
1083 0 : if (osAttrKey == "xlink:href" ||
1084 0 : (pszAttrKeyNoNS && EQUAL(pszAttrKeyNoNS, "href")))
1085 : {
1086 0 : if (STARTS_WITH_CI(pszAttrVal, "urn:adv:oid:"))
1087 0 : pszAttrVal += 12;
1088 0 : else if (STARTS_WITH_CI(
1089 : pszAttrVal,
1090 : "https://registry.gdi-de.org/codelist/"))
1091 0 : pszAttrVal = strrchr(pszAttrVal, '/') + 1;
1092 : }
1093 :
1094 0 : poFeature->SetPropertyDirectly(nAttrIndex, CPLStrdup(pszAttrVal));
1095 0 : pszAttrVal = nullptr;
1096 : }
1097 : }
1098 106 : }
1099 :
1100 : /************************************************************************/
1101 : /* HugeFileResolver() */
1102 : /* Returns true for success */
1103 : /************************************************************************/
1104 :
1105 0 : bool NASReader::HugeFileResolver(const char * /*pszFile */,
1106 : bool /* bSqliteIsTempFile */,
1107 : int /* iSqliteCacheMB */)
1108 : {
1109 0 : CPLDebug("NAS", "HugeFileResolver() not currently implemented for NAS.");
1110 0 : return false;
1111 : }
1112 :
1113 : /************************************************************************/
1114 : /* PrescanForTemplate() */
1115 : /* Returns true for success */
1116 : /************************************************************************/
1117 :
1118 0 : bool NASReader::PrescanForTemplate(void)
1119 :
1120 : {
1121 0 : CPLDebug("NAS", "PrescanForTemplate() not currently implemented for NAS.");
1122 0 : return false;
1123 : }
1124 :
1125 : /************************************************************************/
1126 : /* ResolveXlinks() */
1127 : /* Returns true for success */
1128 : /************************************************************************/
1129 :
1130 0 : bool NASReader::ResolveXlinks(const char * /*pszFile */,
1131 : bool * /*pbOutIsTempFile */,
1132 : char ** /*papszSkip */, const bool /*bStrict */)
1133 : {
1134 0 : CPLDebug("NAS", "ResolveXlinks() not currently implemented for NAS.");
1135 0 : return false;
1136 : }
1137 :
1138 : /************************************************************************/
1139 : /* SetFilteredClassName() */
1140 : /************************************************************************/
1141 :
1142 12 : bool NASReader::SetFilteredClassName(const char *pszClassName)
1143 : {
1144 12 : CPLFree(m_pszFilteredClassName);
1145 12 : m_pszFilteredClassName = pszClassName ? CPLStrdup(pszClassName) : nullptr;
1146 12 : return true;
1147 : }
1148 :
1149 : /************************************************************************/
1150 : /* ConvertGeometry() */
1151 : /************************************************************************/
1152 :
1153 2 : OGRGeometry *NASReader::ConvertGeometry(OGRGeometry *poGeom)
1154 : {
1155 : // poGeom = OGRGeometryFactory::forceToLineString( poGeom, false );
1156 2 : if (poGeom != nullptr)
1157 : {
1158 2 : if (wkbFlatten(poGeom->getGeometryType()) == wkbMultiLineString)
1159 : {
1160 0 : poGeom = OGRGeometryFactory::forceTo(poGeom, wkbLineString);
1161 : }
1162 : }
1163 2 : return poGeom;
1164 : }
|