]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
7b5857ae17327508d148486f0d11458cb9dbbb00
[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 10:25:52 $
7   Version:   $Revision: 1.80 $
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       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
233  *          directory starts.
234  * @param   method Method to call
235  * @param   arg    Argument to pass to the method
236  * @param   argDelete    Argument 
237  * \warning In python : the arg parameter isn't considered
238  */
239 void DicomDir::SetStartMethod( DicomDir::Method* method, void* arg, 
240                                DicomDir::Method* argDelete )
241 {
242    if( StartArg && StartMethodArgDelete )
243    {
244       StartMethodArgDelete( StartArg );
245    }
246
247    StartMethod          = method;
248    StartArg             = arg;
249    StartMethodArgDelete = argDelete;
250 }
251
252 /**
253  * \ingroup DicomDir
254  * \brief   Set the method to delete the argument
255  *          The argument is destroyed when the method is changed or when the
256  *          class is destroyed
257  * @param   method Method to call to delete the argument
258  */
259 void DicomDir::SetStartMethodArgDelete( DicomDir::Method* method ) 
260 {
261    StartMethodArgDelete = method;
262 }
263
264 /**
265  * \ingroup DicomDir
266  * \brief   Set the progress method to call when the parsing of the
267  *          directory progress
268  * @param   method Method to call
269  * @param   arg    Argument to pass to the method
270  * @param   argDelete    Argument  
271  * \warning In python : the arg parameter isn't considered
272  */
273 void DicomDir::SetProgressMethod( DicomDir::Method* method, void* arg, 
274                                   DicomDir::Method* argDelete )
275 {
276    if( ProgressArg && ProgressMethodArgDelete )
277    {
278       ProgressMethodArgDelete( ProgressArg );
279    }
280
281    ProgressMethod          = method;
282    ProgressArg             = arg;
283    ProgressMethodArgDelete = argDelete;
284 }
285
286 /**
287  * \ingroup DicomDir
288  * \brief   Set the method to delete the argument
289  *          The argument is destroyed when the method is changed or when the 
290  *          class is destroyed          
291  * @param   method Method to call to delete the argument
292  */
293 void DicomDir::SetProgressMethodArgDelete( DicomDir::Method* method )
294 {
295    ProgressMethodArgDelete = method;
296 }
297
298 /**
299  * \ingroup DicomDir
300  * \brief   Set the end method to call when the parsing of the directory ends
301  * @param   method Method to call
302  * @param   arg    Argument to pass to the method
303  * @param   argDelete    Argument 
304  * \warning In python : the arg parameter isn't considered
305  */
306 void DicomDir::SetEndMethod( DicomDir::Method* method, void* arg, 
307                              DicomDir::Method* argDelete )
308 {
309    if( EndArg && EndMethodArgDelete )
310    {
311       EndMethodArgDelete( EndArg );
312    }
313
314    EndMethod          = method;
315    EndArg             = arg;
316    EndMethodArgDelete = argDelete;
317 }
318
319 /**
320  * \ingroup DicomDir
321  * \brief   Set the method to delete the argument
322  *          The argument is destroyed when the method is changed or when
323  *          the class is destroyed
324  * @param   method Method to call to delete the argument
325  */
326 void DicomDir::SetEndMethodArgDelete( DicomDir::Method* method )
327 {
328    EndMethodArgDelete = method;
329 }
330
331 /**
332  * \ingroup DicomDir
333  * \brief    writes on disc a DICOMDIR
334  * \ warning does NOT add the missing elements in the header :
335  *           it's up to the user doing it !
336  * \todo : to be re-written using the DICOMDIR tree-like structure
337  *         *not* the chained list
338  *         (does NOT exist if the DICOMDIR is user-forged !)
339  * @param  fileName file to be written to 
340  * @return false only when fail to open
341  */
342  
343 bool DicomDir::WriteDicomDir(std::string const& fileName) 
344 {  
345    int i;
346    uint16_t sq[4] = { 0x0004, 0x1220, 0xffff, 0xffff };
347    uint16_t sqt[4]= { 0xfffe, 0xe0dd, 0xffff, 0xffff };
348
349    std::ofstream* fp = new std::ofstream(fileName.c_str(),  
350                                          std::ios::out | std::ios::binary);
351    if( !fp ) 
352    {
353       dbg.Verbose(2, "Failed to open(write) File: ", fileName.c_str());
354       return false;
355    }
356
357    char filePreamble[128];
358    memset(filePreamble, 0, 128);
359    fp->write(filePreamble, 128); //FIXME
360    binary_write( *fp, "DICM");
361  
362    DicomDirMeta *ptrMeta = GetDicomDirMeta();
363    ptrMeta->Write(fp, ExplicitVR);
364    
365    // force writing 0004|1220 [SQ ], that CANNOT exist within DicomDirMeta
366    for(i=0;i<4;++i)
367    {
368       binary_write(*fp, sq[i]);
369    }
370         
371    for(ListDicomDirPatient::iterator cc  = Patients.begin();
372                                      cc != Patients.end();
373                                    ++cc )
374    {
375       (*cc)->Write( fp, ExplicitVR );
376    }
377    
378    // force writing Sequence Delimitation Item
379    for(i=0;i<4;++i)
380    {
381       binary_write(*fp, sqt[i]);  // fffe e0dd ffff ffff 
382    }
383
384    fp->close();
385    return true;
386 }
387
388 //-----------------------------------------------------------------------------
389 // Protected
390
391 /**
392  * \ingroup DicomDir
393  * \brief create a Document-like chained list from a root Directory 
394  * @param path entry point of the tree-like structure
395  */
396 void DicomDir::CreateDicomDirChainedList(std::string const & path)
397 {
398    CallStartMethod();
399    DirList fileList(path,1); // gets recursively the file list
400    unsigned int count = 0;
401    VectDocument list;
402    Header *header;
403
404    TagHT.clear();
405    Patients.clear();
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    switch( type )
557    {
558       case GDCM_DICOMDIR_IMAGE:
559          elemList = Global::GetDicomDirElements()->GetDicomDirImageElements();
560          break;
561
562       case GDCM_DICOMDIR_SERIE:
563          elemList = Global::GetDicomDirElements()->GetDicomDirSerieElements();
564          break;
565
566       case GDCM_DICOMDIR_STUDY:
567          elemList = Global::GetDicomDirElements()->GetDicomDirStudyElements();
568          break;
569
570       case GDCM_DICOMDIR_PATIENT:
571          elemList = Global::GetDicomDirElements()->GetDicomDirPatientElements();
572          break;
573   
574       case GDCM_DICOMDIR_META:
575          elemList = Global::GetDicomDirElements()->GetDicomDirMetaElements();
576          break;
577
578       default:
579          return;
580    }
581    // removed all the seems-to-be-useless stuff about Referenced Image Sequence
582    // to avoid further troubles
583    // imageElem 0008 1140 "" // Referenced Image Sequence
584    // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
585    // imageElem 0008 1150 "" // Referenced SOP Class UID    : to be set/forged later
586    // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
587    // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
588    // for all the relevant elements found in their own spot of the DicomDir.dic
589    // FIXME : troubles found when it's a SeqEntry
590
591    for( it = elemList.begin(); it != elemList.end(); ++it)
592    {
593       tmpGr     = it->Group;
594       tmpEl     = it->Elem;
595       dictEntry = GetPubDict()->GetDictEntryByNumber(tmpGr, tmpEl);
596
597       entry     = new ValEntry( dictEntry ); // Be sure it's never a BinEntry !
598
599       entry->SetOffset(0); // just to avoid further missprinting
600       entry->SetLength(0); // just to avoid further missprinting
601
602       if( header )
603       {
604          // NULL when we Build Up (ex nihilo) a DICOMDIR
605          //   or when we add the META elems
606          val = header->GetEntryByNumber(tmpGr, tmpEl);
607       }
608       else
609       {
610          val = GDCM_UNFOUND;
611       }
612
613       if( val == GDCM_UNFOUND) 
614       {
615          if( tmpGr == 0x0004 && tmpEl == 0x1130 ) // File-set ID
616          {
617            // force to the *end* File Name
618            val = Util::GetName( path );
619          }
620          else if( tmpGr == 0x0004 && tmpEl == 0x1500 ) // Only used for image
621          {
622             if( header->GetFileName().substr(0, path.length()) != path )
623             {
624                dbg.Verbose(0, "DicomDir::SetElement : the base path"
625                               " of file name is incorrect");
626                val = header->GetFileName();
627             }
628             else
629             {
630                val = &(header->GetFileName().c_str()[path.length()]);
631             }
632          }
633          else
634          {
635             val = it->Value;
636          }
637       }
638       else
639       {
640          if ( header->GetEntryLengthByNumber(tmpGr,tmpEl) == 0 )
641             val = it->Value;
642       }
643
644      // GDCM_UNFOUND or not !
645
646       entry->SetValue( val ); // troubles expected when vr=SQ ...
647
648       if( dictEntry )
649       {
650          if( dictEntry->GetGroup() == 0xfffe )
651          {
652             entry->SetLength( entry->GetValue().length() ); // FIXME 
653          }
654          else if( dictEntry->GetVR() == "UL" || dictEntry->GetVR() == "SL" )
655          {
656             entry->SetLength(4);
657          }
658          else if( dictEntry->GetVR() == "US" || dictEntry->GetVR() == "SS" )
659          {
660             entry->SetLength(2); 
661          }
662          else if( dictEntry->GetVR() == "SQ" )
663          {
664             entry->SetLength( 0xffffffff );
665          }
666          else
667          {
668             entry->SetLength( entry->GetValue().length() );
669          }
670       }
671
672       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
673       {
674          std::cout << "GDCM_DICOMDIR_META ?!? should never print that" 
675                    << std::endl;
676       }
677       si->AddEntry(entry);
678    }
679    switch( type )
680    {
681       case GDCM_DICOMDIR_IMAGE:
682          AddDicomDirImageToEnd(si);
683          break;
684
685       case GDCM_DICOMDIR_SERIE:
686          AddDicomDirSerieToEnd(si);
687          break;
688
689       case GDCM_DICOMDIR_STUDY:
690          AddDicomDirStudyToEnd(si);
691          break;
692
693       case GDCM_DICOMDIR_PATIENT:
694          AddDicomDirPatientToEnd(si);
695          break;
696
697       default:
698          return;
699    }
700    //int count=1;            // find a trick to increment
701    //s->AddEntry(si, count); // Seg Faults 
702
703 }
704
705 //-----------------------------------------------------------------------------
706 /**
707  * \brief   CallStartMethod
708  */
709 void DicomDir::CallStartMethod()
710 {
711    Progress = 0.0f;
712    Abort    = false;
713    if( StartMethod )
714    {
715       StartMethod( StartArg );
716    }
717 }
718
719 //-----------------------------------------------------------------------------
720 /**
721  * \ingroup DicomDir
722  * \brief   CallProgressMethod
723  */
724 void DicomDir::CallProgressMethod()
725 {
726    if( ProgressMethod )
727    {
728       ProgressMethod( ProgressArg );
729    }
730 }
731
732 //-----------------------------------------------------------------------------
733 /**
734  * \ingroup DicomDir
735  * \brief   CallEndMethod
736  */
737 void DicomDir::CallEndMethod()
738 {
739    Progress = 1.0f;
740    if( EndMethod )
741    {
742       EndMethod( EndArg );
743    }
744 }
745
746 //-----------------------------------------------------------------------------
747 // Private
748 /**
749  * \ingroup DicomDir
750  * \brief create a 'DicomDir' from a DICOMDIR Header 
751  */
752 void DicomDir::CreateDicomDir()
753 {
754    // The list is parsed. 
755    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
756    //  1 - we save the beginning iterator
757    //  2 - we continue to parse
758    //  3 - we find an other tag
759    //       + we create the object for the precedent tag
760    //       + loop to 1 -
761
762    // Directory record sequence
763    DocEntry *e = GetDocEntryByNumber(0x0004, 0x1220);
764    if ( !e )
765    {
766       dbg.Verbose(0, "DicomDir::DicomDir : NO Directory record"
767                      " sequence (0x0004,0x1220)");
768       /// \todo FIXME: what to do when the parsed file IS NOT a DICOMDIR file ? 
769       return;         
770    }
771    
772    SeqEntry* s = dynamic_cast<SeqEntry*>(e);
773    if ( !s )
774    {
775       dbg.Verbose(0, "DicomDir::CreateDicomDir: no SeqEntry present");
776       // useless : (0x0004,0x1220) IS a Sequence !
777       return;
778    }
779
780    DicomDirType type; // = DicomDir::GDCM_DICOMDIR_META;
781    MetaElems = NewMeta();
782
783    ListSQItem listItems = s->GetSQItems();
784    
785    DocEntry * d;
786    std::string v;
787    for( ListSQItem::iterator i = listItems.begin(); 
788                              i !=listItems.end(); ++i ) 
789    {
790       d = (*i)->GetDocEntryByNumber(0x0004, 0x1430); // Directory Record Type
791       if ( ValEntry* valEntry = dynamic_cast< ValEntry* >(d) )
792       {
793          v = valEntry->GetValue();
794       }
795       else
796       {
797          dbg.Verbose(0, "DicomDir::CreateDicomDir: not a ValEntry.");
798          continue;
799       }
800
801       if( v == "PATIENT " )
802       {
803          AddDicomDirPatientToEnd( *i );
804          type = DicomDir::GDCM_DICOMDIR_PATIENT;
805       }
806       else if( v == "STUDY " )
807       {
808          AddDicomDirStudyToEnd( *i );
809          type = DicomDir::GDCM_DICOMDIR_STUDY;
810       }
811       else if( v == "SERIES" )
812       {
813          AddDicomDirSerieToEnd( *i );
814          type = DicomDir::GDCM_DICOMDIR_SERIE;
815       }
816       else if( v == "IMAGE " ) 
817       {
818          AddDicomDirImageToEnd( *i );
819          type = DicomDir::GDCM_DICOMDIR_IMAGE;
820       }
821       else
822       {
823          // It was not a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
824          // neither an 'IMAGE' SQItem. Skip to next item.
825          continue;
826       }
827    }
828 }
829
830 /**
831  * \ingroup DicomDir
832  * \brief Well ... there is only one occurence  
833  */
834 void DicomDir::AddDicomDirMeta()
835 {
836    if( MetaElems )
837    {
838       delete MetaElems;
839    }
840    MetaElems = new DicomDirMeta( &TagHT );
841 }
842
843 /**
844  * \ingroup DicomDir
845  * \brief  AddDicomDirPatientToEnd 
846  * @param   s SQ Item to enqueue to the DicomPatient chained List
847  */
848 void DicomDir::AddDicomDirPatientToEnd(SQItem *s)
849 {
850    Patients.push_back(new DicomDirPatient(s, &TagHT));
851 }
852
853 /**
854  * \ingroup DicomDir
855  * \brief  AddDicomDirStudyToEnd 
856  * @param   s SQ Item to enqueue to the DicomDirStudy chained List
857  */
858  void DicomDir::AddDicomDirStudyToEnd(SQItem *s)
859 {
860    if( Patients.size() > 0 )
861    {
862       ListDicomDirPatient::iterator itp = Patients.end();
863       itp--;
864       (*itp)->AddDicomDirStudy(new DicomDirStudy(s, &TagHT));
865    }
866 }
867
868 /**
869  * \ingroup DicomDir
870  * \brief  AddDicomDirSerieToEnd 
871  * @param   s SQ Item to enqueue to the DicomDirSerie chained List
872  */
873 void DicomDir::AddDicomDirSerieToEnd(SQItem *s)
874 {
875    if( Patients.size() > 0 )
876    {
877       ListDicomDirPatient::iterator itp = Patients.end();
878       itp--;
879
880       if( (*itp)->GetDicomDirStudies().size() > 0 )
881       {
882          ListDicomDirStudy::const_iterator itst = 
883             (*itp)->GetDicomDirStudies().end();
884          itst--;
885          (*itst)->AddDicomDirSerie(new DicomDirSerie(s, &TagHT));
886       }
887    }
888 }
889
890 /**
891  * \ingroup DicomDir
892  * \brief   AddDicomDirImageToEnd
893  * @param   s SQ Item to enqueue to the DicomDirImage chained List
894  */
895  void DicomDir::AddDicomDirImageToEnd(SQItem *s)
896 {
897    if( Patients.size() > 0 )
898    {
899       ListDicomDirPatient::iterator itp = Patients.end();
900       itp--;
901
902       if( (*itp)->GetDicomDirStudies().size() > 0 )
903       {
904          ListDicomDirStudy::const_iterator itst = 
905             (*itp)->GetDicomDirStudies().end();
906          itst--;
907
908          if( (*itst)->GetDicomDirSeries().size() > 0 )
909          {
910             ListDicomDirSerie::const_iterator its = (*itst)->GetDicomDirSeries().end();
911             its--;
912             (*its)->AddDicomDirImage(new DicomDirImage(s, &TagHT));
913          }
914       }
915    }
916 }
917
918 /**
919  * \ingroup DicomDir
920  * \brief  for each Header of the chained list, add/update the Patient/Study/Serie/Image info 
921  * @param   path path of the root directory
922  * @param   list chained list of Headers
923  */
924 void DicomDir::SetElements(std::string const & path, VectDocument const &list)
925 {
926    std::string patPrevName         = "", patPrevID  = "";
927    std::string studPrevInstanceUID = "", studPrevID = "";
928    std::string serPrevInstanceUID  = "", serPrevID  = "";
929
930    std::string patCurName,         patCurID;
931    std::string studCurInstanceUID, studCurID;
932    std::string serCurInstanceUID,  serCurID;
933
934    for( VectDocument::const_iterator it = list.begin();
935                                      it != list.end(); ++it )
936    {
937       // get the current file characteristics
938       patCurName         = (*it)->GetEntryByNumber(0x0010,0x0010); 
939       patCurID           = (*it)->GetEntryByNumber(0x0010,0x0011); 
940       studCurInstanceUID = (*it)->GetEntryByNumber(0x0020,0x000d);            
941       studCurID          = (*it)->GetEntryByNumber(0x0020,0x0010);            
942       serCurInstanceUID  = (*it)->GetEntryByNumber(0x0020,0x000e);            
943       serCurID           = (*it)->GetEntryByNumber(0x0020,0x0011);
944
945       if( patCurName != patPrevName || patCurID != patPrevID)
946       {
947          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
948       }
949
950       // if new Study Deal with 'STUDY' Elements   
951       if( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID )
952       {
953          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
954       }
955
956       // if new Serie Deal with 'SERIE' Elements   
957       if( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID )
958       {
959          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
960       }
961       
962       // Always Deal with 'IMAGE' Elements  
963       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
964
965       patPrevName         = patCurName;
966       patPrevID           = patCurID;
967       studPrevInstanceUID = studCurInstanceUID;
968       studPrevID          = studCurID;
969       serPrevInstanceUID  = serCurInstanceUID;
970       serPrevID           = serCurID;
971    }
972 }
973
974 /**
975  * \ingroup DicomDir
976  * \brief   compares two dgcmHeaders
977  */
978 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
979 {
980    return *header1 < *header2;
981 }
982 } // end namespace gdcm
983
984 //-----------------------------------------------------------------------------