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