File: dsrxmld.cc

package info (click to toggle)
dcmtk 3.6.9-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 95,648 kB
  • sloc: ansic: 426,874; cpp: 318,177; makefile: 6,401; sh: 4,341; yacc: 1,026; xml: 482; lex: 321; perl: 277
file content (767 lines) | stat: -rw-r--r-- 25,368 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
/*
 *
 *  Copyright (C) 2003-2024, OFFIS e.V.
 *  All rights reserved.  See COPYRIGHT file for details.
 *
 *  This software and supporting documentation were developed by
 *
 *    OFFIS e.V.
 *    R&D Division Health
 *    Escherweg 2
 *    D-26121 Oldenburg, Germany
 *
 *
 *  Module: dcmsr
 *
 *  Author: Joerg Riesmeier
 *
 *  Purpose:
 *    classes: DSRXMLDocument
 *
 */


#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */

#include "dcmtk/dcmsr/dsrxmld.h"

#ifdef WITH_LIBXML
#include <libxml/xmlversion.h>

#ifdef LIBXML_SCHEMAS_ENABLED
#include <libxml/xmlschemas.h>
#endif /* LIBXML_SCHEMAS_ENABLED */

// This function is also used in xml2dcm, try to stay in sync!
#if defined(HAVE_VSNPRINTF) && defined(HAVE_PROTOTYPE_VSNPRINTF)
extern "C" void errorFunction(void * ctx, const char *msg, ...)
#else
extern "C" void errorFunction(void * /* ctx */, const char *msg, ...)
#endif
{
    OFLogger xmlLogger = OFLog::getLogger("dcmtk.dcmsr.libxml");

    if (!xmlLogger.isEnabledFor(OFLogger::DEBUG_LOG_LEVEL))
        return;

#if defined(HAVE_VSNPRINTF) && defined(HAVE_PROTOTYPE_VSNPRINTF)
    // libxml calls us multiple times for one line of log output which would
    // result in garbled output. To avoid this, we buffer the output in a local
    // string in the caller which we get through our 'ctx' parameter. Then, we
    // output this string on one go when we receive a newline.
    OFString &buffer = *OFstatic_cast(OFString*, ctx);
    va_list ap;
    char buf[1024];

    va_start(ap, msg);
#ifdef HAVE_PROTOTYPE_STD__VSNPRINTF
    std::vsnprintf(buf, 1024, msg, ap);
#else
    vsnprintf(buf, 1024, msg, ap);
#endif
    va_end(ap);

    // Since we can't do anything about a too small buffer for vsnprintf(), we
    // ignore it. But we do make sure the buffer is null-terminated!
    buf[1023] = '\0';
    buffer += buf;

    // If there is a full line in the buffer...
    size_t pos = buffer.find('\n');
    while (pos != OFString_npos)
    {
        // ..output it and remove it from the buffer
        OFLOG_DEBUG(xmlLogger, buffer.substr(0, pos));
        buffer.erase(0, pos + 1);

        pos = buffer.find('\n');
    }
#else
    // No vsnprint, but at least vfprintf. Output the messages directly to stderr.
    va_list ap;
    va_start(ap, msg);
#ifdef HAVE_PROTOTYPE_STD__VFPRINTF
    std::vfprintf(stderr, msg, ap);
#else
    vfprintf(stderr, msg, ap);
#endif
    va_end(ap);
#endif

#ifndef HAVE_VSNPRINTF
    // Only the vsnprintf() branch above uses 'buffer' which means the compiler
    // would warn about an unused variable if HAVE_VSNPRINTF is undefined.
    buffer += "";
#endif
}

#endif /* WITH_LIBXML */


/* ------------------------ */


DSRXMLDocument::DSRXMLDocument()
  : Document(NULL),
    EncodingHandler(NULL)
{
}


DSRXMLDocument::~DSRXMLDocument()
{
    clear();
}


void DSRXMLDocument::clear()
{
#ifdef WITH_LIBXML
    /* free allocated memory */
    xmlFreeDoc(Document);
#endif
    /* remove all references to libxml structures */
    Document = NULL;
    EncodingHandler = NULL;
}


OFBool DSRXMLDocument::valid() const
{
    return (Document != NULL);
}


