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