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