#ifdef WITH_LIBXML
OFCondition DSRXMLDocument::read(const OFString &filename,
                                 const size_t flags)
{
    OFCondition result = SR_EC_InvalidDocument;
    /* temporary buffer needed for errorFunction - more detailed explanation there */
    OFString tmpErrorString;
    /* first remove any possibly existing document from memory */
    clear();

#if LIBXML_VERSION < 20703
    /*
     * the following settings have been deprecated in newer versions of libxml,
     * or they are not needed any more:
     */

    /* do not substitute entities (other than the standard ones) */
    xmlSubstituteEntitiesDefault(0);
    /* add line number to debug messages */
    xmlLineNumbersDefault(1);
    /* enable libxml warnings and error messages */
    xmlGetWarningsDefaultValue = 1;
#endif

    xmlSetGenericErrorFunc(&tmpErrorString, errorFunction);

    xmlGenericError(xmlGenericErrorContext, "--- libxml parsing ------\n");
    /* build an XML tree from the given file */
#if LIBXML_VERSION < 20703
    /* xmlParseFile() is deprecated in newer versions of libxml */
    Document = xmlParseFile(filename.c_str());
#else
    Document = xmlReadFile(filename.c_str(), NULL /*encoding*/, XML_PARSE_NONET);
#endif
    if (Document != NULL)
    {
        OFBool isValid = OFTrue;
        /* validate Schema */
        if (flags & XF_validateSchema)
        {
            xmlGenericError(xmlGenericErrorContext, "--- libxml validating ---\n");
#ifdef LIBXML_SCHEMAS_ENABLED
#if 1
            /* create context for Schema validation */
            xmlSchemaParserCtxtPtr context = xmlSchemaNewParserCtxt(DCMSR_XML_XSD_FILE);
            xmlSchemaSetParserErrors(context, errorFunction, errorFunction, &tmpErrorString);
            /* parse Schema file */
            xmlSchemaPtr schema = xmlSchemaParse(context);
            if (schema != NULL)
            {
                xmlSchemaValidCtxtPtr validCtx = xmlSchemaNewValidCtxt(schema);
                xmlSchemaSetValidErrors(validCtx, errorFunction, errorFunction, &tmpErrorString);
                /* validate the document */
                isValid = (xmlSchemaValidateDoc(validCtx, Document) == 0);
                xmlSchemaFreeValidCtxt(validCtx);
                xmlSchemaFree(schema);
            } else
                xmlGenericError(xmlGenericErrorContext, "error: failed to compile schema \"%s\"\n", DCMSR_XML_XSD_FILE);
            xmlSchemaFreeParserCtxt(context);
#else // 0
            /* ### the following code fragment is not yet working! ### */

            /* create context for Schema validation */
            xmlSchemaValidCtxtPtr context = xmlSchemaNewValidCtxt(NULL);
            xmlSchemaSetValidErrors(context, errorFunction, errorFunction, &tmpErrorString);
            /* validate the document */
            isValid = (xmlSchemaValidateDoc(context, Document) == 0);
#endif
#else /* LIBXML_SCHEMAS_ENABLED */
            xmlGenericError(xmlGenericErrorContext, "no support for XML Schema\n");
#endif
        }
        xmlGenericError(xmlGenericErrorContext, "-------------------------\n");
        /* check whether the document is of the right kind */
        xmlNodePtr current = xmlDocGetRootElement(Document);
        if ((current != NULL) && isValid)
        {
            /* check namespace declaration (if required) */
            if (!(flags & XF_useDcmsrNamespace) ||
                (xmlSearchNsByHref(Document, current, OFreinterpret_cast(const xmlChar *, DCMSR_XML_NAMESPACE_URI)) != NULL))
            {
                result = EC_Normal;
            } else
                DCMSR_ERROR("Document has wrong type, dcmsr namespace not found");
        } else {
            if (isValid)
                DCMSR_ERROR("Document is empty");
            else
                DCMSR_ERROR("Document does not validate");
        }
    } else {
        xmlGenericError(xmlGenericErrorContext, "-------------------------\n");
        DCMSR_ERROR("Could not parse document");
    }

    /* Reset to default function because we used a local string as context for
     * the error function.
     */
    xmlSetGenericErrorFunc(NULL, NULL);
    return result;
}
#else /* WITH_LIBXML */
OFCondition DSRXMLDocument::read(const OFString &,
                                 const size_t)
{
    return EC_IllegalCall;
}
#endif


