/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 | 2.59M | : pszValue(CPLStrdup(pszValueIn)), papoChildNodes(nullptr), |
41 | 2.59M | poParent(nullptr), nChildren(0) |
42 | 2.59M | { |
43 | 2.59M | } |
44 | | |
45 | | /************************************************************************/ |
46 | | /* ~OGR_SRSNode() */ |
47 | | /************************************************************************/ |
48 | | |
49 | | OGR_SRSNode::~OGR_SRSNode() |
50 | | |
51 | 2.59M | { |
52 | 2.59M | CPLFree(pszValue); |
53 | | |
54 | 2.59M | ClearChildren(); |
55 | 2.59M | } |
56 | | |
57 | | /************************************************************************/ |
58 | | /* ~Listener() */ |
59 | | /************************************************************************/ |
60 | | |
61 | 256k | OGR_SRSNode::Listener::~Listener() = default; |
62 | | |
63 | | /************************************************************************/ |
64 | | /* RegisterListener() */ |
65 | | /************************************************************************/ |
66 | | |
67 | | void OGR_SRSNode::RegisterListener(const std::shared_ptr<Listener> &listener) |
68 | 76.1k | { |
69 | 76.1k | m_listener = listener; |
70 | 76.1k | } |
71 | | |
72 | | /************************************************************************/ |
73 | | /* notifyChange() */ |
74 | | /************************************************************************/ |
75 | | |
76 | | void OGR_SRSNode::notifyChange() |
77 | 5.10M | { |
78 | 5.10M | auto locked = m_listener.lock(); |
79 | 5.10M | if (locked) |
80 | 5.10M | { |
81 | 5.10M | locked->notifyChange(this); |
82 | 5.10M | } |
83 | 5.10M | } |
84 | | |
85 | | /************************************************************************/ |
86 | | /* ClearChildren() */ |
87 | | /************************************************************************/ |
88 | | |
89 | | /** Clear children nodes |
90 | | */ |
91 | | void OGR_SRSNode::ClearChildren() |
92 | | |
93 | 5.18M | { |
94 | 7.69M | for (int i = 0; i < nChildren; i++) |
95 | 2.51M | { |
96 | 2.51M | delete papoChildNodes[i]; |
97 | 2.51M | } |
98 | | |
99 | 5.18M | CPLFree(papoChildNodes); |
100 | | |
101 | 5.18M | papoChildNodes = nullptr; |
102 | 5.18M | nChildren = 0; |
103 | 5.18M | } |
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 | 7.78k | { |
134 | 7.78k | if (iChild < 0 || iChild >= nChildren) |
135 | 0 | return nullptr; |
136 | | |
137 | 7.78k | return papoChildNodes[iChild]; |
138 | 7.78k | } |
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 | 505k | { |
153 | 505k | if (iChild < 0 || iChild >= nChildren) |
154 | 0 | return nullptr; |
155 | | |
156 | 505k | return papoChildNodes[iChild]; |
157 | 505k | } |
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 | 49.7k | { |
183 | 49.7k | if (nChildren > 0 && EQUAL(pszName, pszValue)) |
184 | 20.4k | 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 | 60.7k | for (int i = 0; i < nChildren; i++) |
191 | 51.6k | { |
192 | 51.6k | if (EQUAL(papoChildNodes[i]->pszValue, pszName) && |
193 | 51.6k | papoChildNodes[i]->nChildren > 0) |
194 | 20.1k | return papoChildNodes[i]; |
195 | 51.6k | } |
196 | | |
197 | | /* -------------------------------------------------------------------- */ |
198 | | /* Then get each child to check their children. */ |
199 | | /* -------------------------------------------------------------------- */ |
200 | 17.7k | for (int i = 0; i < nChildren; i++) |
201 | 8.92k | { |
202 | 8.92k | OGR_SRSNode *poNode = papoChildNodes[i]->GetNode(pszName); |
203 | 8.92k | if (poNode != nullptr) |
204 | 306 | return poNode; |
205 | 8.92k | } |
206 | | |
207 | 8.81k | return nullptr; |
208 | 9.11k | } |
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 | 2.51M | { |
250 | 2.51M | InsertChild(poNew, nChildren); |
251 | 2.51M | } |
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 | 2.51M | { |
273 | 2.51M | if (iChild > nChildren) |
274 | 0 | iChild = nChildren; |
275 | | |
276 | 2.51M | nChildren++; |
277 | 2.51M | papoChildNodes = static_cast<OGR_SRSNode **>( |
278 | 2.51M | CPLRealloc(papoChildNodes, sizeof(void *) * nChildren)); |
279 | | |
280 | 2.51M | memmove(papoChildNodes + iChild + 1, papoChildNodes + iChild, |
281 | 2.51M | sizeof(void *) * (nChildren - iChild - 1)); |
282 | | |
283 | 2.51M | papoChildNodes[iChild] = poNew; |
284 | 2.51M | poNew->poParent = this; |
285 | | |
286 | 2.51M | poNew->m_listener = m_listener; |
287 | 2.51M | notifyChange(); |
288 | 2.51M | } |
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 | 63 | { |
306 | 63 | if (iChild < 0 || iChild >= nChildren) |
307 | 0 | return; |
308 | | |
309 | 63 | delete papoChildNodes[iChild]; |
310 | 104 | while (iChild < nChildren - 1) |
311 | 41 | { |
312 | 41 | papoChildNodes[iChild] = papoChildNodes[iChild + 1]; |
313 | 41 | iChild++; |
314 | 41 | } |
315 | | |
316 | 63 | nChildren--; |
317 | 63 | notifyChange(); |
318 | 63 | } |
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 | 777 | { |
338 | 3.72k | for (int i = 0; i < nChildren; i++) |
339 | 3.19k | { |
340 | 3.19k | if (EQUAL(papoChildNodes[i]->pszValue, pszValueIn)) |
341 | 241 | return i; |
342 | 3.19k | } |
343 | | |
344 | 536 | return -1; |
345 | 777 | } |
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 | 2.58M | { |
374 | 2.58M | CPLFree(pszValue); |
375 | 2.58M | pszValue = CPLStrdup(pszNewValue); |
376 | 2.58M | notifyChange(); |
377 | 2.58M | } |
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 | 5.80k | { |
392 | 5.80k | OGR_SRSNode *poNew = new OGR_SRSNode(pszValue); |
393 | | |
394 | 11.5k | for (int i = 0; i < nChildren; i++) |
395 | 5.70k | { |
396 | 5.70k | poNew->AddChild(papoChildNodes[i]->Clone()); |
397 | 5.70k | } |
398 | 5.80k | poNew->m_listener = m_listener; |
399 | | |
400 | 5.80k | return poNew; |
401 | 5.80k | } |
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 | 46.0k | { |
412 | | // Non-terminals are never quoted. |
413 | 46.0k | if (GetChildCount() != 0) |
414 | 16.4k | 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 | 29.5k | if (poParent != nullptr && EQUAL(poParent->GetValue(), "AUTHORITY")) |
419 | 2.90k | 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 | 26.6k | if (poParent != nullptr && EQUAL(poParent->GetValue(), "AXIS") && |
424 | 26.6k | this != poParent->GetChild(0)) |
425 | 2.13k | return FALSE; |
426 | | |
427 | 24.5k | if (poParent != nullptr && EQUAL(poParent->GetValue(), "CS") && |
428 | 24.5k | this == poParent->GetChild(0)) |
429 | 81 | 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 | 24.4k | if ((pszValue[0] == 'e' || pszValue[0] == 'E')) |
434 | 1.87k | return TRUE; |
435 | | |
436 | | // Non-numeric tokens are generally quoted while clean numeric values |
437 | | // are generally not. |
438 | 97.7k | for (int i = 0; pszValue[i] != '\0'; i++) |
439 | 87.9k | { |
440 | 87.9k | if ((pszValue[i] < '0' || pszValue[i] > '9') && pszValue[i] != '.' && |
441 | 87.9k | pszValue[i] != '-' && pszValue[i] != '+' && pszValue[i] != 'e' && |
442 | 87.9k | pszValue[i] != 'E') |
443 | 12.7k | return TRUE; |
444 | 87.9k | } |
445 | | |
446 | 9.84k | return FALSE; |
447 | 22.5k | } |
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 | 46.0k | { |
468 | | /* -------------------------------------------------------------------- */ |
469 | | /* Build a list of the WKT format for the children. */ |
470 | | /* -------------------------------------------------------------------- */ |
471 | 46.0k | char **papszChildrenWkt = |
472 | 46.0k | static_cast<char **>(CPLCalloc(sizeof(char *), nChildren + 1)); |
473 | 46.0k | size_t nLength = strlen(pszValue) + 4; |
474 | | |
475 | 91.0k | for (int i = 0; i < nChildren; i++) |
476 | 45.0k | { |
477 | 45.0k | papoChildNodes[i]->exportToWkt(papszChildrenWkt + i); |
478 | 45.0k | nLength += strlen(papszChildrenWkt[i]) + 1; |
479 | 45.0k | } |
480 | | |
481 | | /* -------------------------------------------------------------------- */ |
482 | | /* Allocate the result string. */ |
483 | | /* -------------------------------------------------------------------- */ |
484 | 46.0k | *ppszResult = static_cast<char *>(CPLMalloc(nLength)); |
485 | 46.0k | *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 | 46.0k | if (NeedsQuoting()) |
493 | 17.5k | { |
494 | 17.5k | strcat(*ppszResult, "\""); |
495 | 17.5k | strcat(*ppszResult, pszValue); // Should we do quoting? |
496 | 17.5k | strcat(*ppszResult, "\""); |
497 | 17.5k | } |
498 | 28.5k | else |
499 | 28.5k | strcat(*ppszResult, pszValue); |
500 | | |
501 | | /* -------------------------------------------------------------------- */ |
502 | | /* Add the children strings with appropriate brackets and commas. */ |
503 | | /* -------------------------------------------------------------------- */ |
504 | 46.0k | if (nChildren > 0) |
505 | 16.4k | strcat(*ppszResult, "["); |
506 | | |
507 | 91.0k | for (int i = 0; i < nChildren; i++) |
508 | 45.0k | { |
509 | 45.0k | strcat(*ppszResult, papszChildrenWkt[i]); |
510 | 45.0k | if (i == nChildren - 1) |
511 | 16.4k | strcat(*ppszResult, "]"); |
512 | 28.5k | else |
513 | 28.5k | strcat(*ppszResult, ","); |
514 | 45.0k | } |
515 | | |
516 | 46.0k | CSLDestroy(papszChildrenWkt); |
517 | | |
518 | 46.0k | return OGRERR_NONE; |
519 | 46.0k | } |
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 | 76.0k | { |
658 | 76.0k | int nNodes = 0; |
659 | 76.0k | return importFromWkt(ppszInput, 0, &nNodes); |
660 | 76.0k | } |
661 | | |
662 | | OGRErr OGR_SRSNode::importFromWkt(const char **ppszInput, int nRecLevel, |
663 | | int *pnNodes) |
664 | | |
665 | 2.58M | { |
666 | | // Sanity checks. |
667 | 2.58M | if (nRecLevel == 10) |
668 | 0 | { |
669 | 0 | return OGRERR_CORRUPT_DATA; |
670 | 0 | } |
671 | 2.58M | if (*pnNodes == 1000) |
672 | 1 | { |
673 | 1 | return OGRERR_CORRUPT_DATA; |
674 | 1 | } |
675 | | |
676 | 2.58M | const char *pszInput = *ppszInput; |
677 | 2.58M | bool bInQuotedString = false; |
678 | | |
679 | | /* -------------------------------------------------------------------- */ |
680 | | /* Clear any existing children of this node. */ |
681 | | /* -------------------------------------------------------------------- */ |
682 | 2.58M | ClearChildren(); |
683 | | |
684 | | /* -------------------------------------------------------------------- */ |
685 | | /* Read the ``value'' for this node. */ |
686 | | /* -------------------------------------------------------------------- */ |
687 | 2.58M | { |
688 | 2.58M | char szToken[512]; // do not initialize whole buffer. significant |
689 | | // overhead |
690 | 2.58M | size_t nTokenLen = 0; |
691 | 2.58M | szToken[0] = '\0'; |
692 | | |
693 | 26.4M | while (*pszInput != '\0' && nTokenLen + 1 < sizeof(szToken)) |
694 | 26.4M | { |
695 | 26.4M | if (*pszInput == '"') |
696 | 1.99M | { |
697 | 1.99M | bInQuotedString = !bInQuotedString; |
698 | 1.99M | } |
699 | 24.4M | else if (!bInQuotedString && |
700 | 24.4M | (*pszInput == '[' || *pszInput == ']' || |
701 | 12.3M | *pszInput == ',' || *pszInput == '(' || *pszInput == ')')) |
702 | 2.58M | { |
703 | 2.58M | break; |
704 | 2.58M | } |
705 | 21.8M | else if (!bInQuotedString && |
706 | 21.8M | (*pszInput == ' ' || *pszInput == '\t' || |
707 | 9.75M | *pszInput == 10 || *pszInput == 13)) |
708 | 481 | { |
709 | | // Skip over whitespace. |
710 | 481 | } |
711 | 21.8M | else |
712 | 21.8M | { |
713 | 21.8M | szToken[nTokenLen++] = *pszInput; |
714 | 21.8M | } |
715 | | |
716 | 23.8M | pszInput++; |
717 | 23.8M | } |
718 | | |
719 | 2.58M | if (*pszInput == '\0' || nTokenLen == sizeof(szToken) - 1) |
720 | 101 | return OGRERR_CORRUPT_DATA; |
721 | | |
722 | 2.58M | szToken[nTokenLen++] = '\0'; |
723 | 2.58M | SetValue(szToken); |
724 | 2.58M | } |
725 | | |
726 | | /* -------------------------------------------------------------------- */ |
727 | | /* Read children, if we have a sublist. */ |
728 | | /* -------------------------------------------------------------------- */ |
729 | 2.58M | if (*pszInput == '[' || *pszInput == '(') |
730 | 913k | { |
731 | 913k | do |
732 | 2.51M | { |
733 | 2.51M | pszInput++; // Skip bracket or comma. |
734 | | |
735 | 2.51M | OGR_SRSNode *poNewChild = new OGR_SRSNode(); |
736 | 2.51M | poNewChild->m_listener = m_listener; |
737 | | |
738 | 2.51M | (*pnNodes)++; |
739 | 2.51M | const OGRErr eErr = |
740 | 2.51M | poNewChild->importFromWkt(&pszInput, nRecLevel + 1, pnNodes); |
741 | 2.51M | if (eErr != OGRERR_NONE) |
742 | 175 | { |
743 | 175 | delete poNewChild; |
744 | 175 | return eErr; |
745 | 175 | } |
746 | | |
747 | 2.51M | AddChild(poNewChild); |
748 | | |
749 | | // Swallow whitespace. |
750 | 2.51M | while (isspace(static_cast<unsigned char>(*pszInput))) |
751 | 0 | pszInput++; |
752 | 2.51M | } while (*pszInput == ','); |
753 | | |
754 | 913k | if (*pszInput != ')' && *pszInput != ']') |
755 | 4 | return OGRERR_CORRUPT_DATA; |
756 | | |
757 | 913k | pszInput++; |
758 | 913k | } |
759 | | |
760 | 2.58M | *ppszInput = pszInput; |
761 | | |
762 | 2.58M | return OGRERR_NONE; |
763 | 2.58M | } |
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 | } |