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