1 /*
2 *
3 * Copyright (C) 2000-2018, 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: dcmsr
15 *
16 * Author: Joerg Riesmeier
17 *
18 * Purpose:
19 * classes: DSRByReferenceTreeNode
20 *
21 */
22
23
24 #include "dcmtk/config/osconfig.h" /* make sure OS specific configuration is included first */
25
26 #include "dcmtk/dcmsr/dsrtypes.h"
27 #include "dcmtk/dcmsr/dsrreftn.h"
28 #include "dcmtk/dcmsr/dsrxmld.h"
29
30 #include "dcmtk/dcmdata/dcdeftag.h"
31 #include "dcmtk/dcmdata/dcvrul.h"
32
33
DSRByReferenceTreeNode(const E_RelationshipType relationshipType)34 DSRByReferenceTreeNode::DSRByReferenceTreeNode(const E_RelationshipType relationshipType)
35 : DSRDocumentTreeNode(relationshipType, VT_byReference),
36 ValidReference(OFFalse),
37 ReferencedContentItem(),
38 ReferencedNodeID(0),
39 TargetValueType(VT_invalid)
40 {
41 }
42
43
DSRByReferenceTreeNode(const E_RelationshipType relationshipType,const size_t referencedNodeID,const E_ValueType targetValueType)44 DSRByReferenceTreeNode::DSRByReferenceTreeNode(const E_RelationshipType relationshipType,
45 const size_t referencedNodeID,
46 const E_ValueType targetValueType)
47 : DSRDocumentTreeNode(relationshipType, VT_byReference),
48 ValidReference(OFFalse),
49 ReferencedContentItem(),
50 ReferencedNodeID(referencedNodeID),
51 TargetValueType(targetValueType)
52 {
53 }
54
55
DSRByReferenceTreeNode(const DSRByReferenceTreeNode & node)56 DSRByReferenceTreeNode::DSRByReferenceTreeNode(const DSRByReferenceTreeNode &node)
57 : DSRDocumentTreeNode(node),
58 ValidReference(OFFalse),
59 ReferencedContentItem(node.ReferencedContentItem),
60 ReferencedNodeID(0),
61 TargetValueType(VT_invalid)
62 {
63 }
64
65
~DSRByReferenceTreeNode()66 DSRByReferenceTreeNode::~DSRByReferenceTreeNode()
67 {
68 }
69
70
operator ==(const DSRDocumentTreeNode & node) const71 OFBool DSRByReferenceTreeNode::operator==(const DSRDocumentTreeNode &node) const
72 {
73 /* call comparison operator of base class (includes check of value type) */
74 OFBool result = DSRDocumentTreeNode::operator==(node);
75 if (result)
76 {
77 /* it's safe to cast the type since the value type has already been checked */
78 const DSRByReferenceTreeNode &byRefNode = OFstatic_cast(const DSRByReferenceTreeNode &, node);
79 if (ValidReference && byRefNode.ValidReference)
80 {
81 /* check referenced node ID only */
82 result = (ReferencedNodeID == byRefNode.ReferencedNodeID);
83 } else {
84 /* check whether both references are invalid */
85 result = (ValidReference == byRefNode.ValidReference);
86 }
87 }
88 return result;
89 }
90
91
operator !=(const DSRDocumentTreeNode & node) const92 OFBool DSRByReferenceTreeNode::operator!=(const DSRDocumentTreeNode &node) const
93 {
94 /* call comparison operator of base class (includes check of value type) */
95 OFBool result = DSRDocumentTreeNode::operator!=(node);
96 if (!result)
97 {
98 /* it's safe to cast the type since the value type has already been checked */
99 const DSRByReferenceTreeNode &byRefNode = OFstatic_cast(const DSRByReferenceTreeNode &, node);
100 if (ValidReference && byRefNode.ValidReference)
101 {
102 /* check referenced node ID only */
103 result = (ReferencedNodeID != byRefNode.ReferencedNodeID);
104 } else {
105 /* check whether either of the references is invalid */
106 result = (ValidReference != byRefNode.ValidReference);
107 }
108 }
109 return result;
110 }
111
112
clone() const113 DSRByReferenceTreeNode *DSRByReferenceTreeNode::clone() const
114 {
115 return new DSRByReferenceTreeNode(*this);
116 }
117
118
clear()119 void DSRByReferenceTreeNode::clear()
120 {
121 DSRDocumentTreeNode::clear();
122 ValidReference = OFFalse;
123 ReferencedContentItem.clear();
124 ReferencedNodeID = 0;
125 TargetValueType = VT_invalid;
126 }
127
128
isValid() const129 OFBool DSRByReferenceTreeNode::isValid() const
130 {
131 /* ConceptNameCodeSequence not allowed */
132 return DSRDocumentTreeNode::isValid() && getConceptName().isEmpty() && hasValidValue();
133 }
134
135
hasValidValue() const136 OFBool DSRByReferenceTreeNode::hasValidValue() const
137 {
138 return ValidReference;
139 }
140
141
print(STD_NAMESPACE ostream & stream,const size_t flags) const142 OFCondition DSRByReferenceTreeNode::print(STD_NAMESPACE ostream &stream,
143 const size_t flags) const
144 {
145 DCMSR_PRINT_ANSI_ESCAPE_CODE(DCMSR_ANSI_ESCAPE_CODE_RELATIONSHIP_TYPE)
146 stream << relationshipTypeToReadableName(getRelationshipType()) << " ";
147 DCMSR_PRINT_ANSI_ESCAPE_CODE(DCMSR_ANSI_ESCAPE_CODE_ITEM_VALUE)
148 if (ReferencedContentItem.empty())
149 stream << "?";
150 else
151 stream << ReferencedContentItem;
152 /* print node ID (might be useful for debugging purposes) */
153 if (flags & PF_printNodeID)
154 {
155 DCMSR_PRINT_ANSI_ESCAPE_CODE(DCMSR_ANSI_ESCAPE_CODE_RESET)
156 stream << " = id:" << ReferencedNodeID;
157 }
158 return EC_Normal;
159 }
160
161
writeXML(STD_NAMESPACE ostream & stream,const size_t flags) const162 OFCondition DSRByReferenceTreeNode::writeXML(STD_NAMESPACE ostream &stream,
163 const size_t flags) const
164 {
165 OFCondition result = EC_Normal;
166 writeXMLItemStart(stream, flags, OFFalse /*closingBracket*/);
167 stream << " ref=\"" << ReferencedNodeID << "\">" << OFendl;
168 /* basically, there should be no child content items but ... */
169 result = DSRDocumentTreeNode::writeXML(stream, flags);
170 writeXMLItemEnd(stream, flags);
171 return result;
172 }
173
174
invalidateReference()175 void DSRByReferenceTreeNode::invalidateReference()
176 {
177 ValidReference = OFFalse;
178 }
179
180
updateReference(const size_t referencedNodeID,const E_ValueType targetValueType)181 OFBool DSRByReferenceTreeNode::updateReference(const size_t referencedNodeID,
182 const E_ValueType targetValueType)
183 {
184 ReferencedNodeID = referencedNodeID;
185 TargetValueType = targetValueType;
186 /* check whether the given reference is valid */
187 ValidReference = (ReferencedNodeID > 0);
188 return ValidReference;
189 }
190
191
updateReference(const OFString & referencedContentItem)192 OFBool DSRByReferenceTreeNode::updateReference(const OFString &referencedContentItem)
193 {
194
195 ReferencedContentItem = referencedContentItem;
196 /* tbd: check for valid reference could be more strict */
197 ValidReference = checkForValidReference(ReferencedContentItem);
198 return ValidReference;
199 }
200
201
202 // protected methods
203
readContentItem(DcmItem & dataset,const size_t)204 OFCondition DSRByReferenceTreeNode::readContentItem(DcmItem &dataset,
205 const size_t /*flags*/)
206 {
207 DcmUnsignedLong delem(DCM_ReferencedContentItemIdentifier);
208 /* clear before reading */
209 ReferencedContentItem.clear();
210 updateReference(0, VT_invalid);
211 /* read ReferencedContentItemIdentifier */
212 OFCondition result = getAndCheckElementFromDataset(dataset, delem, "1-n", "1C", "by-reference relationship");
213 if (result.good())
214 {
215 /* create reference string from unsigned long values */
216 Uint32 value = 0;
217 char buffer[20];
218 const unsigned long count = delem.getVM();
219 for (unsigned long i = 0; i < count; i++)
220 {
221 if (i > 0)
222 ReferencedContentItem += '.';
223 if (delem.getUint32(value, i).good())
224 ReferencedContentItem += numberToString(OFstatic_cast(size_t, value), buffer);
225 }
226 }
227 return result;
228 }
229
230
writeContentItem(DcmItem & dataset) const231 OFCondition DSRByReferenceTreeNode::writeContentItem(DcmItem &dataset) const
232 {
233 OFCondition result = SR_EC_InvalidValue;
234 /* only write references with valid format */
235 if (checkForValidReference(ReferencedContentItem))
236 {
237 result = EC_Normal;
238 DcmUnsignedLong delem(DCM_ReferencedContentItemIdentifier);
239 /* create unsigned long values from reference string */
240 size_t posStart = 0;
241 size_t posEnd = 0;
242 unsigned long i = 0;
243 do {
244 /* search for next separator */
245 posEnd = ReferencedContentItem.find('.', posStart);
246 /* is last segment? */
247 if (posEnd == OFString_npos)
248 delem.putUint32(OFstatic_cast(Uint32, DSRTypes::stringToNumber(ReferencedContentItem.substr(posStart).c_str())), i);
249 else {
250 delem.putUint32(OFstatic_cast(Uint32, DSRTypes::stringToNumber(ReferencedContentItem.substr(posStart, posEnd - posStart).c_str())), i);
251 posStart = posEnd + 1;
252 }
253 i++;
254 } while (posEnd != OFString_npos);
255 /* write ReferencedContentItemIdentifier */
256 addElementToDataset(result, dataset, new DcmUnsignedLong(delem), "1-n", "1", "by-reference relationship");
257 }
258 return result;
259 }
260
261
readXMLContentItem(const DSRXMLDocument & doc,DSRXMLCursor cursor,const size_t)262 OFCondition DSRByReferenceTreeNode::readXMLContentItem(const DSRXMLDocument &doc,
263 DSRXMLCursor cursor,
264 const size_t /*flags*/)
265 {
266 OFCondition result = SR_EC_CorruptedXMLStructure;
267 if (cursor.valid())
268 {
269 OFString refID;
270 /* get "ref" attribute */
271 if (!doc.getStringFromAttribute(cursor, refID, "ref").empty())
272 {
273 ReferencedNodeID = stringToNumber(refID.c_str());
274 /* this does not mean that the reference is really correct, this will be checked later */
275 result = EC_Normal;
276 } else
277 result = SR_EC_InvalidValue;
278 }
279 return result;
280 }
281
282
renderHTMLContentItem(STD_NAMESPACE ostream & docStream,STD_NAMESPACE ostream &,const size_t,size_t &,const size_t) const283 OFCondition DSRByReferenceTreeNode::renderHTMLContentItem(STD_NAMESPACE ostream &docStream,
284 STD_NAMESPACE ostream & /*annexStream*/,
285 const size_t /*nestingLevel*/,
286 size_t & /*annexNumber*/,
287 const size_t /*flags*/) const
288 {
289 /* render reference string */
290 docStream << "Content Item <a href=\"#content_item_" << ReferencedNodeID << "\">by-reference</a>" << OFendl;
291 return EC_Normal;
292 }
293
294
setConceptName(const DSRCodedEntryValue &,const OFBool)295 OFCondition DSRByReferenceTreeNode::setConceptName(const DSRCodedEntryValue & /*conceptName*/,
296 const OFBool /*check*/)
297 {
298 /* invalid: no concept name allowed */
299 return EC_IllegalCall;
300 }
301
302
setObservationDateTime(const OFString &,const OFBool)303 OFCondition DSRByReferenceTreeNode::setObservationDateTime(const OFString & /*observationDateTime*/,
304 const OFBool /*check*/)
305 {
306 /* invalid: no observation date/time allowed */
307 return EC_IllegalCall;
308 }
309
310
setObservationDateTime(const DcmElement &,const unsigned long,const OFBool)311 OFCondition DSRByReferenceTreeNode::setObservationDateTime(const DcmElement & /*delem*/,
312 const unsigned long /*pos*/,
313 const OFBool /*check*/)
314 {
315 /* invalid: no observation date/time allowed */
316 return EC_IllegalCall;
317 }
318
319
setObservationDateTime(DcmItem &,const DcmTagKey &,const unsigned long,const OFBool)320 OFCondition DSRByReferenceTreeNode::setObservationDateTime(DcmItem & /*dataset*/,
321 const DcmTagKey & /*tagKey*/,
322 const unsigned long /*pos*/,
323 const OFBool /*check*/)
324 {
325 /* invalid: no observation date/time allowed */
326 return EC_IllegalCall;
327 }
328
329
setObservationUID(const OFString &,const OFBool)330 OFCondition DSRByReferenceTreeNode::setObservationUID(const OFString & /*observationUID*/,
331 const OFBool /*check*/)
332 {
333 /* invalid: no observation unique identifier allowed */
334 return EC_IllegalCall;
335 }
336
337
setTemplateIdentification(const OFString &,const OFString &,const OFString &,const OFBool)338 OFCondition DSRByReferenceTreeNode::setTemplateIdentification(const OFString & /*templateIdentifier*/,
339 const OFString & /*mappingResource*/,
340 const OFString & /*mappingResourceUID*/,
341 const OFBool /*check*/)
342 {
343 /* invalid: no template identification allowed */
344 return EC_IllegalCall;
345 }
346