Coverage Report

Created: 2026-06-05 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dcmtk/dcmdata/libsrc/dcvrat.cc
Line
Count
Source
1
/*
2
 *
3
 *  Copyright (C) 1994-2024, OFFIS e.V.
4
 *  All rights reserved.  See COPYRIGHT file for details.
5
 *
6
 *  This software and supporting documentation were developed by
7
 *
8
 *    OFFIS e.V.
9
 *    R&D Division Health
10
 *    Escherweg 2
11
 *    D-26121 Oldenburg, Germany
12
 *
13
 *
14
 *  Module:  dcmdata
15
 *
16
 *  Author:  Gerd Ehlers, Andreas Barth
17
 *
18
 *  Purpose: Implementation of class DcmAttributeTag
19
 *
20
 */
21
22
23
#include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
24
#include "dcmtk/dcmdata/dcvrat.h"
25
#include "dcmtk/ofstd/ofstd.h"
26
#include "dcmtk/ofstd/ofstream.h"
27
#include "dcmtk/dcmdata/dcjson.h"
28
29
// ********************************
30
31
32
DcmAttributeTag::DcmAttributeTag(const DcmTag &tag)
33
0
  : DcmElement(tag, 0)
34
0
{
35
0
}
36
37
DcmAttributeTag::DcmAttributeTag(const DcmTag &tag,
38
                                 const Uint32 len)
39
0
  : DcmElement(tag, len)
40
0
{
41
0
}
42
43
44
DcmAttributeTag::DcmAttributeTag(const DcmAttributeTag &old)
45
0
  : DcmElement(old)
46
0
{
47
0
}
48
49
50
DcmAttributeTag::~DcmAttributeTag()
51
0
{
52
0
}
53
54
55
DcmAttributeTag &DcmAttributeTag::operator=(const DcmAttributeTag &obj)
56
0
{
57
0
    DcmElement::operator=(obj);
58
0
    return *this;
59
0
}
60
61
62
int DcmAttributeTag::compare(const DcmElement& rhs) const
63
0
{
64
0
    int result = DcmElement::compare(rhs);
65
0
    if (result != 0)
66
0
    {
67
0
        return result;
68
0
    }
69
70
    /* cast away constness (dcmdata is not const correct...) */
71
0
    DcmAttributeTag* myThis = NULL;
72
0
    DcmAttributeTag* myRhs = NULL;
73
0
    myThis = OFconst_cast(DcmAttributeTag*, this);
74
0
    myRhs = OFstatic_cast(DcmAttributeTag*, OFconst_cast(DcmElement*, &rhs));
75
76
    /* compare number of values */
77
0
    unsigned long thisNumValues = myThis->getNumberOfValues();
78
0
    unsigned long rhsNumValues = myRhs->getNumberOfValues();
79
0
    if (thisNumValues < rhsNumValues)
80
0
    {
81
0
        return -1;
82
0
    }
83
0
    else if (thisNumValues > rhsNumValues)
84
0
    {
85
0
        return 1;
86
0
    }
87
88
    /* iterate over all components and test equality */
89
0
    for (unsigned long count = 0; count < thisNumValues; count++)
90
0
    {
91
0
        DcmTagKey val;
92
0
        if (myThis->getTagVal(val, count).good())
93
0
        {
94
0
            DcmTagKey rhsVal;
95
0
            if (myRhs->getTagVal(rhsVal, count).good())
96
0
            {
97
0
                if (val > rhsVal)
98
0
                {
99
0
                    return 1;
100
0
                }
101
0
                else if (val < rhsVal)
102
0
                {
103
0
                    return -1;
104
0
                }
105
                /* otherwise they are equal, continue comparison */
106
0
            }
107
0
        }
108
0
    }
109
110
    /* all values as well as VM equal: objects are equal */
111
0
    return 0;
112
0
}
113
114
115
OFCondition DcmAttributeTag::copyFrom(const DcmObject& rhs)
116
0
{
117
0
    if (this != &rhs)
118
0
    {
119
0
        if (rhs.ident() != ident()) return EC_IllegalCall;
120
0
        *this = OFstatic_cast(const DcmAttributeTag &, rhs);
121
0
    }
122
0
    return EC_Normal;
123
0
}
124
125
126
// ********************************
127
128
129
DcmEVR DcmAttributeTag::ident() const
130
0
{
131
0
    return EVR_AT;
132
0
}
133
134
135
OFCondition DcmAttributeTag::checkValue(const OFString &vm,
136
                                       const OFBool /*oldFormat*/)
