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