Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: OpenGIS Simple Features Reference Implementation
4 : * Purpose: The OGR_SRSNode class.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Les Technologies SoftMap Inc.
9 : * Copyright (c) 2010-2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include "cpl_port.h"
15 : #include "ogr_spatialref.h"
16 :
17 : #include <cctype>
18 : #include <cstddef>
19 : #include <cstring>
20 :
21 : #include "ogr_core.h"
22 : #include "ogr_p.h"
23 : #include "cpl_conv.h"
24 : #include "cpl_error.h"
25 : #include "cpl_string.h"
26 :
27 : /************************************************************************/
28 : /* OGR_SRSNode() */
29 : /************************************************************************/
30 :
31 : /**
32 : * Constructor.
33 : *
34 : * @param pszValueIn this optional parameter can be used to initialize
35 : * the value of the node upon creation. If omitted the node will be created
36 : * with a value of "". Newly created OGR_SRSNodes have no children.
37 : */
38 :
39 917629 : OGR_SRSNode::OGR_SRSNode(const char *pszValueIn)
40 917629 : : pszValue(CPLStrdup(pszValueIn)), papoChildNodes(nullptr),
41 917629 : poParent(nullptr), nChildren(0)
42 : {
43 917629 : }
44 :
45 : /************************************************************************/
46 : /* ~OGR_SRSNode() */
47 : /************************************************************************/
48 :
49 892628 : OGR_SRSNode::~OGR_SRSNode()
50 :
51 : {
52 892628 : CPLFree(pszValue);
53 :
54 892628 : ClearChildren();
55 892628 : }
56 :
57 : /************************************************************************/
58 : /* ~Listener() */
59 : /************************************************************************/
60 :
61 : OGR_SRSNode::Listener::~Listener() = default;
62 :
63 : /************************************************************************/
64 : /* RegisterListener() */
65 : /************************************************************************/
66 :
67 22591 : void OGR_SRSNode::RegisterListener(const std::shared_ptr<Listener> &listener)
68 : {
69 22591 : m_listener = listener;
70 22591 : }
71 :
72 : /************************************************************************/
73 : /* notifyChange() */
74 : /************************************************************************/
75 :
76 1789440 : void OGR_SRSNode::notifyChange()
77 : {
78 3578880 : auto locked = m_listener.lock();
79 1789440 : if (locked)
80 : {
81 1764200 : locked->notifyChange(this);
82 : }
83 1789440 : }
84 :
85 : /************************************************************************/
86 : /* ClearChildren() */
87 : /************************************************************************/
88 :
89 : /** Clear children nodes
90 : */
91 1786560 : void OGR_SRSNode::ClearChildren()
92 :
93 : {
94 2656440 : for (int i = 0; i < nChildren; i++)
95 : {
96 869871 : delete papoChildNodes[i];
97 : }
98 :
99 1786560 : CPLFree(papoChildNodes);
100 :
101 1786560 : papoChildNodes = nullptr;
102 1786560 : nChildren = 0;
103 1786560 : }
104 :
105 : /************************************************************************/
106 : /* GetChildCount() */
107 : /************************************************************************/
108 :
109 : /**
110 : * \fn int OGR_SRSNode::GetChildCount() const;
111 : *
112 : * Get number of children nodes.
113 : *
114 : * @return 0 for leaf nodes, or the number of children nodes.
115 : */
116 :
117 : /************************************************************************/
118 : /* GetChild() */
119 : /************************************************************************/
120 :
121 : /**
122 : * Fetch requested child.
123 : *
124 : * @param iChild the index of the child to fetch, from 0 to
125 : * GetChildCount() - 1.
126 : *
127 : * @return a pointer to the child OGR_SRSNode, or NULL if there is no such
128 : * child.
129 : */
130 :
131 35654 : OGR_SRSNode *OGR_SRSNode::GetChild(int iChild)
132 :
133 : {
134 35654 : if (iChild < 0 || iChild >= nChildren)
135 0 : return nullptr;
136 :
137 35654 : return papoChildNodes[iChild];
138 : }
139 :
140 : /**
141 : * Fetch requested child.
142 : *
143 : * @param iChild the index of the child to fetch, from 0 to
144 : * GetChildCount() - 1.
145 : *
146 : * @return a pointer to the child OGR_SRSNode, or NULL if there is no such
147 : * child.
148 : */
149 :
150 163228 : const OGR_SRSNode *OGR_SRSNode::GetChild(int iChild) const
151 :
152 : {
153 163228 : if (iChild < 0 || iChild >= nChildren)
154 0 : return nullptr;
155 :
156 163228 : return papoChildNodes[iChild];
157 : }
158 :
159 : /************************************************************************/
160 : /* GetNode() */
161 : /************************************************************************/
162 :
163 : /**
164 : * Find named node in tree.
165 : *
166 : * This method does a pre-order traversal of the node tree searching for
167 : * a node with this exact value (case insensitive), and returns it. Leaf
168 : * nodes are not considered, under the assumption that they are just
169 : * attribute value nodes.
170 : *
171 : * If a node appears more than once in the tree (such as UNIT for instance),
172 : * the first encountered will be returned. Use GetNode() on a subtree to be
173 : * more specific.
174 : *
175 : * @param pszName the name of the node to search for.
176 : *
177 : * @return a pointer to the node found, or NULL if none.
178 : */
179 :
180 828518 : OGR_SRSNode *OGR_SRSNode::GetNode(const char *pszName)
181 :
182 : {
183 828518 : if (nChildren > 0 && EQUAL(pszName, pszValue))
184 37289 : return this;
185 :
186 : /* -------------------------------------------------------------------- */
187 : /* First we check the immediate children so we will get an */
188 : /* immediate child in preference to a subchild. */
189 : /* -------------------------------------------------------------------- */
190 1632270 : for (int i = 0; i < nChildren; i++)
191 : {
192 891975 : if (EQUAL(papoChildNodes[i]->pszValue, pszName) &&
193 50937 : papoChildNodes[i]->nChildren > 0)
194 50937 : return papoChildNodes[i];
195 : }
196 :
197 : /* -------------------------------------------------------------------- */
198 : /* Then get each child to check their children. */
199 : /* -------------------------------------------------------------------- */
200 1448500 : for (int i = 0; i < nChildren; i++)
201 : {
202 718696 : OGR_SRSNode *poNode = papoChildNodes[i]->GetNode(pszName);
203 718696 : if (poNode != nullptr)
204 10487 : return poNode;
205 : }
206 :
207 729805 : return nullptr;
208 : }
209 :
210 : /**
211 : * Find named node in tree.
212 : *
213 : * This method does a pre-order traversal of the node tree searching for
214 : * a node with this exact value (case insensitive), and returns it. Leaf
215 : * nodes are not considered, under the assumption that they are just
216 : * attribute value nodes.
217 : *
218 : * If a node appears more than once in the tree (such as UNIT for instance),
219 : * the first encountered will be returned. Use GetNode() on a subtree to be
220 : * more specific.
221 : *
222 : * @param pszName the name of the node to search for.
223 : *
224 : * @return a pointer to the node found, or NULL if none.
225 : */
226 :
227 93 : const OGR_SRSNode *OGR_SRSNode::GetNode(const char *pszName) const
228 :
229 : {
230 93 : return const_cast<OGR_SRSNode *>(this)->GetNode(pszName);
231 : }
232 :
233 : /************************************************************************/
234 : /* AddChild() */
235 : /************************************************************************/
236 :
237 : /**
238 : * Add passed node as a child of target node.
239 : *
240 : * Note that ownership of the passed node is assumed by the node on which
241 : * the method is invoked ... use the Clone() method if the original is to
242 : * be preserved. New children are always added at the end of the list.
243 : *
244 : * @param poNew the node to add as a child.
245 : */
246 :
247 894704 : void OGR_SRSNode::AddChild(OGR_SRSNode *poNew)
248 :
249 : {
250 894704 : InsertChild(poNew, nChildren);
251 894704 : }
252 :
253 : /************************************************************************/
254 : /* InsertChild() */
255 : /************************************************************************/
256 :
257 : /**
258 : * Insert the passed node as a child of target node, at the indicated
259 : * position.
260 : *
261 : * Note that ownership of the passed node is assumed by the node on which
262 : * the method is invoked ... use the Clone() method if the original is to
263 : * be preserved. All existing children at location iChild and beyond are
264 : * push down one space to make space for the new child.
265 : *
266 : * @param poNew the node to add as a child.
267 : * @param iChild position to insert, use 0 to insert at the beginning.
268 : */
269 :
270 894710 : void OGR_SRSNode::InsertChild(OGR_SRSNode *poNew, int iChild)
271 :
272 : {
273 894710 : if (iChild > nChildren)
274 0 : iChild = nChildren;
275 :
276 894710 : nChildren++;
277 894710 : papoChildNodes = static_cast<OGR_SRSNode **>(
278 894710 : CPLRealloc(papoChildNodes, sizeof(void *) * nChildren));
279 :
280 894710 : memmove(papoChildNodes + iChild + 1, papoChildNodes + iChild,
281 894710 : sizeof(void *) * (nChildren - iChild - 1));
282 :
283 894710 : papoChildNodes[iChild] = poNew;
284 894710 : poNew->poParent = this;
285 :
286 894710 : poNew->m_listener = m_listener;
287 894710 : notifyChange();
288 894710 : }
289 :
290 : /************************************************************************/
291 : /* DestroyChild() */
292 : /************************************************************************/
293 :
294 : /**
295 : * Remove a child node, and it's subtree.
296 : *
297 : * Note that removing a child node will result in children after it
298 : * being renumbered down one.
299 : *
300 : * @param iChild the index of the child.
301 : */
302 :
303 395 : void OGR_SRSNode::DestroyChild(int iChild)
304 :
305 : {
306 395 : if (iChild < 0 || iChild >= nChildren)
307 0 : return;
308 :
309 395 : delete papoChildNodes[iChild];
310 809 : while (iChild < nChildren - 1)
311 : {
312 414 : papoChildNodes[iChild] = papoChildNodes[iChild + 1];
313 414 : iChild++;
314 : }
315 :
316 395 : nChildren--;
317 395 : notifyChange();
318 : }
319 :
320 : /************************************************************************/
321 : /* FindChild() */
322 : /************************************************************************/
323 :
324 : /**
325 : * Find the index of the child matching the given string.
326 : *
327 : * Note that the node value must match pszValue with the exception of
328 : * case. The comparison is case insensitive.
329 : *
330 : * @param pszValueIn the node value being searched for.
331 : *
332 : * @return the child index, or -1 on failure.
333 : */
334 :
335 53976 : int OGR_SRSNode::FindChild(const char *pszValueIn) const
336 :
337 : {
338 192632 : for (int i = 0; i < nChildren; i++)
339 : {
340 178714 : if (EQUAL(papoChildNodes[i]->pszValue, pszValueIn))
341 40058 : return i;
342 : }
343 :
344 13918 : return -1;
345 : }
346 :
347 : /************************************************************************/
348 : /* GetValue() */
349 : /************************************************************************/
350 :
351 : /**
352 : * \fn const char *OGR_SRSNode::GetValue() const;
353 : *
354 : * Fetch value string for this node.
355 : *
356 : * @return A non-NULL string is always returned. The returned pointer is to
357 : * the internal value of this node, and should not be modified, or freed.
358 : */
359 :
360 : /************************************************************************/
361 : /* SetValue() */
362 : /************************************************************************/
363 :
364 : /**
365 : * Set the node value.
366 : *
367 : * @param pszNewValue the new value to assign to this node. The passed
368 : * string is duplicated and remains the responsibility of the caller.
369 : */
370 :
371 894335 : void OGR_SRSNode::SetValue(const char *pszNewValue)
372 :
373 : {
374 894335 : CPLFree(pszValue);
375 894335 : pszValue = CPLStrdup(pszNewValue);
376 894335 : notifyChange();
377 894335 : }
378 :
379 : /************************************************************************/
380 : /* Clone() */
381 : /************************************************************************/
382 :
383 : /**
384 : * Make a duplicate of this node, and it's children.
385 : *
386 : * @return a new node tree, which becomes the responsibility of the caller.
387 : */
388 :
389 2629 : OGR_SRSNode *OGR_SRSNode::Clone() const
390 :
391 : {
392 2629 : OGR_SRSNode *poNew = new OGR_SRSNode(pszValue);
393 :
394 5195 : for (int i = 0; i < nChildren; i++)
395 : {
396 2566 : poNew->AddChild(papoChildNodes[i]->Clone());
397 : }
398 2629 : poNew->m_listener = m_listener;
399 :
400 2629 : return poNew;
401 : }
402 :
403 : /************************************************************************/
404 : /* NeedsQuoting() */
405 : /* */
406 : /* Does this node need to be quoted when it is exported to Wkt? */
407 : /************************************************************************/
408 :
409 268368 : int OGR_SRSNode::NeedsQuoting() const
410 :
411 : {
412 : // Non-terminals are never quoted.
413 268368 : if (GetChildCount() != 0)
414 96458 : return FALSE;
415 :
416 : // As per bugzilla bug 201, the OGC spec says the authority code
417 : // needs to be quoted even though it appears well behaved.
418 171910 : if (poParent != nullptr && EQUAL(poParent->GetValue(), "AUTHORITY"))
419 48022 : return TRUE;
420 :
421 : // As per bugzilla bug 294, the OGC spec says the direction
422 : // values for the AXIS keywords should *not* be quoted.
423 151746 : if (poParent != nullptr && EQUAL(poParent->GetValue(), "AXIS") &&
424 27858 : this != poParent->GetChild(0))
425 13929 : return FALSE;
426 :
427 109959 : if (poParent != nullptr && EQUAL(poParent->GetValue(), "CS") &&
428 0 : this == poParent->GetChild(0))
429 0 : return FALSE;
430 :
431 : // Strings starting with e or E are not valid numeric values, so they
432 : // need quoting, like in AXIS["E",EAST]
433 109959 : if ((pszValue[0] == 'e' || pszValue[0] == 'E'))
434 7129 : return TRUE;
435 :
436 : // Non-numeric tokens are generally quoted while clean numeric values
437 : // are generally not.
438 427303 : for (int i = 0; pszValue[i] != '\0'; i++)
439 : {
440 389798 : if ((pszValue[i] < '0' || pszValue[i] > '9') && pszValue[i] != '.' &&
441 65516 : pszValue[i] != '-' && pszValue[i] != '+' && pszValue[i] != 'e' &&
442 65327 : pszValue[i] != 'E')
443 65325 : return TRUE;
444 : }
445 :
446 37505 : return FALSE;
447 : }
448 :
449 : /************************************************************************/
450 : /* exportToWkt() */
451 : /************************************************************************/
452 :
453 : /**
454 : * Convert this tree of nodes into WKT format.
455 : *
456 : * Note that the returned WKT string should be freed with
457 : * CPLFree() when no longer needed. It is the responsibility of the caller.
458 : *
459 : * @param ppszResult the resulting string is returned in this pointer.
460 : *
461 : * @return currently OGRERR_NONE is always returned, but the future it
462 : * is possible error conditions will develop.
463 : */
464 :
465 268296 : OGRErr OGR_SRSNode::exportToWkt(char **ppszResult) const
466 :
467 : {
468 : /* -------------------------------------------------------------------- */
469 : /* Build a list of the WKT format for the children. */
470 : /* -------------------------------------------------------------------- */
471 : char **papszChildrenWkt =
472 268296 : static_cast<char **>(CPLCalloc(sizeof(char *), nChildren + 1));
473 268296 : size_t nLength = strlen(pszValue) + 4;
474 :
475 529361 : for (int i = 0; i < nChildren; i++)
476 : {
477 261065 : papoChildNodes[i]->exportToWkt(papszChildrenWkt + i);
478 261065 : nLength += strlen(papszChildrenWkt[i]) + 1;
479 : }
480 :
481 : /* -------------------------------------------------------------------- */
482 : /* Allocate the result string. */
483 : /* -------------------------------------------------------------------- */
484 268296 : *ppszResult = static_cast<char *>(CPLMalloc(nLength));
485 268296 : *ppszResult[0] = '\0';
486 :
487 : /* -------------------------------------------------------------------- */
488 : /* Capture this nodes value. We put it in double quotes if */
489 : /* this is a leaf node, otherwise we assume it is a well formed */
490 : /* node name. */
491 : /* -------------------------------------------------------------------- */
492 268296 : if (NeedsQuoting())
493 : {
494 120450 : strcat(*ppszResult, "\"");
495 120450 : strcat(*ppszResult, pszValue); // Should we do quoting?
496 120450 : strcat(*ppszResult, "\"");
497 : }
498 : else
499 147846 : strcat(*ppszResult, pszValue);
500 :
501 : /* -------------------------------------------------------------------- */
502 : /* Add the children strings with appropriate brackets and commas. */
503 : /* -------------------------------------------------------------------- */
504 268296 : if (nChildren > 0)
505 96432 : strcat(*ppszResult, "[");
506 :
507 529361 : for (int i = 0; i < nChildren; i++)
508 : {
509 261065 : strcat(*ppszResult, papszChildrenWkt[i]);
510 261065 : if (i == nChildren - 1)
511 96432 : strcat(*ppszResult, "]");
512 : else
513 164633 : strcat(*ppszResult, ",");
514 : }
515 :
516 268296 : CSLDestroy(papszChildrenWkt);
517 :
518 268296 : return OGRERR_NONE;
519 : }
520 :
521 : /************************************************************************/
522 : /* exportToPrettyWkt() */
523 : /************************************************************************/
524 :
525 : /**
526 : * Convert this tree of nodes into pretty WKT format.
527 : *
528 : * Note that the returned WKT string should be freed with
529 : * CPLFree() when no longer needed. It is the responsibility of the caller.
530 : *
531 : * @param ppszResult the resulting string is returned in this pointer.
532 : *
533 : * @param nDepth depth of the node
534 : *
535 : * @return currently OGRERR_NONE is always returned, but the future it
536 : * is possible error conditions will develop.
537 : */
538 :
539 72 : OGRErr OGR_SRSNode::exportToPrettyWkt(char **ppszResult, int nDepth) const
540 :
541 : {
542 : /* -------------------------------------------------------------------- */
543 : /* Build a list of the WKT format for the children. */
544 : /* -------------------------------------------------------------------- */
545 : char **papszChildrenWkt =
546 72 : static_cast<char **>(CPLCalloc(sizeof(char *), nChildren + 1));
547 72 : size_t nLength = strlen(pszValue) + 4;
548 :
549 142 : for (int i = 0; i < nChildren; i++)
550 : {
551 70 : papoChildNodes[i]->exportToPrettyWkt(papszChildrenWkt + i, nDepth + 1);
552 70 : nLength += strlen(papszChildrenWkt[i]) + 2 + nDepth * 4;
553 : }
554 :
555 : /* -------------------------------------------------------------------- */
556 : /* Allocate the result string. */
557 : /* -------------------------------------------------------------------- */
558 72 : *ppszResult = static_cast<char *>(CPLMalloc(nLength));
559 72 : *ppszResult[0] = '\0';
560 :
561 : /* -------------------------------------------------------------------- */
562 : /* Capture this nodes value. We put it in double quotes if */
563 : /* this is a leaf node, otherwise we assume it is a well formed */
564 : /* node name. */
565 : /* -------------------------------------------------------------------- */
566 72 : if (NeedsQuoting())
567 : {
568 26 : strcat(*ppszResult, "\"");
569 26 : strcat(*ppszResult, pszValue); // Should we do quoting?
570 26 : strcat(*ppszResult, "\"");
571 : }
572 : else
573 : {
574 46 : strcat(*ppszResult, pszValue);
575 : }
576 :
577 : /* -------------------------------------------------------------------- */
578 : /* Add the children strings with appropriate brackets and commas. */
579 : /* -------------------------------------------------------------------- */
580 72 : if (nChildren > 0)
581 26 : strcat(*ppszResult, "[");
582 :
583 142 : for (int i = 0; i < nChildren; i++)
584 : {
585 70 : if (papoChildNodes[i]->GetChildCount() > 0)
586 : {
587 24 : strcat(*ppszResult, "\n");
588 160 : for (int j = 0; j < 4 * nDepth; j++)
589 136 : strcat(*ppszResult, " ");
590 : }
591 70 : strcat(*ppszResult, papszChildrenWkt[i]);
592 70 : if (i < nChildren - 1)
593 44 : strcat(*ppszResult, ",");
594 : }
595 :
596 72 : if (nChildren > 0)
597 : {
598 26 : if ((*ppszResult)[strlen(*ppszResult) - 1] == ',')
599 0 : (*ppszResult)[strlen(*ppszResult) - 1] = '\0';
600 :
601 26 : strcat(*ppszResult, "]");
602 : }
603 :
604 72 : CSLDestroy(papszChildrenWkt);
605 :
606 72 : return OGRERR_NONE;
607 : }
608 :
609 : /************************************************************************/
610 : /* importFromWkt() */
611 : /************************************************************************/
612 :
613 : /**
614 : * Import from WKT string.
615 : *
616 : * This method will wipe the existing children and value of this node, and
617 : * reassign them based on the contents of the passed WKT string. Only as
618 : * much of the input string as needed to construct this node, and its
619 : * children is consumed from the input string, and the input string pointer
620 : * is then updated to point to the remaining (unused) input.
621 : *
622 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
623 : * point to remaining unused input text.
624 : *
625 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
626 : * fails for any reason.
627 : * @deprecated GDAL 2.3. Use importFromWkt(const char**) instead.
628 : */
629 :
630 0 : OGRErr OGR_SRSNode::importFromWkt(char **ppszInput)
631 :
632 : {
633 0 : int nNodes = 0;
634 0 : return importFromWkt(const_cast<const char **>(ppszInput), 0, &nNodes);
635 : }
636 :
637 : /**
638 : * Import from WKT string.
639 : *
640 : * This method will wipe the existing children and value of this node, and
641 : * reassign them based on the contents of the passed WKT string. Only as
642 : * much of the input string as needed to construct this node, and its
643 : * children is consumed from the input string, and the input string pointer
644 : * is then updated to point to the remaining (unused) input.
645 : *
646 : * @param ppszInput Pointer to pointer to input. The pointer is updated to
647 : * point to remaining unused input text.
648 : *
649 : * @return OGRERR_NONE if import succeeds, or OGRERR_CORRUPT_DATA if it
650 : * fails for any reason.
651 : *
652 : * @since GDAL 2.3
653 : */
654 :
655 22883 : OGRErr OGR_SRSNode::importFromWkt(const char **ppszInput)
656 :
657 : {
658 22883 : int nNodes = 0;
659 45766 : return importFromWkt(ppszInput, 0, &nNodes);
660 : }
661 :
662 893936 : OGRErr OGR_SRSNode::importFromWkt(const char **ppszInput, int nRecLevel,
663 : int *pnNodes)
664 :
665 : {
666 : // Sanity checks.
667 893936 : if (nRecLevel == 10)
668 : {
669 0 : return OGRERR_CORRUPT_DATA;
670 : }
671 893936 : if (*pnNodes == 1000)
672 : {
673 0 : return OGRERR_CORRUPT_DATA;
674 : }
675 :
676 893936 : const char *pszInput = *ppszInput;
677 893936 : bool bInQuotedString = false;
678 :
679 : /* -------------------------------------------------------------------- */
680 : /* Clear any existing children of this node. */
681 : /* -------------------------------------------------------------------- */
682 893936 : ClearChildren();
683 :
684 : /* -------------------------------------------------------------------- */
685 : /* Read the ``value'' for this node. */
686 : /* -------------------------------------------------------------------- */
687 : {
688 : char szToken[512]; // do not initialize whole buffer. significant
689 : // overhead
690 893936 : size_t nTokenLen = 0;
691 893936 : szToken[0] = '\0';
692 :
693 8202570 : while (*pszInput != '\0' && nTokenLen + 1 < sizeof(szToken))
694 : {
695 8202570 : if (*pszInput == '"')
696 : {
697 793906 : bInQuotedString = !bInQuotedString;
698 : }
699 7408660 : else if (!bInQuotedString &&
700 4282810 : (*pszInput == '[' || *pszInput == ']' ||
701 3762840 : *pszInput == ',' || *pszInput == '(' || *pszInput == ')'))
702 : {
703 : break;
704 : }
705 6514730 : else if (!bInQuotedString &&
706 3388870 : (*pszInput == ' ' || *pszInput == '\t' ||
707 3388380 : *pszInput == 10 || *pszInput == 13))
708 : {
709 : // Skip over whitespace.
710 : }
711 : else
712 : {
713 6514190 : szToken[nTokenLen++] = *pszInput;
714 : }
715 :
716 7308630 : pszInput++;
717 : }
718 :
719 893936 : if (*pszInput == '\0' || nTokenLen == sizeof(szToken) - 1)
720 0 : return OGRERR_CORRUPT_DATA;
721 :
722 893936 : szToken[nTokenLen++] = '\0';
723 893936 : SetValue(szToken);
724 : }
725 :
726 : /* -------------------------------------------------------------------- */
727 : /* Read children, if we have a sublist. */
728 : /* -------------------------------------------------------------------- */
729 893936 : if (*pszInput == '[' || *pszInput == '(')
730 : {
731 556279 : do
732 : {
733 871053 : pszInput++; // Skip bracket or comma.
734 :
735 871053 : OGR_SRSNode *poNewChild = new OGR_SRSNode();
736 871053 : poNewChild->m_listener = m_listener;
737 :
738 871053 : (*pnNodes)++;
739 : const OGRErr eErr =
740 871053 : poNewChild->importFromWkt(&pszInput, nRecLevel + 1, pnNodes);
741 871053 : if (eErr != OGRERR_NONE)
742 : {
743 0 : delete poNewChild;
744 0 : return eErr;
745 : }
746 :
747 871053 : AddChild(poNewChild);
748 :
749 : // Swallow whitespace.
750 871053 : while (isspace(static_cast<unsigned char>(*pszInput)))
751 0 : pszInput++;
752 871053 : } while (*pszInput == ',');
753 :
754 314774 : if (*pszInput != ')' && *pszInput != ']')
755 0 : return OGRERR_CORRUPT_DATA;
756 :
757 314774 : pszInput++;
758 : }
759 :
760 893936 : *ppszInput = pszInput;
761 :
762 893936 : return OGRERR_NONE;
763 : }
764 :
765 : /************************************************************************/
766 : /* MakeValueSafe() */
767 : /************************************************************************/
768 :
769 : /**
770 : * Massage value string, stripping special characters so it will be a
771 : * database safe string.
772 : *
773 : * The operation is also applies to all subnodes of the current node.
774 : */
775 :
776 0 : void OGR_SRSNode::MakeValueSafe()
777 :
778 : {
779 : /* -------------------------------------------------------------------- */
780 : /* First process subnodes. */
781 : /* -------------------------------------------------------------------- */
782 0 : for (int iChild = 0; iChild < GetChildCount(); iChild++)
783 : {
784 0 : GetChild(iChild)->MakeValueSafe();
785 : }
786 :
787 : /* -------------------------------------------------------------------- */
788 : /* Skip numeric nodes. */
789 : /* -------------------------------------------------------------------- */
790 0 : if ((pszValue[0] >= '0' && pszValue[0] <= '9') || pszValue[0] != '.')
791 0 : return;
792 :
793 : /* -------------------------------------------------------------------- */
794 : /* Translate non-alphanumeric values to underscores. */
795 : /* -------------------------------------------------------------------- */
796 0 : for (int i = 0; pszValue[i] != '\0'; i++)
797 : {
798 0 : if (!(pszValue[i] >= 'A' && pszValue[i] <= 'Z') &&
799 0 : !(pszValue[i] >= 'a' && pszValue[i] <= 'z') &&
800 0 : !(pszValue[i] >= '0' && pszValue[i] <= '9'))
801 : {
802 0 : pszValue[i] = '_';
803 : }
804 : }
805 :
806 : /* -------------------------------------------------------------------- */
807 : /* Remove repeated and trailing underscores. */
808 : /* -------------------------------------------------------------------- */
809 0 : int j = 0;
810 0 : for (int i = 1; pszValue[i] != '\0'; i++)
811 : {
812 0 : if (pszValue[j] == '_' && pszValue[i] == '_')
813 0 : continue;
814 :
815 0 : pszValue[++j] = pszValue[i];
816 : }
817 :
818 0 : if (pszValue[j] == '_')
819 0 : pszValue[j] = '\0';
820 : else
821 0 : pszValue[j + 1] = '\0';
822 : }
823 :
824 : /************************************************************************/
825 : /* StripNodes() */
826 : /************************************************************************/
827 :
828 : /**
829 : * Strip child nodes matching name.
830 : *
831 : * Removes any descendant nodes of this node that match the given name.
832 : * Of course children of removed nodes are also discarded.
833 : *
834 : * @param pszName the name for nodes that should be removed.
835 : */
836 :
837 5654 : void OGR_SRSNode::StripNodes(const char *pszName)
838 :
839 : {
840 : /* -------------------------------------------------------------------- */
841 : /* Strip any children matching this name. */
842 : /* -------------------------------------------------------------------- */
843 5654 : while (FindChild(pszName) >= 0)
844 76 : DestroyChild(FindChild(pszName));
845 :
846 : /* -------------------------------------------------------------------- */
847 : /* Recurse */
848 : /* -------------------------------------------------------------------- */
849 10841 : for (int i = 0; i < GetChildCount(); i++)
850 5263 : GetChild(i)->StripNodes(pszName);
851 5578 : }
|