137
0
{
138
    /* check VM only, further checks on the attribute tags could be added later */
139
0
    return DcmElement::checkVM(getVM(), vm);
140
0
}
141
142
143
unsigned long DcmAttributeTag::getVM()
144
0
{
145
0
    return getNumberOfValues();
146
0
}
147
148
149
unsigned long DcmAttributeTag::getNumberOfValues()
150
0
{
151
    /* attribute tags store pairs of 16 bit values */
152
0
    return OFstatic_cast(unsigned long, getLengthField() / (2 * sizeof(Uint16)));
153
0
}
154
155
156
// ********************************
157
158
159
void DcmAttributeTag::print(STD_NAMESPACE ostream& out,
160
                            const size_t flags,
161
                            const int level,
162
                            const char * /*pixelFileName*/,
163
                            size_t * /*pixelCounter*/)
164
0
{
165
0
    if (valueLoaded())
166
0
    {
167
        /* get unsigned integer data */
168
0
        Uint16 *uintVals;
169
0
        errorFlag = getUint16Array(uintVals);
170
0
        const unsigned long count = getNumberOfValues();
171
0
        if ((uintVals != NULL) && (count > 0))
172
0
        {
173
            /* determine number of values to be printed */
174
0
            unsigned long expectedLength = count * (11 + 1) - 1;
175
0
            const unsigned long printCount =
176
0
                ((expectedLength > DCM_OptPrintLineLength) && (flags & DCMTypes::PF_shortenLongTagValues)) ?
177
0
                (DCM_OptPrintLineLength - 3 /* for "..." */ + 1) / (11 /* (gggg,eeee) */ + 1 /* for "\" */) : count;
178
0
            unsigned long printedLength = printCount * (11 + 1) - 1;
179
            /* print line start with tag and VR */
180
0
            printInfoLineStart(out, flags, level);
181
            /* print multiple values */
182
0
            if (printCount > 0)
183
0
            {
184
0
                out << STD_NAMESPACE hex << STD_NAMESPACE setfill('0');
185
                /* print tag values (group,element) in hex mode */
186
0
                out << '(' << STD_NAMESPACE setw(4) << (*(uintVals++));
187
0
                out << ',' << STD_NAMESPACE setw(4) << (*(uintVals++)) << ')';
188
0
                for (unsigned long i = 1; i < printCount; i++)
189
0
                {
190
0
                    out << "\\" << '(' << STD_NAMESPACE setw(4) << (*(uintVals++));
191
0
                    out << ',' << STD_NAMESPACE setw(4) << (*(uintVals++)) << ')';
192
0
                }
193
                /* reset i/o manipulators */
194
0
                out << STD_NAMESPACE dec << STD_NAMESPACE setfill(' ');
195
0
            }
196
            /* print trailing "..." if data has been truncated */
197
0
            if (printCount < count)
198
0
            {
199
0
                out << "...";
200
0
                printedLength += 3;
201
0
            }
202
            /* print line end with length, VM and tag name */
203
0
            printInfoLineEnd(out, flags, printedLength);
204
0
        } else
205
0
            printInfoLine(out, flags, level, "(no value available)");
206
0
    } else
207
0
        printInfoLine(out, flags, level, "(not loaded)");
208
0
}
209
210
211
// ********************************
212
213
214
OFCondition DcmAttributeTag::writeXML(STD_NAMESPACE ostream &out,
215
                                      const size_t flags)
216
0
{
217
    /* AT requires special handling in the Native DICOM Model format */
218
0
    if (flags & DCMTypes::XF_useNativeModel)
219
0
    {
220
        /* write normal XML start tag */
221
0
        DcmElement::writeXMLStartTag(out, flags);
222
        /* get unsigned integer data */
223
0
        Uint16 *uintVals;
224
0
        getUint16Array(uintVals);
225
0
        const unsigned long vm = getVM();
226
        /* check for empty/invalid value */
227
0
        if ((uintVals != NULL) && (vm > 0))
228
0
        {
229
0
            out << STD_NAMESPACE uppercase << STD_NAMESPACE setfill('0');
230
            /* print tag values "ggggeeee" in hex mode (upper case!) */
231
0
            for (unsigned long valNo = 0; valNo < vm; valNo++)
232
0
            {
233
0
                out << "<Value number=\"" << (valNo + 1) << "\">";
234
0
                out << STD_NAMESPACE hex << STD_NAMESPACE setw(4) << (*(uintVals++));
235
0
                out << STD_NAMESPACE setw(4) << (*(uintVals++)) << STD_NAMESPACE dec;
236
0
                out << "</Value>" << OFendl;
237
0
            }
238
            /* reset i/o manipulators */
239
0
            out << STD_NAMESPACE nouppercase << STD_NAMESPACE setfill(' ');
240
0
        }
241
        /* write normal XML end tag */
242
0
        DcmElement::writeXMLEndTag(out, flags);
243
        /* always report success */
244
0
        return EC_Normal;
245
0
    } else  {
246
        /* DCMTK-specific format does not require anything special */
247
0
        return DcmElement::writeXML(out, flags);
248
0
    }
249
0
}
250
251
252
// ********************************
253
254
255
OFCondition DcmAttributeTag::writeJson(STD_NAMESPACE ostream &out,
256
                                       DcmJsonFormat &format)