OFBool DSRXMLDocument::encodingHandlerValid() const
{
    return (EncodingHandler != NULL);
}


#ifdef WITH_LIBXML
OFCondition DSRXMLDocument::setEncodingHandler(const char *charset)
{
    OFCondition result = EC_IllegalParameter;
    if ((charset != NULL) && (strlen(charset) > 0))
    {
        /* find appropriate encoding handler */
        EncodingHandler = xmlFindCharEncodingHandler(charset);
        if (EncodingHandler != NULL)
            result = EC_Normal;
    }
    return result;
}
#else /* WITH_LIBXML */
OFCondition DSRXMLDocument::setEncodingHandler(const char *)
{
    return EC_IllegalCall;
}
#endif


DSRXMLCursor DSRXMLDocument::getRootNode() const
{
    DSRXMLCursor cursor;
#ifdef WITH_LIBXML
    /* set cursor to root node */
    cursor.Node = xmlDocGetRootElement(Document);
#endif
    return cursor;
}


#ifdef WITH_LIBXML
DSRXMLCursor DSRXMLDocument::getNamedNode(const DSRXMLCursor &cursor,
                                          const char *name,
                                          const OFBool required) const
{
    DSRXMLCursor result;
    /* check whether given name is valid */
    if ((name != NULL) && (strlen(name) > 0))
    {
        xmlNodePtr current = cursor.Node;
        /* iterate over all nodes */
        while (current != NULL)
        {
            /* ignore blank (empty or whitespace only) nodes */
            while ((current != NULL) && xmlIsBlankNode(current))
                current = current->next;
            if (current != NULL)
            {
                /* check whether node has expected name */
                if (xmlStrcmp(current->name, OFreinterpret_cast(const xmlChar *, name)) == 0)
                    break;
                /* proceed with next node */
                current = current->next;
            }
        }
        if (current == NULL)
        {
            /* report error message */
            if (required)
            {
                OFString tmpString;
                DCMSR_ERROR("Document of the wrong type, '" << name
                    << "' expected at " << getFullNodePath(cursor, tmpString, OFTrue /*omitCurrent*/));
            }
        } else {
            /* return new node position */
            result.Node = current;
        }
    }
    return result;
}
#else /* WITH_LIBXML */
DSRXMLCursor DSRXMLDocument::getNamedNode(const DSRXMLCursor &,
                                          const char *,
                                          const OFBool) const
{
    DSRXMLCursor result;
    return result;
}
#endif


#ifdef WITH_LIBXML
DSRXMLCursor DSRXMLDocument::getNamedChildNode(const DSRXMLCursor &cursor,
                                               const char *name,
                                               const OFBool required) const
{
    DSRXMLCursor result;
    const DSRXMLCursor childCursor = cursor.getChild();
    /* check whether child node is valid */
    if (childCursor.valid())
        result = getNamedNode(childCursor, name, required);
    else if (required)
    {
        OFString tmpString;
        DCMSR_ERROR("Document of the wrong type, '" << name
            << "' expected at " << getFullNodePath(cursor, tmpString, OFFalse /*omitCurrent*/));
    }
    return result;
}
#else /* WITH_LIBXML */
DSRXMLCursor DSRXMLDocument::getNamedChildNode(const DSRXMLCursor &,
                                               const char *,
                                               const OFBool) const
{
    DSRXMLCursor result;
    return result;
}
#endif


#ifdef WITH_LIBXML
OFBool DSRXMLDocument::matchNode(const DSRXMLCursor &cursor,
                                 const char *name) const
{
    OFBool result = OFFalse;
    if (cursor.Node != NULL)
    {
        /* check whether node name matches */
        if ((name != NULL) && (strlen(name) > 0))
            result = (xmlStrcmp(cursor.Node->name, OFreinterpret_cast(const xmlChar *, name)) == 0);
    }
    return result;
}
#else /* WITH_LIBXML */
OFBool DSRXMLDocument::matchNode(const DSRXMLCursor &,
                                 const char *) const
{
    return OFFalse;
}
#endif


