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