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