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