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