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