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