Coverage Report

Created: 2025-06-13 06:29

/src/gdal/ogr/ogr_srsnode.cpp
Line
Count
Source (jump to first uncovered line)
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
OGR_SRSNode::OGR_SRSNode(const char *pszValueIn)
40
0
    : pszValue(CPLStrdup(pszValueIn)), papoChildNodes(nullptr),
41
0
      poParent(nullptr), nChildren(0)
42
0
{
43
0
}
44
45
/************************************************************************/
46
/*                            ~OGR_SRSNode()                            */
47
/************************************************************************/
48
49
OGR_SRSNode::~OGR_SRSNode()
50
51
0
{
52
0
    CPLFree(pszValue);
53
54
0
    ClearChildren();
55
0
}
56
57
/************************************************************************/
58
/*                             ~Listener()                              */
59
/************************************************************************/
60
61
0
OGR_SRSNode::Listener::~Listener() = default;
62
63
/************************************************************************/
64
/*                           RegisterListener()                         */
65
/************************************************************************/
66
67
void OGR_SRSNode::RegisterListener(const std::shared_ptr<Listener> &listener)
68
0
{
69
0
    m_listener = listener;
70
0
}
71
72
/************************************************************************/
73
/*                             notifyChange()                           */
74
/************************************************************************/
75
76
void OGR_SRSNode::notifyChange()
77
0
{
78
0
    auto locked = m_listener.lock();
79
0
    if (locked)
80
0
    {
81
0
        locked->notifyChange(this);
82
0
    }
83
0
}
84
85
/************************************************************************/
86
/*                           ClearChildren()                            */
87
/************************************************************************/
88
89
/** Clear children nodes
90
 */
