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