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