]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
some more DICOMDIR fixes
[gdcm.git] / src / gdcmDicomDir.cxx
1 /*=========================================================================
2   
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDicomDir.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/08/30 16:15:40 $
7   Version:   $Revision: 1.66 $
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.htm 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 #include <string>
20 #include <algorithm>
21 #include <sys/types.h>
22
23 #ifdef _MSC_VER 
24    #include <direct.h>
25 #else
26    #include <unistd.h>
27 #endif
28
29 #include "gdcmDicomDir.h"
30 #include "gdcmDicomDirStudy.h"
31 #include "gdcmDicomDirSerie.h"
32 #include "gdcmDicomDirImage.h"
33 #include "gdcmDirList.h"
34 #include "gdcmUtil.h"
35 #include "gdcmDebug.h"
36 #include "gdcmGlobal.h"
37 #include "gdcmHeader.h"
38 #include "gdcmSeqEntry.h"
39 #include "gdcmSQItem.h"
40 #include "gdcmValEntry.h"
41
42 //-----------------------------------------------------------------------------
43 //  For full DICOMDIR description, see:
44 //  PS 3.3-2003, pages 731-750
45 //-----------------------------------------------------------------------------
46 // Constructor / Destructor
47
48 /**
49  * \ingroup gdcmDicomDir
50  * \brief   Constructor : creates an empty gdcmDicomDir
51  */
52 gdcmDicomDir::gdcmDicomDir()
53    :gdcmDocument( )
54
55    Initialize();  // sets all private fields to NULL
56    std::string pathBidon = "Bidon"; // Sorry, NULL not allowed ...   
57    metaElems = NewMeta();
58 }
59
60 /**
61  * \brief Constructor Parses recursively the directory and creates the DicomDir
62  *        or uses an already built DICOMDIR, depending on 'parseDir' value.
63  * @param FileName        name 
64  *                      - of the root directory (parseDir = true)
65  *                      - of the DICOMDIR       (parseDir = false)
66  * @param parseDir boolean
67  *                      - true if user passed an entry point 
68  *                        and wants to explore recursively the directories
69  *                      - false if user passed an already built DICOMDIR file
70  *                        and wants to use it 
71  */
72 gdcmDicomDir::gdcmDicomDir(std::string const & fileName, bool parseDir ):
73    gdcmDocument( fileName )
74 {
75    // que l'on ai passe un root directory ou un DICOMDIR
76    // et quelle que soit la valeur de parseDir,
77    // on a deja lance gdcmDocument 
78    Initialize();  // sets all private fields to NULL
79
80    // gdcmDocument already executed
81    // if user passed a root directory, sure we didn't get anything
82
83    if ( TagHT.begin() == TagHT.end() ) // when user passed a Directory to parse
84    {
85       dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : entry HT empty");
86
87       if ( fileName.size() == 1 && fileName[0] == '.' )
88       {
89          // user passed '.' as Name
90          // we get current directory name
91          char* dummy = new char[1000];
92          getcwd(dummy, (size_t)1000);
93          SetFileName( dummy ); // will be converted into a string
94          delete[] dummy;       // no longer needed   
95       }
96
97       if ( parseDir ) // user asked for a recursive parsing of a root directory
98       {
99          metaElems = NewMeta();
100
101          dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : Parse directory"
102                         " and create the DicomDir");
103          ParseDirectory();
104       }
105       else
106       {
107          /// \todo if parseDir == false, it should be tagged as an error
108          // NON ! il suffit d'appeler ParseDirectory() 
109          // apres le constructeur
110       }
111    }
112    else // Only if user passed a DICOMDIR
113    {
114       // Directory record sequence
115       gdcmDocEntry *e = GetDocEntryByNumber(0x0004, 0x1220);
116       if ( !e )
117       {
118          dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : NO Directory record"
119                         " sequence (0x0004,0x1220)");
120          /// \todo FIXME : what do we do when the parsed file IS NOT a
121          ///       DICOMDIR file ?         
122       }
123       CreateDicomDir();
124    }
125 }
126
127 /**
128  * \brief  Canonical destructor 
129  */
130 gdcmDicomDir::~gdcmDicomDir() 
131 {
132    SetStartMethod(NULL);
133    SetProgressMethod(NULL);
134    SetEndMethod(NULL);
135    for(ListDicomDirPatient::iterator cc = patients.begin();
136                                      cc!= patients.end();
137                                    ++cc)
138    {
139       delete *cc;
140    }
141    if ( metaElems )
142    {
143       delete metaElems;
144    }
145 }
146
147 //-----------------------------------------------------------------------------
148 // Print
149 /**
150  * \brief  Canonical Printer 
151  */
152 void gdcmDicomDir::Print(std::ostream &os)
153 {
154    if(metaElems)
155    {
156       metaElems->SetPrintLevel(PrintLevel);
157       metaElems->Print(os);   
158    }   
159    for(ListDicomDirPatient::iterator cc  = patients.begin();
160                                      cc != patients.end();
161                                    ++cc)
162    {
163      (*cc)->SetPrintLevel( PrintLevel );
164      (*cc)->Print( os );
165    }
166 }
167
168 //-----------------------------------------------------------------------------
169 // Public
170 /**
171  * \brief  This predicate, based on hopefully reasonable heuristics,
172  *         decides whether or not the current header was properly parsed
173  *         and contains the mandatory information for being considered as
174  *         a well formed and usable DicomDir.
175  * @return true when gdcmDocument is the one of a reasonable DicomDir,
176  *         false otherwise. 
177  */
178 bool gdcmDicomDir::IsReadable()
179 {
180    if( !gdcmDocument::IsReadable() )
181    {
182       return false;
183    }
184    if( !metaElems )
185    {
186       return false;
187    }
188    if( patients.size() <= 0 )
189    {
190       return false;
191    }
192
193    return true;
194 }
195
196 /**
197  * \brief Sets all fields to NULL
198  */
199
200 void gdcmDicomDir::Initialize()
201 {
202    startMethod             = NULL;
203    progressMethod          = NULL;
204    endMethod               = NULL;
205    startMethodArgDelete    = NULL;
206    progressMethodArgDelete = NULL;
207    endMethodArgDelete      = NULL;
208    startArg                = NULL;
209    progressArg             = NULL;
210    endArg                  = NULL;
211
212    progress = 0.0;
213    abort = false;
214
215    metaElems = 0;   
216 }
217
218
219 /**
220  * \ingroup gdcmDicomDir
221  * \brief  fills the whole structure, starting from a root Directory
222  */
223 void gdcmDicomDir::ParseDirectory()
224 {
225    CreateDicomDirChainedList( GetFileName() );
226    CreateDicomDir();
227 }
228
229 /**
230  * \ingroup gdcmDicomDir
231  * \brief   Set the start method to call when the parsing of the directory starts
232  * @param   method Method to call
233  * @param   arg    Argument to pass to the method
234  * @param   argDelete    Argument 
235  * \warning In python : the arg parameter isn't considered
236  */
237 void gdcmDicomDir::SetStartMethod(gdcmMethod *method, void *arg, 
238                                   gdcmMethod *argDelete )
239 {
240    if( startArg && startMethodArgDelete )
241    {
242       startMethodArgDelete( startArg );
243    }
244
245    startMethod          = method;
246    startArg             = arg;
247    startMethodArgDelete = argDelete;
248 }
249
250 /**
251  * \ingroup gdcmDicomDir
252  * \brief   Set the method to delete the argument
253  *          The argument is destroyed when the method is changed or when the
254  *          class is destroyed
255  * @param   method Method to call to delete the argument
256  */
257 void gdcmDicomDir::SetStartMethodArgDelete(gdcmMethod *method) 
258 {
259    startMethodArgDelete = method;
260 }
261
262 /**
263  * \ingroup gdcmDicomDir
264  * \brief   Set the progress method to call when the parsing of the directory progress
265  * @param   method Method to call
266  * @param   arg    Argument to pass to the method
267  * @param   argDelete    Argument  
268  * \warning In python : the arg parameter isn't considered
269  */
270 void gdcmDicomDir::SetProgressMethod(gdcmMethod *method, void *arg, 
271                                      gdcmMethod *argDelete )
272 {
273    if( progressArg && progressMethodArgDelete )
274    {
275       progressMethodArgDelete( progressArg );
276    }
277
278    progressMethod          = method;
279    progressArg             = arg;
280    progressMethodArgDelete = argDelete;
281 }
282
283 /**
284  * \ingroup gdcmDicomDir
285  * \brief   Set the method to delete the argument
286  *          The argument is destroyed when the method is changed or when the 
287  *          class is destroyed          
288  * @param   method Method to call to delete the argument
289  */
290 void gdcmDicomDir::SetProgressMethodArgDelete(gdcmMethod *method)
291 {
292    progressMethodArgDelete = method;
293 }
294
295 /**
296  * \ingroup gdcmDicomDir
297  * \brief   Set the end method to call when the parsing of the directory ends
298  * @param   method Method to call
299  * @param   arg    Argument to pass to the method
300  * @param   argDelete    Argument 
301  * \warning In python : the arg parameter isn't considered
302  */
303 void gdcmDicomDir::SetEndMethod(gdcmMethod *method, void *arg, 
304                                 gdcmMethod *argDelete )
305 {
306    if( endArg && endMethodArgDelete )
307    {
308       endMethodArgDelete( endArg );
309    }
310
311    endMethod          = method;
312    endArg             = arg;
313    endMethodArgDelete = argDelete;
314 }
315
316 /**
317  * \ingroup gdcmDicomDir
318  * \brief   Set the method to delete the argument
319  *          The argument is destroyed when the method is changed or when the class
320  *          is destroyed
321  * @param   method Method to call to delete the argument
322  */
323 void gdcmDicomDir::SetEndMethodArgDelete(gdcmMethod *method)
324 {
325    endMethodArgDelete = method;
326 }
327
328 /**
329  * \ingroup gdcmDicomDir
330  * \brief   writes on disc a DICOMDIR
331  * \ warning does NOT add the missing elements in the header :
332  *           it's up to the user doing it !
333  * \todo : to be re-written using the DICOMDIR tree-like structure
334  *         *not* the chained list
335  *         (does NOT exist if the DICOMDIR is user-forged !)
336  * @param  fileName file to be written to 
337  * @return false only when fail to open
338  */
339  
340 bool gdcmDicomDir::Write(std::string const & fileName) 
341 {  
342    uint16_t sq[4] = { 0x0004, 0x1220, 0xffff, 0xffff };
343    uint16_t sqt[4]= { 0xfffe, 0xe0dd, 0xffff, 0xffff };
344
345    FILE * fp = fopen(fileName.c_str(), "wb");
346    if( !fp ) 
347    {
348       printf("Failed to open(write) File [%s] \n", fileName.c_str());
349       return false;
350    }
351
352    uint8_t* filePreamble = new  uint8_t[128];
353    memset(filePreamble, 0, 128);
354    fwrite(filePreamble,128,1,fp);
355    fwrite("DICM",4,1,fp);
356    delete[] filePreamble;
357    
358  //  UpdateDirectoryRecordSequenceLength(); // TODO (if *really* usefull)
359  
360    gdcmDicomDirMeta *ptrMeta = GetDicomDirMeta();
361    ptrMeta->Write(fp, gdcmExplicitVR);
362    
363    // force writing 0004|1220 [SQ ], that CANNOT exist within gdcmDicomDirMeta
364    fwrite(&sq[0],8,1,fp);  // 0004 1220 ffff ffff
365         
366    for(ListDicomDirPatient::iterator cc = patients.begin();cc!=patients.end();++cc)
367    {
368       (*cc)->Write( fp, gdcmExplicitVR );
369    }
370    
371    // force writing Sequence Delimitation Item
372    fwrite(&sqt[0],8,1,fp);  // fffe e0dd ffff ffff 
373
374    fclose( fp );
375    return true;
376 }
377
378 //-----------------------------------------------------------------------------
379 // Protected
380
381 /**
382  * \ingroup gdcmDicomDir
383  * \brief create a gdcmDocument-like chained list from a root Directory 
384  * @param path entry point of the tree-like structure
385  */
386 void gdcmDicomDir::CreateDicomDirChainedList(std::string const & path)
387 {
388    CallStartMethod();
389    gdcmDirList fileList(path,1); // gets recursively the file list
390    unsigned int count = 0;
391    VectDocument list;
392    gdcmHeader *header;
393
394    TagHT.clear();
395    patients.clear();
396
397    for( gdcmDirList::iterator it  = fileList.begin();
398                               it != fileList.end();
399                               ++it )
400    {
401       progress = (float)(count+1)/(float)fileList.size();
402       CallProgressMethod();
403       if( abort )
404       {
405          break;
406       }
407
408       header = new gdcmHeader( it->c_str() );
409       if( !header )
410       {
411          dbg.Verbose( 1,
412                       "gdcmDicomDir::CreateDicomDirChainedList: "
413                       "failure in new Header ",
414                       it->c_str() );
415       }
416       
417       if( header->IsReadable() )
418       {
419          // Add the file header to the chained list:
420          list.push_back(header);
421          dbg.Verbose( 1,
422                       "gdcmDicomDir::CreateDicomDirChainedList: readable ",
423                       it->c_str() );
424
425        }
426        else
427        {
428           delete header;
429        }
430        count++;
431    }
432    // sorts Patient/Study/Serie/
433    std::sort(list.begin(), list.end(), gdcmDicomDir::HeaderLessThan );
434    std::string tmp = fileList.GetDirName();
435       
436    //for each Header of the chained list, add/update the Patient/Study/Serie/Image info
437    SetElements(tmp, list);
438    CallEndMethod();
439 }
440
441 /**
442  * \ingroup gdcmDicomDir
443  * \brief   adds *the* Meta to a partially created DICOMDIR
444  */
445   
446 gdcmDicomDirMeta * gdcmDicomDir::NewMeta()
447 {
448    gdcmDicomDirMeta *m = new gdcmDicomDirMeta( &TagHT );
449   
450    if ( TagHT.begin() != TagHT.end() ) // after Document Parsing
451    { 
452       TagDocEntryHT::iterator lastOneButSequence = TagHT.end();
453       lastOneButSequence --;
454       // This works because ALL the 'out of Sequence' Tags belong to Meta Elems
455       // (we skip 0004|1220 [Directory record sequence] )
456       for ( TagDocEntryHT::iterator cc  = TagHT.begin(); 
457                                     cc != lastOneButSequence;
458                                    ++cc)
459       {
460          m->AddDocEntry( cc->second );
461       }
462    }
463    else  // after root directory parsing
464    {
465      std::list<gdcmElement> elemList;
466      elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirMetaElements();
467      m->FillObject(elemList);
468     }
469    m->SetSQItemNumber(0); // To avoid further missprinting
470    return m;  
471 }
472
473
474 /**
475  * \brief   adds a new Patient (with the basic elements) to a partially created DICOMDIR
476  */
477 gdcmDicomDirPatient * gdcmDicomDir::NewPatient()
478 {
479    std::list<gdcmElement>::iterator it;
480    uint16_t tmpGr,tmpEl;
481    gdcmDictEntry *dictEntry;
482    gdcmValEntry *entry;
483
484    std::list<gdcmElement> elemList;   
485    elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements(); 
486    gdcmSQItem *s = new gdcmSQItem(0);
487
488    // for all the DicomDirPatient Elements      
489    for( it = elemList.begin(); it != elemList.end(); ++it ) 
490    {
491       tmpGr     = it->group;
492       tmpEl     = it->elem;
493       dictEntry = GetPubDict()->GetDictEntryByNumber(tmpGr, tmpEl);
494       entry     = new gdcmValEntry( dictEntry );
495       entry->SetOffset(0); // just to avoid further missprinting
496       entry->SetValue( it->value );
497
498       // dealing with value length ...
499       
500       if( dictEntry->GetGroup() == 0xfffe)
501       {
502          entry->SetLength(entry->GetValue().length());
503       }
504       else if( dictEntry->GetVR() == "UL" || dictEntry->GetVR() == "SL" )
505       {
506          entry->SetLength( 4 );
507       } 
508       else if( dictEntry->GetVR() == "US" || dictEntry->GetVR() == "SS" )
509       {
510          entry->SetLength(2); 
511       } 
512       else if( dictEntry->GetVR() == "SQ" )
513       {
514          entry->SetLength( 0xffffffff );
515       }
516       else
517       {
518          entry->SetLength( entry->GetValue().length() );
519       }
520       s->AddDocEntry( entry );
521    }
522
523    gdcmDicomDirPatient *p = new gdcmDicomDirPatient(s, &TagHT);
524    patients.push_front( p );
525
526    return p;   
527 }
528
529 /**
530  * \brief   adds to the HTable 
531  *          the gdcmEntries (Dicom Elements) corresponding to the given type
532  * @param   path full path file name (only used when type = GDCM_DICOMDIR_IMAGE
533  * @param   type gdcmObject type to create (GDCM_DICOMDIR_PATIENT,
534  *          GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
535  * @param   header gdcmHeader of the current file
536  */
537 void gdcmDicomDir::SetElement(std::string &path,gdcmDicomDirType type,
538                               gdcmDocument *header)
539 {
540    std::list<gdcmElement> elemList;
541    std::list<gdcmElement>::iterator it;
542    uint16_t tmpGr, tmpEl;
543    gdcmDictEntry *dictEntry;
544    gdcmValEntry *entry;
545    std::string val;
546    gdcmSQItem *si = new gdcmSQItem(0); // all the items will be at level 1
547    switch( type )
548    {
549       case GDCM_DICOMDIR_IMAGE:
550          elemList = gdcmGlobal::GetDicomDirElements()->GetDicomDirImageElements();
551          break;
552
553       case GDCM_DICOMDIR_SERIE:
554          elemList = gdcmGlobal::GetDicomDirElements()->GetDicomDirSerieElements();
555          break;
556
557       case GDCM_DICOMDIR_STUDY:
558          elemList = gdcmGlobal::GetDicomDirElements()->GetDicomDirStudyElements();
559          break;
560
561       case GDCM_DICOMDIR_PATIENT:
562          elemList = gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements();
563          break;
564   
565       case GDCM_DICOMDIR_META:
566          elemList = gdcmGlobal::GetDicomDirElements()->GetDicomDirMetaElements();
567          break;
568
569       default:
570          return;
571    }
572    // removed all the seems-to-be-useless stuff about Referenced Image Sequence
573    // to avoid further troubles
574    // imageElem 0008 1140 "" // Referenced Image Sequence
575    // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
576    // imageElem 0008 1150 "" // Referenced SOP Class UID    : to be set/forged later
577    // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
578    // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
579    // for all the relevant elements found in their own spot of the DicomDir.dic
580    // FIXME : troubles found when it's a SeqEntry
581
582    for( it = elemList.begin(); it != elemList.end(); ++it)
583    {
584       tmpGr     = it->group;
585       tmpEl     = it->elem;
586       dictEntry = GetPubDict()->GetDictEntryByNumber(tmpGr, tmpEl);
587
588       entry     = new gdcmValEntry( dictEntry ); // Be sure it's never a BinEntry !
589
590       entry->SetOffset(0); // just to avoid further missprinting
591       entry->SetLength(0); // just to avoid further missprinting
592
593       if( header ) // NULL when we Build Up (ex nihilo) a DICOMDIR
594                    //   or when we add the META elems
595          val = header->GetEntryByNumber(tmpGr, tmpEl);
596       else
597          val = GDCM_UNFOUND;
598
599       if( val == GDCM_UNFOUND) 
600       {
601          if( tmpGr == 0x0004 && tmpEl == 0x1130 ) // File-set ID
602          {
603            // force to the *end* File Name
604            val = GetName( path );
605          }
606          else if( tmpGr == 0x0004 && tmpEl == 0x1500 ) // Only used for image
607          {
608             if( header->GetFileName().substr(0, path.length()) != path )
609             {
610                dbg.Verbose(0, "gdcmDicomDir::SetElement : the base path"
611                               " of file name is incorrect");
612                val = header->GetFileName();
613             }
614             else
615                val = &(header->GetFileName().c_str()[path.length()]);
616          }
617          else
618             val = it->value;
619       }
620       else
621       {
622          if ( header->GetEntryLengthByNumber(tmpGr,tmpEl) == 0 )
623             val = it->value;
624       }
625
626      // GDCM_UNFOUND or not !
627
628       entry->SetValue( val ); // troubles expected when vr=SQ ...
629
630       if( dictEntry )
631       {
632          if( dictEntry->GetGroup() == 0xfffe )
633          {
634             entry->SetLength( entry->GetValue().length() ); // FIXME 
635          }
636          else if( dictEntry->GetVR() == "UL" || dictEntry->GetVR() == "SL" )
637          {
638             entry->SetLength(4);
639          }
640          else if( dictEntry->GetVR() == "US" || dictEntry->GetVR() == "SS" )
641          {
642             entry->SetLength(2); 
643          }
644          else if( dictEntry->GetVR() == "SQ" )
645          {
646             entry->SetLength( 0xffffffff );
647          }
648          else
649          {
650             entry->SetLength( entry->GetValue().length() );
651          }
652       }
653
654       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
655       {
656          std::cout << "GDCM_DICOMDIR_META ?!? should never print that" 
657                    << std::endl;
658       }
659       si->AddEntry(entry);
660    }
661    switch( type )
662    {
663       case GDCM_DICOMDIR_IMAGE:
664          AddDicomDirImageToEnd(si);
665          break;
666
667       case GDCM_DICOMDIR_SERIE:
668          AddDicomDirSerieToEnd(si);
669          break;
670
671       case GDCM_DICOMDIR_STUDY:
672          AddDicomDirStudyToEnd(si);
673          break;
674
675       case GDCM_DICOMDIR_PATIENT:
676          AddDicomDirPatientToEnd(si);
677          break;
678
679       default:
680          return;
681    }
682    //int count=1;            // find a trick to increment
683    //s->AddEntry(si, count); // Seg Faults 
684
685 }
686
687 /**
688  * \brief   CallStartMethod
689  */
690 void gdcmDicomDir::CallStartMethod()
691 {
692    progress = 0.0f;
693    abort    = false;
694    if( startMethod )
695    {
696       startMethod( startArg );
697    }
698 }
699
700 /**
701  * \ingroup gdcmDicomDir
702  * \brief   CallProgressMethod
703  */
704 void gdcmDicomDir::CallProgressMethod()
705 {
706    if( progressMethod )
707    {
708       progressMethod( progressArg );
709    }
710 }
711
712 /**
713  * \ingroup gdcmDicomDir
714  * \brief   CallEndMethod
715  */
716 void gdcmDicomDir::CallEndMethod()
717 {
718    progress = 1.0f;
719    if( endMethod )
720    {
721       endMethod( endArg );
722    }
723 }
724
725 //-----------------------------------------------------------------------------
726 // Private
727 /**
728  * \ingroup gdcmDicomDir
729  * \brief create a 'gdcmDicomDir' from a DICOMDIR gdcmHeader 
730  */
731 void gdcmDicomDir::CreateDicomDir()
732 {
733    // The list is parsed. 
734    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
735    //  1 - we save the beginning iterator
736    //  2 - we continue to parse
737    //  3 - we find an other tag
738    //       + we create the object for the precedent tag
739    //       + loop to 1 -
740
741    // Directory record sequence
742    gdcmDocEntry *e = GetDocEntryByNumber(0x0004, 0x1220);
743    if ( !e )
744    {
745       dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : NO Directory record"
746                      " sequence (0x0004,0x1220)");
747       /// \todo FIXME: what to do when the parsed file IS NOT a DICOMDIR file ? 
748       return;         
749    }
750    
751    gdcmSeqEntry* s = dynamic_cast<gdcmSeqEntry*>(e);
752    if ( !s )
753    {
754       dbg.Verbose(0, "gdcmDicomDir::CreateDicomDir: no SeqEntry present");
755       // useless : (0x0004,0x1220) IS a Sequence !
756       return;
757    }
758
759    gdcmDicomDirType type = gdcmDicomDir::GDCM_DICOMDIR_META;
760    metaElems = NewMeta();
761
762    ListSQItem listItems = s->GetSQItems();
763    
764    gdcmDocEntry * d;
765    std::string v;
766    for( ListSQItem::iterator i = listItems.begin(); 
767                              i !=listItems.end(); ++i ) 
768    {
769       d = (*i)->GetDocEntryByNumber(0x0004, 0x1430); // Directory Record Type
770       if ( gdcmValEntry* ValEntry = dynamic_cast< gdcmValEntry* >(d) )
771       {
772          v = ValEntry->GetValue();
773       }
774       else
775       {
776          dbg.Verbose(0, "gdcmDicomDir::CreateDicomDir: not a ValEntry.");
777          continue;
778       }
779
780       if( v == "PATIENT " )
781       {
782          AddDicomDirPatientToEnd( *i );
783          type = gdcmDicomDir::GDCM_DICOMDIR_PATIENT;
784       }
785       else if( v == "STUDY " )
786       {
787          AddDicomDirStudyToEnd( *i );
788          type = gdcmDicomDir::GDCM_DICOMDIR_STUDY;
789       }
790       else if( v == "SERIES" )
791       {
792          AddDicomDirSerieToEnd( *i );
793          type = gdcmDicomDir::GDCM_DICOMDIR_SERIE;
794       }
795       else if( v == "IMAGE " ) 
796       {
797          AddDicomDirImageToEnd( *i );
798          type = gdcmDicomDir::GDCM_DICOMDIR_IMAGE;
799       }
800       else
801       {
802          // It was not a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
803          // neither an 'IMAGE' SQItem. Skip to next item.
804          continue;
805       }
806    }
807 }
808
809 /**
810  * \ingroup gdcmDicomDir
811  * \brief Well ... there is only one occurence  
812  */
813 void gdcmDicomDir::AddDicomDirMeta()
814 {
815    if( metaElems )
816    {
817       delete metaElems;
818    }
819    metaElems = new gdcmDicomDirMeta( &TagHT );
820 }
821
822 /**
823  * \ingroup gdcmDicomDir
824  * \brief  AddDicomDirPatientToEnd 
825  * @param   s SQ Item to enqueue to the DicomPatient chained List
826  */
827 void gdcmDicomDir::AddDicomDirPatientToEnd(gdcmSQItem *s)
828 {
829    patients.push_back(new gdcmDicomDirPatient(s, &TagHT));
830 }
831
832 /**
833  * \ingroup gdcmDicomDir
834  * \brief  AddDicomDirStudyToEnd 
835  * @param   s SQ Item to enqueue to the DicomDirStudy chained List
836  */
837  void gdcmDicomDir::AddDicomDirStudyToEnd(gdcmSQItem *s)
838 {
839    if( patients.size() > 0 )
840    {
841       ListDicomDirPatient::iterator itp = patients.end();
842       itp--;
843       (*itp)->AddDicomDirStudy(new gdcmDicomDirStudy(s, &TagHT));
844    }
845 }
846
847 /**
848  * \ingroup gdcmDicomDir
849  * \brief  AddDicomDirSerieToEnd 
850  * @param   s SQ Item to enqueue to the DicomDirSerie chained List
851  */
852 void gdcmDicomDir::AddDicomDirSerieToEnd(gdcmSQItem *s)
853 {
854    if( patients.size() > 0 )
855    {
856       ListDicomDirPatient::iterator itp = patients.end();
857       itp--;
858
859       if( (*itp)->GetDicomDirStudies().size() > 0 )
860       {
861          ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
862          itst--;
863          (*itst)->AddDicomDirSerie(new gdcmDicomDirSerie(s, &TagHT));
864       }
865    }
866 }
867
868 /**
869  * \ingroup gdcmDicomDir
870  * \brief   AddDicomDirImageToEnd
871  * @param   s SQ Item to enqueue to the DicomDirImage chained List
872  */
873  void gdcmDicomDir::AddDicomDirImageToEnd(gdcmSQItem *s)
874 {
875    if( patients.size() > 0 )
876    {
877       ListDicomDirPatient::iterator itp = patients.end();
878       itp--;
879
880       if( (*itp)->GetDicomDirStudies().size() > 0 )
881       {
882          ListDicomDirStudy::iterator itst = (*itp)->GetDicomDirStudies().end();
883          itst--;
884
885          if( (*itst)->GetDicomDirSeries().size() > 0 )
886          {
887             ListDicomDirSerie::iterator its = (*itst)->GetDicomDirSeries().end();
888             its--;
889             (*its)->AddDicomDirImage(new gdcmDicomDirImage(s, &TagHT));
890          }
891       }
892    }
893 }
894
895 /**
896  * \ingroup gdcmDicomDir
897  * \brief  for each Header of the chained list, add/update the Patient/Study/Serie/Image info 
898  * @param   path path of the root directory
899  * @param   list chained list of Headers
900  */
901 void gdcmDicomDir::SetElements(std::string &path, VectDocument &list)
902 {
903    std::string patPrevName         = "", patPrevID  = "";
904    std::string studPrevInstanceUID = "", studPrevID = "";
905    std::string serPrevInstanceUID  = "", serPrevID  = "";
906
907    std::string patCurName,         patCurID;
908    std::string studCurInstanceUID, studCurID;
909    std::string serCurInstanceUID,  serCurID;
910
911    //SetElement( path, GDCM_DICOMDIR_META,NULL); // already done (NewMeta) 
912
913    for( VectDocument::iterator it = list.begin();
914                               it != list.end(); ++it )
915    {
916       // get the current file characteristics
917       patCurName         = (*it)->GetEntryByNumber(0x0010,0x0010); 
918       patCurID           = (*it)->GetEntryByNumber(0x0010,0x0011); 
919       studCurInstanceUID = (*it)->GetEntryByNumber(0x0020,0x000d);            
920       studCurID          = (*it)->GetEntryByNumber(0x0020,0x0010);            
921       serCurInstanceUID  = (*it)->GetEntryByNumber(0x0020,0x000e);            
922       serCurID           = (*it)->GetEntryByNumber(0x0020,0x0011);
923
924       if( patCurName != patPrevName || patCurID != patPrevID)
925       {
926          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
927       }
928
929       // if new Study Deal with 'STUDY' Elements   
930       if( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID )
931       {
932          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
933       }
934
935       // if new Serie Deal with 'SERIE' Elements   
936       if( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID )
937       {
938          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
939       }
940       
941       // Always Deal with 'IMAGE' Elements  
942       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
943
944       patPrevName         = patCurName;
945       patPrevID           = patCurID;
946       studPrevInstanceUID = studCurInstanceUID;
947       studPrevID          = studCurID;
948       serPrevInstanceUID  = serCurInstanceUID;
949       serPrevID           = serCurID;
950    }
951 }
952
953 /**
954  * \ingroup gdcmDicomDir
955  * \brief   compares two dgcmHeaders
956  */
957 bool gdcmDicomDir::HeaderLessThan(gdcmDocument *header1, gdcmDocument *header2)
958 {
959
960 std::cout <<header1->GetFileName() << " " << header2->GetFileName() <<std::endl;
961    return *header1 < *header2;
962 }
963
964 /**
965  * \brief   Sets the accurate value for the (0x0004,0x1220) element of a DICOMDIR
966  */
967 void gdcmDicomDir::UpdateDirectoryRecordSequenceLength()
968 {
969
970 /// \todo FIXME : to go on compiling
971 ///
972 /// to be re written !
973 ///   int offset = 0;
974 ///   ListTag::iterator it;
975 ///   uint16_t gr, el;
976 ///   std::string vr;
977 ///   for(it=listEntries.begin();it!=listEntries.end();++it) {
978 ///      gr = (*it)->GetGroup();
979 ///      el = (*it)->GetElement();
980 ///      vr = (*it)->GetVR();      
981 ///      if (gr !=0xfffe) {
982 ///         if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {    
983 ///            offset +=  4; // explicit VR AND OB, OW, SQ : 4 more bytes
984 ///         }         
985 ///         offset += 2 + 2 + 4 + (*it)->GetLength(); 
986 ///      } else {
987 ///         offset +=  4; // delimiters don't have a value.     
988 ///      }            
989 ///   }   
990 ///   //bool res=SetEntryLengthByNumber(offset, 0x0004, 0x1220); // Hope there is no dupps.
991 ///    SetEntryLengthByNumber(offset, 0x0004, 0x1220); // Hope there is no dupps.
992 ///   return;
993 ///
994 }
995
996 //-----------------------------------------------------------------------------