1 /*
2  *
3  *  Copyright (C) 1994-2019, 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: Interface of class DcmDirectoryRecord
19  *
20  */
21 
22 #ifndef DCDIRREC_H
23 #define DCDIRREC_H
24 
25 #include "dcmtk/config/osconfig.h"    /* make sure OS specific configuration is included first */
26 
27 #include "dcmtk/dcmdata/dcitem.h"
28 #include "dcmtk/dcmdata/dcsequen.h"
29 #include "dcmtk/dcmdata/dcfilefo.h"
30 
31 
32 /// types of directory records in a DICOMDIR
33 typedef enum {
34     /// root
35     ERT_root = 0,
36     /// curve (retired)
37     ERT_Curve = 1,
38     /// film box (retired)
39     ERT_FilmBox = 2,
40     /// film session (retired)
41     ERT_FilmSession = 3,
42     /// image
43     ERT_Image = 4,
44     /// image box (retired)
45     ERT_ImageBox = 5,
46     /// interpretation (retired)
47     ERT_Interpretation = 6,
48     /// modality LUT (retired)
49     ERT_ModalityLut = 7,
50     /// MRDR (retired)
51     ERT_Mrdr = 8,
52     /// overlay (retired)
53     ERT_Overlay = 9,
54     /// patient
55     ERT_Patient = 10,
56     /// print queue (retired)
57     ERT_PrintQueue = 11,
58     /// private
59     ERT_Private = 12,
60     /// results
61     ERT_Results = 13,
62     /// series
63     ERT_Series = 14,
64     /// study
65     ERT_Study = 15,
66     /// study component (retired)
67     ERT_StudyComponent = 16,
68     /// topic (retired)
69     ERT_Topic = 17,
70     /// visit (retired)
71     ERT_Visit = 18,
72     /// VOI LUT (retired)
73     ERT_VoiLut = 19,
74     /// SR document
75     ERT_SRDocument = 20,
76     /// presentation state
77     ERT_Presentation = 21,
78     /// waveform
79     ERT_Waveform = 22,
80     /// RT dose
81     ERT_RTDose = 23,
82     /// RT structure set
83     ERT_RTStructureSet = 24,
84     /// RT plan
85     ERT_RTPlan = 25,
86     /// RT treatment record
87     ERT_RTTreatRecord = 26,
88     /// stored print (retired)
89     ERT_StoredPrint = 27,
90     /// key object selection document
91     ERT_KeyObjectDoc = 28,
92     /// registration
93     ERT_Registration = 29,
94     /// fiducial
95     ERT_Fiducial = 30,
96     /// raw data
97     ERT_RawData = 31,
98     /// spectroscopy
99     ERT_Spectroscopy = 32,
100     /// encapsulated document
101     ERT_EncapDoc = 33,
102     /// value map
103     ERT_ValueMap = 34,
104     /// hanging protocol
105     ERT_HangingProtocol = 35,
106     /// stereometric relationships
107     ERT_Stereometric = 36,
108     /// HL7 structured document (retired)
109     ERT_HL7StrucDoc = 37,
110     /// palette
111     ERT_Palette = 38,
112     /// surface
113     ERT_Surface = 39,
114     /// measurement
115     ERT_Measurement = 40,
116     /// implant
117     ERT_Implant = 41,
118     /// implant group
119     ERT_ImplantGroup = 42,
120     /// implant assembly
121     ERT_ImplantAssy = 43,
122     /// plan
123     ERT_Plan = 44,
124     /// surface scan
125     ERT_SurfaceScan = 45,
126     /// tractography
127     ERT_Tract = 46,
128     /// assessment
129     ERT_Assessment = 47,
130     /// radiotherapy
131     ERT_Radiotherapy = 48
132 } E_DirRecType;
133 
134 
135 class DcmDicomDir;
136 
137 /** a class representing a directory record dataset in a DICOMDIR.
138  */
139 class DCMTK_DCMDATA_EXPORT DcmDirectoryRecord : public DcmItem
140 {
141 
142     friend class DcmDicomDir;
143 
144 public:
145     /// default constructor
146     DcmDirectoryRecord();
147 
148     /** constructor.
149      *  Create new element from given tag and length.
150      *  @param tag attribute tag
151      *  @param len length of the attribute value
152      */
153     DcmDirectoryRecord(const DcmTag &tag,
154                        const Uint32 len);
155 
156     /** constructor
157      *  @param recordType record type
158      *  @param referencedFileID referenced file ID in DICOM format
159      *  @param sourceFileName path to referenced file in operating system specific format
160      *  @param fileFormat fileFormat for sourceFileName, can be NULL
161      */
162     DcmDirectoryRecord(const E_DirRecType recordType,
163                        const char *referencedFileID,     // DICOM format with '\\'
164                        const OFFilename &sourceFileName, // OS format
165                        DcmFileFormat* fileFormat = NULL);
166 
167     /** constructor
168      *  @param recordTypeName record type as string
169      *  @param referencedFileID referenced file ID in DICOM format
170      *  @param sourceFileName path to referenced file in operating system specific format
171      *  @param fileFormat fileFormat for sourceFileName, can be NULL
172      */
173     DcmDirectoryRecord(const char *recordTypeName,
174                        const char *referencedFileID,     // DICOM format with '\\'
175                        const OFFilename &sourceFileName, // OS format
176                        DcmFileFormat* fileFormat = NULL);
177 
178     /** copy constructor
179      *  @param oldDirRec element to be copied
180      */
181     DcmDirectoryRecord(const DcmDirectoryRecord &oldDirRec);
182 
183     /** assignment operator
184      *  @param obj the directory record to be copied
185      */
186     DcmDirectoryRecord &operator=(const DcmDirectoryRecord &obj);
187 
188     /// destructor
189     virtual ~DcmDirectoryRecord();
190 
191     /** clone method
192      *  @return deep copy of this object
193      */
clone()194     virtual DcmObject *clone() const
195     {
196       return new DcmDirectoryRecord(*this);
197     }
198 
199     /** Virtual object copying. This method can be used for DcmObject
200      *  and derived classes to get a deep copy of an object. Internally
201      *  the assignment operator is called if the given DcmObject parameter
202      *  is of the same type as "this" object instance. If not, an error
203      *  is returned. This function permits copying an object by value
204      *  in a virtual way which therefore is different to just calling the
205      *  assignment operator of DcmElement which could result in slicing
206      *  the object.
207      *  @param rhs - [in] The instance to copy from. Has to be of the same
208      *                class type as "this" object
209      *  @return EC_Normal if copying was successful, error otherwise
210      */
211     virtual OFCondition copyFrom(const DcmObject& rhs);
212 
213     /** return identifier for this class. Every class derived from this class
214      *  returns a unique value of type enum DcmEVR for this call. This is used
215      *  as a "poor man's RTTI" to correctly identify instances derived from
216      *  this class even on compilers not supporting RTTI.
217      *  @return type identifier of this class
218      */
219     virtual DcmEVR ident() const;
220 
221     /// returns current status flag
error()222     inline OFCondition error() const { return errorFlag; }
223 
224     /** mode specifying whether the SpecificCharacterSet (0008,0005) element should be
225      *  checked by convertCharacterSet() or not, i.e.\ whether this element might be
226      *  present on this dataset-level.
227      *  @return always returns OFTrue, i.e.\ SpecificCharacterSet should be checked
228      */
checkForSpecificCharacterSet()229     virtual OFBool checkForSpecificCharacterSet() const { return OFTrue; }
230 
231     /** convert all element values that are contained in this record and that are
232      *  affected by SpecificCharacterSet from the given source character set to the given
233      *  destination character set. The defined terms for a particular character set can
234      *  be found in the DICOM standard, e.g. "ISO_IR 100" for ISO 8859-1 (Latin 1) or
235      *  "ISO_IR 192" for Unicode in UTF-8. An empty string denotes the default character
236      *  repertoire, which is ASCII (7-bit). If multiple values are given for 'fromCharset'
237      *  (separated by a backslash) code extension techniques are used and escape sequences
238      *  may be encountered in the source string to switch between the specified character
239      *  sets.
240      *  @param fromCharset name of the source character set(s) used for the conversion
241      *  @param toCharset name of the destination character set used for the conversion.
242      *    Only a single value is permitted (i.e. no code extensions).
243      *  @param flags optional flag used to customize the conversion (see DCMTypes::CF_xxx)
244      *  @param updateCharset if OFTrue, the SpecificCharacterSet (0008,0005) element is
245      *    updated, i.e.\ the current value is either replaced or a new element is inserted
246      *    or the existing element is deleted. If OFFalse the SpecificCharacterSet element
247      *    remains unchanged.
248      *  @return status, EC_Normal if successful, an error code otherwise
249      */
250     virtual OFCondition convertCharacterSet(const OFString &fromCharset,
251                                             const OFString &toCharset,
252                                             const size_t flags = 0,
253                                             const OFBool updateCharset = OFFalse);
254 
255     /** convert all element values that are contained in this record and that are
256      *  affected by SpecificCharacterSet to the given destination character set. If not
257      *  disabled, the source character set is determined automatically from the value of
258      *  the SpecificCharacterSet (0008,0005) element. The defined terms for the
259      *  destination character set can be found in the DICOM standard, e.g. "ISO_IR 100"
260      *  for ISO 8859-1 (Latin 1) or "ISO_IR 192" for Unicode in UTF-8. An empty string
261      *  denotes the default character repertoire, which is ASCII (7-bit).
262      *  @param toCharset name of the destination character set used for the conversion.
263      *    Only a single value is permitted (i.e. no code extensions).
264      *  @param flags optional flag used to customize the conversion (see DCMTypes::CF_xxx)
265      *  @param ignoreCharset if OFTrue, the value of SpecificCharacterSet is ignored.
266      *    Also see checkForSpecificCharacterSet().
267      *  @return status, EC_Normal if successful, an error code otherwise
268      */
269     virtual OFCondition convertCharacterSet(const OFString &toCharset,
270                                             const size_t flags = 0,
271                                             const OFBool ignoreCharset = OFFalse);
272 
273     /** convert all element values that are contained in this record and that are
274      *  affected by SpecificCharacterSet from the currently selected source character
275      *  set to the currently selected destination character set. Since the Basic
276      *  Directory IOD, which specifies the structure and content of a DICOMDIR, does not
277      *  contain the SpecificCharacterSet (0008,0005) element in the main dataset but in
278      *  each directory record, this method also checks for this element and creates a new
279      *  character set converter for the contained data elements (if needed).
280      *  @param converter character set converter to be used to convert the element values
281      *  @return status, EC_Normal if successful, an error code otherwise
282      */
283     virtual OFCondition convertCharacterSet(DcmSpecificCharacterSet &converter);
284 
285     /** print all elements of the item to a stream
286      *  @param out output stream
287      *  @param flags optional flag used to customize the output (see DCMTypes::PF_xxx)
288      *  @param level current level of nested items. Used for indentation.
289      *  @param pixelFileName not used
290      *  @param pixelCounter not used
291      */
292     virtual void print(STD_NAMESPACE ostream &out,
293                        const size_t flags = 0,
294                        const int level = 0,
295                        const char *pixelFileName = NULL,
296                        size_t *pixelCounter = NULL);
297 
298     /** This function reads the information of all attributes which
299      *  are captured in the input stream and captures this information
300      *  in elementList. Each attribute is represented as an element
301      *  in this list. If not all information for an attribute could be
302      *  read from the stream, the function returns EC_StreamNotifyClient.
303      *  @param inStream      The stream which contains the information.
304      *  @param xfer          The transfer syntax which was used to encode
305      *                       the information in inStream.
306      *  @param glenc         Encoding type for group length; specifies
307      *                       what will be done with group length tags.
308      *  @param maxReadLength Maximum read length for reading an attribute value.
309      *  @return status, EC_Normal if successful, an error code otherwise
310      */
311     virtual OFCondition read(DcmInputStream &inStream,
312                              const E_TransferSyntax xfer,
313                              const E_GrpLenEncoding glenc = EGL_noChange,
314                              const Uint32 maxReadLength = DCM_MaxReadLength);
315 
316     /** write object in XML format
317      *  @param out output stream to which the XML document is written
318      *  @param flags optional flag used to customize the output (see DCMTypes::XF_xxx)
319      *  @return status, EC_Normal if successful, an error code otherwise
320      */
321     virtual OFCondition writeXML(STD_NAMESPACE ostream &out,
322                                  const size_t flags = 0);
323 
324 
325     /** check the currently stored element value
326      *  @param autocorrect correct value length if OFTrue
327      *  @return status, EC_Normal if value length is correct, an error code otherwise
328      */
329     virtual OFCondition verify(const OFBool autocorrect = OFFalse);
330 
331     /** a complex, stack-based, hierarchical search method. It allows for a search
332      *  for a DICOM object with a given attribute within a given container,
333      *  hierarchically, from a starting position identified through a cursor stack.
334      *  @param xtag the DICOM attribute tag we are searching for
335      *  @param resultStack depending on the search mode (see below), this parameter
336      *     either serves as an input and output parameter, or as an output parameter
337      *     only (the latter being the default). When used as an input parameter,
338      *     the cursor stack defines the start position for the search within a
339      *     hierarchical DICOM dataset. Upon successful return, the stack contains
340      *     the position of the element found, in the form of a pointer to each dataset,
341      *     sequence, item and element from the main dataset down to the found element.
342      *  @param mode search mode, controls how the search stack is handled.
343      *     In the default mode, ESM_fromHere, the stack is ignored on input, and
344      *     the search starts in the object for which this method is called.
345      *     In the other modes, the stack is used both as an input and an output
346      *     parameter and defines the starting point for the search.
347      *  @param searchIntoSub if true, the search will be performed hierarchically descending
348      *    into the sequences and items of the dataset. If false, only the current container
349      *    (sequence or item) will be traversed.
350      *  @return EC_Normal if found, EC_TagNotFound if not found, an error code is something went wrong.
351      */
352     virtual OFCondition search(const DcmTagKey &xtag,               // in
353                                DcmStack &resultStack,               // inout
354                                E_SearchMode mode = ESM_fromHere,    // in
355                                OFBool searchIntoSub = OFTrue);      // in
356 
357     /// get record type of this directory record
358     virtual E_DirRecType getRecordType();
359 
360     /** if this directory record references an MRDR (multi-reference directory record),
361      *  return pointer to the MRDR referenced by this object.
362      *  @return pointer to MRDR referenced by this object or NULL of no MRDR referenced
363      */
364     virtual DcmDirectoryRecord* getReferencedMRDR();
365 
366     /** create a reference from this record to an MRDR
367      *  @param mrdr pointer to MRDR
368      *  @return EC_Normal upon success, an error code otherwise
369      */
370     virtual OFCondition assignToMRDR(DcmDirectoryRecord *mrdr );    // in
371 
372     /** open a DICOM file and make this directory record into a directory
373      *  record for that DICOM file. The most relevant record keys
374      *  (SOP Class UID, SOP instance UID, Transfer Syntax UID) are inserted
375      *  into the directory record.
376      *  @param referencedFileID referenced file ID in DICOM format
377      *  @param sourceFileName path to file in operating system specific format
378      *  @return EC_Normal upon success, an error code otherwise
379      */
380     virtual OFCondition assignToSOPFile(const char *referencedFileID,
381                                         const OFFilename &sourceFileName);
382 
383     /// return number of directory records that are child record of this one
384     virtual unsigned long cardSub() const;
385 
386     /** insert a child directory record
387      *  @param dirRec directory record to be inserted. Must be allocated on heap, ownership is
388      *    transferred to this object
389      *  @param where index where to insert object
390      *  @param before flag indicating whether to insert the record before or after the element
391      *    identified by where
392      *  @return EC_Normal upon success, an error code otherwise
393      */
394     virtual OFCondition insertSub(DcmDirectoryRecord* dirRec,
395                                   unsigned long where = DCM_EndOfListIndex,
396                                   OFBool before = OFFalse);
397 
398     /** insert new directory child record at the current position.
399      *  The current position is stored internally in the 'lowerLevelList' member variable.
400      *  @param dirRec new child record to be inserted
401      *  @param before flag indicating whether to insert the record before (OFFalse) or
402      *    after (OFTrue) the current position
403      *  @return status, EC_Normal upon success, an error code otherwise
404      */
405     virtual OFCondition insertSubAtCurrentPos(DcmDirectoryRecord *dirRec,
406                                               OFBool before = OFFalse);
407 
408     /** access child directory record. Returns a pointer to the object maintained
409      *  as a child, not a copy.
410      *  @param num index, must be < cardSub()
411      *  @return pointer to child directory record or NULL if not found
412      */
413     virtual DcmDirectoryRecord* getSub(const unsigned long num);
414 
415     /** get next directory child record starting at a given record
416      *  @param dirRec record to start from (goto first record if NULL)
417      *  @return pointer to next record if successful, NULL otherwise
418      */
419     virtual DcmDirectoryRecord* nextSub(const DcmDirectoryRecord *dirRec);
420 
421     /** remove child directory record. If found, the record is not deleted but
422      *  returned to the caller who is responsible for further management of the
423      *  DcmDirectoryRecord object.
424      *  @param num index number of element, must be < cardSub()
425      *  @return pointer to DcmDirectoryRecord if found, NULL otherwise
426      */
427     virtual DcmDirectoryRecord* removeSub(const unsigned long num);
428 
429     /** remove child directory record. If found, the record is not deleted but
430      *  returned to the caller who is responsible for further management of the
431      *  DcmDirectoryRecord object.
432      *  @param dirRec pointer to element to be removed from list
433      *  @return pointer to element if found, NULL otherwise
434      */
435     virtual DcmDirectoryRecord* removeSub(DcmDirectoryRecord *dirRec);
436 
437     /** remove child directory record and delete file referenced by that record, if any
438      *  @param num index number of element, must be < cardSub()
439      *  @return status, EC_Normal upon success, an error code otherwise
440      */
441     virtual OFCondition deleteSubAndPurgeFile(const unsigned long num);
442 
443     /** remove child directory record and delete file referenced by that record, if any
444      *  @param dirRec pointer to element to be removed from list
445      *  @return status, EC_Normal upon success, an error code otherwise
446      */
447     virtual OFCondition deleteSubAndPurgeFile(DcmDirectoryRecord *dirRec);
448 
449     /// revert the list of child directory records to default constructed (empty) state
450     virtual OFCondition clearSub();
451 
452     /** store the filename from which this directory record was read from
453      *  @param fname filename, must not be empty
454      */
455     virtual void setRecordsOriginFile(const OFFilename &fname);
456 
457     /// get the filename from which this directory record was read from, empty if not set
458     virtual const OFFilename &getRecordsOriginFile();
459 
460     /// get the offset in file of this directory record
461     Uint32 getFileOffset() const;
462 
463 protected:
464 
465     // side-effect-free conversion routines:
466     E_DirRecType        recordNameToType(const char *recordTypeName);
467     char*               buildFileName(const char *origName, char *destName, size_t len) const;
468     OFCondition         checkHierarchy(const E_DirRecType upperRecord,
469                                        const E_DirRecType lowerRecord);
470 
471     // access to data elements within the Directory Records:
472     OFCondition         setRecordType(E_DirRecType newType);
473     E_DirRecType        lookForRecordType();
474     OFCondition         setReferencedFileID( const char *referencedFileID);
475     const char*         lookForReferencedFileID();
476     DcmDirectoryRecord* lookForReferencedMRDR();
477     const char*         getReferencedFileName();      // local or in MRDR
478     OFCondition         setRecordInUseFlag(const Uint16 newFlag);
479     Uint16              lookForRecordInUseFlag();
480     Uint32              setFileOffset(Uint32 position);
481 
482     // access to MRDR data element:
483     OFCondition         setNumberOfReferences(Uint32 newRefNum);
484     Uint32              lookForNumberOfReferences();
485     Uint32              increaseRefNum();
486     Uint32              decreaseRefNum();
487 
488     // misc:
489     /** Load all necessary info for this directory record.
490      *  @param referencedFileID file ID that is being referenced, may be NULL
491      *  @param sourceFileName filename for the DICOM file, may be empty (unspecified)
492      *  @param fileFormat If not NULL, then this should be the result of loading
493      *         sourceFileName. May only be non-NULL if sourceFileName isn't empty.
494      */
495     OFCondition         fillElementsAndReadSOP(const char *referencedFileID,
496                                                const OFFilename &sourceFileName,
497                                                DcmFileFormat *fileFormat = NULL);
498     OFCondition         masterInsertSub(DcmDirectoryRecord *dirRec,
499                                         const unsigned long where = DCM_EndOfListIndex);
500     OFCondition         purgeReferencedFile();
501 
502 private:
503 
504     /// filename (path) of the file from which this directory record was read
505     OFFilename recordsOriginFile;
506 
507     /// list of child directory records, kept in a sequence of items
508     DcmSequenceOfItems *lowerLevelList;
509 
510     /// directory record type of this record
511     E_DirRecType DirRecordType;
512 
513     /// pointer to multi-referenced directory record (MRDR) if this record refers to one, NULL otherwise
514     DcmDirectoryRecord *referencedMRDR;
515 
516     /// number of other directory records referring to this one; used for MRDR records
517     Uint32 numberOfReferences;
518 
519     /// byte offset at which the start of this directory record resides in the file from which it was read
520     Uint32 offsetInFile;
521 
522 };
523 
524 
525 #endif // DCDIRREC_H
526