]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
- now, DicomDir stuff stakes into account the 'new' structure
[gdcm.git] / src / gdcmDicomDir.cxx
1 /*=========================================================================
2   
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDicomDir.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/08/26 15:29:52 $
7   Version:   $Revision: 1.64 $
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[3]= { 0xfffe, 0xe0dd, 0x0000 };
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  //  UpdateDirectoryRecordSequenceLength(); // TODO (if *really* usefull)
360    gdcmDicomDirMeta *ptrMeta = GetDicomDirMeta();
361    ptrMeta->Write(fp, gdcmExplicitVR);
362    
363    // force writing 0004|1220 [SQ ], that CANNOT exist within gdcmDicomDirMeta
364    fwrite(&sq[0],4,1,fp);  // 0004 1220 
365    fwrite("SQ"  ,2,1,fp);  // SQ
366    fwrite(&sq[2],6,1,fp);  // 00 ffffffff
367       
368    for(ListDicomDirPatient::iterator cc = patients.begin();cc!=patients.end();++cc)
369    {
370       (*cc)->Write( fp, gdcmExplicitVR );
371    }
372    // force writing Sequence Delimitation Item
373    fwrite(&sqt[0],4,1,fp);  // fffe e0dd 
374    fwrite("UL"   ,2,1,fp);  // UL
375    fwrite(&sqt[2],1,1,fp);  // 00 
376    fclose( fp );
377    return true;
378 }
379
380 //-----------------------------------------------------------------------------
381 // Protected
382
383 /**
384  * \ingroup gdcmDicomDir
385  * \brief create a gdcmDocument-like chained list from a root Directory 
386  * @param path entry point of the tree-like structure
387  */
388 void gdcmDicomDir::CreateDicomDirChainedList(std::string const & path)
389 {
390    CallStartMethod();
391    gdcmDirList fileList(path,1); // gets recursively the file list
392    unsigned int count = 0;
393    VectDocument list;
394    gdcmHeader *header;
395
396    TagHT.clear();
397    patients.clear();
398
399    for( gdcmDirList::iterator it  = fileList.begin();
400                               it != fileList.end();
401                               ++it )
402    {
403       progress = (float)(count+1)/(float)fileList.size();
404       CallProgressMethod();
405       if( abort )
406       {
407          break;
408       }
409
410       header = new gdcmHeader( it->c_str() );
411       if( !header )
412       {
413          dbg.Verbose( 1,
414                       "gdcmDicomDir::CreateDicomDirChainedList: "
415                       "failure in new Header ",
416                       it->c_str() );
417       }
418       if( header->IsReadable() )
419       {
420          // Add the file header to the chained list:
421          list.push_back(header);
422          dbg.Verbose( 1,
423                       "gdcmDicomDir::CreateDicomDirChainedList: readable ",
424                       it->c_str() );
425        }
426        else
427        {
428           delete header;
429        }
430        count++;
431    }
432
433    // sorts Patient/Study/Serie/
434    std::sort(list.begin(), list.end(), gdcmDicomDir::HeaderLessThan );
435
436    std::string tmp = fileList.GetDirName();
437       
438    //for each Header of the chained list, add/update the Patient/Study/Serie/Image info
439    SetElements(tmp, list);      
440    CallEndMethod();
441 }
442
443 /**
444  * \ingroup gdcmDicomDir
445  * \brief   adds *the* Meta to a partially created DICOMDIR
446  */
447   
448 gdcmDicomDirMeta * gdcmDicomDir::NewMeta()
449 {
450    gdcmDicomDirMeta *m = new gdcmDicomDirMeta( &TagHT );
451   
452    if ( TagHT.begin() != TagHT.end() ) // after Document Parsing
453    { 
454       TagDocEntryHT::iterator lastOneButSequence = TagHT.end();
455       lastOneButSequence --;
456       // This works because ALL the 'out of Sequence' Tags belong to Meta Elems
457       // (we skip 0004|1220 [Directory record sequence] )
458       for ( TagDocEntryHT::iterator cc  = TagHT.begin(); 
459                                     cc != lastOneButSequence;
460                                    ++cc)
461       {
462          m->AddDocEntry( cc->second );
463       }
464    }
465    else  // after root directory parsing
466    {
467      //cout << "gdcmDicomDir::NewMeta avec FillObject" << endl;
468      std::list<gdcmElement> elemList;
469      elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirMetaElements();
470      m->FillObject(elemList);
471      // we create the Sequence manually
472      //gdcmSeqEntry *se =NewSeqEntryByNumber(0x0004, 0x1220); // NOT YET!
473      //m->AddEntry(se);
474     }
475    m->SetSQItemNumber(0); // To avoid further missprinting
476    return m;  
477 }
478
479
480 /**
481  * \brief   adds a new Patient (with the basic elements) to a partially created DICOMDIR
482  */
483 gdcmDicomDirPatient * gdcmDicomDir::NewPatient()
484 {
485    std::list<gdcmElement>::iterator it;
486    uint16_t tmpGr,tmpEl;
487    gdcmDictEntry *dictEntry;
488    gdcmValEntry *entry;
489
490    std::list<gdcmElement> elemList;   
491    elemList=gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements(); 
492 // Looks nice, but gdcmDicomDir IS NOT a gdcmObject ... 
493 //   gdcmDicomDirPatient *p = new gdcmDicomDirPatient(ptagHT);
494 //   FillObject(elemList);
495 //   patients.push_front( p );
496 //   return p;    
497 /// \todo TODO : find a trick to use FillObject !!!
498
499    gdcmSQItem *s = new gdcmSQItem(0);
500
501    // for all the DicomDirPatient Elements      
502    for( it = elemList.begin(); it != elemList.end(); ++it ) 
503    {
504       tmpGr     = it->group;
505       tmpEl     = it->elem;
506       dictEntry = GetPubDict()->GetDictEntryByNumber(tmpGr, tmpEl);
507       entry     = new gdcmValEntry( dictEntry );
508       entry->SetOffset(0); // just to avoid further missprinting
509       entry->SetValue( it->value );
510
511       // dealing with value length ...
512       
513       if( dictEntry->GetGroup() == 0xfffe)
514       {
515          entry->SetLength(entry->GetValue().length());
516       }
517       else if( dictEntry->GetVR() == "UL" || dictEntry->GetVR() == "SL" )
518       {
519          entry->SetLength( 4 );
520       } 
521       else if( dictEntry->GetVR() == "US" || dictEntry->GetVR() == "SS" )
522       {
523          entry->SetLength(2); 
524       } 
525       else if( dictEntry->GetVR() == "SQ" )
526       {
527          entry->SetLength( 0xffffffff );
528       }
529       else
530       {
531          entry->SetLength( entry->GetValue().length() );
532       }
533       s->AddDocEntry( entry );
534    }
535
536    gdcmDicomDirPatient *p = new gdcmDicomDirPatient(s, &TagHT);
537    patients.push_front( p );
538
539    return p;   
540 }
541
542 /**
543  * \brief   adds to the HTable 
544  *          the gdcmEntries (Dicom Elements) corresponding to the given type
545  * @param   path full path file name (only used when type = GDCM_DICOMDIR_IMAGE
546  * @param   type gdcmObject type to create (GDCM_DICOMDIR_PATIENT,
547  *          GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
548  * @param   header gdcmHeader of the current file
549  */
550 void gdcmDicomDir::SetElement(std::string &path,gdcmDicomDirType type,
551                               gdcmDocument *header)
552 {
553    std::list<gdcmElement> elemList;
554    std::list<gdcmElement>::iterator it;
555    uint16_t tmpGr, tmpEl;
556    gdcmDictEntry *dictEntry;
557    gdcmValEntry *entry;
558    std::string val;
559    gdcmSQItem *si = new gdcmSQItem(0); // all the items will be at level 1
560    switch( type )
561    {
562       case GDCM_DICOMDIR_IMAGE:
563          elemList = gdcmGlobal::GetDicomDirElements()->GetDicomDirImageElements();
564          break;
565
566       case GDCM_DICOMDIR_SERIE:
567          elemList = gdcmGlobal::GetDicomDirElements()->GetDicomDirSerieElements();
568          break;
569
570       case GDCM_DICOMDIR_STUDY:
571          elemList = gdcmGlobal::GetDicomDirElements()->GetDicomDirStudyElements();
572          break;
573
574       case GDCM_DICOMDIR_PATIENT:
575          elemList = gdcmGlobal::GetDicomDirElements()->GetDicomDirPatientElements();
576          break;
577   
578       case GDCM_DICOMDIR_META:
579          elemList = gdcmGlobal::GetDicomDirElements()->GetDicomDirMetaElements();
580          // add already done ?
581          break;
582
583       default:
584          return;
585    }
586    // removed all the seems-to-be-useless stuff about Referenced Image Sequence
587    // to avoid further troubles
588    // imageElem 0008 1140 "" // Referenced Image Sequence
589    // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
590    // imageElem 0008 1150 "" // Referenced SOP Class UID    : to be set/forged later
591    // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
592    // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
593    // for all the relevant elements found in their own spot of the DicomDir.dic
594    // FIXME : troubles found when it's a SeqEntry
595
596    for( it = elemList.begin(); it != elemList.end(); ++it)
597    {
598       tmpGr     = it->group;
599       tmpEl     = it->elem;
600       dictEntry = GetPubDict()->GetDictEntryByNumber(tmpGr, tmpEl);
601
602       entry     = new gdcmValEntry( dictEntry ); // Be sure it's never a BinEntry !
603
604       entry->SetOffset(0); // just to avoid further missprinting
605       entry->SetLength(0); // just to avoid further missprinting
606
607       if( header ) // NULL when we Build Up (ex nihilo) a DICOMDIR
608                    //   or when we add the META elems
609          val = header->GetEntryByNumber(tmpGr, tmpEl);
610       else
611          val = GDCM_UNFOUND;
612
613       if( val == GDCM_UNFOUND) 
614       {
615          if( tmpGr == 0x0004 && tmpEl == 0x1130 ) // File-set ID
616          {
617            // force to the *end* File Name
618            val = GetName( path );
619          }
620          else if( tmpGr == 0x0004 && tmpEl == 0x1500 ) // Only used for image
621          {
622             if( header->GetFileName().substr(0, path.length()) != path )
623             {
624                dbg.Verbose(0, "gdcmDicomDir::SetElement : the base path"
625                               " of file name is incorrect");
626                val = header->GetFileName();
627             }
628             else
629                val = &(header->GetFileName().c_str()[path.length()]);
630          }
631          else
632             val = it->value;
633       }
634       else
635       {
636          if ( header->GetEntryLengthByNumber(tmpGr,tmpEl) == 0 )
637             val = it->value;
638       }
639
640      // GDCM_UNFOUND or not !
641
642       entry->SetValue( val ); // troubles expected when vr=SQ ...
643
644       if( dictEntry )
645       {
646          if( dictEntry->GetGroup() == 0xfffe )
647          {
648             entry->SetLength( entry->GetValue().length() ); // FIXME 
649          }
650          else if( dictEntry->GetVR() == "UL" || dictEntry->GetVR() == "SL" )
651          {
652             entry->SetLength(4);
653          }
654          else if( dictEntry->GetVR() == "US" || dictEntry->GetVR() == "SS" )
655          {
656             entry->SetLength(2); 
657          }
658          else if( dictEntry->GetVR() == "SQ" )
659          {
660             entry->SetLength( 0xffffffff );
661          }
662          else
663          {
664             entry->SetLength( entry->GetValue().length() );
665          }
666       }
667
668       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
669          std::cout << " special Treatment for GDCM_DICOMDIR_META" << std::endl;
670       si->AddEntry(entry);
671    }
672    switch( type )
673    {
674       case GDCM_DICOMDIR_IMAGE:
675          AddDicomDirImageToEnd(si);
676          break;
677
678       case GDCM_DICOMDIR_SERIE:
679          AddDicomDirSerieToEnd(si);
680          break;
681
682       case GDCM_DICOMDIR_STUDY:
683          AddDicomDirStudyToEnd(si);
684          break;
685
686       case GDCM_DICOMDIR_PATIENT:
687          AddDicomDirPatientToEnd(si);
688          break;
689
690       default:
691          return;
692    }
693    //int count=1;            // find a trick to increment
694    //s->AddEntry(si, count); // Seg Faults 
695
696 }
697
698 /**
699  * \brief   CallStartMethod
700  */
701 void gdcmDicomDir::CallStartMethod()
702 {
703    progress = 0.0f;
704    abort    = false;
705    if( startMethod )
706    {
707       startMethod( startArg );
708    }
709 }
710
711 /**
712  * \ingroup gdcmDicomDir
713  * \brief   CallProgressMethod
714  */
715 void gdcmDicomDir::CallProgressMethod()
716 {
717    if( progressMethod )
718    {
719       progressMethod( progressArg );
720    }
721 }
722
723 /**
724  * \ingroup gdcmDicomDir
725  * \brief   CallEndMethod
726  */
727 void gdcmDicomDir::CallEndMethod()
728 {
729    progress = 1.0f;
730    if( endMethod )
731    {
732       endMethod( endArg );
733    }
734 }
735
736 //-----------------------------------------------------------------------------
737 // Private
738 /**
739  * \ingroup gdcmDicomDir
740  * \brief create a 'gdcmDicomDir' from a DICOMDIR gdcmHeader 
741  */
742 void gdcmDicomDir::CreateDicomDir()
743 {
744    // The list is parsed. 
745    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
746    //  1 - we save the beginning iterator
747    //  2 - we continue to parse
748    //  3 - we find an other tag
749    //       + we create the object for the precedent tag
750    //       + loop to 1 -
751
752    // Directory record sequence
753    gdcmDocEntry *e = GetDocEntryByNumber(0x0004, 0x1220);
754    if ( !e )
755    {
756       dbg.Verbose(0, "gdcmDicomDir::gdcmDicomDir : NO Directory record"
757                      " sequence (0x0004,0x1220)");
758       /// \todo FIXME: what to do when the parsed file IS NOT a DICOMDIR file ? 
759       return;         
760    }
761    
762    gdcmSeqEntry* s = dynamic_cast<gdcmSeqEntry*>(e);
763    if ( !s )
764    {
765       dbg.Verbose(0, "gdcmDicomDir::CreateDicomDir: no SeqEntry present");
766       // useless : (0x0004,0x1220) IS a Sequence !
767       return;
768    }
769
770    gdcmDicomDirType type = gdcmDicomDir::GDCM_DICOMDIR_META;
771    metaElems = NewMeta();
772
773    ListSQItem listItems = s->GetSQItems();
774    
775    gdcmDocEntry * d;
776    std::string v;
777    for( ListSQItem::iterator i = listItems.begin(); 
778                              i !=listItems.end(); ++i ) 
779    {
780       d = (*i)->GetDocEntryByNumber(0x0004, 0x1430); // Directory Record Type
781       if ( gdcmValEntry* ValEntry = dynamic_cast< gdcmValEntry* >(d) )
782       {
783          v = ValEntry->GetValue();
784       }
785       else
786       {
787          dbg.Verbose(0, "gdcmDicomDir::CreateDicomDir: not a ValEntry.");
788          continue;
789       }
790
791       if( v == "PATIENT " )
792       {
793          AddDicomDirPatientToEnd( *i );
794          type = gdcmDicomDir::GDCM_DICOMDIR_PATIENT;
795       }
796       else if( v == "STUDY " )
797       {
798          AddDicomDirStudyToEnd( *i );
799          type = gdcmDicomDir::GDCM_DICOMDIR_STUDY;
800       }
801       else if( v == "SERIES" )
802       {
803          AddDicomDirSerieToEnd( *i );
804          type = gdcmDicomDir::GDCM_DICOMDIR_SERIE;
805       }
806       else if( v == "IMAGE " ) 
807       {
808          AddDicomDirImageToEnd( *i );
809          type = gdcmDicomDir::GDCM_DICOMDIR_IMAGE;
810       }
811       else
812       {
813          // It was not a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
814          // neither an 'IMAGE' SQItem. Skip to next item.
815          continue;
816       }
817    }
818 }
819
820 /**
821  * \ingroup gdcmDicomDir
822  * \brief Well ... there is only one occurence  
823  */
824 void gdcmDicomDir::AddDicomDirMeta()
825 {
826    if( metaElems )
827    {
828       delete metaElems;
829    }
830    metaElems = new gdcmDicomDirMeta( &TagHT );
831 }
832
833 /**
834  * \ingroup gdcmDicomDir
835  * \brief  AddDicomDirPatientToEnd 
836  * @param   s SQ Item to enqueue to the DicomPatient chained List
837  */
838 void gdcmDicomDir::AddDicomDirPatientToEnd(gdcmSQItem *s)
839 {
840    patients.push_back(new gdcmDicomDirPatient(s, &TagHT));
841 }
842
843 /**
844  * \ingroup gdcmDicomDir
845  * \brief  AddDicomDirStudyToEnd 
846  * @param   s SQ Item to enqueue to the DicomDirStudy chained List
847  */
848  void gdcmDicomDir::AddDicomDirStudyToEnd(gdcmSQItem *s)
849 {
850    if( patients.size() > 0 )
851    {
852       ListDicomDirPatient::iterator itp = patients.end();
853       itp--;
854       (*itp)->AddDicomDirStudy(new gdcmDicomDirStudy(s, &TagHT));
855    }
856 }
857
858 /**
859  * \ingroup gdcmDicomDir
860  * \brief  AddDicomDirSerieToEnd 
861  * @param   s SQ Item to enqueue to the DicomDirSerie chained List
862  */
863 void gdcmDicomDir::AddDicomDirSerieToEnd(gdcmSQItem *s)
864 {
865    if( patients.size() > 0 )
866    {
867       ListDicomDirPatient::iterator itp = patients.end();
868       itp--;
869
870       if( (*itp)->GetDicomDirStudies().size() > 0 )
871       {
872          ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
873          itst--;
874          (*itst)->AddDicomDirSerie(new gdcmDicomDirSerie(s, &TagHT));
875       }
876    }
877 }
878
879 /**
880  * \ingroup gdcmDicomDir
881  * \brief   AddDicomDirImageToEnd
882  * @param   s SQ Item to enqueue to the DicomDirImage chained List
883  */
884  void gdcmDicomDir::AddDicomDirImageToEnd(gdcmSQItem *s)
885 {
886    if( patients.size() > 0 )
887    {
888       ListDicomDirPatient::iterator itp = patients.end();
889       itp--;
890
891       if( (*itp)->GetDicomDirStudies().size() > 0 )
892       {
893          ListDicomDirStudy::iterator itst = (*itp)->GetDicomDirStudies().end();
894          itst--;
895
896          if( (*itst)->GetDicomDirSeries().size() > 0 )
897          {
898             ListDicomDirSerie::iterator its = (*itst)->GetDicomDirSeries().end();
899             its--;
900             (*its)->AddDicomDirImage(new gdcmDicomDirImage(s, &TagHT));
901          }
902       }
903    }
904 }
905
906 /**
907  * \ingroup gdcmDicomDir
908  * \brief  for each Header of the chained list, add/update the Patient/Study/Serie/Image info 
909  * @param   path path of the root directory
910  * @param   list chained list of Headers
911  */
912 void gdcmDicomDir::SetElements(std::string &path, VectDocument &list)
913 {
914    std::string patPrevName         = "", patPrevID  = "";
915    std::string studPrevInstanceUID = "", studPrevID = "";
916    std::string serPrevInstanceUID  = "", serPrevID  = "";
917
918    std::string patCurName,         patCurID;
919    std::string studCurInstanceUID, studCurID;
920    std::string serCurInstanceUID,  serCurID;
921
922    //SetElement( path, GDCM_DICOMDIR_META,NULL); // already done (NewMeta) 
923
924    for( VectDocument::iterator it = list.begin();
925                               it != list.end(); ++it )
926    {
927       // get the current file characteristics
928       patCurName         = (*it)->GetEntryByNumber(0x0010,0x0010); 
929       patCurID           = (*it)->GetEntryByNumber(0x0010,0x0011); 
930       studCurInstanceUID = (*it)->GetEntryByNumber(0x0020,0x000d);            
931       studCurID          = (*it)->GetEntryByNumber(0x0020,0x0010);            
932       serCurInstanceUID  = (*it)->GetEntryByNumber(0x0020,0x000e);            
933       serCurID           = (*it)->GetEntryByNumber(0x0020,0x0011);
934
935       if( patCurName != patPrevName || patCurID != patPrevID)
936       {
937          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
938       }
939
940       // if new Study Deal with 'STUDY' Elements   
941       if( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID )
942       {
943          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
944       }
945
946       // if new Serie Deal with 'SERIE' Elements   
947       if( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID )
948       {
949          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
950       }
951       
952       // Always Deal with 'IMAGE' Elements  
953       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
954
955       patPrevName         = patCurName;
956       patPrevID           = patCurID;
957       studPrevInstanceUID = studCurInstanceUID;
958       studPrevID          = studCurID;
959       serPrevInstanceUID  = serCurInstanceUID;
960       serPrevID           = serCurID;
961    }
962 }
963
964 /**
965  * \ingroup gdcmDicomDir
966  * \brief   compares two dgcmHeaders
967  */
968 bool gdcmDicomDir::HeaderLessThan(gdcmDocument *header1, gdcmDocument *header2)
969 {
970    return *header1 < *header2;
971 }
972
973 /**
974  * \brief   Sets the accurate value for the (0x0004,0x1220) element of a DICOMDIR
975  */
976 void gdcmDicomDir::UpdateDirectoryRecordSequenceLength()
977 {
978
979 /// \todo FIXME : to go on compiling
980 ///
981 /// to be re written !
982 ///   int offset = 0;
983 ///   ListTag::iterator it;
984 ///   uint16_t gr, el;
985 ///   std::string vr;
986 ///   for(it=listEntries.begin();it!=listEntries.end();++it) {
987 ///      gr = (*it)->GetGroup();
988 ///      el = (*it)->GetElement();
989 ///      vr = (*it)->GetVR();      
990 ///      if (gr !=0xfffe) {
991 ///         if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {    
992 ///            offset +=  4; // explicit VR AND OB, OW, SQ : 4 more bytes
993 ///         }         
994 ///         offset += 2 + 2 + 4 + (*it)->GetLength(); 
995 ///      } else {
996 ///         offset +=  4; // delimiters don't have a value.     
997 ///      }            
998 ///   }   
999 ///   //bool res=SetEntryLengthByNumber(offset, 0x0004, 0x1220); // Hope there is no dupps.
1000 ///    SetEntryLengthByNumber(offset, 0x0004, 0x1220); // Hope there is no dupps.
1001 ///   return;
1002 ///
1003 }
1004
1005 //-----------------------------------------------------------------------------