#ifdef WITH_LIBXML
OFCondition DSRXMLDocument::checkNode(const DSRXMLCursor &cursor,
                                      const char *name) const
{
    OFCondition result = EC_IllegalParameter;
    /* check whether parameters are valid */
    if ((name != NULL) && (strlen(name) > 0))
    {
        /* check whether node is valid at all */
        if (cursor.Node != NULL)
        {
            /* check whether node has expected name */
            if (xmlStrcmp(cursor.Node->name, OFreinterpret_cast(const xmlChar *, name)) != 0)
            {
                DCMSR_ERROR("Document of the wrong type, was '" << OFreinterpret_cast(const char *, cursor.Node->name)
                    << "', '" << name << "' expected");
                result = SR_EC_InvalidDocument;
            } else
                result = EC_Normal;
        } else {
            DCMSR_ERROR("Document of the wrong type, '" << name << "' expected");
            result = EC_IllegalParameter;
        }
    }
    return result;
}
#else /* WITH_LIBXML */
OFCondition DSRXMLDocument::checkNode(const DSRXMLCursor &,
                                      const char *) const
{
    return EC_IllegalCall;
}
#endif


#ifdef WITH_LIBXML
OFBool DSRXMLDocument::convertUtf8ToCharset(const xmlChar *fromString,
                                            OFString &toString) const
{
    OFBool result = OFFalse;
    if (EncodingHandler != NULL)
    {
        /* prepare input/output buffers */
        xmlBufferPtr fromBuffer = xmlBufferCreate();
        xmlBufferPtr toBuffer = xmlBufferCreate();
        xmlBufferCat(fromBuffer, fromString);
        /* convert character encoding of given string */
        result = (xmlCharEncOutFunc(EncodingHandler, toBuffer, fromBuffer) >= 0);
        if (result)
            toString = OFreinterpret_cast(const char *, xmlBufferContent(toBuffer));
        /* free allocated memory */
        xmlBufferFree(toBuffer);
        xmlBufferFree(fromBuffer);
    }
    return result;
}
#else /* WITH_LIBXML */
OFBool DSRXMLDocument::convertUtf8ToCharset(const xmlChar *,
                                            OFString &) const
{
    return OFFalse;
}
#endif


#ifdef WITH_LIBXML
OFBool DSRXMLDocument::hasAttribute(const DSRXMLCursor &cursor,
                                    const char *name) const
{
    OFBool result = OFFalse;
    if (cursor.Node != NULL)
    {
        /* check whether attribute exists */
        if ((name != NULL) && (strlen(name) > 0))
            result = (xmlHasProp(cursor.Node, OFreinterpret_cast(const xmlChar *, name)) != NULL);
    }
    return result;
}
#else /* WITH_LIBXML */
OFBool DSRXMLDocument::hasAttribute(const DSRXMLCursor &,
                                    const char *) const
{
    return OFFalse;
}
#endif


#ifdef WITH_LIBXML
OFString &DSRXMLDocument::getStringFromAttribute(const DSRXMLCursor &cursor,
                                                 OFString &stringValue,
                                                 const char *name,
                                                 const OFBool encoding,
                                                 const OFBool required) const
{
    /* always clear result string */
    stringValue.clear();
    /* check whether parameters are valid */
    if ((cursor.Node != NULL) && (name != NULL) && (strlen(name) > 0))
    {
        /* get the XML attribute value */
        xmlChar *attrVal = xmlGetProp(cursor.Node, OFreinterpret_cast(const xmlChar *, name));
        if ((attrVal != NULL) && (xmlStrlen(attrVal) > 0))
        {
            /* put value to the result variable */
            if (!encoding || !convertUtf8ToCharset(attrVal, stringValue))
                stringValue = OFreinterpret_cast(const char *, attrVal);
        } else if (required)
            printMissingAttributeError(cursor, name);
        /* free allocated memory */
        xmlFree(attrVal);
    }
    return stringValue;
}
#else /* WITH_LIBXML */
OFString &DSRXMLDocument::getStringFromAttribute(const DSRXMLCursor &,
                                                 OFString &stringValue,
                                                 const char *,
                                                 const OFBool,
                                                 const OFBool) const
{
    stringValue.clear();
    return stringValue;
}
#endif


