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