]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
ENH: Removed all FILE* ref and replace by ifstream/ofstream. For now I use a temp...
[gdcm.git] / src / gdcmDicomDir.cxx
1 /*=========================================================================
2   
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDicomDir.cxx,v $
5   Language:  C++
6   Date:      $Date: 2004/10/22 03:05:40 $
7   Version:   $Revision: 1.74 $
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      std::list<Element> elemList;
467      elemList=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    std::list<Element>::iterator it;
480    uint16_t tmpGr,tmpEl;
481    DictEntry *dictEntry;
482    ValEntry *entry;
483
484    std::list<Element> elemList;   
485    elemList=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    std::list<Element> elemList;
541    std::list<Element>::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  * \brief   CallStartMethod
698  */
699 void DicomDir::CallStartMethod()
700 {
701    Progress = 0.0f;
702    Abort    = false;
703    if( StartMethod )
704    {
705       StartMethod( StartArg );
706    }
707 }
708
709 /**
710  * \ingroup DicomDir
711  * \brief   CallProgressMethod
712  */
713 void DicomDir::CallProgressMethod()
714 {
715    if( ProgressMethod )
716    {
717       ProgressMethod( ProgressArg );
718    }
719 }
720
721 /**
722  * \ingroup DicomDir
723  * \brief   CallEndMethod
724  */
725 void DicomDir::CallEndMethod()
726 {
727    Progress = 1.0f;
728    if( EndMethod )
729    {
730       EndMethod( EndArg );
731    }
732 }
733
734 //-----------------------------------------------------------------------------
735 // Private
736 /**
737  * \ingroup DicomDir
738  * \brief create a 'DicomDir' from a DICOMDIR Header 
739  */
740 void DicomDir::CreateDicomDir()
741 {
742    // The list is parsed. 
743    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
744    //  1 - we save the beginning iterator
745    //  2 - we continue to parse
746    //  3 - we find an other tag
747    //       + we create the object for the precedent tag
748    //       + loop to 1 -
749
750    // Directory record sequence
751    DocEntry *e = GetDocEntryByNumber(0x0004, 0x1220);
752    if ( !e )
753    {
754       dbg.Verbose(0, "DicomDir::DicomDir : NO Directory record"
755                      " sequence (0x0004,0x1220)");
756       /// \todo FIXME: what to do when the parsed file IS NOT a DICOMDIR file ? 
757       return;         
758    }
759    
760    SeqEntry* s = dynamic_cast<SeqEntry*>(e);
761    if ( !s )
762    {
763       dbg.Verbose(0, "DicomDir::CreateDicomDir: no SeqEntry present");
764       // useless : (0x0004,0x1220) IS a Sequence !
765       return;
766    }
767
768    DicomDirType type = DicomDir::GDCM_DICOMDIR_META;
769    MetaElems = NewMeta();
770
771    ListSQItem listItems = s->GetSQItems();
772    
773    DocEntry * d;
774    std::string v;
775    for( ListSQItem::iterator i = listItems.begin(); 
776                              i !=listItems.end(); ++i ) 
777    {
778       d = (*i)->GetDocEntryByNumber(0x0004, 0x1430); // Directory Record Type
779       if ( ValEntry* valEntry = dynamic_cast< ValEntry* >(d) )
780       {
781          v = valEntry->GetValue();
782       }
783       else
784       {
785          dbg.Verbose(0, "DicomDir::CreateDicomDir: not a ValEntry.");
786          continue;
787       }
788
789       if( v == "PATIENT " )
790       {
791          AddDicomDirPatientToEnd( *i );
792          type = DicomDir::GDCM_DICOMDIR_PATIENT;
793       }
794       else if( v == "STUDY " )
795       {
796          AddDicomDirStudyToEnd( *i );
797          type = DicomDir::GDCM_DICOMDIR_STUDY;
798       }
799       else if( v == "SERIES" )
800       {
801          AddDicomDirSerieToEnd( *i );
802          type = DicomDir::GDCM_DICOMDIR_SERIE;
803       }
804       else if( v == "IMAGE " ) 
805       {
806          AddDicomDirImageToEnd( *i );
807          type = DicomDir::GDCM_DICOMDIR_IMAGE;
808       }
809       else
810       {
811          // It was not a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
812          // neither an 'IMAGE' SQItem. Skip to next item.
813          continue;
814       }
815    }
816 }
817
818 /**
819  * \ingroup DicomDir
820  * \brief Well ... there is only one occurence  
821  */
822 void DicomDir::AddDicomDirMeta()
823 {
824    if( MetaElems )
825    {
826       delete MetaElems;
827    }
828    MetaElems = new DicomDirMeta( &TagHT );
829 }
830
831 /**
832  * \ingroup DicomDir
833  * \brief  AddDicomDirPatientToEnd 
834  * @param   s SQ Item to enqueue to the DicomPatient chained List
835  */
836 void DicomDir::AddDicomDirPatientToEnd(SQItem *s)
837 {
838    Patients.push_back(new DicomDirPatient(s, &TagHT));
839 }
840
841 /**
842  * \ingroup DicomDir
843  * \brief  AddDicomDirStudyToEnd 
844  * @param   s SQ Item to enqueue to the DicomDirStudy chained List
845  */
846  void DicomDir::AddDicomDirStudyToEnd(SQItem *s)
847 {
848    if( Patients.size() > 0 )
849    {
850       ListDicomDirPatient::iterator itp = Patients.end();
851       itp--;
852       (*itp)->AddDicomDirStudy(new DicomDirStudy(s, &TagHT));
853    }
854 }
855
856 /**
857  * \ingroup DicomDir
858  * \brief  AddDicomDirSerieToEnd 
859  * @param   s SQ Item to enqueue to the DicomDirSerie chained List
860  */
861 void DicomDir::AddDicomDirSerieToEnd(SQItem *s)
862 {
863    if( Patients.size() > 0 )
864    {
865       ListDicomDirPatient::iterator itp = Patients.end();
866       itp--;
867
868       if( (*itp)->GetDicomDirStudies().size() > 0 )
869       {
870          ListDicomDirStudy::iterator itst=(*itp)->GetDicomDirStudies().end();
871          itst--;
872          (*itst)->AddDicomDirSerie(new DicomDirSerie(s, &TagHT));
873       }
874    }
875 }
876
877 /**
878  * \ingroup DicomDir
879  * \brief   AddDicomDirImageToEnd
880  * @param   s SQ Item to enqueue to the DicomDirImage chained List
881  */
882  void DicomDir::AddDicomDirImageToEnd(SQItem *s)
883 {
884    if( Patients.size() > 0 )
885    {
886       ListDicomDirPatient::iterator itp = Patients.end();
887       itp--;
888
889       if( (*itp)->GetDicomDirStudies().size() > 0 )
890       {
891          ListDicomDirStudy::iterator itst = (*itp)->GetDicomDirStudies().end();
892          itst--;
893
894          if( (*itst)->GetDicomDirSeries().size() > 0 )
895          {
896             ListDicomDirSerie::iterator its = (*itst)->GetDicomDirSeries().end();
897             its--;
898             (*its)->AddDicomDirImage(new DicomDirImage(s, &TagHT));
899          }
900       }
901    }
902 }
903
904 /**
905  * \ingroup DicomDir
906  * \brief  for each Header of the chained list, add/update the Patient/Study/Serie/Image info 
907  * @param   path path of the root directory
908  * @param   list chained list of Headers
909  */
910 void DicomDir::SetElements(std::string &path, VectDocument &list)
911 {
912    std::string patPrevName         = "", patPrevID  = "";
913    std::string studPrevInstanceUID = "", studPrevID = "";
914    std::string serPrevInstanceUID  = "", serPrevID  = "";
915
916    std::string patCurName,         patCurID;
917    std::string studCurInstanceUID, studCurID;
918    std::string serCurInstanceUID,  serCurID;
919
920    for( VectDocument::iterator it = list.begin();
921                               it != list.end(); ++it )
922    {
923       // get the current file characteristics
924       patCurName         = (*it)->GetEntryByNumber(0x0010,0x0010); 
925       patCurID           = (*it)->GetEntryByNumber(0x0010,0x0011); 
926       studCurInstanceUID = (*it)->GetEntryByNumber(0x0020,0x000d);            
927       studCurID          = (*it)->GetEntryByNumber(0x0020,0x0010);            
928       serCurInstanceUID  = (*it)->GetEntryByNumber(0x0020,0x000e);            
929       serCurID           = (*it)->GetEntryByNumber(0x0020,0x0011);
930
931       if( patCurName != patPrevName || patCurID != patPrevID)
932       {
933          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
934       }
935
936       // if new Study Deal with 'STUDY' Elements   
937       if( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID )
938       {
939          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
940       }
941
942       // if new Serie Deal with 'SERIE' Elements   
943       if( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID )
944       {
945          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
946       }
947       
948       // Always Deal with 'IMAGE' Elements  
949       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
950
951       patPrevName         = patCurName;
952       patPrevID           = patCurID;
953       studPrevInstanceUID = studCurInstanceUID;
954       studPrevID          = studCurID;
955       serPrevInstanceUID  = serCurInstanceUID;
956       serPrevID           = serCurID;
957    }
958 }
959
960 /**
961  * \ingroup DicomDir
962  * \brief   compares two dgcmHeaders
963  */
964 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
965 {
966    return *header1 < *header2;
967 }
968 } // end namespace gdcm
969
970 //-----------------------------------------------------------------------------