]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
* src/ : rename some methods on Entry (SetXxx, InsertXxx) to have a better
[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 15:44:23 $
7   Version:   $Revision: 1.118 $
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    return NULL;
339 }
340
341 /**
342  * \brief    writes on disc a DICOMDIR
343  * \ warning does NOT add the missing elements in the header :
344  *           it's up to the user doing it !
345  * \todo : to be re-written using the DICOMDIR tree-like structure
346  *         *not* the chained list
347  *         (does NOT exist if the DICOMDIR is user-forged !)
348  * @param  fileName file to be written to 
349  * @return false only when fail to open
350  */
351  
352 bool DicomDir::WriteDicomDir(std::string const &fileName) 
353 {  
354    int i;
355    uint16_t sq[4] = { 0x0004, 0x1220, 0xffff, 0xffff };
356    uint16_t sqt[4]= { 0xfffe, 0xe0dd, 0xffff, 0xffff };
357
358    std::ofstream *fp = new std::ofstream(fileName.c_str(),  
359                                          std::ios::out | std::ios::binary);
360    if( !fp ) 
361    {
362       gdcmVerboseMacro("Failed to open(write) File: " << fileName.c_str());
363       return false;
364    }
365
366    char filePreamble[128];
367    memset(filePreamble, 0, 128);
368    fp->write(filePreamble, 128); //FIXME
369    binary_write( *fp, "DICM");
370  
371    DicomDirMeta *ptrMeta = GetMeta();
372    ptrMeta->WriteContent(fp, ExplicitVR);
373    
374    // force writing 0004|1220 [SQ ], that CANNOT exist within DicomDirMeta
375    for(i=0;i<4;++i)
376    {
377       binary_write(*fp, sq[i]);
378    }
379         
380    for(ListDicomDirPatient::iterator cc  = Patients.begin();
381                                      cc != Patients.end();
382                                    ++cc )
383    {
384       (*cc)->WriteContent( fp, ExplicitVR );
385    }
386    
387    // force writing Sequence Delimitation Item
388    for(i=0;i<4;++i)
389    {
390       binary_write(*fp, sqt[i]);  // fffe e0dd ffff ffff 
391    }
392
393    fp->close();
394    delete fp;
395
396    return true;
397 }
398
399 //-----------------------------------------------------------------------------
400 // Protected
401
402 /**
403  * \brief create a Document-like chained list from a root Directory 
404  * @param path entry point of the tree-like structure
405  */
406 void DicomDir::CreateDicomDirChainedList(std::string const & path)
407 {
408    CallStartMethod();
409    DirList dirList(path,1); // gets recursively the file list
410    unsigned int count = 0;
411    VectDocument list;
412    File *header;
413
414    DirListType fileList = dirList.GetFilenames();
415
416    for( DirListType::iterator it  = fileList.begin();
417                               it != fileList.end();
418                               ++it )
419    {
420       Progress = (float)(count+1)/(float)fileList.size();
421       CallProgressMethod();
422       if( Abort )
423       {
424          break;
425       }
426
427       header = new File( it->c_str() );
428       if( !header )
429       {
430          gdcmVerboseMacro( "Failure in new gdcm::File " << it->c_str() );
431          continue;
432       }
433       
434       if( header->IsReadable() )
435       {
436          // Add the file to the chained list:
437          list.push_back(header);
438          gdcmVerboseMacro( "Readable " << it->c_str() );
439        }
440        else
441        {
442           delete header;
443        }
444        count++;
445    }
446    // sorts Patient/Study/Serie/
447    std::sort(list.begin(), list.end(), DicomDir::HeaderLessThan );
448    
449    std::string tmp = dirList.GetDirName();      
450    //for each File of the chained list, add/update the Patient/Study/Serie/Image info
451    SetElements(tmp, list);
452    CallEndMethod();
453
454    for(VectDocument::iterator itDoc=list.begin();
455        itDoc!=list.end();
456        ++itDoc)
457    {
458       delete dynamic_cast<File *>(*itDoc);
459    }
460 }
461
462 /**
463  * \brief   adds *the* Meta to a partially created DICOMDIR
464  */
465   
466 DicomDirMeta *DicomDir::NewMeta()
467 {
468    if( MetaElems )
469       delete MetaElems;
470
471  // friend class hunting : we miss GetLastEntry and GetPreviousEntry
472  //                  to be able to remove any direct reference to TagHT
473    DocEntry *entry = GetFirstEntry();
474    if( entry )
475    //if ( TagHT.begin() != TagHT.end() ) // after Document Parsing
476    { 
477       MetaElems = new DicomDirMeta(true);
478
479       entry = GetFirstEntry();
480       while( entry )
481       {
482          if( dynamic_cast<SeqEntry *>(entry) )
483             break;
484
485          RemoveEntryNoDestroy(entry);
486          MetaElems->AddEntry(entry);
487
488          entry = GetFirstEntry();
489       }
490       /*TagDocEntryHT::iterator lastOneButSequence = TagHT.end();
491       lastOneButSequence --;
492       // ALL the 'out of Sequence' Tags belong to Meta Elems
493       // (we skip 0004|1220 [Directory record sequence] )
494       for ( TagDocEntryHT::iterator cc  = TagHT.begin(); 
495                                     cc != lastOneButSequence;
496                                    ++cc)
497       {
498          MetaElems->AddEntry( cc->second );
499       }*/
500    }
501    else  // after root directory parsing
502    {
503       MetaElems = new DicomDirMeta(false);
504    }
505    MetaElems->SetSQItemNumber(0); // To avoid further missprinting
506    return MetaElems;  
507 }
508
509 /**
510  * \brief   adds a new Patient (with the basic elements) to a partially created
511  *          DICOMDIR
512  */
513 DicomDirPatient *DicomDir::NewPatient()
514 {
515    DicomDirPatient *p = new DicomDirPatient();
516    AddPatientToEnd( p );
517    return p;
518 }
519
520 /**
521  * \brief   Remove all Patients
522  */
523 void DicomDir::ClearPatient()
524 {
525    for(ListDicomDirPatient::iterator cc = Patients.begin();
526                                      cc!= Patients.end();
527                                    ++cc)
528    {
529       delete *cc;
530    }
531    Patients.clear();
532 }
533
534 /**
535  * \brief   adds to the HTable 
536  *          the Entries (Dicom Elements) corresponding to the given type
537  * @param   path full path file name (only used when type = GDCM_DICOMDIR_IMAGE
538  * @param   type DicomDirObject type to create (GDCM_DICOMDIR_PATIENT,
539  *          GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
540  * @param   header Header of the current file
541  */
542 void DicomDir::SetElement(std::string const &path, DicomDirType type,
543                           Document *header)
544 {
545    ListDicomDirElem elemList; //FIXME this is going to be a by copy operation
546    ListDicomDirElem::const_iterator it;
547    uint16_t tmpGr, tmpEl;
548    DictEntry *dictEntry;
549    ValEntry *entry;
550    std::string val;
551    SQItem *si;
552
553    switch( type )
554    {
555       case GDCM_DICOMDIR_IMAGE:
556          elemList = Global::GetDicomDirElements()->GetDicomDirImageElements();
557          si = new DicomDirImage(true);
558          if( !AddImageToEnd(static_cast<DicomDirImage *>(si)) )
559          {
560             delete si;
561             gdcmErrorMacro( "Add ImageToEnd failed");
562          }
563          break;
564       case GDCM_DICOMDIR_SERIE:
565          elemList = Global::GetDicomDirElements()->GetDicomDirSerieElements();
566          si = new DicomDirSerie(true);
567          if( !AddSerieToEnd(static_cast<DicomDirSerie *>(si)) )
568          {
569             delete si;
570             gdcmErrorMacro( "Add SerieToEnd failed");
571          }
572          break;
573       case GDCM_DICOMDIR_STUDY:
574          elemList = Global::GetDicomDirElements()->GetDicomDirStudyElements();
575          si = new DicomDirStudy(true);
576          if( !AddStudyToEnd(static_cast<DicomDirStudy *>(si)) )
577          {
578             delete si;
579             gdcmErrorMacro( "Add StudyToEnd failed");
580          }
581          break;
582       case GDCM_DICOMDIR_PATIENT:
583          elemList = Global::GetDicomDirElements()->GetDicomDirPatientElements();
584          si = new DicomDirPatient(true);
585          if( !AddPatientToEnd(static_cast<DicomDirPatient *>(si)) )
586          {
587             delete si;
588             gdcmErrorMacro( "Add PatientToEnd failed");
589          }
590          break;
591       case GDCM_DICOMDIR_META:
592          elemList = Global::GetDicomDirElements()->GetDicomDirMetaElements();
593          si = new DicomDirMeta(true);
594          if( MetaElems )
595          {
596             delete MetaElems;
597             gdcmErrorMacro( "MetaElements already exist, they will be destroyed");
598          }
599          MetaElems = static_cast<DicomDirMeta *>(si);
600          break;
601       default:
602          return;
603    }
604    // removed all the seems-to-be-useless stuff about Referenced Image Sequence
605    // to avoid further troubles
606    // imageElem 0008 1140 "" // Referenced Image Sequence
607    // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
608    // imageElem 0008 1150 "" // Referenced SOP Class UID    : to be set/forged later
609    // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
610    // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
611    // for all the relevant elements found in their own spot of the DicomDir.dic
612    // FIXME : troubles found when it's a SeqEntry
613
614    for( it = elemList.begin(); it != elemList.end(); ++it)
615    {
616       tmpGr     = it->Group;
617       tmpEl     = it->Elem;
618       dictEntry = GetPubDict()->GetEntry(tmpGr, tmpEl);
619
620       entry     = new ValEntry( dictEntry ); // Be sure it's never a BinEntry !
621
622       entry->SetOffset(0); // just to avoid further missprinting
623
624       if( header )
625       {
626          // NULL when we Build Up (ex nihilo) a DICOMDIR
627          //   or when we add the META elems
628          val = header->GetEntryValue(tmpGr, tmpEl);
629       }
630       else
631       {
632          val = GDCM_UNFOUND;
633       }
634
635       if( val == GDCM_UNFOUND) 
636       {
637          if( tmpGr == 0x0004 && tmpEl == 0x1130 ) // File-set ID
638          {
639            // force to the *end* File Name
640            val = Util::GetName( path );
641          }
642          else if( tmpGr == 0x0004 && tmpEl == 0x1500 ) // Only used for image
643          {
644             if( header->GetFileName().substr(0, path.length()) != path )
645             {
646                gdcmVerboseMacro( "The base path of file name is incorrect");
647                val = header->GetFileName();
648             }
649             else
650             {
651                val = &(header->GetFileName().c_str()[path.length()]);
652             }
653          }
654          else
655          {
656             val = it->Value;
657          }
658       }
659       else
660       {
661          if ( header->GetEntryLength(tmpGr,tmpEl) == 0 )
662             val = it->Value;
663       }
664
665       entry->SetValue( val ); // troubles expected when vr=SQ ...
666
667       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
668       {
669          gdcmVerboseMacro("GDCM_DICOMDIR_META ?!? should never print that");
670       }
671       si->AddEntry(entry);
672    }
673 }
674
675 //-----------------------------------------------------------------------------
676 /**
677  * \brief   CallStartMethod
678  */
679 void DicomDir::CallStartMethod()
680 {
681    Progress = 0.0f;
682    Abort    = false;
683    if( StartMethod )
684    {
685       StartMethod( StartArg );
686    }
687 }
688
689 //-----------------------------------------------------------------------------
690 /**
691  * \brief   CallProgressMethod
692  */
693 void DicomDir::CallProgressMethod()
694 {
695    if( ProgressMethod )
696    {
697       ProgressMethod( ProgressArg );
698    }
699 }
700
701 //-----------------------------------------------------------------------------
702 /**
703  * \brief   CallEndMethod
704  */
705 void DicomDir::CallEndMethod()
706 {
707    Progress = 1.0f;
708    if( EndMethod )
709    {
710       EndMethod( EndArg );
711    }
712 }
713
714 //-----------------------------------------------------------------------------
715 // Private
716 /**
717  * \brief Sets all fields to NULL
718  */
719 void DicomDir::Initialize()
720 {
721    StartMethod             = NULL;
722    ProgressMethod          = NULL;
723    EndMethod               = NULL;
724    StartMethodArgDelete    = NULL;
725    ProgressMethodArgDelete = NULL;
726    EndMethodArgDelete      = NULL;
727    StartArg                = NULL;
728    ProgressArg             = NULL;
729    EndArg                  = NULL;
730
731    Progress = 0.0;
732    Abort = false;
733
734    MetaElems = NULL;   
735 }
736
737 /**
738  * \brief create a 'DicomDir' from a DICOMDIR Header 
739  */
740 void DicomDir::CreateDicomDir()
741 {
742    // The list is parsed. 
743    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
744    //  1 - we save the beginning iterator
745    //  2 - we continue to parse
746    //  3 - we find an other tag
747    //       + we create the object for the precedent tag
748    //       + loop to 1 -
749
750    // Directory record sequence
751    DocEntry *e = GetDocEntry(0x0004, 0x1220);
752    if ( !e )
753    {
754       gdcmVerboseMacro( "NO Directory record sequence (0x0004,0x1220)");
755       /// \todo FIXME: what to do when the parsed file IS NOT a DICOMDIR file ? 
756       return;         
757    }
758    
759    SeqEntry *s = dynamic_cast<SeqEntry *>(e);
760    if ( !s )
761    {
762       gdcmVerboseMacro( "No SeqEntry present");
763       // useless : (0x0004,0x1220) IS a Sequence !
764       return;
765    }
766
767    NewMeta();
768    
769    DocEntry *d;
770    std::string v;
771    SQItem *si;
772
773    SQItem *tmpSI=s->GetFirstSQItem();
774    while(tmpSI)
775    {
776       d = tmpSI->GetDocEntry(0x0004, 0x1430); // Directory Record Type
777       if ( ValEntry* valEntry = dynamic_cast<ValEntry *>(d) )
778       {
779          v = valEntry->GetValue();
780       }
781       else
782       {
783          gdcmVerboseMacro( "Not a ValEntry.");
784          continue;
785       }
786
787       if( v == "PATIENT " )
788       {
789          si = new DicomDirPatient(true);
790          if( !AddPatientToEnd( static_cast<DicomDirPatient *>(si)) )
791          {
792             delete si;
793             si = NULL;
794             gdcmErrorMacro( "Add PatientToEnd failed");
795          }
796       }
797       else if( v == "STUDY " )
798       {
799          si = new DicomDirStudy(true);
800          if( !AddStudyToEnd( static_cast<DicomDirStudy *>(si)) )
801          {
802             delete si;
803             si = NULL;
804             gdcmErrorMacro( "Add AddStudyToEnd failed");
805          }
806       }
807       else if( v == "SERIES" )
808       {
809          si = new DicomDirSerie(true);
810          if( !AddSerieToEnd( static_cast<DicomDirSerie *>(si)) )
811          {
812             delete si;
813             si = NULL;
814             gdcmErrorMacro( "Add AddSerieToEnd failed");
815          }
816       }
817       else if( v == "IMAGE " ) 
818       {
819          si = new DicomDirImage(true);
820          if( !AddImageToEnd( static_cast<DicomDirImage *>(si)) )
821          {
822             delete si;
823             si = NULL;
824             gdcmErrorMacro( "Add AddImageToEnd failed");
825          }
826       }
827       else
828       {
829          // It was not a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
830          // neither an 'IMAGE' SQItem. Skip to next item.
831          continue;
832       }
833
834       //if( si )
835          //MoveSQItem(si,tmpSI);
836       tmpSI=s->GetNextSQItem();
837    }
838 // friend hunting : this one will be difficult to remove !
839    ClearEntry();
840 }
841
842 /**
843  * \brief  AddPatientToEnd 
844  * @param   dd SQ Item to enqueue to the DicomPatient chained List
845  */
846 bool DicomDir::AddPatientToEnd(DicomDirPatient *dd)
847 {
848    Patients.push_back(dd);
849    return true;
850 }
851
852 /**
853  * \brief  AddStudyToEnd 
854  * @param   dd SQ Item to enqueue to the DicomDirStudy chained List
855  */
856 bool DicomDir::AddStudyToEnd(DicomDirStudy *dd)
857 {
858    if( Patients.size() > 0 )
859    {
860       ListDicomDirPatient::iterator itp = Patients.end();
861       itp--;
862       (*itp)->AddStudy(dd);
863       return true;
864    }
865    return false;
866 }
867
868 /**
869  * \brief  AddSerieToEnd 
870  * @param   dd SQ Item to enqueue to the DicomDirSerie chained List
871  */
872 bool DicomDir::AddSerieToEnd(DicomDirSerie *dd)
873 {
874    if( Patients.size() > 0 )
875    {
876       ListDicomDirPatient::iterator itp = Patients.end();
877       itp--;
878
879       DicomDirStudy *study = (*itp)->GetLastStudy();
880       if( study )
881       {
882          study->AddSerie(dd);
883          return true;
884       }
885    }
886    return false;
887 }
888
889 /**
890  * \brief   AddImageToEnd
891  * @param   dd SQ Item to enqueue to the DicomDirImage chained List
892  */
893 bool DicomDir::AddImageToEnd(DicomDirImage *dd)
894 {
895    if( Patients.size() > 0 )
896    {
897       ListDicomDirPatient::iterator itp = Patients.end();
898       itp--;
899
900       DicomDirStudy *study = (*itp)->GetLastStudy();
901       if( study )
902       {
903          DicomDirSerie *serie = study->GetLastSerie();
904          if( serie )
905          {
906             serie->AddImage(dd);
907             return true;
908          }
909       }
910    }
911    return false;
912 }
913
914 /**
915  * \brief  for each Header of the chained list, add/update the Patient/Study/Serie/Image info 
916  * @param   path path of the root directory
917  * @param   list chained list of Headers
918  */
919 void DicomDir::SetElements(std::string const & path, VectDocument const &list)
920 {
921    ClearEntry();
922    ClearPatient();
923
924    std::string patPrevName         = "", patPrevID  = "";
925    std::string studPrevInstanceUID = "", studPrevID = "";
926    std::string serPrevInstanceUID  = "", serPrevID  = "";
927
928    std::string patCurName,         patCurID;
929    std::string studCurInstanceUID, studCurID;
930    std::string serCurInstanceUID,  serCurID;
931
932    bool first = true;
933    for( VectDocument::const_iterator it = list.begin();
934                                      it != list.end(); ++it )
935    {
936       // get the current file characteristics
937       patCurName         = (*it)->GetEntryValue(0x0010,0x0010);
938       patCurID           = (*it)->GetEntryValue(0x0010,0x0011);
939       studCurInstanceUID = (*it)->GetEntryValue(0x0020,0x000d);
940       studCurID          = (*it)->GetEntryValue(0x0020,0x0010);
941       serCurInstanceUID  = (*it)->GetEntryValue(0x0020,0x000e);
942       serCurID           = (*it)->GetEntryValue(0x0020,0x0011);
943
944       if( patCurName != patPrevName || patCurID != patPrevID || first )
945       {
946          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
947          first = true;
948       }
949
950       // if new Study Deal with 'STUDY' Elements   
951       if( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID 
952          || first )
953       {
954          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
955          first = true;
956       }
957
958       // if new Serie Deal with 'SERIE' Elements   
959       if( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID
960          || first )
961       {
962          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
963          first = true;
964       }
965       
966       // Always Deal with 'IMAGE' Elements  
967       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
968
969       patPrevName         = patCurName;
970       patPrevID           = patCurID;
971       studPrevInstanceUID = studCurInstanceUID;
972       studPrevID          = studCurID;
973       serPrevInstanceUID  = serCurInstanceUID;
974       serPrevID           = serCurID;
975       first = false;
976    }
977 }
978
979 /**
980  * \brief   Move the content of the source SQItem to the destination SQItem
981  *          Only DocEntry's are moved
982  * @param dst destination SQItem
983  * @param src source SQItem
984  */
985 void DicomDir::MoveSQItem(DocEntrySet *dst,DocEntrySet *src)
986 {
987    DocEntry *entry;
988
989    entry = src->GetFirstEntry();
990    while(entry)
991    {
992       src->RemoveEntryNoDestroy(entry);
993       dst->AddEntry(entry);
994       // we destroyed -> the current iterator is not longer valid
995       entry = src->GetFirstEntry();
996    }
997 }
998
999 /**
1000  * \brief   compares two files
1001  */
1002 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
1003 {
1004    return *header1 < *header2;
1005 }
1006
1007 } // end namespace gdcm
1008
1009 //-----------------------------------------------------------------------------