]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
(try to) solve the trouble with leading / in filenames
[gdcm.git] / src / gdcmDicomDir.cxx
1 /*=========================================================================
2   
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDicomDir.cxx,v $
5   Language:  C++
6   Date:      $Date: 2006/01/18 10:17:40 $
7   Version:   $Revision: 1.184 $
8   
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.
12   
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.
16   
17 =========================================================================*/
18
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 "gdcmDicomDirPatient.h"
30 #include "gdcmDicomDirMeta.h"
31 #include "gdcmDicomDirElement.h"
32 #include "gdcmDirList.h"
33 #include "gdcmUtil.h"
34 #include "gdcmDebug.h"
35 #include "gdcmGlobal.h"
36 #include "gdcmFile.h"
37 #include "gdcmSeqEntry.h"
38 #include "gdcmSQItem.h"
39 #include "gdcmDataEntry.h"
40 #include "gdcmCommandManager.h"
41
42 #include <fstream>
43 #include <string>
44 #include <algorithm>
45 #include <sys/types.h>
46
47 #ifdef _MSC_VER
48 #   define getcwd _getcwd
49 #endif
50
51 #if defined(_MSC_VER) || defined(__BORLANDC__)
52 #   include <direct.h>
53 #else
54 #   include <unistd.h>
55 #endif
56 // ----------------------------------------------------------------------------
57 //         Note for future developpers
58 // ----------------------------------------------------------------------------
59 //
60 //  Dicom PS 3.3 describes the relationship between Directory Records, as follow
61 //    (see also PS 4.3, 2004, page 50 for Entity-Relationship model)
62 //
63 //  Directory Record Type      Directory Record Types which may be included
64 //                                in the next lower-level directory Entity
65 //
66 // (Root directory Entity)     PATIENT, TOPIC, PRIVATE
67 //
68 // PATIENT                     STUDY, PRIVATE
69 //
70 // STUDY                       SERIES, VISIT, RESULTS, STUDY COMPONENT, PRIVATE
71 //
72 // SERIES                      IMAGE, OVERLAYS, MODALITY LUT, VOI LUT,
73 //                             CURVE, STORED PRINT, RT DOSE, RT STRUCTURE SET
74 //                             RT PLAN, RT TREAT RECORD, PRESENTATION, WAVEFORM,
75 //                             SR DOCUMENT, KEY OBJECT DOC, SPECTROSCOPY,
76 //                             RAW DATA, REGISTRATION, FIDUCIAL, PRIVATE,
77 //                             ENCAP DOC
78 // IMAGE
79 // OVERLAY
80 // MODALITY LUT
81 // VOI LUT
82 // CURVE
83 // STORED PRINT
84 // RT DOSE
85 // RT STRUCTURE SET
86 // RT PLAN
87 // RT TREAT RECORD
88 // PRESENTATION
89 // WAVEFORM
90 // SR DOCUMENT
91 // KEY OBJECT DOC
92 // SPECTROSCOPY
93 // RAW DATA
94 // REGISTRATION
95 // FIDUCIAL
96 // PRIVATE
97 // ENCAP DOC
98 // 
99 // ----------------------
100 // The current gdcm version only deals with :
101 //
102 // (Root directory Entity)     PATIENT
103 // PATIENT                     STUDY
104 // STUDY                       SERIES
105 // STUDY                       VISIT 
106 // SERIES                      IMAGE
107 // IMAGE                       /
108 //
109 // DicomDir::CreateDicomDir will have to be completed
110 // Treelike structure management will have to be upgraded
111 // ----------------------------------------------------------------------------
112     
113 namespace gdcm 
114 {
115 //-----------------------------------------------------------------------------
116 // Constructor / Destructor
117 /**
118  * \brief   Constructor : creates an empty DicomDir
119  */
120 DicomDir::DicomDir()
121 {
122    Initialize();  // sets all private fields to NULL
123    ParseDir = false;
124    NewMeta();
125 }
126
127 /**
128  * \brief  Canonical destructor 
129  */
130 DicomDir::~DicomDir() 
131 {
132    ClearPatient();
133    if ( MetaElems )
134    {
135       MetaElems->Delete();
136    }
137 }
138
139 //-----------------------------------------------------------------------------
140 // Public
141
142 /**
143  * \brief   Loader. use SetFileName(fn) 
144  *                  or SetLoadMode(lm) + SetDirectoryName(dn)  before !  
145  * @return false if file cannot be open or no swap info was found,
146  *         or no tag was found.
147  */
148 bool DicomDir::Load( ) 
149 {
150    if (!ParseDir)
151    {
152       if ( ! this->Document::Load( ) )
153          return false;
154    }
155    return DoTheLoadingJob( );   
156 }
157
158 /**
159  * \brief   Does the Loading Job (internal use only)
160  * @return false if file cannot be open or no swap info was found,
161  *         or no tag was found.
162  */
163 bool DicomDir::DoTheLoadingJob( ) 
164 {
165    Progress = 0.0f;
166    Abort = false;
167
168    if (!ParseDir)
169    {
170    // Only if user passed a DICOMDIR
171    // ------------------------------
172       Fp = 0;
173       if (!Document::Load() )
174       {
175          return false;
176       }
177
178       if ( GetFirstEntry() == 0 ) // when user passed a Directory to parse
179       {
180          gdcmWarningMacro( "Entry HT empty for file: "<< GetFileName());
181          return false;
182       }
183       // Directory record sequence
184       DocEntry *e = GetDocEntry(0x0004, 0x1220);
185       if ( !e )
186       {
187          gdcmWarningMacro( "NO 'Directory record sequence' (0x0004,0x1220)"
188                           << " in file " << GetFileName());
189          return false;
190       }
191       else
192          CreateDicomDir();
193    }
194    else
195    {
196    // Only if user passed a root directory
197    // ------------------------------------
198       if ( GetFileName() == "." )
199       {
200          // user passed '.' as Name
201          // we get current directory name
202          char buf[2048];
203          const char *cwd = getcwd(buf, 2048);
204          if( cwd )
205          {
206            SetFileName( buf ); // will be converted into a string
207          }
208          else
209          {
210            gdcmErrorMacro( "Path was too long to fit on 2048 bytes" );
211          }
212       }
213       NewMeta();
214       gdcmDebugMacro( "Parse directory and create the DicomDir : " 
215                          << GetFileName() );
216       ParseDirectory();
217    }
218    return true;
219 }
220
221 /**
222  * \brief  This predicate, based on hopefully reasonable heuristics,
223  *         decides whether or not the current document was properly parsed
224  *         and contains the mandatory information for being considered as
225  *         a well formed and usable DicomDir.
226  * @return true when Document is the one of a reasonable DicomDir,
227  *         false otherwise. 
228  */
229 bool DicomDir::IsReadable()
230 {
231    if ( Filetype == Unknown )
232    {
233       gdcmErrorMacro( "Wrong filetype for " << GetFileName());
234       return false;
235    }
236    if ( !MetaElems )
237    {
238       gdcmWarningMacro( "Meta Elements missing in DicomDir");
239       return false;
240    }
241    if ( Patients.size() <= 0 )
242    {
243       gdcmWarningMacro( "NO Patient in DicomDir");
244       return false;
245    }
246
247    return true;
248 }
249
250 /**
251  * \brief   adds *the* Meta to a partially created DICOMDIR
252  */  
253 DicomDirMeta *DicomDir::NewMeta()
254 {
255    if ( MetaElems )
256       MetaElems->Delete();
257
258    DocEntry *entry = GetFirstEntry();
259    if ( entry )
260    { 
261       MetaElems = DicomDirMeta::New(true); // true = empty
262
263       entry = GetFirstEntry();
264       while( entry )
265       {
266          if ( dynamic_cast<SeqEntry *>(entry) )
267             break;
268
269          MetaElems->AddEntry(entry);
270          RemoveEntry(entry);
271
272          entry = GetFirstEntry();
273       }
274    }
275    else  // after root directory parsing
276    {
277       MetaElems = DicomDirMeta::New(false); // false = not empty
278    }
279    MetaElems->SetSQItemNumber(0); // To avoid further missprinting
280    return MetaElems;  
281 }
282
283 /**
284  * \brief   adds a new Patient (with the basic elements) to a partially created
285  *          DICOMDIR
286  */
287 DicomDirPatient *DicomDir::NewPatient()
288 {
289    DicomDirPatient *dd = DicomDirPatient::New();
290    AddPatientToEnd( dd );
291    return dd;
292 }
293
294 /**
295  * \brief   Remove all Patients
296  */
297 void DicomDir::ClearPatient()
298 {
299    for(ListDicomDirPatient::iterator cc = Patients.begin();
300                                      cc!= Patients.end();
301                                    ++cc)
302    {
303       (*cc)->Unregister();
304    }
305    Patients.clear();
306 }
307
308 /**
309  * \brief   Get the first entry while visiting the DicomDirPatients
310  * \return  The first DicomDirPatient if found, otherwhise NULL
311  */ 
312 DicomDirPatient *DicomDir::GetFirstPatient()
313 {
314    ItPatient = Patients.begin();
315    if ( ItPatient != Patients.end() )
316       return *ItPatient;
317    return NULL;
318 }
319
320 /**
321  * \brief   Get the next entry while visiting the DicomDirPatients
322  * \note : meaningfull only if GetFirstEntry already called
323  * \return  The next DicomDirPatient if found, otherwhise NULL
324  */
325 DicomDirPatient *DicomDir::GetNextPatient()
326 {
327    gdcmAssertMacro (ItPatient != Patients.end());
328
329    ++ItPatient;
330    if ( ItPatient != Patients.end() )
331       return *ItPatient;
332    return NULL;
333 }
334
335 /**
336  * \brief  fills the whole structure, starting from a root Directory
337  */
338 void DicomDir::ParseDirectory()
339 {
340    CreateDicomDirChainedList( GetFileName() );
341    CreateDicomDir();
342 }
343
344 /**
345  * \brief    writes on disc a DICOMDIR
346  * \ warning does NOT add the missing elements in the header :
347  *           it's up to the user doing it !
348  * @param  fileName file to be written to 
349  * @return false only when fail to open
350  */
351  
352 bool DicomDir::Write(std::string const &fileName) 
353 {  
354    int i;
355    uint16_t sq[6] = { 0x0004, 0x1220, 0x5153, 0x0000, 0xffff, 0xffff };
356    uint16_t sqt[4]= { 0xfffe, 0xe0dd, 0x0000, 0x0000 };
357
358    std::ofstream *fp = new std::ofstream(fileName.c_str(),  
359                                          std::ios::out | std::ios::binary);
360    if ( !fp ) 
361    {
362       gdcmWarningMacro("Failed to open(write) File: " << fileName.c_str());
363       return false;
364    }
365
366    char filePreamble[128];
367    memset(filePreamble, 0, 128);
368    fp->write(filePreamble, 128);
369    binary_write( *fp, "DICM");
370  
371    DicomDirMeta *ptrMeta = GetMeta();
372    ptrMeta->WriteContent(fp, ExplicitVR);
373    
374    // force writing 0004|1220 [SQ ], that CANNOT exist within DicomDirMeta
375    for(i=0;i<6;++i)
376    {
377       binary_write(*fp, sq[i]);
378    }
379         
380    for(ListDicomDirPatient::iterator cc  = Patients.begin();
381                                      cc != Patients.end();
382                                    ++cc )
383    {
384       (*cc)->WriteContent( fp, ExplicitVR );
385    }
386    
387    // force writing Sequence Delimitation Item
388    for(i=0;i<4;++i)
389    {
390       binary_write(*fp, sqt[i]);  // fffe e0dd 0000 0000 
391    }
392
393    fp->close();
394    delete fp;
395
396    return true;
397 }
398
399 /**
400  * \brief    Anonymize a DICOMDIR
401  * @return true 
402  */
403  
404 bool DicomDir::Anonymize() 
405 {
406    DataEntry *v;
407    // Something clever to be found to forge the Patient names
408    std::ostringstream s;
409    int i = 1;
410    for(ListDicomDirPatient::iterator cc = Patients.begin();
411                                      cc!= Patients.end();
412                                    ++cc)
413    {
414       s << i;
415       v = (*cc)->GetDataEntry(0x0010, 0x0010) ; // Patient's Name
416       if (v)
417       {
418          v->SetString(s.str());
419       }
420
421       v = (*cc)->GetDataEntry(0x0010, 0x0020) ; // Patient ID
422       if (v)
423       {
424          v->SetString(" ");
425       }
426
427       v = (*cc)->GetDataEntry(0x0010, 0x0030) ; // Patient's BirthDate
428       if (v)
429       {
430          v->SetString(" ");
431       }
432       s << "";
433       i++;
434    }
435    return true;
436 }
437
438 /**
439  * \brief Copies all the attributes from an other DocEntrySet 
440  * @param set entry to copy from
441  * @remarks The contained DocEntries are not copied, only referenced
442  */
443 void DicomDir::Copy(DocEntrySet *set)
444 {
445    // Remove all previous childs
446    ClearPatient();
447
448    Document::Copy(set);
449
450    DicomDir *dd = dynamic_cast<DicomDir *>(set);
451    if( dd )
452    {
453       if(MetaElems)
454          MetaElems->Unregister();
455       MetaElems = dd->MetaElems;
456       if(MetaElems)
457          MetaElems->Register();
458
459       Patients = dd->Patients;
460       for(ItPatient = Patients.begin();ItPatient != Patients.end();++ItPatient)
461          (*ItPatient)->Register();
462    }
463 }
464
465 //-----------------------------------------------------------------------------
466 // Protected
467 /**
468  * \brief create a Document-like chained list from a root Directory 
469  * @param path entry point of the tree-like structure
470  */
471 void DicomDir::CreateDicomDirChainedList(std::string const &path)
472 {
473    CallStartMethod();
474    DirList dirList(path,1); // gets recursively the file list
475    unsigned int count = 0;
476    VectDocument list;
477    File *f;
478
479    DirListType fileList = dirList.GetFilenames();
480    unsigned int nbFile = fileList.size();
481    for( DirListType::iterator it  = fileList.begin();
482                               it != fileList.end();
483                               ++it )
484    {
485       Progress = (float)(count+1)/(float)nbFile;
486       CallProgressMethod();
487       if ( Abort )
488       {
489          break;
490       }
491
492       f = File::New( );
493       f->SetLoadMode(LoadMode); // we allow user not to load Sequences, 
494                                 //        or Shadow groups, or ......
495       f->SetFileName( it->c_str() );
496       f->Load( );
497
498       if ( f->IsReadable() )
499       {
500          // Add the file to the chained list:
501          list.push_back(f);
502          gdcmDebugMacro( "Readable " << it->c_str() );
503        }
504        else
505        {
506           f->Delete();
507        }
508        count++;
509    }
510    // sorts Patient/Study/Serie/
511    std::sort(list.begin(), list.end(), DicomDir::HeaderLessThan );
512    
513    std::string tmp = dirList.GetDirName();      
514    //for each File of the chained list, add/update the Patient/Study/Serie/Image info
515    SetElements(tmp, list);
516    CallEndMethod();
517
518    for(VectDocument::iterator itDoc=list.begin();
519        itDoc!=list.end();
520        ++itDoc)
521    {
522       dynamic_cast<File *>(*itDoc)->Delete();
523    }
524 }
525
526
527 //-----------------------------------------------------------------------------
528 // Private
529 /**
530  * \brief Sets all fields to NULL
531  */
532 void DicomDir::Initialize()
533 {
534    Progress = 0.0;
535    Abort = false;
536
537    MetaElems = NULL;   
538 }
539
540 /**
541  * \brief create a 'gdcm::DicomDir' from a DICOMDIR Header 
542  */
543 void DicomDir::CreateDicomDir()
544 {
545    // The SeqEntries of "Directory Record Sequence" are parsed. 
546    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
547    //  1 - we save the beginning iterator
548    //  2 - we continue to parse
549    //  3 - we find an other tag
550    //       + we create the object for the precedent tag
551    //       + loop to 1 -
552    gdcmDebugMacro("Create DicomDir");
553
554    // Directory record sequence
555    DocEntry *e = GetDocEntry(0x0004, 0x1220);
556    if ( !e )
557    {
558       gdcmWarningMacro( "No Directory Record Sequence (0004,1220) found");
559       return;         
560    }
561    
562    SeqEntry *s = dynamic_cast<SeqEntry *>(e);
563    if ( !s )
564    {
565       gdcmWarningMacro( "Element (0004,1220) is not a Sequence ?!?");
566       return;
567    }
568
569    NewMeta();
570    
571    DocEntry *d;
572    std::string v;
573    SQItem *si;
574
575    SQItem *tmpSI=s->GetFirstSQItem();
576    while(tmpSI)
577    {
578       d = tmpSI->GetDocEntry(0x0004, 0x1430); // Directory Record Type
579       if ( DataEntry *dataEntry = dynamic_cast<DataEntry *>(d) )
580       {
581          v = dataEntry->GetString();
582       }
583       else
584       {
585          gdcmWarningMacro( "(0004,1430) not a DataEntry ?!?");
586          continue;
587       }
588
589       // A decent DICOMDIR has much more images than series,
590       // more series than studies, and so on.
591       // This is the right order to perform the tests
592
593       if ( v == "IMAGE " ) 
594       {
595          si = DicomDirImage::New(true);
596          if ( !AddImageToEnd( static_cast<DicomDirImage *>(si)) )
597          {
598             si->Delete();
599             si = NULL;
600             gdcmErrorMacro( "Add AddImageToEnd failed");
601          }
602       }
603       else if ( v == "SERIES" )
604       {
605          si = DicomDirSerie::New(true);
606          if ( !AddSerieToEnd( static_cast<DicomDirSerie *>(si)) )
607          {
608             si->Delete();
609             si = NULL;
610             gdcmErrorMacro( "Add AddSerieToEnd failed");
611          }
612       }
613       else if ( v == "VISIT " )
614       {
615          si = DicomDirVisit::New(true);
616          if ( !AddVisitToEnd( static_cast<DicomDirVisit *>(si)) )
617          {
618             si->Delete();
619             si = NULL;
620             gdcmErrorMacro( "Add AddVisitToEnd failed");
621          }
622       }
623       else if ( v == "STUDY " )
624       {
625          si = DicomDirStudy::New(true);
626          if ( !AddStudyToEnd( static_cast<DicomDirStudy *>(si)) )
627          {
628             si->Delete();
629             si = NULL;
630             gdcmErrorMacro( "Add AddStudyToEnd failed");
631          }
632       }
633       else if ( v == "PATIENT " )
634       {
635          si = DicomDirPatient::New(true);
636          if ( !AddPatientToEnd( static_cast<DicomDirPatient *>(si)) )
637          {
638             si->Delete();
639             si = NULL;
640             gdcmErrorMacro( "Add PatientToEnd failed");
641          }
642       }
643       else
644       {
645          // It was neither a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
646          // nor an 'IMAGE' SQItem. Skip to next item.
647          gdcmDebugMacro( " -------------------------------------------"
648          << "a non PATIENT/STUDY/SERIE/IMAGE SQItem was found : "
649          << v);
650
651         // FIXME : deal with other item types !
652         tmpSI=s->GetNextSQItem(); // To avoid infinite loop
653         continue;
654       }
655       if ( si )
656          si->Copy(tmpSI);
657
658       tmpSI=s->GetNextSQItem();
659    }
660    ClearEntry();
661 }
662
663 /**
664  * \brief  AddPatientToEnd 
665  * @param   dd SQ Item to enqueue to the DicomPatient chained List
666  */
667 bool DicomDir::AddPatientToEnd(DicomDirPatient *dd)
668 {
669    Patients.push_back(dd);
670    return true;
671 }
672
673 /**
674  * \brief  AddStudyToEnd 
675  * @param   dd SQ Item to enqueue to the DicomDirStudy chained List
676  */
677 bool DicomDir::AddStudyToEnd(DicomDirStudy *dd)
678 {
679    if ( Patients.size() > 0 )
680    {
681       ListDicomDirPatient::iterator itp = Patients.end();
682       itp--;
683       (*itp)->AddStudy(dd);
684       return true;
685    }
686    return false;
687 }
688
689 /**
690  * \brief  AddSerieToEnd 
691  * @param   dd SQ Item to enqueue to the DicomDirSerie chained List
692  */
693 bool DicomDir::AddSerieToEnd(DicomDirSerie *dd)
694 {
695    if ( Patients.size() > 0 )
696    {
697       ListDicomDirPatient::iterator itp = Patients.end();
698       itp--;
699
700       DicomDirStudy *study = (*itp)->GetLastStudy();
701       if ( study )
702       {
703          study->AddSerie(dd);
704          return true;
705       }
706    }
707    return false;
708 }
709
710 /**
711  * \brief  AddVisitToEnd 
712  * @param   dd SQ Item to enqueue to the DicomDirVisit chained List
713  */
714 bool DicomDir::AddVisitToEnd(DicomDirVisit *dd)
715 {
716    if ( Patients.size() > 0 )
717    {
718       ListDicomDirPatient::iterator itp = Patients.end();
719       itp--;
720
721       DicomDirStudy *study = (*itp)->GetLastStudy();
722       if ( study )
723       {
724          study->AddVisit(dd);
725          return true;
726       }
727    }
728    return false;
729 }
730 /**
731  * \brief   AddImageToEnd
732  * @param   dd SQ Item to enqueue to the DicomDirImage chained List
733  */
734 bool DicomDir::AddImageToEnd(DicomDirImage *dd)
735 {
736    if ( Patients.size() > 0 )
737    {
738       ListDicomDirPatient::iterator itp = Patients.end();
739       itp--;
740
741       DicomDirStudy *study = (*itp)->GetLastStudy();
742       if ( study )
743       {
744          DicomDirSerie *serie = study->GetLastSerie();
745          if ( serie )
746          {
747             serie->AddImage(dd);
748             return true;
749          }
750       }
751    }
752    return false;
753 }
754
755 /**
756  * \brief  for each Header of the chained list, 
757  *         add/update the Patient/Study/Serie/Image info 
758  * @param   path path of the root directory
759  * @param   list chained list of Headers
760  */
761 void DicomDir::SetElements(std::string const &path, VectDocument const &list)
762 {
763    ClearEntry();
764    ClearPatient();
765
766    std::string patPrevName         = "", patPrevID  = "";
767    std::string studPrevInstanceUID = "", studPrevID = "";
768    std::string serPrevInstanceUID  = "", serPrevID  = "";
769
770    std::string patCurName,         patCurID;
771    std::string studCurInstanceUID, studCurID;
772    std::string serCurInstanceUID,  serCurID;
773
774    bool first = true;
775    for( VectDocument::const_iterator it = list.begin();
776                                      it != list.end(); 
777                                    ++it )
778    {
779       // get the current file characteristics
780       patCurName         = (*it)->GetEntryString(0x0010,0x0010);
781       patCurID           = (*it)->GetEntryString(0x0010,0x0011);
782       studCurInstanceUID = (*it)->GetEntryString(0x0020,0x000d);
783       studCurID          = (*it)->GetEntryString(0x0020,0x0010);
784       serCurInstanceUID  = (*it)->GetEntryString(0x0020,0x000e);
785       serCurID           = (*it)->GetEntryString(0x0020,0x0011);
786
787       if ( patCurName != patPrevName || patCurID != patPrevID || first )
788       {
789          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
790          first = true;
791       }
792
793       // if new Study, deal with 'STUDY' Elements   
794       if ( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID 
795          || first )
796       {
797          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
798          first = true;
799       }
800
801       // if new Serie, deal with 'SERIE' Elements   
802       if ( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID
803          || first )
804       {
805          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
806       }
807       
808       // Always Deal with 'IMAGE' Elements  
809       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
810
811       patPrevName         = patCurName;
812       patPrevID           = patCurID;
813       studPrevInstanceUID = studCurInstanceUID;
814       studPrevID          = studCurID;
815       serPrevInstanceUID  = serCurInstanceUID;
816       serPrevID           = serCurID;
817       first = false;
818    }
819 }
820
821 /**
822  * \brief   adds to the HTable 
823  *          the Entries (Dicom Elements) corresponding to the given type
824  * @param   path full path file name (only used when type = GDCM_DICOMDIR_IMAGE
825  * @param   type DicomDirObject type to create (GDCM_DICOMDIR_PATIENT,
826  *          GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
827  * @param   header Header of the current file
828  */
829 void DicomDir::SetElement(std::string const &path, DicomDirType type,
830                           Document *header)
831 {
832    ListDicomDirElem elemList;
833    ListDicomDirElem::const_iterator it;
834    uint16_t tmpGr, tmpEl;
835    DictEntry *dictEntry;
836    DataEntry *entry;
837    std::string val;
838    SQItem *si;
839
840    switch( type )
841    {
842       case GDCM_DICOMDIR_IMAGE:
843          elemList = Global::GetDicomDirElements()->GetDicomDirImageElements();
844          si = DicomDirImage::New(true);
845          if ( !AddImageToEnd(static_cast<DicomDirImage *>(si)) )
846          {
847             si->Delete();
848             gdcmErrorMacro( "Add ImageToEnd failed");
849          }
850          break;
851       case GDCM_DICOMDIR_SERIE:
852          elemList = Global::GetDicomDirElements()->GetDicomDirSerieElements();
853          si = DicomDirSerie::New(true);
854          if ( !AddSerieToEnd(static_cast<DicomDirSerie *>(si)) )
855          {
856             si->Delete();
857             gdcmErrorMacro( "Add SerieToEnd failed");
858          }
859          break;
860       case GDCM_DICOMDIR_STUDY:
861          elemList = Global::GetDicomDirElements()->GetDicomDirStudyElements();
862          si = DicomDirStudy::New(true);
863          if ( !AddStudyToEnd(static_cast<DicomDirStudy *>(si)) )
864          {
865             si->Delete();
866             gdcmErrorMacro( "Add StudyToEnd failed");
867          }
868          break;
869       case GDCM_DICOMDIR_PATIENT:
870          elemList = Global::GetDicomDirElements()->GetDicomDirPatientElements();
871          si = DicomDirPatient::New(true);
872          if ( !AddPatientToEnd(static_cast<DicomDirPatient *>(si)) )
873          {
874             si->Delete();
875             gdcmErrorMacro( "Add PatientToEnd failed");
876          }
877          break;
878       case GDCM_DICOMDIR_META:
879          if ( MetaElems )
880          {
881             MetaElems->Delete();
882             gdcmErrorMacro( "MetaElements already exist, they will be destroyed");
883          }
884          elemList = Global::GetDicomDirElements()->GetDicomDirMetaElements();
885          MetaElems = DicomDirMeta::New(true);
886          si = MetaElems;
887          break;
888       default:
889          return;
890    }
891    
892    // FIXME : troubles found when it's a SeqEntry
893       
894    // removed all the seems-to-be-useless stuff about Referenced Image Sequence
895    // to avoid further troubles
896    // imageElem 0008 1140 "" // Referenced Image Sequence
897    // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
898    // imageElem 0008 1150 "" // Referenced SOP Class UID    : to be set/forged later
899    // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
900    // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
901  
902    std::string referencedVal;
903    // for all the relevant elements found in their own spot of the DicomDir.dic
904    for( it = elemList.begin(); it != elemList.end(); ++it)
905    {
906       tmpGr     = it->Group;
907       tmpEl     = it->Elem;
908       dictEntry = GetPubDict()->GetEntry(tmpGr, tmpEl);
909
910       entry     = DataEntry::New( dictEntry ); 
911       entry->SetOffset(0); // just to avoid further missprinting
912
913       if ( header )
914       {
915          // NULL when we Build Up (ex nihilo) a DICOMDIR
916          //   or when we add the META elems
917  
918             val = header->GetEntryString(tmpGr, tmpEl); 
919       }
920       else
921       {
922          val = GDCM_UNFOUND;
923       }
924
925       if ( val == GDCM_UNFOUND) 
926       {       
927          if ( tmpGr == 0x0004 ) // never present in File !     
928          {
929             switch (tmpEl)
930             {
931             case 0x1130: // File-set ID
932                // force to the *end* File Name
933                val = Util::GetName( path );
934                break;
935       
936             case 0x1500: // Only used for image    
937                if ( header->GetFileName().substr(0, path.length()) != path )
938                { 
939                  gdcmWarningMacro( "The base path of file name is incorrect");
940                  val = header->GetFileName();
941                }
942                else
943                { 
944                  // avoid the first '/' in File name !
945                  if ( header->GetFileName().c_str()[path.length()] 
946                                                       == GDCM_FILESEPARATOR )
947                     val = &(header->GetFileName().c_str()[path.length()+1]);
948                  else  
949                     val = &(header->GetFileName().c_str()[path.length()]);   
950                }
951                break;
952     
953              case 0x1510:  // Referenced SOP Class UID in File
954                referencedVal = header->GetEntryString(0x0008, 0x0016);
955                // FIXME : probabely something to check
956                val = referencedVal;
957                break;
958        
959              case 0x1511: // Referenced SOP Instance UID in File
960                referencedVal = header->GetEntryString(0x0008, 0x0018);
961                // FIXME : probabely something to check
962                val = referencedVal;
963                break;
964     
965             case 0x1512: // Referenced Transfer Syntax UID in File
966                referencedVal = header->GetEntryString(0x0002, 0x0010);
967                // FIXME : probabely something to check
968                val = referencedVal;
969                break;
970     
971             default :
972                val = it->Value;   
973             } 
974          }
975          else
976          {
977             // If the entry is not found in the Header, don't write its 'value' in the DICOMDIR !
978             entry->Delete();
979             continue;
980           }
981       }
982       else
983       {
984          if ( header->GetEntryLength(tmpGr,tmpEl) == 0 )
985          {
986             val = it->Value;
987             // Don't polute the DICOMDIR with empty fields
988             if (val == "")
989             {
990                entry->Delete();
991                continue;
992             }  
993          }    
994       }
995
996 /* FIX later the pb of creating the 'Implementation Version Name'!
997
998       if (val == GDCM_UNFOUND)
999          val = "";
1000
1001       if ( tmpGr == 0x0002 && tmpEl == 0x0013)
1002       { 
1003          // 'Implementation Version Name'
1004          std::string val = "GDCM ";
1005          val += Util::GetVersion();
1006       }
1007 */ 
1008
1009       entry->SetString( val ); // troubles expected when vr=SQ ...
1010
1011       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
1012       {
1013          gdcmDebugMacro("GDCM_DICOMDIR_META ?!? should never print that");
1014       }
1015       
1016       si->AddEntry(entry);
1017       entry->Delete();
1018    }
1019 }
1020
1021 /**
1022  * \brief   Move the content of the source SQItem to the destination SQItem
1023  *          Only DocEntry's are moved
1024  * @param dst destination SQItem
1025  * @param src source SQItem
1026  */
1027 void DicomDir::MoveSQItem(DocEntrySet *dst, DocEntrySet *src)
1028
1029    DocEntry *entry;
1030 // todo : rewrite the whole stuff, without using RemoveEntry an AddEntry,
1031 //        to save time
1032    entry = src->GetFirstEntry();
1033    while(entry)
1034    {
1035       dst->AddEntry(entry);  // use it, *before* removing it!
1036       src->RemoveEntry(entry);
1037       // we destroyed -> the current iterator is not longer valid
1038       entry = src->GetFirstEntry();
1039    }
1040 }
1041
1042 /**
1043  * \brief   compares two files
1044  */
1045 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
1046 {
1047    return *header1 < *header2;
1048 }
1049
1050 //-----------------------------------------------------------------------------
1051 // Print
1052 /**
1053  * \brief  Canonical Printer 
1054  * @param   os ostream we want to print in
1055  * @param indent Indentation string to be prepended during printing
1056  */
1057 void DicomDir::Print(std::ostream &os, std::string const & )
1058 {
1059    if ( MetaElems )
1060    {
1061       MetaElems->SetPrintLevel(PrintLevel);
1062       MetaElems->Print(os);   
1063    }   
1064    for(ListDicomDirPatient::iterator cc  = Patients.begin();
1065                                      cc != Patients.end();
1066                                    ++cc)
1067    {
1068      (*cc)->SetPrintLevel(PrintLevel);
1069      (*cc)->Print(os);
1070    }
1071 }
1072
1073 //-----------------------------------------------------------------------------
1074 } // end namespace gdcm