1 /*=========================================================================
4 Module: $RCSfile: gdcmDicomDir.cxx,v $
6 Date: $Date: 2010/04/09 15:23:40 $
7 Version: $Revision: 1.198 $
9 Copyright (c) CREATIS (Centre de Recherche et d'Applications en Traitement de
10 l'Image). All rights reserved. See Doc/License.txt or
11 http://www.creatis.insa-lyon.fr/Public/Gdcm/License.html for details.
13 This software is distributed WITHOUT ANY WARRANTY; without even
14 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15 PURPOSE. See the above copyright notices for more information.
17 =========================================================================*/
19 //-----------------------------------------------------------------------------
20 // For full DICOMDIR description, see:
21 // PS 3.3-2003, pages 731-750
22 //-----------------------------------------------------------------------------
23 #include "gdcmDicomDir.h"
24 #include "gdcmDicomDirObject.h"
25 #include "gdcmDicomDirStudy.h"
26 #include "gdcmDicomDirSerie.h"
27 #include "gdcmDicomDirVisit.h"
28 #include "gdcmDicomDirImage.h"
29 #include "gdcmDicomDirPrivate.h"
30 #include "gdcmDicomDirPatient.h"
31 #include "gdcmDicomDirMeta.h"
32 #include "gdcmDicomDirElement.h"
33 #include "gdcmDirList.h"
35 #include "gdcmDebug.h"
36 #include "gdcmGlobal.h"
38 #include "gdcmSeqEntry.h"
39 #include "gdcmSQItem.h"
40 #include "gdcmDataEntry.h"
41 #include "gdcmCommandManager.h"
46 #include <sys/types.h>
49 # define getcwd _getcwd
52 #if defined(_MSC_VER) || defined(__BORLANDC__)
58 #if defined(__BORLANDC__)
59 #include <mem.h> // for memset
62 // ----------------------------------------------------------------------------
63 // Note for future developpers
64 // ----------------------------------------------------------------------------
66 // Dicom PS 3.3 describes the relationship between Directory Records, as follow
67 // (see also PS 4.3, 2004, page 50 for Entity-Relationship model)
69 // Directory Record Type Directory Record Types which may be included
70 // in the next lower-level directory Entity
72 // (Root directory Entity) PATIENT, TOPIC, PRIVATE
74 // PATIENT STUDY, PRIVATE
76 // STUDY SERIES, VISIT, RESULTS, STUDY COMPONENT, PRIVATE
78 // SERIES IMAGE, OVERLAYS, MODALITY LUT, VOI LUT,
79 // CURVE, STORED PRINT, RT DOSE, RT STRUCTURE SET
80 // RT PLAN, RT TREAT RECORD, PRESENTATION, WAVEFORM,
81 // SR DOCUMENT, KEY OBJECT DOC, SPECTROSCOPY,
82 // RAW DATA, REGISTRATION, FIDUCIAL, PRIVATE,
108 // see also : ftp://medical.nema.org/medical/dicom/final/cp343_ft.doc
110 RELATIONSHIP BETWEEN DIRECTORY RECORDS
112 Directory Record Type Directory Record Types which may be included
113 in the next lower-level directory Entity
115 (Root Directory Entity) PATIENT, TOPIC, PRIVATE
117 PATIENT STUDY, PRIVATE
119 STUDY SERIES, VISIT, RESULTS, STUDY COMPONENT, PRIVATE
121 SERIES IMAGE, OVERLAY, MODALITY LUT, VOI LUT, CURVE,
122 STORED PRINT, RT DOSE, RT STRUCTURE SET, RT PLAN,
123 RT TREAT RECORD, PRESENTATION, WAVEFORM, SR DOCUMENT,
124 KEY OBJECT DOC, SPECTROSCOPY, RAW DATA, PRIVATE
133 RT STRUCTURE SET PRIVATE
135 RT TREAT RECORD PRIVATE
139 KEY OBJECT DOC PRIVATE
143 TOPIC STUDY, SERIES, IMAGE, OVERLAY, MODALITY LUT, VOI LUT,
144 CURVE, STORED PRINT, RT DOSE, RT STRUCTURE SET,
145 RT PLAN, RT TREAT RECORD, PRESENTATION, WAVEFORM,
146 SR DOCUMENT, KEY OBJECT DOC, SPECTROSCOPY, RAW DATA,
151 RESULTS INTERPRETATION, PRIVATE
153 INTERPRETATION PRIVATE
154 STUDY COMPONENT PRIVATE
155 PRIVATE PRIVATE, (any of the above as privately defined)
156 MRDR (Not applicable)
158 Note : Directory Record Types PRINT QUEUE, FILM SESSION, FILM BOX, and
159 IMAGE BOX were previously defined in DICOM. They have been retired.
163 // ----------------------
164 // The current gdcm version only deals with :
166 // (Root directory Entity) PATIENT
173 // DicomDir::CreateDicomDir will have to be completed
174 // Treelike structure management will have to be upgraded
175 // ----------------------------------------------------------------------------
177 namespace GDCM_NAME_SPACE
179 //-----------------------------------------------------------------------------
180 // Constructor / Destructor
182 * \brief Constructor : creates an empty DicomDir
186 Initialize(); // sets all private fields to NULL
191 //#ifndef GDCM_LEGACY_REMOVE
193 * \brief Constructor Parses recursively the directory and creates the DicomDir
194 * or uses an already built DICOMDIR, depending on 'parseDir' value.
195 * @param fileName name
196 * - of the root directory (parseDir = true)
197 * - of the DICOMDIR (parseDir = false)
198 * @param parseDir boolean
199 * - true if user passed an entry point
200 * and wants to explore recursively the directories
201 * - false if user passed an already built DICOMDIR file
202 * and wants to use it
203 * @deprecated use : new DicomDir() + [ SetLoadMode(lm) + ] SetDirectoryName(name)
204 * or : new DicomDir() + SetFileName(name)
207 DicomDir::DicomDir(std::string const &fileName, bool parseDir ):
210 // At this step, Document constructor is already executed,
211 // whatever user passed (either a root directory or a DICOMDIR)
212 // and whatever the value of parseDir was.
213 // (nothing is checked in Document constructor, to avoid overhead)
216 SetLoadMode (LD_ALL); // concerns only dicom files
217 SetFileName( fileName );
224 * \brief Canonical destructor
226 DicomDir::~DicomDir()
235 //-----------------------------------------------------------------------------
239 * \brief Loader. use SetFileName(fn)
240 * or SetLoadMode(lm) + SetDirectoryName(dn) before !
241 * @return false if file cannot be open or no swap info was found,
242 * or no tag was found.
244 bool DicomDir::Load( )
248 if ( ! this->Document::Load( ) )
251 return DoTheLoadingJob( );
253 //#ifndef GDCM_LEGACY_REMOVE
255 * \brief Loader. (DEPRECATED : kept not to break the API)
256 * @param fileName file to be open for parsing
257 * @return false if file cannot be open or no swap info was found,
258 * or no tag was found.
259 * @deprecated use SetFileName(n) + Load() instead
262 bool DicomDir::Load(std::string const &fileName )
264 // We should clean out anything that already exists.
265 Initialize(); // sets all private fields to NULL
267 SetFileName( fileName );
270 if ( ! this->Document::Load( ) )
273 return DoTheLoadingJob( );
279 * \brief Does the Loading Job (internal use only)
280 * @return false if file cannot be open or no swap info was found,
281 * or no tag was found.
283 bool DicomDir::DoTheLoadingJob( )
290 // Only if user passed a DICOMDIR
291 // ------------------------------
293 if (!Document::Load() )
298 if ( GetFirstEntry() == 0 ) // when user passed a Directory to parse
300 gdcmWarningMacro( "Entry HT empty for file: "<< GetFileName());
303 // Directory record sequence
304 DocEntry *e = GetDocEntry(0x0004, 0x1220);
307 gdcmWarningMacro( "NO 'Directory record sequence' (0x0004,0x1220)"
308 << " in file " << GetFileName());
318 // Only if user passed a root directory
319 // ------------------------------------
320 if ( GetFileName() == "." )
322 // user passed '.' as Name
323 // we get current directory name
325 const char *cwd = getcwd(buf, 2048);
328 SetFileName( buf ); // will be converted into a string
332 gdcmErrorMacro( "Path was too long to fit on 2048 bytes" );
336 gdcmDebugMacro( "Parse directory and create the DicomDir : "
344 * \brief This predicate, based on hopefully reasonable heuristics,
345 * decides whether or not the current document was properly parsed
346 * and contains the mandatory information for being considered as
347 * a well formed and usable DicomDir.
348 * @return true when Document is the one of a reasonable DicomDir,
351 bool DicomDir::IsReadable()
353 if ( Filetype == Unknown )
355 gdcmErrorMacro( "Wrong filetype for " << GetFileName());
360 gdcmWarningMacro( "Meta Elements missing in DicomDir");
363 if ( Patients.size() <= 0 )
365 gdcmWarningMacro( "NO Patient in DicomDir");
373 * \brief adds *the* Meta to a partially created DICOMDIR
375 DicomDirMeta *DicomDir::NewMeta()
381 DocEntry *entry = GetFirstEntry();
384 MetaElems = DicomDirMeta::New(true); // true = empty
386 entry = GetFirstEntry();
389 if ( dynamic_cast<SeqEntry *>(entry) )
392 MetaElems->AddEntry(entry);
395 entry = GetFirstEntry();
398 else // after root directory parsing
400 MetaElems = DicomDirMeta::New(false); // false = not empty
402 MetaElems->SetSQItemNumber(0); // To avoid further missprinting
407 * \brief adds a new Patient (with the basic elements) to a partially created
410 DicomDirPatient *DicomDir::NewPatient()
412 DicomDirPatient *dd = DicomDirPatient::New();
413 AddPatientToEnd( dd );
418 * \brief Remove all Patients
420 void DicomDir::ClearPatient()
422 for(ListDicomDirPatient::iterator cc = Patients.begin();
432 * \brief Get the first entry while visiting the DicomDirPatients
433 * \return The first DicomDirPatient if found, otherwhise NULL
435 DicomDirPatient *DicomDir::GetFirstPatient()
437 ItPatient = Patients.begin();
438 if ( ItPatient != Patients.end() )
444 * \brief Get the next entry while visiting the DicomDirPatients
445 * \note : meaningfull only if GetFirstEntry already called
446 * \return The next DicomDirPatient if found, otherwhise NULL
448 DicomDirPatient *DicomDir::GetNextPatient()
450 gdcmAssertMacro (ItPatient != Patients.end());
453 if ( ItPatient != Patients.end() )
459 * \brief fills the whole structure, starting from a root Directory
461 void DicomDir::ParseDirectory()
463 CreateDicomDirChainedList( GetFileName() );
468 * \brief writes on disc a DICOMDIR
469 * \ warning does NOT add the missing elements in the header :
470 * it's up to the user doing it !
471 * @param fileName file to be written to
472 * @return false only when fail to open
475 bool DicomDir::Write(std::string const &fileName)
478 uint16_t sq[6] = { 0x0004, 0x1220, 0x5153, 0x0000, 0xffff, 0xffff };
479 uint16_t sqt[4]= { 0xfffe, 0xe0dd, 0x0000, 0x0000 };
481 std::ofstream *fp = new std::ofstream(fileName.c_str(),
482 std::ios::out | std::ios::binary);
485 gdcmWarningMacro("Failed to open(write) File: " << fileName.c_str());
489 char filePreamble[128];
490 memset(filePreamble, 0, 128);
491 fp->write(filePreamble, 128);
492 binary_write( *fp, "DICM");
494 DicomDirMeta *ptrMeta = GetMeta();
495 ptrMeta->WriteContent(fp, ExplicitVR, true, false);
497 // force writing 0004|1220 [SQ ], that CANNOT exist within DicomDirMeta
500 binary_write(*fp, sq[i]);
503 for(ListDicomDirPatient::iterator cc = Patients.begin();
504 cc != Patients.end();
507 (*cc)->WriteContent( fp, ExplicitVR, false, true );
510 // force writing Sequence Delimitation Item
513 binary_write(*fp, sqt[i]); // fffe e0dd 0000 0000
523 * \brief Anonymize a DICOMDIR
527 bool DicomDir::Anonymize()
530 // Something clever to be found to forge the Patient names
531 std::ostringstream s;
533 for(ListDicomDirPatient::iterator cc = Patients.begin();
538 v = (*cc)->GetDataEntry(0x0010, 0x0010) ; // Patient's Name
541 v->SetString(s.str());
544 v = (*cc)->GetDataEntry(0x0010, 0x0020) ; // Patient ID
550 v = (*cc)->GetDataEntry(0x0010, 0x0030) ; // Patient's BirthDate
562 * \brief Copies all the attributes from an other DocEntrySet
563 * @param set entry to copy from
564 * @remarks The contained DocEntries are not copied, only referenced
566 void DicomDir::Copy(DocEntrySet *set)
568 // Remove all previous childs
573 DicomDir *dd = dynamic_cast<DicomDir *>(set);
577 MetaElems->Unregister();
578 MetaElems = dd->MetaElems;
580 MetaElems->Register();
582 Patients = dd->Patients;
583 for(ItPatient = Patients.begin();ItPatient != Patients.end();++ItPatient)
584 (*ItPatient)->Register();
588 //-----------------------------------------------------------------------------
591 * \brief create a Document-like chained list from a root Directory
592 * @param path entry point of the tree-like structure
594 void DicomDir::CreateDicomDirChainedList(std::string const &path)
597 DirList dirList(path,1); // gets recursively the file list
598 unsigned int count = 0;
602 DirListType fileList = dirList.GetFilenames();
603 unsigned int nbFile = fileList.size();
604 for( DirListType::iterator it = fileList.begin();
605 it != fileList.end();
608 Progress = (float)(count+1)/(float)nbFile;
609 CallProgressMethod();
616 f->SetLoadMode(LoadMode); // we allow user not to load Sequences,
617 // or Shadow groups, or ......
618 f->SetFileName( it->c_str() );
621 if ( f->IsReadable() )
623 // Add the file to the chained list:
625 gdcmDebugMacro( "Readable " << it->c_str() );
633 // sorts Patient/Study/Serie/
634 std::sort(list.begin(), list.end(), DicomDir::HeaderLessThan );
636 std::string tmp = dirList.GetDirName();
637 //for each File of the chained list, add/update the Patient/Study/Serie/Image info
638 SetElements(tmp, list);
641 for(VectDocument::iterator itDoc=list.begin();
645 dynamic_cast<File *>(*itDoc)->Delete();
650 //-----------------------------------------------------------------------------
653 * \brief Sets all fields to NULL
655 void DicomDir::Initialize()
664 * \brief create a 'gdcm::DicomDir' from a DICOMDIR Header
666 void DicomDir::CreateDicomDir()
668 // The SeqEntries of "Directory Record Sequence" are parsed.
669 // When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
670 // N.B. : VISIT, PRIVATE not fully dealt with
671 // 1 - we save the beginning iterator
672 // 2 - we continue to parse
673 // 3 - we find an other tag
674 // + we create the object for the precedent tag
676 gdcmDebugMacro("Create DicomDir");
678 // Directory record sequence
679 DocEntry *e = GetDocEntry(0x0004, 0x1220);
682 gdcmWarningMacro( "No Directory Record Sequence (0004,1220) found");
686 SeqEntry *s = dynamic_cast<SeqEntry *>(e);
689 gdcmWarningMacro( "Element (0004,1220) is not a Sequence ?!?");
699 SQItem *tmpSI=s->GetFirstSQItem();
702 d = tmpSI->GetDocEntry(0x0004, 0x1430); // Directory Record Type
703 if ( DataEntry *dataEntry = dynamic_cast<DataEntry *>(d) )
705 v = dataEntry->GetString();
709 gdcmWarningMacro( "(0004,1430) not a DataEntry ?!?");
713 // A decent DICOMDIR has much more images than series,
714 // more series than studies, and so on.
715 // This is the right order to perform the tests
719 si = DicomDirImage::New(true); // true = empty
720 if ( !AddImageToEnd( static_cast<DicomDirImage *>(si)) )
724 gdcmErrorMacro( "Add AddImageToEnd failed");
727 else if ( v == "SERIES" )
729 si = DicomDirSerie::New(true); // true = empty
730 if ( !AddSerieToEnd( static_cast<DicomDirSerie *>(si)) )
734 gdcmErrorMacro( "Add AddSerieToEnd failed");
737 else if ( v == "VISIT " )
739 si = DicomDirVisit::New(true); // true = empty
740 if ( !AddVisitToEnd( static_cast<DicomDirVisit *>(si)) )
744 gdcmErrorMacro( "Add AddVisitToEnd failed");
747 else if ( v == "STUDY " )
749 si = DicomDirStudy::New(true); // true = empty
750 if ( !AddStudyToEnd( static_cast<DicomDirStudy *>(si)) )
754 gdcmErrorMacro( "Add AddStudyToEnd failed");
757 else if ( v == "PATIENT " )
759 si = DicomDirPatient::New(true); // true = empty
760 if ( !AddPatientToEnd( static_cast<DicomDirPatient *>(si)) )
764 gdcmErrorMacro( "Add PatientToEnd failed");
767 /// \todo : deal with PRIVATE (not so easy, since PRIVATE appears
768 /// at different levels ?!? )
770 else if ( v == "PRIVATE " ) // for SIEMENS 'CSA Non Image'
773 gdcmWarningMacro( " -------------------------------------------"
774 << "a PRIVATE SQItem was found : " << v);
775 si = DicomDirPrivate::New(true); // true = empty
776 if ( !AddPrivateToEnd( static_cast<DicomDirPrivate *>(si)) )
780 gdcmErrorMacro( "Add PrivateToEnd failed");
785 // It was neither a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
786 // nor an 'IMAGE' SQItem. Skip to next item.
787 gdcmWarningMacro( " -------------------------------------------"
788 << "a non PATIENT/STUDY/SERIE/IMAGE /VISIT/PRIVATE SQItem was found : "
791 // FIXME : deal with other item types !
792 tmpSI=s->GetNextSQItem(); // To avoid infinite loop
798 tmpSI=s->GetNextSQItem();
804 * \brief AddPatientToEnd
805 * @param dd SQ Item to enqueue to the DicomPatient chained List
807 bool DicomDir::AddPatientToEnd(DicomDirPatient *dd)
809 Patients.push_back(dd);
814 * \brief AddStudyToEnd
815 * @param dd SQ Item to enqueue to the DicomDirStudy chained List
817 bool DicomDir::AddStudyToEnd(DicomDirStudy *dd)
819 if ( Patients.size() > 0 )
821 ListDicomDirPatient::iterator itp = Patients.end();
823 (*itp)->AddStudy(dd);
830 * \brief AddSerieToEnd
831 * @param dd SQ Item to enqueue to the DicomDirSerie chained List
833 bool DicomDir::AddSerieToEnd(DicomDirSerie *dd)
835 if ( Patients.size() > 0 )
837 ListDicomDirPatient::iterator itp = Patients.end();
840 DicomDirStudy *study = (*itp)->GetLastStudy();
851 * \brief AddVisitToEnd
852 * @param dd SQ Item to enqueue to the DicomDirVisit chained List
854 bool DicomDir::AddVisitToEnd(DicomDirVisit *dd)
856 if ( Patients.size() > 0 )
858 ListDicomDirPatient::iterator itp = Patients.end();
861 DicomDirStudy *study = (*itp)->GetLastStudy();
871 * \brief AddImageToEnd
872 * @param dd SQ Item to enqueue to the DicomDirImage chained List
874 bool DicomDir::AddImageToEnd(DicomDirImage *dd)
876 if ( Patients.size() > 0 )
878 ListDicomDirPatient::iterator itp = Patients.end();
881 DicomDirStudy *study = (*itp)->GetLastStudy();
884 DicomDirSerie *serie = study->GetLastSerie();
896 * \brief AddPrivateToEnd
897 * @param dd SQ Item to enqueue to the DicomDirPrivate chained List
898 * (checked for SIEMENS 'CSA non image')
900 bool DicomDir::AddPrivateToEnd(DicomDirPrivate *dd)
902 if ( Patients.size() > 0 )
904 ListDicomDirPatient::iterator itp = Patients.end();
907 DicomDirStudy *study = (*itp)->GetLastStudy();
910 DicomDirSerie *serie = study->GetLastSerie();
913 serie->AddPrivate(dd);
922 * \brief for each Header of the chained list,
923 * add/update the Patient/Study/Serie/Image info
924 * @param path path of the root directory
925 * @param list chained list of Headers
927 void DicomDir::SetElements(std::string const &path, VectDocument const &list)
932 std::string patPrevName = "", patPrevID = "";
933 std::string studPrevInstanceUID = "", studPrevID = "";
934 std::string serPrevInstanceUID = "", serPrevID = "";
936 std::string patCurName, patCurID;
937 std::string studCurInstanceUID, studCurID;
938 std::string serCurInstanceUID, serCurID;
941 for( VectDocument::const_iterator it = list.begin();
945 // get the current file characteristics
946 patCurName = (*it)->GetEntryString(0x0010,0x0010);
947 if (patCurName == "") // to prevent further troubles when wild anonymization was performed
948 patCurName = "gdcm^Patient";
949 patCurID = (*it)->GetEntryString(0x0010,0x0011);
950 studCurInstanceUID = (*it)->GetEntryString(0x0020,0x000d);
951 studCurID = (*it)->GetEntryString(0x0020,0x0010);
952 serCurInstanceUID = (*it)->GetEntryString(0x0020,0x000e);
953 serCurID = (*it)->GetEntryString(0x0020,0x0011);
955 if ( patCurName != patPrevName || patCurID != patPrevID || first )
957 SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
961 // if new Study, deal with 'STUDY' Elements
962 if ( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID
965 SetElement(path, GDCM_DICOMDIR_STUDY, *it);
969 // if new Serie, deal with 'SERIE' Elements
970 if ( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID
973 SetElement(path, GDCM_DICOMDIR_SERIE, *it);
976 // Always Deal with 'IMAGE' Elements
977 SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
979 patPrevName = patCurName;
980 patPrevID = patCurID;
981 studPrevInstanceUID = studCurInstanceUID;
982 studPrevID = studCurID;
983 serPrevInstanceUID = serCurInstanceUID;
984 serPrevID = serCurID;
990 * \brief adds to the HTable
991 * the Entries (Dicom Elements) corresponding to the given type
992 * @param path full path file name (only used when type = GDCM_DICOMDIR_IMAGE
993 * @param type DicomDirObject type to create (GDCM_DICOMDIR_PATIENT,
994 * GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
995 * @param header Header of the current file
997 void DicomDir::SetElement(std::string const &path, DicomDirType type,
1000 ListDicomDirElem elemList;
1001 ListDicomDirElem::const_iterator it;
1002 uint16_t tmpGr, tmpEl;
1003 //DictEntry *dictEntry;
1009 case GDCM_DICOMDIR_IMAGE:
1010 elemList = Global::GetDicomDirElements()->GetDicomDirImageElements();
1011 si = DicomDirImage::New(true);
1012 if ( !AddImageToEnd(static_cast<DicomDirImage *>(si)) )
1015 gdcmErrorMacro( "Add ImageToEnd failed");
1018 case GDCM_DICOMDIR_SERIE:
1019 elemList = Global::GetDicomDirElements()->GetDicomDirSerieElements();
1020 si = DicomDirSerie::New(true);
1021 if ( !AddSerieToEnd(static_cast<DicomDirSerie *>(si)) )
1024 gdcmErrorMacro( "Add SerieToEnd failed");
1027 case GDCM_DICOMDIR_STUDY:
1028 elemList = Global::GetDicomDirElements()->GetDicomDirStudyElements();
1029 si = DicomDirStudy::New(true);
1030 if ( !AddStudyToEnd(static_cast<DicomDirStudy *>(si)) )
1033 gdcmErrorMacro( "Add StudyToEnd failed");
1036 case GDCM_DICOMDIR_PATIENT:
1037 elemList = Global::GetDicomDirElements()->GetDicomDirPatientElements();
1038 si = DicomDirPatient::New(true);
1039 if ( !AddPatientToEnd(static_cast<DicomDirPatient *>(si)) )
1042 gdcmErrorMacro( "Add PatientToEnd failed");
1045 case GDCM_DICOMDIR_META: // never used ?!? --> Done within DoTheLoadingJob
1048 MetaElems->Delete();
1049 gdcmErrorMacro( "MetaElements already exist, they will be destroyed");
1051 elemList = Global::GetDicomDirElements()->GetDicomDirMetaElements();
1052 MetaElems = DicomDirMeta::New(true);
1059 // FIXME : troubles found when it's a SeqEntry
1061 // removed all the seems-to-be-useless stuff about Referenced Image Sequence
1062 // to avoid further troubles
1063 // imageElem 0008 1140 "" // Referenced Image Sequence
1064 // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
1065 // imageElem 0008 1150 "" // Referenced SOP Class UID : to be set/forged later
1066 // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
1067 // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
1069 std::string referencedVal;
1070 // for all the relevant elements found in their own spot of the DicomDir.dic
1071 for( it = elemList.begin(); it != elemList.end(); ++it)
1076 entry = DataEntry::New(tmpGr, tmpEl, it->VR); // dicomelements file was modified, to store VR
1077 entry->SetOffset(0); // just to avoid further missprinting
1081 // NULL when we Build Up (ex nihilo) a DICOMDIR
1082 // or when we add the META elems
1083 val = header->GetEntryString(tmpGr, tmpEl);
1090 if ( val == GDCM_UNFOUND)
1092 if ( tmpGr == 0x0004 ) // never present in File !
1096 case 0x1130: // File-set ID
1097 // force to the *end* File Name
1098 val = Util::GetName( path );
1101 case 0x1500: // Only used for image
1102 if ( header->GetFileName().substr(0, path.length()) != path )
1104 gdcmWarningMacro( "The base path of file name is incorrect");
1105 val = header->GetFileName();
1109 // avoid the first '/' in File name !
1110 if ( header->GetFileName().c_str()[path.length()]
1111 == GDCM_FILESEPARATOR )
1112 val = &(header->GetFileName().c_str()[path.length()+1]);
1114 val = &(header->GetFileName().c_str()[path.length()]);
1118 case 0x1510: // Referenced SOP Class UID in File
1119 referencedVal = header->GetEntryString(0x0008, 0x0016);
1120 // FIXME : probabely something to check
1121 val = referencedVal;
1124 case 0x1511: // Referenced SOP Instance UID in File
1125 referencedVal = header->GetEntryString(0x0008, 0x0018);
1126 // FIXME : probabely something to check
1127 val = referencedVal;
1130 case 0x1512: // Referenced Transfer Syntax UID in File
1131 referencedVal = header->GetEntryString(0x0002, 0x0010);
1132 // FIXME : probabely something to check
1133 val = referencedVal;
1142 // If the entry is not found in the Header, don't write its 'value' in the DICOMDIR !
1149 if ( header->GetEntryLength(tmpGr,tmpEl) == 0 )
1152 // Don't polute the DICOMDIR with empty fields
1161 /* FIX later the pb of creating the 'Implementation Version Name'!
1163 if (val == GDCM_UNFOUND)
1166 if ( tmpGr == 0x0002 && tmpEl == 0x0013)
1168 // 'Implementation Version Name'
1169 std::string val = "GDCM ";
1170 val += Util::GetVersion();
1174 entry->SetString( val ); // troubles expected when vr=SQ ...
1176 if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
1178 gdcmDebugMacro("GDCM_DICOMDIR_META ?!? should never print that");
1181 si->AddEntry(entry);
1187 * \brief Move the content of the source SQItem to the destination SQItem
1188 * Only DocEntry's are moved
1189 * @param dst destination SQItem
1190 * @param src source SQItem
1192 void DicomDir::MoveSQItem(DocEntrySet *dst, DocEntrySet *src)
1195 // todo : rewrite the whole stuff, without using RemoveEntry an AddEntry,
1197 entry = src->GetFirstEntry();
1200 dst->AddEntry(entry); // use it, *before* removing it!
1201 src->RemoveEntry(entry);
1202 // we destroyed -> the current iterator is not longer valid
1203 entry = src->GetFirstEntry();
1208 * \brief compares two files
1210 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
1212 return *header1 < *header2;
1215 //-----------------------------------------------------------------------------
1218 * \brief Canonical Printer
1219 * @param os ostream we want to print in
1220 * @param indent Indentation string to be prepended during printing
1222 void DicomDir::Print(std::ostream &os, std::string const & )
1226 MetaElems->SetPrintLevel(PrintLevel);
1227 MetaElems->Print(os);
1229 for(ListDicomDirPatient::iterator cc = Patients.begin();
1230 cc != Patients.end();
1233 (*cc)->SetPrintLevel(PrintLevel);
1238 //-----------------------------------------------------------------------------
1239 } // end namespace gdcm