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