91
void OGR_SRSNode::ClearChildren()
92
93
0
{
94
0
    for (int i = 0; i < nChildren; i++)
95
0
    {
96
0
        delete papoChildNodes[i];
97
0
    }
98
99
0
    CPLFree(papoChildNodes);
100
101
0
    papoChildNodes = nullptr;
102
0
    nChildren = 0;
103
0
}
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
OGR_SRSNode *OGR_SRSNode::GetChild(int iChild)
132
133
0
{
134
0
    if (iChild < 0 || iChild >= nChildren)
135
0
        return nullptr;
136
137
0
    return papoChildNodes[iChild];
138
0
}
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
const OGR_SRSNode *OGR_SRSNode::GetChild(int iChild) const
151
152
0
{
153
0
    if (iChild < 0 || iChild >= nChildren)
154
0
        return nullptr;
155
156
0
    return papoChildNodes[iChild];
157
0
}
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
OGR_SRSNode *OGR_SRSNode::GetNode(const char *pszName)
181
182
0
{
183
0
    if (nChildren > 0 && EQUAL(pszName, pszValue))
184
0
        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
0
    for (int i = 0; i < nChildren; i++)
191
0
    {
192
0
        if (EQUAL(papoChildNodes[i]->pszValue, pszName) &&
193
0
            papoChildNodes[i]->nChildren > 0)
194
0
            return papoChildNodes[i];
195
0
    }
196
197
    /* -------------------------------------------------------------------- */
198
    /*      Then get each child to check their children.                    */
199
    /* -------------------------------------------------------------------- */
200
0
    for (int i = 0; i < nChildren; i++)
201
0
    {
202
0
        OGR_SRSNode *poNode = papoChildNodes[i]->GetNode(pszName);
203
0
        if (poNode != nullptr)
204
0
            return poNode;
205
0
    }
206
207
0
    return nullptr;
208
0
}
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
const OGR_SRSNode *OGR_SRSNode::GetNode(const char *pszName) const
228
229
0
{
230
0
    return const_cast<OGR_SRSNode *>(this)->GetNode(pszName);
231
0
}
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
void OGR_SRSNode::AddChild(OGR_SRSNode *poNew)
248
249
0
{
250
0
    InsertChild(poNew, nChildren);
251
0
}
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
void OGR_SRSNode::InsertChild(OGR_SRSNode *poNew, int iChild)
271
272
0
{
273
0
    if (iChild > nChildren)
274
0
        iChild = nChildren;
275
276
0
    nChildren++;
277
0
    papoChildNodes = static_cast<OGR_SRSNode **>(
278
0
        CPLRealloc(papoChildNodes, sizeof(void *) * nChildren));
279
280
0
    memmove(papoChildNodes + iChild + 1, papoChildNodes + iChild,
281
0
            sizeof(void *) * (nChildren - iChild - 1));
282
283
0
    papoChildNodes[iChild] = poNew;
284
0
    poNew->poParent = this;
285
286
0
    poNew->m_listener = m_listener;
287
0
    notifyChange();
288
0
}
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
void OGR_SRSNode::DestroyChild(int iChild)
304
305
0
{
306
0
    if (iChild < 0 || iChild >= nChildren)
307
0
        return;
308
309
0
    delete papoChildNodes[iChild];
310
0
    while (iChild < nChildren - 1)
311
0
    {
312
0
        papoChildNodes[iChild] = papoChildNodes[iChild + 1];
313
0
        iChild++;
314
0
    }
315
316
0
    nChildren--;
317
0
    notifyChange();
318
0
}
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
int OGR_SRSNode::FindChild(const char *pszValueIn) const
336
337
0
{
338
0
    for (int i = 0; i < nChildren; i++)
339
0
    {
340
0
        if (EQUAL(papoChildNodes[i]->pszValue, pszValueIn))
341
0
            return i;
342
0
    }
343
344
0
    return -1;
345
0
}
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
void OGR_SRSNode::SetValue(const char *pszNewValue)
372
373
0
{
374
0
    CPLFree(pszValue);
375
0
    pszValue = CPLStrdup(pszNewValue);
376
0
    notifyChange();
377
0
}
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
OGR_SRSNode *OGR_SRSNode::Clone() const
390
391
0
{
392
0
    OGR_SRSNode *poNew = new OGR_SRSNode(pszValue);
393
394
0
    for (int i = 0; i < nChildren; i++)
395
0
    {
396
0
        poNew->AddChild(papoChildNodes[i]->Clone());
397
0
    }
398
0
    poNew->m_listener = m_listener;
399
400
0
    return poNew;
401
0
}
402
403
/************************************************************************/
404
/*                            NeedsQuoting()                            */
405
/*                                                                      */
406
/*      Does this node need to be quoted when it is exported to Wkt?    */
407
/************************************************************************/
408
409
int OGR_SRSNode::NeedsQuoting() const
410
411
0
{
412
    // Non-terminals are never quoted.
413
0
    if (GetChildCount() != 0)
414
0
        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
0
    if (poParent != nullptr && EQUAL(poParent->GetValue(), "AUTHORITY"))
419
0
        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
0
    if (poParent != nullptr && EQUAL(poParent->GetValue(), "AXIS") &&
424
0
        this != poParent->GetChild(0))
425
0
        return FALSE;
426
427
0
    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
0
    if ((pszValue[0] == 'e' || pszValue[0] == 'E'))
434
0
        return TRUE;
435
436
    // Non-numeric tokens are generally quoted while clean numeric values
437
    // are generally not.
438
0
    for (int i = 0; pszValue[i] != '\0'; i++)
439
0
    {
440
0
        if ((pszValue[i] < '0' || pszValue[i] > '9') && pszValue[i] != '.' &&
441
0
            pszValue[i] != '-' && pszValue[i] != '+' && pszValue[i] != 'e' &&
442
0
            pszValue[i] != 'E')
443
0
            return TRUE;
444
0
    }
445
446
0
    return FALSE;
447
0
}
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
OGRErr OGR_SRSNode::exportToWkt(char **ppszResult) const
466
467
0
{
468
    /* -------------------------------------------------------------------- */
469
    /*      Build a list of the WKT format for the children.                */
470
    /* -------------------------------------------------------------------- */
471
0
    char **papszChildrenWkt =
472
0
        static_cast<char **>(CPLCalloc(sizeof(char *), nChildren + 1));
473
0
    size_t nLength = strlen(pszValue) + 4;
474
475
0
    for (int i = 0; i < nChildren; i++)
476
0
    {
477
0
        papoChildNodes[i]->exportToWkt(papszChildrenWkt + i);
478
0
        nLength += strlen(papszChildrenWkt[i]) + 1;
479
0
    }
480
481
    /* -------------------------------------------------------------------- */
482
    /*      Allocate the result string.                                     */
483
    /* -------------------------------------------------------------------- */
484
0
    *ppszResult = static_cast<char *>(CPLMalloc(nLength));
485
0
    *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
0
    if (NeedsQuoting())
493
0
    {
494
0
        strcat(*ppszResult, "\"");
495
0
        strcat(*ppszResult, pszValue);  // Should we do quoting?
496
0
        strcat(*ppszResult, "\"");
497
0
    }
498
0
    else
499
0
        strcat(*ppszResult, pszValue);
500
501
    /* -------------------------------------------------------------------- */
502
    /*      Add the children strings with appropriate brackets and commas.  */
503
    /* -------------------------------------------------------------------- */
504
0
    if (nChildren > 0)
505
0
        strcat(*ppszResult, "[");
506
507
0
    for (int i = 0; i < nChildren; i++)
508
0
    {
509
0
        strcat(*ppszResult, papszChildrenWkt[i]);
510
0
        if (i == nChildren - 1)
511
0
            strcat(*ppszResult, "]");
512
0
        else
513
0
            strcat(*ppszResult, ",");
514
0
    }
515
516
0
    CSLDestroy(papszChildrenWkt);
517
518
0
    return OGRERR_NONE;
519
0
}
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
OGRErr OGR_SRSNode::exportToPrettyWkt(char **ppszResult, int nDepth) const
540
541
0
{
542
    /* -------------------------------------------------------------------- */
543
    /*      Build a list of the WKT format for the children.                */
544
    /* -------------------------------------------------------------------- */
545
0
    char **papszChildrenWkt =
546
0
        static_cast<char **>(CPLCalloc(sizeof(char *), nChildren + 1));
547
0
    size_t nLength = strlen(pszValue) + 4;
548
549
0
    for (int i = 0; i < nChildren; i++)
550
0
    {
551
0
        papoChildNodes[i]->exportToPrettyWkt(papszChildrenWkt + i, nDepth + 1);
552
0
        nLength += strlen(papszChildrenWkt[i]) + 2 + nDepth * 4;
553
0
    }
554
555
    /* -------------------------------------------------------------------- */
556
    /*      Allocate the result string.                                     */
557
    /* -------------------------------------------------------------------- */
558
0
    *ppszResult = static_cast<char *>(CPLMalloc(nLength));
559
0
    *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
0
    if (NeedsQuoting())
567
0
    {
568
0
        strcat(*ppszResult, "\"");
569
0
        strcat(*ppszResult, pszValue);  // Should we do quoting?
570
0
        strcat(*ppszResult, "\"");
571
0
    }
572
0
    else
573
0
    {
574
0
        strcat(*ppszResult, pszValue);
575
0
    }
576
577
    /* -------------------------------------------------------------------- */
578
    /*      Add the children strings with appropriate brackets and commas.  */
579
    /* -------------------------------------------------------------------- */
580
0
    if (nChildren > 0)
581
0
        strcat(*ppszResult, "[");
582
583
0
    for (int i = 0; i < nChildren; i++)
584
0
    {
585
0
        if (papoChildNodes[i]->GetChildCount() > 0)
586
0
        {
587
0
            strcat(*ppszResult, "\n");
588
0
            for (int j = 0; j < 4 * nDepth; j++)
589
0
                strcat(*ppszResult, " ");
590
0
        }
591
0
        strcat(*ppszResult, papszChildrenWkt[i]);
592
0
        if (i < nChildren - 1)
593
0
            strcat(*ppszResult, ",");
594
0
    }
595
596
0
    if (nChildren > 0)
597
0
    {
598
0
        if ((*ppszResult)[strlen(*ppszResult) - 1] == ',')
599
0
            (*ppszResult)[strlen(*ppszResult) - 1] = '\0';
600
601
0
        strcat(*ppszResult, "]");
602
0
    }
603
604
0
    CSLDestroy(papszChildrenWkt);
605
606
0
    return OGRERR_NONE;
607
0
}
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
OGRErr OGR_SRSNode::importFromWkt(char **ppszInput)
631
632
0
{
633
0
    int nNodes = 0;
634
0
    return importFromWkt(const_cast<const char **>(ppszInput), 0, &nNodes);
635
0
}
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
OGRErr OGR_SRSNode::importFromWkt(const char **ppszInput)
656
657
0
{
658
0
    int nNodes = 0;
659
0
    return importFromWkt(ppszInput, 0, &nNodes);
660
0
}
661
662
OGRErr OGR_SRSNode::importFromWkt(const char **ppszInput, int nRecLevel,
663
                                  int *pnNodes)
