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