/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 | } |