664
665
0
{
666
    // Sanity checks.
667
0
    if (nRecLevel == 10)
668
0
    {
669
0
        return OGRERR_CORRUPT_DATA;
670
0
    }
671
0
    if (*pnNodes == 1000)
672
0
    {
673
0
        return OGRERR_CORRUPT_DATA;
674
0
    }
675
676
0
    const char *pszInput = *ppszInput;
677
0
    bool bInQuotedString = false;
678
679
    /* -------------------------------------------------------------------- */
680
    /*      Clear any existing children of this node.                       */
681
    /* -------------------------------------------------------------------- */
682
0
    ClearChildren();
683
684
    /* -------------------------------------------------------------------- */
685
    /*      Read the ``value'' for this node.                               */
686
    /* -------------------------------------------------------------------- */
687
0
    {
688
0
        char szToken[512];  // do not initialize whole buffer. significant
689
                            // overhead
690
0
        size_t nTokenLen = 0;
691
0
        szToken[0] = '\0';
692
693
0
        while (*pszInput != '\0' && nTokenLen + 1 < sizeof(szToken))
694
0
        {
695
0
            if (*pszInput == '"')
696
0
            {
697
0
                bInQuotedString = !bInQuotedString;
698
0
            }
699
0
            else if (!bInQuotedString &&
700
0
                     (*pszInput == '[' || *pszInput == ']' ||
701
0
                      *pszInput == ',' || *pszInput == '(' || *pszInput == ')'))
702
0
            {
703
0
                break;
704
0
            }
705
0
            else if (!bInQuotedString &&
706
0
                     (*pszInput == ' ' || *pszInput == '\t' ||
707
0
                      *pszInput == 10 || *pszInput == 13))
708
0
            {
709
                // Skip over whitespace.
710
0
            }
711
0
            else
712
0
            {
713
0
                szToken[nTokenLen++] = *pszInput;
714
0
            }
715
716
0
            pszInput++;
717
0
        }
718
719
0
        if (*pszInput == '\0' || nTokenLen == sizeof(szToken) - 1)
720
0
            return OGRERR_CORRUPT_DATA;
721
722
0
        szToken[nTokenLen++] = '\0';
723
0
        SetValue(szToken);
724
0
    }
725
726
    /* -------------------------------------------------------------------- */
727
    /*      Read children, if we have a sublist.                            */
728
    /* -------------------------------------------------------------------- */
729
0
    if (*pszInput == '[' || *pszInput == '(')
730
0
    {
731
0
        do
732
0
        {
733
0
            pszInput++;  // Skip bracket or comma.
734
735
0
            OGR_SRSNode *poNewChild = new OGR_SRSNode();
736
0
            poNewChild->m_listener = m_listener;
737
738
0
            (*pnNodes)++;
739
0
            const OGRErr eErr =
740
0
                poNewChild->importFromWkt(&pszInput, nRecLevel + 1, pnNodes);
741
0
            if (eErr != OGRERR_NONE)
742
0
            {
743
0
                delete poNewChild;
744
0
                return eErr;
745
0
            }
746
747
0
            AddChild(poNewChild);
748
749
            // Swallow whitespace.
750
0
            while (isspace(static_cast<unsigned char>(*pszInput)))
751
0
                pszInput++;
752
0
        } while (*pszInput == ',');
753
754
0
        if (*pszInput != ')' && *pszInput != ']')
755
0
            return OGRERR_CORRUPT_DATA;
756
757
0
        pszInput++;
758
0
    }
759
760
0
    *ppszInput = pszInput;
761
762
0
    return OGRERR_NONE;
763
0
}
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
void OGR_SRSNode::MakeValueSafe()
777
778
0
{
779
    /* -------------------------------------------------------------------- */
780
    /*      First process subnodes.                                         */
781
    /* -------------------------------------------------------------------- */
782
0
    for (int iChild = 0; iChild < GetChildCount(); iChild++)
783
0
    {
784
0
        GetChild(iChild)->MakeValueSafe();
785
0
    }
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
0
    {
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
0
        {
802
0
            pszValue[i] = '_';
803
0
        }
804
0
    }
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
0
    {
812
0
        if (pszValue[j] == '_' && pszValue[i] == '_')
813
0
            continue;
814
815
0
        pszValue[++j] = pszValue[i];
816
0
    }
817
818
0
    if (pszValue[j] == '_')
819
0
        pszValue[j] = '\0';
820
0
    else
821
0
        pszValue[j + 1] = '\0';
822
0
}
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
void OGR_SRSNode::StripNodes(const char *pszName)
838
839
0
{
840
    /* -------------------------------------------------------------------- */
841
    /*      Strip any children matching this name.                          */
842
    /* -------------------------------------------------------------------- */
843
0
    while (FindChild(pszName) >= 0)
844
0
        DestroyChild(FindChild(pszName));
845
846
    /* -------------------------------------------------------------------- */
847
    /*      Recurse                                                         */
848
    /* -------------------------------------------------------------------- */
849
0
    for (int i = 0; i < GetChildCount(); i++)
850
0
        GetChild(i)->StripNodes(pszName);
851
0
}