1 /**************************************************************************\
2 * Copyright (c) Kongsberg Oil & Gas Technologies AS
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are
7 * met:
8 *
9 * Redistributions of source code must retain the above copyright notice,
10 * this list of conditions and the following disclaimer.
11 *
12 * Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * Neither the name of the copyright holder nor the names of its
17 * contributors may be used to endorse or promote products derived from
18 * this software without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 \**************************************************************************/
32
33 #include <Inventor/C/XML/element.h>
34 #include "elementp.h"
35
36 #ifdef HAVE_CONFIG_H
37 #include <config.h>
38 #endif // HAVE_CONFIG_H
39
40 #include <cstdlib>
41 #include <cstdio>
42 #include <cstring>
43 #include <cassert>
44
45 #include <Inventor/C/base/string.h>
46 #include <Inventor/lists/SbList.h>
47
48 #include <Inventor/C/XML/document.h>
49 #include <Inventor/C/XML/attribute.h>
50 #include <Inventor/C/XML/path.h>
51 #include "attributep.h"
52 #include "utils.h"
53
54 // *************************************************************************
55
56 struct cc_xml_elt {
57 char * type;
58 char * data;
59 char * cdata;
60 cc_xml_elt * parent;
61 SbList<cc_xml_attr *> attributes;
62 SbList<cc_xml_elt *> children;
63 };
64
65 // *************************************************************************
66
67 /*!
68 Creates a new element with no type, no attributes, and no children elements.
69 */
70
71 cc_xml_elt *
cc_xml_elt_new(void)72 cc_xml_elt_new(void)
73 {
74 cc_xml_elt * elt = new cc_xml_elt;
75 assert(elt);
76 elt->type = NULL;
77 elt->data = NULL;
78 elt->cdata = NULL;
79 elt->parent = NULL;
80 return elt;
81 }
82
83 /*!
84 Creates a new element with the given type and attributes.
85 The \a attrs argument can be NULL.
86 */
87
88 cc_xml_elt *
cc_xml_elt_new_from_data(const char * type,cc_xml_attr ** attrs)89 cc_xml_elt_new_from_data(const char * type, cc_xml_attr ** attrs)
90 {
91 cc_xml_elt * elt = cc_xml_elt_new();
92 cc_xml_elt_set_type_x(elt, type);
93 if (attrs) { cc_xml_elt_set_attributes_x(elt, attrs); }
94 return elt;
95 }
96
97 /*!
98 Returns a clone of the element, including child elements and attributes.
99 Only the parent connection is dropped.
100 */
101
102 cc_xml_elt *
cc_xml_elt_clone(const cc_xml_elt * elt)103 cc_xml_elt_clone(const cc_xml_elt * elt)
104 {
105 cc_xml_elt * clone = cc_xml_elt_new();
106 if (elt->type) {
107 cc_xml_elt_set_type_x(clone, elt->type);
108 }
109 if (elt->data) {
110 clone->data = cc_xml_strdup(elt->data);
111 }
112 if (elt->cdata) {
113 clone->cdata = cc_xml_strdup(elt->cdata);
114 }
115 int i = 0;
116 for (i = 0; i < elt->attributes.getLength(); ++i) {
117 cc_xml_elt_set_attribute_x(clone, cc_xml_attr_clone(elt->attributes[i]));
118 }
119 for (i = 0; i < elt->children.getLength(); ++i) {
120 cc_xml_elt_add_child_x(clone, cc_xml_elt_clone(elt->children[i]));
121 }
122 return clone;
123 }
124
125 /*!
126 Frees the given \a elt element, including its attributes and children.
127 */
128
129 void
cc_xml_elt_delete_x(cc_xml_elt * elt)130 cc_xml_elt_delete_x(cc_xml_elt * elt)
131 {
132 assert(elt);
133 delete [] elt->type;
134 delete [] elt->data;
135 delete [] elt->cdata;
136 if (elt->attributes.getLength() > 0) {
137 const int num = elt->attributes.getLength();
138 for (int i = 0; i < num; ++i) {
139 cc_xml_attr_delete_x(elt->attributes[i]);
140 }
141 }
142 if (elt->children.getLength() > 0) {
143 const int num = elt->children.getLength();
144 for (int i = 0; i < num; ++i) {
145 cc_xml_elt_delete_x(elt->children[i]);
146 }
147 }
148 delete elt;
149 }
150
151 // *************************************************************************
152
153 /*!
154 Sets the element type identifier. The old value will be freed, if any.
155 The \a type argument can be NULL to just clear the old value.
156 */
157
158 void
cc_xml_elt_set_type_x(cc_xml_elt * elt,const char * type)159 cc_xml_elt_set_type_x(cc_xml_elt * elt, const char * type)
160 {
161 assert(elt);
162 delete [] elt->type;
163 elt->type = NULL;
164 if (type) elt->type = cc_xml_strdup(type);
165 }
166
167 /*!
168 Returns the element type identifier if one is set, and NULL otherwise.
169 */
170
171 const char *
cc_xml_elt_get_type(const cc_xml_elt * elt)172 cc_xml_elt_get_type(const cc_xml_elt * elt)
173 {
174 return elt->type;
175 }
176
177 // FIXME: document behaviour
178
179 void
cc_xml_elt_set_cdata_x(cc_xml_elt * elt,const char * cdata)180 cc_xml_elt_set_cdata_x(cc_xml_elt * elt, const char * cdata)
181 {
182 assert(elt);
183 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
184 if ( cc_xml_elt_get_num_children(elt) == 0 ) {
185 // cdata child not existing yet
186 cc_xml_elt * child = cc_xml_elt_new();
187 cc_xml_elt_set_type_x(child, COIN_XML_CDATA_TYPE);
188 cc_xml_elt_add_child_x(elt, child);
189 elt = child;
190 } else if ( (cc_xml_elt_get_num_children(elt) == 1) &&
191 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
192 // we really want to manipulate the cdata child, not this element
193 elt = elt->children[0];
194 } else {
195 // FIXME: warn that this doesn't make sense 20021119 larsa
196 // but we can run through anyways
197 }
198 }
199 delete [] elt->cdata;
200 elt->cdata = NULL;
201 if ( cdata ) elt->cdata = cc_xml_strdup(cdata);
202 delete [] elt->data;
203 elt->data = NULL;
204 // Update data to whitespace-stripped cdata
205 if( cdata) {
206 const char * startptr = elt->cdata;
207 const char * endptr = startptr + (strlen(startptr) - 1);
208 while ( *startptr == '\t' || *startptr == '\r' || *startptr == '\n' || *startptr == ' ' ) {
209 startptr++;
210 }
211 // FIXME: avoid whitespace-only cdata from parser, then uncomment assert's.
212 // assert(startptr <= endptr && "just whitespace from the front");
213 while ( endptr > startptr && (*endptr == '\t' || *endptr == '\r' || *endptr == '\n' || *endptr == ' ') ) {
214 endptr--;
215 }
216 // assert(endptr > startptr && "makes no sense at all");
217 endptr++;
218 if( endptr > startptr) {
219 elt->data = cc_xml_strndup(startptr, static_cast<int>(endptr - startptr));
220 }
221 }
222 }
223
224 const char *
cc_xml_elt_get_cdata(const cc_xml_elt * elt)225 cc_xml_elt_get_cdata(const cc_xml_elt * elt)
226 {
227 assert(elt);
228 if (strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0) {
229 if ((cc_xml_elt_get_num_children(elt) == 1) &&
230 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0)) {
231 return elt->children[0]->cdata;
232 }
233 }
234 return elt->cdata;
235 }
236
237 const char *
cc_xml_elt_get_data(const cc_xml_elt * elt)238 cc_xml_elt_get_data(const cc_xml_elt * elt)
239 {
240 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
241 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
242 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
243 return elt->children[0]->data;
244 }
245 }
246 return elt->data;
247 }
248
249 // *************************************************************************
250
251 void
cc_xml_elt_remove_all_attributes_x(cc_xml_elt * elt)252 cc_xml_elt_remove_all_attributes_x(cc_xml_elt * elt)
253 {
254 assert(elt);
255 if (elt->attributes.getLength()) {
256 }
257 }
258
259 void
cc_xml_elt_set_attribute_x(cc_xml_elt * elt,cc_xml_attr * attr)260 cc_xml_elt_set_attribute_x(cc_xml_elt * elt, cc_xml_attr * attr)
261 {
262 cc_xml_attr * shadowed = cc_xml_elt_get_attribute(elt, cc_xml_attr_get_name(attr));
263 if (shadowed) {
264 cc_xml_attr_set_value_x(shadowed, cc_xml_attr_get_value(attr));
265 // FIXME: free attr, or replace shadowed by attr and free shadowed?
266 } else {
267 elt->attributes.append(attr);
268 }
269 }
270
271 void
cc_xml_elt_set_attributes_x(cc_xml_elt * elt,cc_xml_attr ** attrs)272 cc_xml_elt_set_attributes_x(cc_xml_elt * elt, cc_xml_attr ** attrs)
273 {
274 for (int c = 0; attrs[c] != NULL; ++c) {
275 cc_xml_elt_set_attribute_x(elt, attrs[c]);
276 }
277 }
278
279 cc_xml_attr *
cc_xml_elt_get_attribute(const cc_xml_elt * elt,const char * attrname)280 cc_xml_elt_get_attribute(const cc_xml_elt * elt, const char * attrname)
281 {
282 assert(elt);
283 const int num = elt->attributes.getLength();
284 for (int c = 0; c < num; ++c) {
285 if (strcmp(attrname, cc_xml_attr_get_name(elt->attributes[c])) == 0)
286 return elt->attributes[c];
287 }
288 return NULL;
289 }
290
291 int
cc_xml_elt_get_num_attributes(const cc_xml_elt * elt)292 cc_xml_elt_get_num_attributes(const cc_xml_elt * elt)
293 {
294 return elt->attributes.getLength();
295 }
296
297 const cc_xml_attr **
cc_xml_elt_get_attributes(const cc_xml_elt * elt)298 cc_xml_elt_get_attributes(const cc_xml_elt * elt)
299 {
300 assert(elt);
301 return const_cast<const cc_xml_attr **>(elt->attributes.getArrayPtr());
302 }
303
304 // *************************************************************************
305
306 cc_xml_elt *
cc_xml_elt_get_parent(const cc_xml_elt * elt)307 cc_xml_elt_get_parent(const cc_xml_elt * elt)
308 {
309 assert(elt);
310 return elt->parent;
311 }
312
313 int
cc_xml_elt_get_num_children(const cc_xml_elt * elt)314 cc_xml_elt_get_num_children(const cc_xml_elt * elt)
315 {
316 assert(elt);
317 return elt->children.getLength();
318 }
319
320 /*!
321 Returns the number of child elements of the given type.
322 */
323
324 int
cc_xml_elt_get_num_children_of_type(const cc_xml_elt * elt,const char * type)325 cc_xml_elt_get_num_children_of_type(const cc_xml_elt * elt, const char * type)
326 {
327 assert(elt);
328 int count = 0;
329 const int numchildren = elt->children.getLength();
330 for (int i = 0; i < numchildren; ++i) {
331 if (strcmp(type, cc_xml_elt_get_type(elt->children[i])) == 0) ++count;
332 }
333 return count;
334 }
335
336 /*!
337 Returns the child element at index \a childidx.
338
339 The index has to be an existing index, invalid values are considered programming errors.
340 */
341
342 cc_xml_elt *
cc_xml_elt_get_child(const cc_xml_elt * elt,int childidx)343 cc_xml_elt_get_child(const cc_xml_elt * elt, int childidx)
344 {
345 assert(elt);
346 assert(childidx >= 0);
347 assert(childidx < cc_xml_elt_get_num_children(elt));
348 return elt->children[childidx];
349 }
350
351 /*!
352 Returns the child index of the child element.
353
354 Giving a child element that is not a child is considered a programming error.
355 */
356
357 int
cc_xml_elt_get_child_index(const cc_xml_elt * elt,const cc_xml_elt * child)358 cc_xml_elt_get_child_index(const cc_xml_elt * elt, const cc_xml_elt * child)
359 {
360 assert(elt);
361 const int numchildren = elt->children.getLength();
362 for (int idx = 0; idx < numchildren; ++idx) {
363 if (elt->children[idx] == child) return idx;
364 }
365 assert(!"no such child");
366 return -1;
367 }
368
369 /*
370 Returns the child element's index when only counting elements of that given type.
371
372 Giving a child element that is not a child is considered a programming error.
373 */
374
375 int
cc_xml_elt_get_child_type_index(const cc_xml_elt * elt,const cc_xml_elt * child)376 cc_xml_elt_get_child_type_index(const cc_xml_elt * elt, const cc_xml_elt * child)
377 {
378 assert(elt);
379 const int numchildren = elt->children.getLength();
380 int idx = -1;
381 const char * type = cc_xml_elt_get_type(child);
382 for (int i = 0; i < numchildren; ++i) {
383 if (strcmp(cc_xml_elt_get_type(elt->children[i]), type) == 0) ++idx;
384 if (elt->children[i] == child) return idx;
385 }
386 assert(!"no such child");
387 return -1;
388 }
389
390 cc_xml_path *
cc_xml_elt_get_path(const cc_xml_elt * elt)391 cc_xml_elt_get_path(const cc_xml_elt * elt)
392 {
393 assert(elt);
394 cc_xml_path * path = cc_xml_path_new();
395 while ( elt != NULL ) {
396 const cc_xml_elt * parent = cc_xml_elt_get_parent(elt);
397 if ( parent != NULL ) {
398 int idx = cc_xml_elt_get_child_type_index(parent, elt);
399 cc_xml_path_prepend_x(path, elt->type, idx);
400 }
401 elt = parent;
402 }
403 return path;
404 }
405
406 cc_xml_elt *
cc_xml_elt_get_child_of_type(const cc_xml_elt * elt,const char * type,int idx)407 cc_xml_elt_get_child_of_type(const cc_xml_elt * elt, const char * type, int idx)
408 {
409 assert(elt);
410 if ( !elt->children.getLength() ) return NULL;
411 int i;
412 for ( i = 0; i < elt->children.getLength(); i++ ) {
413 if ( strcmp(elt->children[i]->type, type) == 0 ) {
414 if ( idx == 0 ) return elt->children[i];
415 idx -= 1;
416 }
417 }
418 return NULL;
419 }
420
421 cc_xml_elt *
cc_xml_elt_get_child_of_type_x(cc_xml_elt * elt,const char * type,int idx)422 cc_xml_elt_get_child_of_type_x(cc_xml_elt * elt, const char * type, int idx)
423 {
424 assert(elt);
425 if ( elt->children.getLength() ) {
426 int i;
427 for ( i = 0; i < elt->children.getLength(); i++ ) {
428 if ( strcmp(elt->children[i]->type, type) == 0 ) {
429 if ( idx == 0 ) return elt->children[i];
430 idx -= 1;
431 }
432 }
433 }
434 cc_xml_elt * child = NULL;
435 while ( idx >= 0 ) {
436 child = cc_xml_elt_new();
437 cc_xml_elt_set_type_x(child, type);
438 cc_xml_elt_add_child_x(elt, child);
439 idx -= 1;
440 }
441 return child;
442 }
443
444 // *************************************************************************
445
446 void
cc_xml_elt_set_parent_x(cc_xml_elt * elt,cc_xml_elt * parent)447 cc_xml_elt_set_parent_x(cc_xml_elt * elt, cc_xml_elt * parent)
448 {
449 assert(elt);
450 elt->parent = parent;
451 }
452
453 void
cc_xml_elt_add_child_x(cc_xml_elt * elt,cc_xml_elt * child)454 cc_xml_elt_add_child_x(cc_xml_elt * elt, cc_xml_elt * child)
455 {
456 assert(elt);
457 assert(child);
458 if (child->parent != NULL) {
459 // FIXME: ERROR - element already a child of another element
460 return;
461 }
462
463 //int numchildren = cc_xml_elt_get_num_children(elt);
464 elt->children.append(child);
465 child->parent = elt;
466 }
467
468 /*! This function will not free the child being removed. Giving a
469 nonexistent child to this function is a programming error and will
470 result in an assert.
471 */
472
473 void
cc_xml_elt_remove_child_x(cc_xml_elt * elt,cc_xml_elt * child)474 cc_xml_elt_remove_child_x(cc_xml_elt * elt, cc_xml_elt * child)
475 {
476 assert(elt);
477 assert(child);
478 const int numchildren = elt->children.getLength();
479 for (int i = 0; i < numchildren; ++i) {
480 if (elt->children[i] == child) {
481 elt->children.remove(i);
482 child->parent = NULL;
483 return;
484 }
485 }
486 assert(!"no such child");
487 }
488
489 void
cc_xml_elt_insert_child_x(cc_xml_elt * elt,cc_xml_elt * child,int idx)490 cc_xml_elt_insert_child_x(cc_xml_elt * elt, cc_xml_elt * child, int idx)
491 {
492 assert(elt);
493 assert(child);
494 if (child->parent != NULL) {
495 // FIXME: error, child already a child of another element
496 return;
497 }
498 const int numchildren = elt->children.getLength();
499 assert(idx >= 0 && idx <= numchildren);
500 elt->children.insert(child, idx);
501 child->parent = elt;
502 }
503
504 int
cc_xml_elt_replace_child_x(cc_xml_elt * elt,cc_xml_elt * oldchild,cc_xml_elt * newchild)505 cc_xml_elt_replace_child_x(cc_xml_elt * elt, cc_xml_elt * oldchild, cc_xml_elt * newchild)
506 {
507 assert(elt);
508 int idx = cc_xml_elt_get_child_index(elt, oldchild);
509 if ( idx == -1 ) return FALSE;
510 cc_xml_elt_remove_child_x(elt, oldchild);
511 cc_xml_elt_insert_child_x(elt, newchild, idx);
512 return TRUE;
513 }
514
515 // *************************************************************************
516
517 int
cc_xml_elt_get_boolean(const cc_xml_elt * elt,int * value)518 cc_xml_elt_get_boolean(const cc_xml_elt * elt, int * value)
519 {
520 assert(elt);
521 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
522 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
523 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
524 elt = elt->children[0];
525 } else {
526 // FIXME: warn about structure senselessness
527 }
528 }
529 const char * data = cc_xml_elt_get_data(elt);
530 assert(value != NULL);
531 if ( data == NULL ) return FALSE;
532 if ( cc_xml_strieq(data, "true") ||
533 cc_xml_strieq(data, "on") ||
534 cc_xml_strieq(data, "t") ) {
535 *value = TRUE;
536 return TRUE;
537 }
538 if ( cc_xml_strieq(data, "false") ||
539 cc_xml_strieq(data, "off") ||
540 cc_xml_strieq(data, "f") ) {
541 *value = FALSE;
542 return TRUE;
543 }
544 return FALSE;
545 }
546
547 int
cc_xml_elt_get_integer(const cc_xml_elt * elt,int * value)548 cc_xml_elt_get_integer(const cc_xml_elt * elt, int * value)
549 {
550 assert(elt);
551 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
552 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
553 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
554 elt = elt->children[0];
555 } else {
556 // FIXME: warn about structure senselessness
557 }
558 }
559 const char * data = cc_xml_elt_get_data(elt);
560 assert(value != NULL);
561 if ( data == NULL ) return FALSE;
562 if ( sscanf(data, "%d", value) == 1 ) return TRUE;
563 return FALSE;
564 }
565
566 int
cc_xml_elt_get_uint64(const cc_xml_elt * elt,uint64_t * value)567 cc_xml_elt_get_uint64(const cc_xml_elt * elt, uint64_t * value)
568 {
569 assert(elt);
570 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
571 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
572 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
573 elt = elt->children[0];
574 } else {
575 // FIXME: warn about structure senselessness
576 }
577 }
578 const char * data = cc_xml_elt_get_data(elt);
579 assert(value != NULL);
580 if ( data == NULL ) return FALSE;
581 if ( sscanf(data, "%lld", value) == 1 ) return TRUE; // FIXME: unsigned
582 return FALSE;
583 }
584
585 int
cc_xml_elt_get_int64(const cc_xml_elt * elt,int64_t * value)586 cc_xml_elt_get_int64(const cc_xml_elt * elt, int64_t * value)
587 {
588 assert(elt);
589 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
590 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
591 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
592 elt = elt->children[0];
593 } else {
594 // FIXME: warn about structure senselessness
595 }
596 }
597 const char * data = cc_xml_elt_get_data(elt);
598 assert(value != NULL);
599 if ( data == NULL ) return FALSE;
600 if ( sscanf(data, "%lld", value) == 1 ) return TRUE;
601 return FALSE;
602 }
603
604 int
cc_xml_elt_get_uint32(const cc_xml_elt * elt,uint32_t * value)605 cc_xml_elt_get_uint32(const cc_xml_elt * elt, uint32_t * value)
606 {
607 assert(elt);
608 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
609 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
610 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
611 elt = elt->children[0];
612 } else {
613 // FIXME: warn about structure senselessness
614 }
615 }
616 const char * data = cc_xml_elt_get_data(elt);
617 assert(value != NULL);
618 if ( data == NULL ) return FALSE;
619 if ( sscanf(data, "%u", value) == 1 ) return TRUE; // FIXME: unsigned
620 return FALSE;
621 }
622
623 int
cc_xml_elt_get_int32(const cc_xml_elt * elt,int32_t * value)624 cc_xml_elt_get_int32(const cc_xml_elt * elt, int32_t * value)
625 {
626 assert(elt);
627 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
628 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
629 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
630 elt = elt->children[0];
631 } else {
632 // FIXME: warn about structure senselessness
633 }
634 }
635 const char * data = cc_xml_elt_get_data(elt);
636 assert(value != NULL);
637 if ( data == NULL ) return FALSE;
638 if ( sscanf(data, "%u", value) == 1 ) return TRUE;
639 return FALSE;
640 }
641
642 int
cc_xml_elt_get_float(const cc_xml_elt * elt,float * value)643 cc_xml_elt_get_float(const cc_xml_elt * elt, float * value)
644 {
645 assert(elt);
646 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
647 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
648 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
649 elt = elt->children[0];
650 } else {
651 // FIXME: warn about structure senselessness
652 }
653 }
654 const char * data = cc_xml_elt_get_data(elt);
655 assert(value != NULL);
656 if ( data == NULL ) return FALSE;
657 if ( sscanf(data, "%g", value) == 1 ) return TRUE;
658 return FALSE;
659 }
660
661 int
cc_xml_elt_get_double(const cc_xml_elt * elt,double * value)662 cc_xml_elt_get_double(const cc_xml_elt * elt, double * value)
663 {
664 assert(elt);
665 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
666 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
667 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
668 elt = elt->children[0];
669 } else {
670 // FIXME: warn about structure senselessness
671 }
672 }
673 const char * data = cc_xml_elt_get_data(elt);
674 assert(value != NULL);
675 if ( data == NULL ) return FALSE;
676 if ( sscanf(data, "%lg", value) == 1 ) return TRUE;
677 return FALSE;
678 }
679
680 void
cc_xml_elt_set_boolean_x(cc_xml_elt * elt,int value)681 cc_xml_elt_set_boolean_x(cc_xml_elt * elt, int value)
682 {
683 assert(elt);
684 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
685 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
686 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
687 elt = elt->children[0];
688 } else {
689 // FIXME: warn about structure senselessness
690 }
691 }
692 cc_xml_elt_set_cdata_x(elt, value ? "true" : "false");
693 }
694
695 void
cc_xml_elt_set_integer_x(cc_xml_elt * elt,int value)696 cc_xml_elt_set_integer_x(cc_xml_elt * elt, int value)
697 {
698 assert(elt);
699 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
700 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
701 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
702 elt = elt->children[0];
703 } else {
704 // FIXME: warn about structure senselessness
705 }
706 }
707 cc_string str;
708 cc_string_construct(&str);
709 cc_string_sprintf(&str, "%d", value);
710 cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
711 cc_string_clean(&str);
712 }
713
714 void
cc_xml_elt_set_uint64_x(cc_xml_elt * elt,uint64_t value)715 cc_xml_elt_set_uint64_x(cc_xml_elt * elt, uint64_t value)
716 {
717 assert(elt);
718 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
719 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
720 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
721 elt = elt->children[0];
722 } else {
723 // FIXME: warn about structure senselessness
724 }
725 }
726 cc_string str;
727 cc_string_construct(&str);
728 cc_string_sprintf(&str, "%lld", value); // FIXME: unsigned
729 cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
730 cc_string_clean(&str);
731 }
732
733 void
cc_xml_elt_set_int64_x(cc_xml_elt * elt,int64_t value)734 cc_xml_elt_set_int64_x(cc_xml_elt * elt, int64_t value)
735 {
736 assert(elt);
737 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
738 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
739 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
740 elt = elt->children[0];
741 } else {
742 // FIXME: warn about structure senselessness
743 }
744 }
745 cc_string str;
746 cc_string_construct(&str);
747 cc_string_sprintf(&str, "%lld", value);
748 cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
749 cc_string_clean(&str);
750 }
751
752 void
cc_xml_elt_set_uint32_x(cc_xml_elt * elt,uint32_t value)753 cc_xml_elt_set_uint32_x(cc_xml_elt * elt, uint32_t value)
754 {
755 assert(elt);
756 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
757 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
758 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
759 elt = elt->children[0];
760 } else {
761 // FIXME: warn about structure senselessness
762 }
763 }
764 cc_string str;
765 cc_string_construct(&str);
766 cc_string_sprintf(&str, "%ld", value); // FIXME: unsigned
767 cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
768 cc_string_clean(&str);
769 }
770
771 void
cc_xml_elt_set_int32_x(cc_xml_elt * elt,int32_t value)772 cc_xml_elt_set_int32_x(cc_xml_elt * elt, int32_t value)
773 {
774 assert(elt);
775 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
776 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
777 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
778 elt = elt->children[0];
779 } else {
780 // FIXME: warn about structure senselessness
781 }
782 }
783 cc_string str;
784 cc_string_construct(&str);
785 cc_string_sprintf(&str, "%ld", value);
786 cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
787 cc_string_clean(&str);
788 }
789
790 void
cc_xml_elt_set_float_x(cc_xml_elt * elt,float value)791 cc_xml_elt_set_float_x(cc_xml_elt * elt, float value)
792 {
793 assert(elt);
794 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
795 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
796 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
797 elt = elt->children[0];
798 } else {
799 // FIXME: warn about structure senselessness
800 }
801 }
802 cc_string str;
803 cc_string_construct(&str);
804 cc_string_sprintf(&str, "%g", value);
805 cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
806 cc_string_clean(&str);
807 }
808
809 void
cc_xml_elt_set_double_x(cc_xml_elt * elt,double value)810 cc_xml_elt_set_double_x(cc_xml_elt * elt, double value)
811 {
812 assert(elt);
813 if ( strcmp(elt->type, COIN_XML_CDATA_TYPE) != 0 ) {
814 if ( (cc_xml_elt_get_num_children(elt) == 1) &&
815 (strcmp(elt->children[0]->type, COIN_XML_CDATA_TYPE) == 0) ) {
816 elt = elt->children[0];
817 } else {
818 // FIXME: warn about structure senselessness
819 }
820 }
821 cc_string str;
822 cc_string_construct(&str);
823 cc_string_sprintf(&str, "%lf", value);
824 cc_xml_elt_set_cdata_x(elt, cc_string_get_text(&str));
825 cc_string_clean(&str);
826 }
827
828 // *************************************************************************
829
830 void
cc_xml_elt_strip_whitespace_x(cc_xml_elt * elt)831 cc_xml_elt_strip_whitespace_x(cc_xml_elt * elt)
832 {
833 // FIXME: update to reflect cdata change - remove function probably
834 assert(elt->type && strcmp(elt->type, COIN_XML_CDATA_TYPE) == 0);
835 assert(0);
836 const char * startptr = elt->data;
837 const char * endptr = startptr + (strlen(startptr) - 1);
838 while ( *startptr ) {
839 switch ( *startptr ) {
840 case '\t':
841 case '\r':
842 case '\n':
843 case ' ':
844 break;
845 default:
846 goto cont1;
847 }
848 startptr++;
849 }
850 assert(0 && "just whitespace from the front");
851 cont1:
852 while ( endptr > startptr ) {
853 switch ( *endptr ) {
854 case '\t':
855 case '\r':
856 case '\n':
857 case ' ':
858 break;
859 default:
860 goto cont2;
861 }
862 endptr--;
863 }
864 assert(0 && "makes no sense at all");
865 cont2:
866 endptr++;
867 if ( (startptr == elt->data) &&
868 (endptr == (startptr + strlen(startptr))) ) return;
869 char * substr = cc_xml_strndup(startptr, static_cast<int>(endptr - startptr));
870 cc_xml_elt_set_cdata_x(elt, substr);
871 delete [] substr;
872 }
873
874 // *************************************************************************
875
876 void
cc_xml_elt_dump_to_file(const cc_xml_elt * elt,int indent,FILE * fp)877 cc_xml_elt_dump_to_file(const cc_xml_elt * elt, int indent, FILE * fp)
878 {
879 // todo
880 // - support notation for empty elements and shortcuts
881 // - document-wide settings? how to propagate...
882 assert( elt && elt->type != NULL );
883 int i;
884 if ( cc_xml_elt_get_num_children(elt) == 1 &&
885 strcmp(COIN_XML_CDATA_TYPE, elt->children[0]->type) == 0 ) {
886 for ( i = 0; i < indent; i++ ) fprintf(fp, " ");
887 if ( elt->children[0]->data != NULL )
888 fprintf(fp, "<%s>%s</%s>\n", elt->type, elt->children[0]->data, elt->type);
889 else
890 fprintf(fp, "<%s/>\n", elt->type);
891 } else if ( strcmp(COIN_XML_CDATA_TYPE, elt->type) != 0 ) {
892 for ( i = 0; i < indent; i++ ) fprintf(fp, " ");
893 fprintf(fp, "<%s>\n", elt->type);
894 const int children = cc_xml_elt_get_num_children(elt);
895 for ( i = 0; i < children; i++ ) {
896 cc_xml_elt * child = cc_xml_elt_get_child(elt, i);
897 cc_xml_elt_dump_to_file(child, indent + 2, fp);
898 }
899 for ( i = 0; i < indent; i++ ) fprintf(fp, " ");
900 fprintf(fp, "</%s>\n", elt->type);
901 } else if ( elt->data != NULL ) {
902 for ( i = 0; i < indent; i++ ) fprintf(fp, " ");
903 fprintf(fp, "%s\n", elt->data);
904 }
905 }
906
907 // *************************************************************************
908
909 /*!
910 \fn cc_xml_elt * cc_xml_elt_get_traversal_next(cc_xml_elt * root, cc_xml_elt * here)
911
912 Utility function for flattening recursive traversals to do-while loops.
913
914 \ingroup cc_xml_elt
915 */
916
917 cc_xml_elt *
cc_xml_elt_get_traversal_next(const cc_xml_elt * root,cc_xml_elt * here)918 cc_xml_elt_get_traversal_next(const cc_xml_elt * root, cc_xml_elt * here)
919 {
920 assert(root && here);
921
922 // first traverse into children - but ignore any "cdata" element...
923 if (cc_xml_elt_get_num_children(here) > 0) {
924 // Check if we can find a child that is not cdata..
925 for (int i = 0; i < cc_xml_elt_get_num_children(here); ++i) {
926 if (strcmp(COIN_XML_CDATA_TYPE, here->children[i]->type) != 0) {
927 return cc_xml_elt_get_child(here, i);
928 }
929 }
930 }
931
932 // if we're here then 'here' has no children (except possibly cdata).
933 do {
934 cc_xml_elt * parent = cc_xml_elt_get_parent(here);
935 if (parent == NULL) return NULL; // here is the root
936 int idx = cc_xml_elt_get_child_index(parent, here);
937 // if 'here' was the last child then set here as parent..
938 if (idx == (cc_xml_elt_get_num_children(parent) - 1)) {
939 here = parent;
940 // if we're back to root, simply quit..
941 if (here == root) return NULL;
942 } else {
943 // there is more children than 'here' left, find someone thats not "cdata".
944 do {
945 ++idx;
946 here = cc_xml_elt_get_child(parent, idx);
947
948 // return this element if it's not a "cdata" element.
949 if (strcmp(COIN_XML_CDATA_TYPE, here->type) != 0) return here;
950
951 } while (idx != (cc_xml_elt_get_num_children(parent) - 1));
952 }
953 } while (TRUE);
954
955 return NULL;
956 } // cc_xml_elt_get_traversal_next()
957
958 // *************************************************************************
959
960 const cc_xml_elt *
cc_xml_elt_find(const cc_xml_elt * root,const cc_xml_path * path)961 cc_xml_elt_find(const cc_xml_elt * root, const cc_xml_path * path)
962 {
963 assert(root && path);
964 cc_xml_elt * elt = const_cast<cc_xml_elt *>(root);
965 while ( (elt != NULL) && (!cc_xml_path_match_p(path, elt)) ) {
966 elt = cc_xml_elt_get_traversal_next(root, elt);
967 }
968 return elt;
969 } // cc_xml_elt_find()
970
971 const cc_xml_elt *
cc_xml_elt_find_next(const cc_xml_elt * root,cc_xml_elt * from,cc_xml_path * path)972 cc_xml_elt_find_next(const cc_xml_elt * root, cc_xml_elt * from, cc_xml_path * path)
973 {
974 assert(root && from && path);
975 cc_xml_elt * elt = from;
976 do {
977 elt = cc_xml_elt_get_traversal_next(root, elt);
978 } while ( (elt != NULL) && (!cc_xml_path_match_p(path, elt)) );
979 return elt;
980 } // cc_xml_elt_find_next()
981
982 // *************************************************************************
983
984 cc_xml_elt *
cc_xml_elt_create_x(cc_xml_elt * from,cc_xml_path * path)985 cc_xml_elt_create_x(cc_xml_elt * from, cc_xml_path * path)
986 {
987 assert(from && path);
988 cc_xml_elt * current = from;
989 const char * type;
990 int idx;
991 int length = cc_xml_path_get_length(path);
992 int i;
993 for ( i = 0; i < length; i++ ) {
994 int lastpos = -1, lastidx = -1;
995 type = cc_xml_path_get_type(path, i);
996 idx = cc_xml_path_get_index(path, i);
997 if ( current->children.getLength() ) {
998 int child;
999 for ( child = 0; child < current->children.getLength(); child++ ) {
1000 if ( strcmp(type, cc_xml_elt_get_type(current->children[child])) == 0 ) {
1001 lastpos = child;
1002 lastidx += 1;
1003 if ( idx == -1 || idx == lastidx ) {
1004 // match - enter this child
1005 goto cont;
1006 }
1007 }
1008 }
1009 // no match - need to create for idx idx
1010 if ( idx == -1 ) idx = lastidx + 1;
1011 while ( lastidx < idx ) {
1012 // insert empty children after lastpos - enter last one
1013 lastidx += 1;
1014 lastpos += 1;
1015 cc_xml_elt * elt = cc_xml_elt_new();
1016 cc_xml_elt_set_type_x(elt, type);
1017 if ( lastpos == 0 ) lastpos = cc_xml_elt_get_num_children(current);
1018 cc_xml_elt_insert_child_x(current, elt, lastpos);
1019 }
1020 } else {
1021 // no children - need to create them
1022 lastpos = -1;
1023 lastidx = -1;
1024 if ( idx == -1 ) idx = 0;
1025 while ( lastidx < idx ) {
1026 cc_xml_elt * newelt = cc_xml_elt_new();
1027 cc_xml_elt_set_type_x(newelt, type);
1028 cc_xml_elt_add_child_x(current, newelt);
1029 lastidx += 1;
1030 lastpos += 1;
1031 }
1032 }
1033 cont:
1034 current = current->children[lastpos];
1035 }
1036 return current;
1037 } // cc_xml_elt_create_x()
1038
1039 // *************************************************************************
1040
1041 size_t
cc_xml_elt_calculate_size(const cc_xml_elt * elt,int indent,int indentincrement)1042 cc_xml_elt_calculate_size(const cc_xml_elt * elt, int indent, int indentincrement)
1043 {
1044 assert(elt);
1045 assert(elt->type);
1046 assert(indent >= 0);
1047
1048 size_t bytes = 0;
1049
1050 // macro to advance a number of bytes
1051 #define ADVANCE_NUM_BYTES(num) \
1052 do { bytes += num; } while (0)
1053
1054 // macro to advance a number of blanks (indentation)
1055 #define ADVANCE_NUM_SPACES(num) \
1056 do { bytes += num; } while (0)
1057
1058 // macro to increment bytecount for string literal
1059 #define ADVANCE_STRING_LITERAL(str) \
1060 do { static const char strobj[] = str; bytes += (sizeof(strobj) - 1); } while (0)
1061
1062 // macro to increment bytecount for run-time string
1063 #define ADVANCE_STRING(str) \
1064 do { bytes += strlen(str); } while (0)
1065
1066 // duplicate block - see cc_xml_elt_write_to_buffer()
1067 if (elt->type && strcmp(elt->type, COIN_XML_CDATA_TYPE) == 0) {
1068 // this is a leaf element character data container
1069 ADVANCE_STRING(elt->cdata);
1070 } else {
1071 ADVANCE_NUM_SPACES(indent);
1072 ADVANCE_STRING_LITERAL("<");
1073 ADVANCE_STRING(elt->type);
1074
1075 int c;
1076 const int numattributes = elt->attributes.getLength();
1077 for (c = 0; c < numattributes; ++c) {
1078 ADVANCE_STRING_LITERAL(" ");
1079 ADVANCE_NUM_BYTES(cc_xml_attr_calculate_size(elt->attributes[c]));
1080 }
1081
1082 const int numchildren = elt->children.getLength();
1083 if (numchildren == 0) { // close element directly
1084 ADVANCE_STRING_LITERAL("/>\n");
1085 } else if ((numchildren == 1) &&
1086 (strcmp(cc_xml_elt_get_type(elt->children[0]), COIN_XML_CDATA_TYPE) == 0)) {
1087 ADVANCE_STRING_LITERAL(">");
1088 ADVANCE_STRING(cc_xml_elt_get_cdata(elt->children[0]));
1089 ADVANCE_STRING_LITERAL("</");
1090 ADVANCE_STRING(elt->type);
1091 ADVANCE_STRING_LITERAL(">\n");
1092 } else {
1093 ADVANCE_STRING_LITERAL(">\n");
1094 for (c = 0; c < numchildren; ++c) {
1095 ADVANCE_NUM_BYTES(cc_xml_elt_calculate_size(elt->children[c], indent + indentincrement, indentincrement));
1096 }
1097 ADVANCE_NUM_SPACES(indent);
1098 ADVANCE_STRING_LITERAL("</");
1099 ADVANCE_STRING(elt->type);
1100 ADVANCE_STRING_LITERAL(">\n");
1101 }
1102 }
1103
1104 #undef ADVANCE_NUM_BYTES
1105 #undef ADVANCE_NUM_SPACES
1106 #undef ADVANCE_STRING
1107 #undef ADVANCE_STRING_LITERAL
1108
1109 return bytes;
1110 }
1111
1112 size_t
cc_xml_elt_write_to_buffer(const cc_xml_elt * elt,char * buffer,size_t bufsize,int indent,int indentincrement)1113 cc_xml_elt_write_to_buffer(const cc_xml_elt * elt, char * buffer, size_t bufsize, int indent, int indentincrement)
1114 {
1115 assert(elt);
1116 assert(elt->type);
1117 assert(indent >= 0);
1118
1119 const size_t assumed = cc_xml_elt_calculate_size(elt, indent, indentincrement);
1120
1121 size_t bytes = 0;
1122 char * hereptr = buffer;
1123 size_t bytesleft = bufsize;
1124
1125 // macro to advance buffer pointer and decrement bytesleft count
1126 #define ADVANCE_NUM_BYTES(len) \
1127 do { const int length = (len); \
1128 bytes += length; \
1129 hereptr += length; \
1130 bytesleft -= length; } while (0)
1131
1132 // macro to copy in a string literal and advance pointers
1133 #define ADVANCE_STRING_LITERAL(str) \
1134 do { static const char strobj[] = str; \
1135 const int strlength = (sizeof(strobj) - 1); \
1136 strncpy(hereptr, strobj, strlength); \
1137 ADVANCE_NUM_BYTES(strlength); } while (0)
1138
1139 // macro to copy in a run-time string and advance pointers
1140 #define ADVANCE_STRING(str) \
1141 do { const int strlength = static_cast<int>(strlen(str)); \
1142 strncpy(hereptr, str, strlength); \
1143 ADVANCE_NUM_BYTES(strlength); } while (0)
1144
1145 // macro to advance a number of blanks (indentation)
1146 #define ADVANCE_NUM_SPACES(num) \
1147 do { const int strlength = (num); \
1148 memset(hereptr, ' ', strlength); \
1149 ADVANCE_NUM_BYTES(strlength); } while (0)
1150
1151 // ***********************************************************************
1152 // almost duplicate block - see cc_xml_elt_calculate_size()
1153 if (elt->type && strcmp(elt->type, COIN_XML_CDATA_TYPE) == 0) {
1154 // this is a leaf element character data container
1155 ADVANCE_STRING(elt->cdata);
1156 } else {
1157 ADVANCE_NUM_SPACES(indent);
1158 ADVANCE_STRING_LITERAL("<");
1159 ADVANCE_STRING(elt->type);
1160
1161 int c;
1162 const int numattributes = elt->attributes.getLength();
1163 for (c = 0; c < numattributes; ++c) {
1164 ADVANCE_STRING_LITERAL(" ");
1165 ADVANCE_NUM_BYTES(cc_xml_attr_write_to_buffer(elt->attributes[c], hereptr, bytesleft));
1166 }
1167
1168 const int numchildren = elt->children.getLength();
1169 if (numchildren == 0) { // close element directly
1170 ADVANCE_STRING_LITERAL("/>\n");
1171 } else if ((numchildren == 1) &&
1172 (strcmp(cc_xml_elt_get_type(elt->children[0]), COIN_XML_CDATA_TYPE) == 0)) {
1173 ADVANCE_STRING_LITERAL(">");
1174 ADVANCE_STRING(cc_xml_elt_get_cdata(elt->children[0]));
1175 ADVANCE_STRING_LITERAL("</");
1176 ADVANCE_STRING(elt->type);
1177 ADVANCE_STRING_LITERAL(">\n");
1178 } else {
1179 ADVANCE_STRING_LITERAL(">\n");
1180 for (c = 0; c < numchildren; ++c) {
1181 ADVANCE_NUM_BYTES(cc_xml_elt_write_to_buffer(elt->children[c], hereptr, bytesleft, indent + indentincrement, indentincrement));
1182 }
1183 ADVANCE_NUM_SPACES(indent);
1184 ADVANCE_STRING_LITERAL("</");
1185 ADVANCE_STRING(elt->type);
1186 ADVANCE_STRING_LITERAL(">\n");
1187 }
1188 }
1189
1190 #undef ADVANCE_NUM_BYTES
1191 #undef ADVANCE_NUM_SPACES
1192 #undef ADVANCE_STRING
1193 #undef ADVANCE_STRING_LITERAL
1194
1195 assert(bytes == assumed);
1196 return bytes;
1197 }
1198
1199 // *************************************************************************
1200