#ifdef WITH_LIBXML
OFCondition DSRXMLDocument::getElementFromAttribute(const DSRXMLCursor &cursor,
                                                    DcmElement &delem,
                                                    const char *name,
                                                    const OFBool encoding,
                                                    const OFBool required) const
{
    OFCondition result = SR_EC_InvalidDocument;
    /* check whether parameters are valid */
    if ((cursor.Node != NULL) && (name != NULL) && (strlen(name) > 0))
    {
        /* get the XML attribute value */
        xmlChar *attrVal = xmlGetProp(cursor.Node, OFreinterpret_cast(const xmlChar *, name));
        if ((attrVal != NULL) && (xmlStrlen(attrVal) > 0))
        {
            OFString attrStr;
            /* put value to DICOM element */
            if (encoding && convertUtf8ToCharset(attrVal, attrStr))
                result = delem.putString(attrStr.c_str());
            else
                result = delem.putString(OFreinterpret_cast(const char *, attrVal));
        } else if (required)
            printMissingAttributeError(cursor, name);
        /* free allocated memory */
        xmlFree(attrVal);
    }
    return result;
}
#else /* WITH_LIBXML */
OFCondition DSRXMLDocument::getElementFromAttribute(const DSRXMLCursor &,
                                                    DcmElement &,
                                                    const char *,
                                                    const OFBool,
                                                    const OFBool) const
{
    return EC_IllegalCall;
}
#endif


#ifdef WITH_LIBXML
OFString &DSRXMLDocument::getStringFromNodeContent(const DSRXMLCursor &cursor,
                                                   OFString &stringValue,
                                                   const char *name,
                                                   const OFBool encoding,
                                                   const OFBool clearString) const
{
    if (clearString)
        stringValue.clear();
    if (cursor.Node != NULL)
    {
        /* compare element name if required */
        if ((name == NULL) || (xmlStrcmp(cursor.Node->name, OFreinterpret_cast(const xmlChar *, name)) == 0))
        {

            /* get the XML node content */
            xmlChar *elemVal = xmlNodeGetContent(cursor.Node);
            /* put value to the result variable */
            if (!encoding || !convertUtf8ToCharset(elemVal, stringValue))
                stringValue = OFreinterpret_cast(const char *, elemVal);
            /* free allocated memory */
            xmlFree(elemVal);
        }
    }
    return stringValue;
}
#else /* WITH_LIBXML */
OFString &DSRXMLDocument::getStringFromNodeContent(const DSRXMLCursor &,
                                                   OFString &stringValue,
                                                   const char *,
                                                   const OFBool,
                                                   const OFBool) const
{
    stringValue.clear();
    return stringValue;
}
#endif


#ifdef WITH_LIBXML
OFCondition DSRXMLDocument::getElementFromNodeContent(const DSRXMLCursor &cursor,
                                                      DcmElement &delem,
                                                      const char *name,
                                                      const OFBool encoding) const
{
    OFCondition result = SR_EC_InvalidDocument;
    if (cursor.Node != NULL)
    {
        /* compare element name if required */
        if ((name == NULL) || (xmlStrcmp(cursor.Node->name, OFreinterpret_cast(const xmlChar *, name)) == 0))
        {
            OFString elemStr;
            /* get the XML node content */
            xmlChar *elemVal = xmlNodeGetContent(cursor.Node);
            /* put value to DICOM element */
            if (encoding && convertUtf8ToCharset(elemVal, elemStr))
                result = delem.putString(elemStr.c_str());
            else
                result = delem.putString(OFreinterpret_cast(const char *, elemVal));
            /* free allocated memory */
            xmlFree(elemVal);
        }
    }
    return result;
}
#else /* WITH_LIBXML */
OFCondition DSRXMLDocument::getElementFromNodeContent(const DSRXMLCursor &,
                                                      DcmElement &,
                                                      const char *,
                                                      const OFBool) const
{
    return EC_IllegalCall;
}
#endif


