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