]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
*** empty log message ***
[gdcm.git] / src / gdcmDicomDir.cxx
1 /*=========================================================================
2   
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDicomDir.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/01/28 15:58:40 $
7   Version:   $Revision: 1.121 $
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 DicomDirMeta *DicomDir::NewMeta()
466 {
467    if( MetaElems )
468       delete MetaElems;
469
470    DocEntry *entry = GetFirstEntry();
471    if( entry )
472    { 
473       MetaElems = new DicomDirMeta(true);
474
475       entry = GetFirstEntry();
476       while( entry )
477       {
478          if( dynamic_cast<SeqEntry *>(entry) )
479             break;
480
481          RemoveEntryNoDestroy(entry);
482          MetaElems->AddEntry(entry);
483
484          entry = GetFirstEntry();
485       }
486    }
487    else  // after root directory parsing
488    {
489       MetaElems = new DicomDirMeta(false);
490    }
491    MetaElems->SetSQItemNumber(0); // To avoid further missprinting
492    return MetaElems;  
493 }
494
495 /**
496  * \brief   adds a new Patient (with the basic elements) to a partially created
497  *          DICOMDIR
498  */
499 DicomDirPatient *DicomDir::NewPatient()
500 {
501    DicomDirPatient *p = new DicomDirPatient();
502    AddPatientToEnd( p );
503    return p;
504 }
505
506 /**
507  * \brief   Remove all Patients
508  */
509 void DicomDir::ClearPatient()
510 {
511    for(ListDicomDirPatient::iterator cc = Patients.begin();
512                                      cc!= Patients.end();
513                                    ++cc)
514    {
515       delete *cc;
516    }
517    Patients.clear();
518 }
519
520 /**
521  * \brief   adds to the HTable 
522  *          the Entries (Dicom Elements) corresponding to the given type
523  * @param   path full path file name (only used when type = GDCM_DICOMDIR_IMAGE
524  * @param   type DicomDirObject type to create (GDCM_DICOMDIR_PATIENT,
525  *          GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
526  * @param   header Header of the current file
527  */
528 void DicomDir::SetElement(std::string const &path, DicomDirType type,
529                           Document *header)
530 {
531    ListDicomDirElem elemList; //FIXME this is going to be a by copy operation
532    ListDicomDirElem::const_iterator it;
533    uint16_t tmpGr, tmpEl;
534    DictEntry *dictEntry;
535    ValEntry *entry;
536    std::string val;
537    SQItem *si;
538
539    switch( type )
540    {
541       case GDCM_DICOMDIR_IMAGE:
542          elemList = Global::GetDicomDirElements()->GetDicomDirImageElements();
543          si = new DicomDirImage(true);
544          if( !AddImageToEnd(static_cast<DicomDirImage *>(si)) )
545          {
546             delete si;
547             gdcmErrorMacro( "Add ImageToEnd failed");
548          }
549          break;
550       case GDCM_DICOMDIR_SERIE:
551          elemList = Global::GetDicomDirElements()->GetDicomDirSerieElements();
552          si = new DicomDirSerie(true);
553          if( !AddSerieToEnd(static_cast<DicomDirSerie *>(si)) )
554          {
555             delete si;
556             gdcmErrorMacro( "Add SerieToEnd failed");
557          }
558          break;
559       case GDCM_DICOMDIR_STUDY:
560          elemList = Global::GetDicomDirElements()->GetDicomDirStudyElements();
561          si = new DicomDirStudy(true);
562          if( !AddStudyToEnd(static_cast<DicomDirStudy *>(si)) )
563          {
564             delete si;
565             gdcmErrorMacro( "Add StudyToEnd failed");
566          }
567          break;
568       case GDCM_DICOMDIR_PATIENT:
569          elemList = Global::GetDicomDirElements()->GetDicomDirPatientElements();
570          si = new DicomDirPatient(true);
571          if( !AddPatientToEnd(static_cast<DicomDirPatient *>(si)) )
572          {
573             delete si;
574             gdcmErrorMacro( "Add PatientToEnd failed");
575          }
576          break;
577       case GDCM_DICOMDIR_META:
578          elemList = Global::GetDicomDirElements()->GetDicomDirMetaElements();
579          si = new DicomDirMeta(true);
580          if( MetaElems )
581          {
582             delete MetaElems;
583             gdcmErrorMacro( "MetaElements already exist, they will be destroyed");
584          }
585          MetaElems = static_cast<DicomDirMeta *>(si);
586          break;
587       default:
588          return;
589    }
590    // removed all the seems-to-be-useless stuff about Referenced Image Sequence
591    // to avoid further troubles
592    // imageElem 0008 1140 "" // Referenced Image Sequence
593    // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
594    // imageElem 0008 1150 "" // Referenced SOP Class UID    : to be set/forged later
595    // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
596    // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
597    // for all the relevant elements found in their own spot of the DicomDir.dic
598    // FIXME : troubles found when it's a SeqEntry
599
600    for( it = elemList.begin(); it != elemList.end(); ++it)
601    {
602       tmpGr     = it->Group;
603       tmpEl     = it->Elem;
604       dictEntry = GetPubDict()->GetEntry(tmpGr, tmpEl);
605
606       entry     = new ValEntry( dictEntry ); // Be sure it's never a BinEntry !
607
608       entry->SetOffset(0); // just to avoid further missprinting
609
610       if( header )
611       {
612          // NULL when we Build Up (ex nihilo) a DICOMDIR
613          //   or when we add the META elems
614          val = header->GetEntryValue(tmpGr, tmpEl);
615       }
616       else
617       {
618          val = GDCM_UNFOUND;
619       }
620
621       if( val == GDCM_UNFOUND) 
622       {
623          if( tmpGr == 0x0004 && tmpEl == 0x1130 ) // File-set ID
624          {
625            // force to the *end* File Name
626            val = Util::GetName( path );
627          }
628          else if( tmpGr == 0x0004 && tmpEl == 0x1500 ) // Only used for image
629          {
630             if( header->GetFileName().substr(0, path.length()) != path )
631             {
632                gdcmVerboseMacro( "The base path of file name is incorrect");
633                val = header->GetFileName();
634             }
635             else
636             {
637                val = &(header->GetFileName().c_str()[path.length()]);
638             }
639          }
640          else
641          {
642             val = it->Value;
643          }
644       }
645       else
646       {
647          if ( header->GetEntryLength(tmpGr,tmpEl) == 0 )
648             val = it->Value;
649       }
650
651       entry->SetValue( val ); // troubles expected when vr=SQ ...
652
653       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
654       {
655          gdcmVerboseMacro("GDCM_DICOMDIR_META ?!? should never print that");
656       }
657       si->AddEntry(entry);
658    }
659 }
660
661 //-----------------------------------------------------------------------------
662 /**
663  * \brief   CallStartMethod
664  */
665 void DicomDir::CallStartMethod()
666 {
667    Progress = 0.0f;
668    Abort    = false;
669    if( StartMethod )
670    {
671       StartMethod( StartArg );
672    }
673 }
674
675 //-----------------------------------------------------------------------------
676 /**
677  * \brief   CallProgressMethod
678  */
679 void DicomDir::CallProgressMethod()
680 {
681    if( ProgressMethod )
682    {
683       ProgressMethod( ProgressArg );
684    }
685 }
686
687 //-----------------------------------------------------------------------------
688 /**
689  * \brief   CallEndMethod
690  */
691 void DicomDir::CallEndMethod()
692 {
693    Progress = 1.0f;
694    if( EndMethod )
695    {
696       EndMethod( EndArg );
697    }
698 }
699
700 //-----------------------------------------------------------------------------
701 // Private
702 /**
703  * \brief Sets all fields to NULL
704  */
705 void DicomDir::Initialize()
706 {
707    StartMethod             = NULL;
708    ProgressMethod          = NULL;
709    EndMethod               = NULL;
710    StartMethodArgDelete    = NULL;
711    ProgressMethodArgDelete = NULL;
712    EndMethodArgDelete      = NULL;
713    StartArg                = NULL;
714    ProgressArg             = NULL;
715    EndArg                  = NULL;
716
717    Progress = 0.0;
718    Abort = false;
719
720    MetaElems = NULL;   
721 }
722
723 /**
724  * \brief create a 'DicomDir' from a DICOMDIR Header 
725  */
726 void DicomDir::CreateDicomDir()
727 {
728    // The list is parsed. 
729    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
730    //  1 - we save the beginning iterator
731    //  2 - we continue to parse
732    //  3 - we find an other tag
733    //       + we create the object for the precedent tag
734    //       + loop to 1 -
735
736    // Directory record sequence
737    DocEntry *e = GetDocEntry(0x0004, 0x1220);
738    if ( !e )
739    {
740       gdcmVerboseMacro( "NO Directory record sequence (0x0004,0x1220)");
741       /// \todo FIXME: what to do when the parsed file IS NOT a DICOMDIR file ? 
742       return;         
743    }
744    
745    SeqEntry *s = dynamic_cast<SeqEntry *>(e);
746    if ( !s )
747    {
748       gdcmVerboseMacro( "No SeqEntry present");
749       // useless : (0x0004,0x1220) IS a Sequence !
750       return;
751    }
752
753    NewMeta();
754    
755    DocEntry *d;
756    std::string v;
757    SQItem *si;
758
759    SQItem *tmpSI=s->GetFirstSQItem();
760    while(tmpSI)
761    {
762       d = tmpSI->GetDocEntry(0x0004, 0x1430); // Directory Record Type
763       if ( ValEntry* valEntry = dynamic_cast<ValEntry *>(d) )
764       {
765          v = valEntry->GetValue();
766       }
767       else
768       {
769          gdcmVerboseMacro( "Not a ValEntry.");
770          continue;
771       }
772
773       if( v == "PATIENT " )
774       {
775          si = new DicomDirPatient(true);
776          if( !AddPatientToEnd( static_cast<DicomDirPatient *>(si)) )
777          {
778             delete si;
779             si = NULL;
780             gdcmErrorMacro( "Add PatientToEnd failed");
781          }
782       }
783       else if( v == "STUDY " )
784       {
785          si = new DicomDirStudy(true);
786          if( !AddStudyToEnd( static_cast<DicomDirStudy *>(si)) )
787          {
788             delete si;
789             si = NULL;
790             gdcmErrorMacro( "Add AddStudyToEnd failed");
791          }
792       }
793       else if( v == "SERIES" )
794       {
795          si = new DicomDirSerie(true);
796          if( !AddSerieToEnd( static_cast<DicomDirSerie *>(si)) )
797          {
798             delete si;
799             si = NULL;
800             gdcmErrorMacro( "Add AddSerieToEnd failed");
801          }
802       }
803       else if( v == "IMAGE " ) 
804       {
805          si = new DicomDirImage(true);
806          if( !AddImageToEnd( static_cast<DicomDirImage *>(si)) )
807          {
808             delete si;
809             si = NULL;
810             gdcmErrorMacro( "Add AddImageToEnd failed");
811          }
812       }
813       else
814       {
815          // It was not a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
816          // neither an 'IMAGE' SQItem. Skip to next item.
817          continue;
818       }
819       tmpSI=s->GetNextSQItem();
820    }
821    ClearEntry();
822 }
823
824 /**
825  * \brief  AddPatientToEnd 
826  * @param   dd SQ Item to enqueue to the DicomPatient chained List
827  */
828 bool DicomDir::AddPatientToEnd(DicomDirPatient *dd)
829 {
830    Patients.push_back(dd);
831    return true;
832 }
833
834 /**
835  * \brief  AddStudyToEnd 
836  * @param   dd SQ Item to enqueue to the DicomDirStudy chained List
837  */
838 bool DicomDir::AddStudyToEnd(DicomDirStudy *dd)
839 {
840    if( Patients.size() > 0 )
841    {
842       ListDicomDirPatient::iterator itp = Patients.end();
843       itp--;
844       (*itp)->AddStudy(dd);
845       return true;
846    }
847    return false;
848 }
849
850 /**
851  * \brief  AddSerieToEnd 
852  * @param   dd SQ Item to enqueue to the DicomDirSerie chained List
853  */
854 bool DicomDir::AddSerieToEnd(DicomDirSerie *dd)
855 {
856    if( Patients.size() > 0 )
857    {
858       ListDicomDirPatient::iterator itp = Patients.end();
859       itp--;
860
861       DicomDirStudy *study = (*itp)->GetLastStudy();
862       if( study )
863       {
864          study->AddSerie(dd);
865          return true;
866       }
867    }
868    return false;
869 }
870
871 /**
872  * \brief   AddImageToEnd
873  * @param   dd SQ Item to enqueue to the DicomDirImage chained List
874  */
875 bool DicomDir::AddImageToEnd(DicomDirImage *dd)
876 {
877    if( Patients.size() > 0 )
878    {
879       ListDicomDirPatient::iterator itp = Patients.end();
880       itp--;
881
882       DicomDirStudy *study = (*itp)->GetLastStudy();
883       if( study )
884       {
885          DicomDirSerie *serie = study->GetLastSerie();
886          if( serie )
887          {
888             serie->AddImage(dd);
889             return true;
890          }
891       }
892    }
893    return false;
894 }
895
896 /**
897  * \brief  for each Header of the chained list, add/update the Patient/Study/Serie/Image info 
898  * @param   path path of the root directory
899  * @param   list chained list of Headers
900  */
901 void DicomDir::SetElements(std::string const & path, VectDocument const &list)
902 {
903    ClearEntry();
904    ClearPatient();
905
906    std::string patPrevName         = "", patPrevID  = "";
907    std::string studPrevInstanceUID = "", studPrevID = "";
908    std::string serPrevInstanceUID  = "", serPrevID  = "";
909
910    std::string patCurName,         patCurID;
911    std::string studCurInstanceUID, studCurID;
912    std::string serCurInstanceUID,  serCurID;
913
914    bool first = true;
915    for( VectDocument::const_iterator it = list.begin();
916                                      it != list.end(); ++it )
917    {
918       // get the current file characteristics
919       patCurName         = (*it)->GetEntryValue(0x0010,0x0010);
920       patCurID           = (*it)->GetEntryValue(0x0010,0x0011);
921       studCurInstanceUID = (*it)->GetEntryValue(0x0020,0x000d);
922       studCurID          = (*it)->GetEntryValue(0x0020,0x0010);
923       serCurInstanceUID  = (*it)->GetEntryValue(0x0020,0x000e);
924       serCurID           = (*it)->GetEntryValue(0x0020,0x0011);
925
926       if( patCurName != patPrevName || patCurID != patPrevID || first )
927       {
928          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
929          first = true;
930       }
931
932       // if new Study Deal with 'STUDY' Elements   
933       if( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID 
934          || first )
935       {
936          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
937          first = true;
938       }
939
940       // if new Serie Deal with 'SERIE' Elements   
941       if( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID
942          || first )
943       {
944          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
945          first = true;
946       }
947       
948       // Always Deal with 'IMAGE' Elements  
949       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
950
951       patPrevName         = patCurName;
952       patPrevID           = patCurID;
953       studPrevInstanceUID = studCurInstanceUID;
954       studPrevID          = studCurID;
955       serPrevInstanceUID  = serCurInstanceUID;
956       serPrevID           = serCurID;
957       first = false;
958    }
959 }
960
961 /**
962  * \brief   Move the content of the source SQItem to the destination SQItem
963  *          Only DocEntry's are moved
964  * @param dst destination SQItem
965  * @param src source SQItem
966  */
967 void DicomDir::MoveSQItem(DocEntrySet *dst,DocEntrySet *src)
968 {
969    DocEntry *entry;
970
971    entry = src->GetFirstEntry();
972    while(entry)
973    {
974       src->RemoveEntryNoDestroy(entry);
975       dst->AddEntry(entry);
976       // we destroyed -> the current iterator is not longer valid
977       entry = src->GetFirstEntry();
978    }
979 }
980
981 /**
982  * \brief   compares two files
983  */
984 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
985 {
986    return *header1 < *header2;
987 }
988
989 } // end namespace gdcm
990
991 //-----------------------------------------------------------------------------