257
0
{
258
    // always write JSON Opener
259
0
    DcmElement::writeJsonOpener(out, format);
260
261
0
    if (!isEmpty())
262
0
    {
263
0
        Uint16 *uintVals;
264
0
        getUint16Array(uintVals);
265
0
        const unsigned long vm = getVM();
266
        // check for empty/invalid value
267
0
        if ((uintVals != NULL) && (vm > 0))
268
0
        {
269
0
            format.printValuePrefix(out);
270
0
            out << STD_NAMESPACE uppercase << STD_NAMESPACE setfill('0');
271
            // print tag values "ggggeeee" in hex mode (upper case!)
272
0
            out << "\"";
273
0
            out << STD_NAMESPACE hex << STD_NAMESPACE setw(4) << (*(uintVals++));
274
0
            out << STD_NAMESPACE setw(4) << (*(uintVals++)) << STD_NAMESPACE dec;
275
0
            out << "\"";
276
0
            for (unsigned long valNo = 1; valNo < vm; valNo++)
277
0
            {
278
0
                format.printNextArrayElementPrefix(out);
279
0
                out << "\"";
280
0
                out << STD_NAMESPACE hex << STD_NAMESPACE setw(4) << (*(uintVals++));
281
0
                out << STD_NAMESPACE setw(4) << (*(uintVals++)) << STD_NAMESPACE dec;
282
0
                out << "\"";
283
0
            }
284
            // reset i/o manipulators
285
0
            out << STD_NAMESPACE nouppercase << STD_NAMESPACE setfill(' ');
286
0
            format.printValueSuffix(out);
287
0
        }
288
0
    }
289
290
    // write normal JSON closer
291
0
    DcmElement::writeJsonCloser(out, format);
292
    // always report success
293
0
    return EC_Normal;
294
0
}
295
296
297
// ********************************
298
299
300
OFCondition DcmAttributeTag::getTagVal(DcmTagKey &tagVal,
301
                                       const unsigned long pos)
302
0
{
303
    /* get unsigned integer data */
304
0
    Uint16 *uintValues;
305
0
    errorFlag = getUint16Array(uintValues);
306
    /* check data before returning */
307
0
    if (errorFlag.good())
308
0
    {
309
0
        if (uintValues == NULL)
310
0
            errorFlag = EC_IllegalCall;
311
0
        else if (pos >= getVM())
312
0
            errorFlag = EC_IllegalParameter;
313
0
        else
314
0
            tagVal.set(uintValues[2 * pos] /*group*/, uintValues[2 * pos + 1] /*element*/);
315
0
    }
316
    /* clear value in case of error */
317
0
    if (errorFlag.bad())
318
0
        tagVal = DcmTagKey();
319
0
    return errorFlag;
320
0
}
321
322
323
OFCondition DcmAttributeTag::getUint16Array(Uint16 *&uintVals)
324
0
{
325
0
    uintVals = OFstatic_cast(Uint16 *, getValue());
326
0
    return errorFlag;
327
0
}
328
329
330
// ********************************
331
332
333
OFCondition DcmAttributeTag::getOFString(OFString &stringVal,
334
                                         const unsigned long pos,
335
                                         OFBool /*normalize*/)
336
0
{
337
0
    DcmTagKey tagVal;
338
    /* get the specified tag value */
339
0
    errorFlag = getTagVal(tagVal, pos);
340
0
    if (errorFlag.good())
341
0
    {
342
        /* ... and convert it to a character string */
343
0
        char buffer[32];
344
0
        OFStandard::snprintf(buffer, sizeof(buffer), "(%4.4x,%4.4x)", tagVal.getGroup(), tagVal.getElement());
345
        /* assign result */
346
0
        stringVal = buffer;
347
0
    }
348
0
    return errorFlag;
349
0
}
350
351
352
// ********************************
353
354
355
OFCondition DcmAttributeTag::putTagVal(const DcmTagKey &tagVal,
356
                                       const unsigned long pos)