#ifdef WITH_LIBXML
OFString &DSRXMLDocument::getFullNodePath(const DSRXMLCursor &cursor,
                                          OFString &stringValue,
                                          const OFBool omitCurrent)
{
    stringValue.clear();
    if (cursor.Node != NULL)
    {
        OFString tmpString;
        xmlNodePtr current = cursor.Node;
        if (!omitCurrent)
            stringValue = OFSTRING_GUARD(OFreinterpret_cast(const char *, current->name));
        /* follow path to parent nodes */
        while (current->parent != NULL)
        {
            current = current->parent;
            tmpString = stringValue;
            stringValue = OFSTRING_GUARD(OFreinterpret_cast(const char *, current->name));
            if (!tmpString.empty())
            {
                stringValue += '/';
                stringValue += tmpString;
            }
        }
        /* avoid empty return value */
        if (stringValue.empty() && omitCurrent)
            stringValue = '.';
    } else
        stringValue = "<invalid>";
    return stringValue;
}
#else /* WITH_LIBXML */
OFString &DSRXMLDocument::getFullNodePath(const DSRXMLCursor &,
                                          OFString &stringValue,
                                          const OFBool)
{
    stringValue.clear();
    return stringValue;
}
#endif


#ifdef WITH_LIBXML
DSRTypes::E_ValueType DSRXMLDocument::getValueTypeFromNode(const DSRXMLCursor &cursor) const
{
    E_ValueType valueType = VT_invalid;
    if (cursor.valid())
    {
        if (xmlStrcmp(cursor.Node->name, OFreinterpret_cast(const xmlChar *, "item")) == 0)
        {
            /* check for "ref" attribute */
            if (xmlHasProp(cursor.Node, OFreinterpret_cast(const xmlChar *, "ref")))
                valueType = DSRTypes::VT_byReference;
            else {
                /* get the XML attribute value */
                xmlChar *attrVal = xmlGetProp(cursor.Node, OFreinterpret_cast(const xmlChar *, "valType"));
                /* try to convert attribute value to SR value type */
                valueType = definedTermToValueType(OFreinterpret_cast(const char *, attrVal));
                /* free allocated memory */
                xmlFree(attrVal);
            }
        } else {
            /* try to convert tag name to SR value type */
            valueType = xmlTagNameToValueType(OFreinterpret_cast(const char *, cursor.Node->name));
        }
    }
    return valueType;
}
#else /* WITH_LIBXML */
DSRTypes::E_ValueType DSRXMLDocument::getValueTypeFromNode(const DSRXMLCursor &) const
{
    return VT_invalid;
}
#endif


DSRTypes::E_RelationshipType DSRXMLDocument::getRelationshipTypeFromNode(const DSRXMLCursor &cursor) const
{
    E_RelationshipType relationshipType = RT_invalid;
    if (cursor.valid())
    {
        OFString tmpString;
        /* get the XML attribute value (if present) */
        if (hasAttribute(cursor, "relType"))
        {
            /* try to convert attribute value to SR relationship type */
            relationshipType = definedTermToRelationshipType(getStringFromAttribute(cursor, tmpString, "relType"));
        } else {
            const DSRXMLCursor childCursor = getNamedNode(cursor.getChild(), "relationship");
            /* try to convert content of "relationship" tag to SR relationship type */
            if (childCursor.valid())
                relationshipType = definedTermToRelationshipType(getStringFromNodeContent(childCursor, tmpString));
        }
    }
    return relationshipType;
}


void DSRXMLDocument::printUnexpectedNodeWarning(const DSRXMLCursor &cursor) const
{
    /* report warning message */
    OFString tmpString;
    DCMSR_WARN("Unexpected node '" << getFullNodePath(cursor, tmpString) << "', skipping");
}


void DSRXMLDocument::printMissingAttributeWarning(const DSRXMLCursor &cursor,
                                                  const char *name) const
{
    /* report warning message */
    if (name != NULL)
    {
        OFString tmpString;
        DCMSR_WARN("XML attribute '" << name << "' missing/empty in " << getFullNodePath(cursor, tmpString));
    }
}


void DSRXMLDocument::printMissingAttributeError(const DSRXMLCursor &cursor,
                                                const char *name) const
{
    /* report error message */
    if (name != NULL)
    {
        OFString tmpString;
        DCMSR_ERROR("XML attribute '" << name << "' missing/empty in " << getFullNodePath(cursor, tmpString));
    }
}


void DSRXMLDocument::printGeneralNodeError(const DSRXMLCursor &cursor,
                                           const OFCondition &result) const
{
    /* report error message */
    if (result.bad())
    {
        OFString tmpString;
        DCMSR_ERROR("Parsing node " << getFullNodePath(cursor, tmpString) << " (" << result.text() << ")");
    }
}