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