357
0
{
358
    /* create tag data */
359
0
    Uint16 uintVals[2];
360
0
    uintVals[0] = tagVal.getGroup();
361
0
    uintVals[1] = tagVal.getElement();
362
    /* change element value */
363
0
    errorFlag = changeValue(uintVals, OFstatic_cast(Uint32, 2 * sizeof(Uint16) * OFstatic_cast(size_t, pos)), 2 * OFstatic_cast(Uint32, sizeof(Uint16)));
364
0
    return errorFlag;
365
0
}
366
367
368
OFCondition DcmAttributeTag::putUint16Array(const Uint16 *uintVals,
369
                                            const unsigned long numUints)
370
0
{
371
0
    errorFlag = EC_Normal;
372
0
    if (numUints > 0)
373
0
    {
374
        /* check for valid data */
375
0
        if (uintVals != NULL)
376
0
            errorFlag = putValue(uintVals, OFstatic_cast(Uint32, 2 * sizeof(Uint16) * OFstatic_cast(size_t, numUints)));
377
0
        else
378
0
            errorFlag = EC_CorruptedData;
379
0
    } else
380
0
        errorFlag = putValue(NULL, 0);
381
0
    return errorFlag;
382
0
}
383
384
385
// ********************************
386
387
388
OFCondition DcmAttributeTag::putString(const char *stringVal)
389
0
{
390
    /* determine length of the string value */
391
0
    const size_t stringLen = (stringVal != NULL) ? strlen(stringVal) : 0;
392
    /* call the real function */
393
0
    return putString(stringVal, OFstatic_cast(Uint32, stringLen));
394
0
}
395
396
397
OFCondition DcmAttributeTag::putString(const char *stringVal,
398
                                       const Uint32 stringLen)
399
0
{
400
0
    errorFlag = EC_Normal;
401
    /* determine VM of the string */
402
0
    unsigned long vm = DcmElement::determineVM(stringVal, stringLen);
403
0
    if (vm > 0)
404
0
    {
405
0
        Uint16 * field = new Uint16[2 * vm];
406
0
        OFString value;
407
0
        size_t pos = 0;
408
        /* retrieve attribute tag data from character string */
409
0
        for (unsigned long i = 0; (i < 2 * vm) && errorFlag.good(); i += 2)
410
0
        {
411
            /* get specified value from multi-valued string */
412
0
            pos = DcmElement::getValueFromString(stringVal, pos, stringLen, value);
413
0
            if (value.empty() || sscanf(value.c_str(), "(%hx,%hx)", &field[i], &field[i + 1]) != 2)
414
0
                errorFlag = EC_CorruptedData;
415
0
        }
416
        /* set binary data as the element value */
417
0
        if (errorFlag.good())
418
0
            errorFlag = putUint16Array(field, vm);
419
        /* delete temporary buffer */
420
0
        delete[] field;
421
0
    } else
422
0
        putValue(NULL, 0);
423
0
    return errorFlag;
424
0
}
425
426
427
// ********************************
428
429
430
OFCondition DcmAttributeTag::verify(const OFBool autocorrect)
431
0
{
432
    /* check for valid value length */
433
0
    if (getLengthField() % (2 * sizeof(Uint16)) != 0)
434
0
    {
435
0
        errorFlag = EC_CorruptedData;
436
0
        if (autocorrect)
437
0
        {
438
            /* strip to valid length */
439
0
            setLengthField(getLengthField() - (getLengthField() % (2* OFstatic_cast(Uint32, sizeof(Uint16)))));
440
0
        }
441
0
    } else
442
0
        errorFlag = EC_Normal;
443
0
    return errorFlag;
444
0
}
445
446
447
// ********************************
448
449
450
OFCondition DcmAttributeTag::checkStringValue(const OFString &value,
451
                                              const OFString &vm)
452
0
{
453
0
    return DcmElement::checkVM(DcmElement::determineVM(value.c_str(), value.length()), vm);
454
0
}
455
456
457
// ********************************
458
459
460
OFBool DcmAttributeTag::isUniversalMatch(const OFBool normalize,
461
                                         const OFBool enableWildCardMatching)
462
0
{
463
0
  if(!isEmpty(normalize))
464
0
  {
465
0
    if(enableWildCardMatching)
466
0
    {
467
0
      OFString value;
468
0
      for(unsigned long valNo = 0; valNo < getVM(); ++valNo)
469
0
      {
470
0
        getOFString(value, valNo, normalize);
471
0
        if(value.find_first_not_of( '*' ) != OFString_npos)
472
0
          return OFFalse;
473
0
      }
474
0
    }
475
0
    else
476
0
      return OFFalse;
477
0
  }
478
0
  return OFTrue;
479
0
}