]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
* ENH : add callback and methods to get the progression of dicomDir
[gdcm.git] / src / gdcmDicomDir.cxx
1 // gdcmDicomDir.cxx
2 //-----------------------------------------------------------------------------
3 #include "gdcmDicomDir.h"
4 #include "gdcmStudy.h"
5 #include "gdcmSerie.h"
6 #include "gdcmImage.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 void StartMethod(void * = NULL)
17 {
18    std::cout<<"Start parsing"<<std::endl;
19 }
20
21 void EndMethod(void * = NULL)
22 {
23    std::cout<<"End parsing"<<std::endl;
24 }
25
26 //-----------------------------------------------------------------------------
27 //  For full DICOMDIR description, see:
28 //  PS 3.3-2003, pages 731-750
29 //-----------------------------------------------------------------------------
30 // Constructor / Destructor
31 /*
32  * \ingroup gdcmDicomDir
33  * \brief   Constructor
34  * @param   Filename
35  * @param   exception_on_error
36  */
37 gdcmDicomDir::gdcmDicomDir(const char *FileName, bool parseDir,
38                            bool exception_on_error):
39    gdcmParser(FileName,exception_on_error,true)
40 {
41    startMethod=StartMethod;
42    progressMethod=NULL;
43    endMethod=EndMethod;
44    startArg=NULL;
45    progressArg=NULL;
46    endArg=NULL;
47
48    progress=NULL;
49    abort=false;
50
51    metaElems=NULL;
52
53    if( GetListEntry().begin()==GetListEntry().end() ) 
54    {
55       dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : entry list empty");
56
57       if(parseDir)
58       {
59          dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : Parse directory and create the DicomDir");
60          ParseDirectory();
61       }
62    }
63    else
64       CreateDicomDir();
65 }
66
67 /*
68  * \ingroup gdcmDicomDir
69  * \brief  Canonical destructor 
70  */
71 gdcmDicomDir::~gdcmDicomDir() 
72 {
73    if(metaElems)
74       delete metaElems;
75    
76    for(ListPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
77    {
78       delete *cc;
79    }
80 }
81
82 //-----------------------------------------------------------------------------
83 // Print
84 /*
85  * \ingroup gdcmDicomDir
86  * \brief  Canonical Printer 
87  */
88 void gdcmDicomDir::Print(std::ostream &os)
89 {
90    if(metaElems)
91    {
92       metaElems->SetPrintLevel(printLevel);
93       metaElems->Print(os);   
94    }
95    
96    for(ListPatient::iterator cc=patients.begin();cc!=patients.end();++cc)
97    {
98      (*cc)->SetPrintLevel(printLevel);
99      (*cc)->Print(os);
100    }
101 }
102
103 //-----------------------------------------------------------------------------
104 // Public
105 /*
106  * \ingroup gdcmDicomDir
107  * \brief  This predicate, based on hopefully reasonable heuristics,
108  *         decides whether or not the current gdcmParser was properly parsed
109  *         and contains the mandatory information for being considered as
110  *         a well formed and usable DicomDir.
111  * @return true when gdcmParser is the one of a reasonable DicomDir,
112  *         false otherwise. 
113  */
114 bool gdcmDicomDir::IsReadable(void)
115 {
116    if(!gdcmParser::IsReadable())
117       return(false);
118    if(!metaElems)
119       return(false);
120    if(patients.size()<=0)
121       return(false);
122
123    return(true);
124 }
125
126 /*
127  * \ingroup gdcmDicomDir
128  * \brief  fills whole the structure
129  */
130 void gdcmDicomDir::ParseDirectory(void)
131 {
132    NewDicomDir(GetPath());
133    CreateDicomDir();
134 }
135
136 /**
137  * \ingroup gdcmDicomDir
138  * \brief   writes on disc a DICOMDIR
139  * \ warning does NOT add the missing elements in the header :
140  *           it's up to the user doing it !
141  * @param  fileName file to be written to 
142  * @return false only when fail to open
143  */
144 bool gdcmDicomDir::Write(std::string fileName) 
145 {
146    FILE * fp1;
147
148    fp1=fopen(fileName.c_str(),"wb");
149    if(fp1==NULL) 
150    {
151       printf("Failed to open(write) File [%s] \n",fileName.c_str());
152       return(false);
153    }
154
155    char * filePreamble;
156    filePreamble=(char*)calloc(128,1);
157    fwrite(filePreamble,128,1,fp1);
158    fwrite("DICM",4,1,fp1);
159    free(filePreamble);
160
161    WriteEntries(fp1,DICOMDIR);
162
163    fclose(fp1);
164
165    return true;
166 }
167
168 //-----------------------------------------------------------------------------
169 // Protected
170 /*
171  * \ingroup gdcmDicomDir
172  * \brief create a gdcmDicomDir from a root Directory 
173  * @param path entry point of the stree-like structure
174  */
175 void gdcmDicomDir::NewDicomDir(std::string path)
176 {
177    CallStartMethod();
178
179    gdcmDirList fileList(path,1);
180    unsigned int count=0;
181    ListHeader list;
182    gdcmHeader *header;
183
184    listEntries.clear();
185    patients.clear();
186
187    for(gdcmDirList::iterator it=fileList.begin(); 
188        it!=fileList.end(); ++it) 
189    {
190       progress=(float)(count+1)/(float)fileList.size();
191       CallProgressMethod();
192       if(abort)
193          break;
194
195       header=new gdcmHeader(it->c_str());
196       if(header->IsReadable())
197          list.push_back(header);
198       else
199          delete header;
200
201       count++;
202    }
203
204    std::sort(list.begin(),list.end(),gdcmDicomDir::HeaderLessThan);
205
206    std::string tmp=fileList.GetDirName();
207    SetElements(tmp,list);
208
209    CallEndMethod();
210 }
211
212 /*
213  * \ingroup gdcmDicomDir
214  * \brief   Get the DicomDir path
215  * @param   
216  */
217 std::string gdcmDicomDir::GetPath(void)
218 {
219    std::string path=GetFileName();
220
221    int pos1=path.rfind("/");
222    int pos2=path.rfind("\\");
223    if(pos1>pos2)
224       path.resize(pos1);
225    else
226       path.resize(pos2);
227
228    return(path);
229 }
230
231 void gdcmDicomDir::CallStartMethod(void)
232 {
233    progress=0.0f;
234    abort=false;
235    if(startMethod)
236       startMethod(startArg);
237 }
238
239 void gdcmDicomDir::CallProgressMethod(void)
240 {
241    if(progressMethod)
242       progressMethod(progressArg);
243 }
244
245 void gdcmDicomDir::CallEndMethod(void)
246 {
247    progress=1.0f;
248    if(endMethod)
249       endMethod(endArg);
250 }
251
252 //-----------------------------------------------------------------------------
253 // Private
254 /*
255  * \ingroup gdcmDicomDir
256  * \brief create a 'gdcmDicomDir' from a DICOMDIR gdcmHeader 
257  */
258 void gdcmDicomDir::CreateDicomDir()
259 {
260    // The list is parsed. When a tag is found :
261    //  1 - we save the beginning iterator
262    //  2 - we continue to parse
263    //  3 - we find an other tag
264    //       + we create the object for the precedent tag
265    //       + loop to 1 -
266
267    gdcmDicomDirType type=gdcmDicomDir::GDCM_META;
268    ListTag::iterator begin;
269    ListTag::iterator end;
270
271    begin=listEntries.begin();
272    end=begin;
273    for(ListTag::iterator i=end;i !=listEntries.end();++i) 
274    {
275       std::string v=(*i)->GetValue();
276       if(v=="PATIENT ") 
277       {
278          end=i;
279          AddObjectToEnd(type,begin,end);
280
281          type=gdcmDicomDir::GDCM_PATIENT;
282          begin=end;
283       } 
284
285       if(v=="STUDY ")
286       {
287          end=i;
288          AddObjectToEnd(type,begin,end);
289
290          type=gdcmDicomDir::GDCM_STUDY;
291          begin=end;
292       }
293
294       if(v=="SERIES") 
295       {
296          end=i;
297          AddObjectToEnd(type,begin,end);
298
299          type=gdcmDicomDir::GDCM_SERIE;
300          begin=end;
301       }
302
303       if(v=="IMAGE ") 
304       {
305          end=i;
306          AddObjectToEnd(type,begin,end);
307
308          type=gdcmDicomDir::GDCM_IMAGE;
309          begin=end;
310       }
311    }
312
313    if(begin!=end)
314    {
315       end=GetListEntry().end();
316       AddObjectToEnd(type,begin,end);
317    }
318 }
319 /*
320  * \ingroup gdcmDicomDir
321  * \brief   
322  * @param   type
323  * @param   begin
324  * @param   end
325  */
326 void gdcmDicomDir::AddObjectToEnd(gdcmDicomDirType type,ListTag::iterator begin,ListTag::iterator end)
327 {
328    if(begin==end)
329       return;
330
331    switch(type)
332    {
333       case gdcmDicomDir::GDCM_META:
334          AddMetaToEnd(begin,end);
335          break;      
336       case gdcmDicomDir::GDCM_PATIENT:
337          AddPatientToEnd(begin,end);
338          break;
339       case gdcmDicomDir::GDCM_STUDY:
340          AddStudyToEnd(begin,end);
341          break;
342       case gdcmDicomDir::GDCM_SERIE:
343          AddSerieToEnd(begin,end);
344          break;
345       case gdcmDicomDir::GDCM_IMAGE:
346          AddImageToEnd(begin,end);
347          break;
348    }
349 }
350
351 /*
352  * \ingroup gdcmDicomDir
353  * \brief Well ... Not realy to end, there is only one occurence  
354  * @param   begin
355  * @param   end
356 */
357 void gdcmDicomDir::AddMetaToEnd(ListTag::iterator begin,ListTag::iterator end)
358 {
359    if(metaElems)
360       delete metaElems;
361    metaElems = new gdcmMeta(begin,end);
362 }
363
364 /*
365  * \ingroup gdcmDicomDir
366  * \brief   
367  * @param   begin
368  * @param   end
369 */
370 void gdcmDicomDir::AddPatientToEnd(ListTag::iterator begin,ListTag::iterator end)
371 {
372    patients.push_back(new gdcmPatient(begin,end));
373 }
374
375 /*
376  * \ingroup gdcmDicomDir
377  * \brief   
378  * @param   begin
379  * @param   end
380  */
381  void gdcmDicomDir::AddStudyToEnd(ListTag::iterator begin,ListTag::iterator end)
382 {
383    if(patients.size()>0)
384    {
385       ListPatient::iterator itp=patients.end();
386       itp--;
387      (*itp)->AddStudy(new gdcmStudy(begin,end));
388    }
389 }
390 /*
391  * \ingroup gdcmDicomDir
392  * \brief   
393  * @param   begin
394  * @param   end
395  */
396 void gdcmDicomDir::AddSerieToEnd(ListTag::iterator begin,ListTag::iterator end)
397 {
398    if(patients.size()>0)
399    {
400       ListPatient::iterator itp=patients.end();
401       itp--;
402
403       if((*itp)->GetStudies().size()>0)
404       {
405          ListStudy::iterator itst=(*itp)->GetStudies().end();
406          itst--;
407         (*itst)->AddSerie(new gdcmSerie(begin,end));
408       }
409    }
410 }
411
412 /*
413  * \ingroup gdcmDicomDir
414  * @param   begin
415  * @param   end
416  * @param   
417  */
418  void gdcmDicomDir::AddImageToEnd(ListTag::iterator begin,ListTag::iterator end)
419 {
420    if(patients.size()>0)
421    {
422       ListPatient::iterator itp=patients.end();
423       itp--;
424
425       if((*itp)->GetStudies().size()>0)
426       {
427          ListStudy::iterator itst=(*itp)->GetStudies().end();
428          itst--;
429
430          if((*itst)->GetSeries().size()>0)
431          {
432             ListSerie::iterator its=(*itst)->GetSeries().end();
433             its--;
434            (*its)->AddImage(new gdcmImage(begin,end));
435          }
436       }
437    }
438 }
439
440 /*
441  * \ingroup gdcmDicomDir
442  * \brief   
443  * @param   path
444  * @param   list
445  */
446 void gdcmDicomDir::SetElements(std::string &path,ListHeader &list)
447 {
448    std::string patPrevName="", patPrevID="";
449    std::string studPrevInstanceUID="", studPrevID="";
450    std::string serPrevInstanceUID="", serPrevID="";
451
452    std::string patCurName, patCurID;
453    std::string studCurInstanceUID, studCurID;
454    std::string serCurInstanceUID, serCurID;
455
456    SetElement(path,GDCM_NONE,NULL);
457
458    ListTag::iterator debPat=listEntries.begin();
459    for(ListHeader::iterator it=list.begin();it!=list.end();++it) 
460    {
461       // get the current file characteristics
462       patCurName=(*it)->GetEntryByNumber(0x0010,0x0010); 
463       patCurID=(*it)->GetEntryByNumber(0x0010,0x0011); 
464       studCurInstanceUID=(*it)->GetEntryByNumber(0x0020,0x000d);            
465       studCurID=(*it)->GetEntryByNumber(0x0020,0x0010);            
466       serCurInstanceUID=(*it)->GetEntryByNumber(0x0020,0x000e);            
467       serCurID=(*it)->GetEntryByNumber(0x0020,0x0011);
468
469       if(patCurName!=patPrevName || patCurID!=patPrevID) 
470          SetElement(path,GDCM_PATIENT,*it);
471
472       // if new Study Deal with 'STUDY' Elements   
473       if(studCurInstanceUID!=studPrevInstanceUID || studCurID!=studPrevID) 
474          SetElement(path,GDCM_STUDY,*it);
475
476       // if new Serie Deal with 'SERIE' Elements   
477       if(serCurInstanceUID!=serPrevInstanceUID || serCurID!=serPrevID) 
478          SetElement(path,GDCM_SERIE,*it);
479       
480       // Always Deal with 'IMAGE' Elements  
481       SetElement(path,GDCM_IMAGE,*it);
482
483       patPrevName=patCurName;
484       patPrevID=patCurID;
485       studPrevInstanceUID=studCurInstanceUID;
486       studPrevID=studCurID;
487       serPrevInstanceUID=serCurInstanceUID;
488       serPrevID=serCurID;
489    }
490 }
491
492 /*
493  * \ingroup gdcmDicomDir
494  * \brief   
495  * @param   path
496  * @param   type
497  * @param   header
498  */
499 void gdcmDicomDir::SetElement(std::string &path,gdcmDicomDirType type,gdcmHeader *header)
500 {
501    std::list<gdcmElement> elemList;
502    std::list<gdcmElement>::iterator it;
503    guint16 tmpGr, tmpEl;
504    gdcmDictEntry *dictEntry;
505    gdcmHeaderEntry *entry;
506    std::string val;
507
508    switch(type)
509    {
510       case GDCM_PATIENT:
511          elemList=gdcmGlobal::GetDicomDirElements()->GetPatientElements();
512          break;
513       case GDCM_STUDY:
514          elemList=gdcmGlobal::GetDicomDirElements()->GetStudyElements();
515          break;
516       case GDCM_SERIE:
517          elemList=gdcmGlobal::GetDicomDirElements()->GetSerieElements();
518          break;
519       case GDCM_IMAGE:
520          elemList=gdcmGlobal::GetDicomDirElements()->GetImageElements();
521          break;
522       case GDCM_NONE:
523          elemList=gdcmGlobal::GetDicomDirElements()->GetMetaElements();
524          break;
525       default:
526          return;
527    }
528
529    for(it=elemList.begin();it!=elemList.end();++it)
530    {
531       tmpGr=it->group;
532       tmpEl=it->elem;
533
534       dictEntry=GetPubDict()->GetDictEntryByNumber(tmpGr,tmpEl);
535       entry=new gdcmHeaderEntry(dictEntry);
536       entry->SetOffset(0); // just to avoid missprinting
537
538       if(header)
539          val=header->GetEntryByNumber(tmpGr,tmpEl);
540       else
541          val=GDCM_UNFOUND;
542
543       if(val==GDCM_UNFOUND) 
544       {
545          if((tmpGr==0x0004) &&(tmpEl==0x1130) )
546          {
547             // TODO force the *end* File Name(remove path)
548             val=path;
549          }
550          else if( (tmpGr==0x0004) && (tmpEl==0x1500) ) // Only used for image
551          {
552             if(header->GetFileName().substr(0,path.length())!=path)
553             {
554                dbg.Verbose(0, "gdcmDicomDir::SetElement : the base path of file name is incorrect");
555                val=header->GetFileName();
556             }
557             else
558                val=&(header->GetFileName()[path.length()]);
559          }
560          else
561          {
562             val=it->value;
563          }
564       }
565       entry->SetValue(val);
566
567       if(dictEntry)
568       {
569          if( (dictEntry->GetVR()=="UL") || (dictEntry->GetVR()=="SL") ) 
570          {
571             entry->SetLength(4);
572          } 
573          else if( (dictEntry->GetVR()=="US") || (dictEntry->GetVR()=="SS") ) 
574          {
575             entry->SetLength(2); 
576          } 
577          else if(dictEntry->GetVR()=="SQ") 
578          {
579             entry->SetLength(0xffffffff);
580          }
581          else
582          {
583             entry->SetLength(entry->GetValue().length());        
584          }
585       }
586
587       listEntries.push_back(entry);
588    }     
589 }
590
591 bool gdcmDicomDir::HeaderLessThan(gdcmHeader *header1,gdcmHeader *header2)
592 {
593    return(*header1<*header2);
594 }
595
596 //-----------------------------------------------------------------------------