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