]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
* FIX : last commit errors... sorry
[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 10:34:59 $
7   Version:   $Revision: 1.124 $
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    // FIXME : troubles found when it's a SeqEntry
574
575    for( it = elemList.begin(); it != elemList.end(); ++it)
576    {
577       tmpGr     = it->Group;
578       tmpEl     = it->Elem;
579       dictEntry = GetPubDict()->GetEntry(tmpGr, tmpEl);
580
581       entry     = new ValEntry( dictEntry ); // Be sure it's never a BinEntry !
582
583       entry->SetOffset(0); // just to avoid further missprinting
584
585       if( header )
586       {
587          // NULL when we Build Up (ex nihilo) a DICOMDIR
588          //   or when we add the META elems
589          val = header->GetEntryValue(tmpGr, tmpEl);
590       }
591       else
592       {
593          val = GDCM_UNFOUND;
594       }
595
596       if( val == GDCM_UNFOUND) 
597       {
598          if( tmpGr == 0x0004 && tmpEl == 0x1130 ) // File-set ID
599          {
600            // force to the *end* File Name
601            val = Util::GetName( path );
602          }
603          else if( tmpGr == 0x0004 && tmpEl == 0x1500 ) // Only used for image
604          {
605             if( header->GetFileName().substr(0, path.length()) != path )
606             {
607                gdcmVerboseMacro( "The base path of file name is incorrect");
608                val = header->GetFileName();
609             }
610             else
611             {
612                val = &(header->GetFileName().c_str()[path.length()]);
613             }
614          }
615          else
616          {
617             val = it->Value;
618          }
619       }
620       else
621       {
622          if ( header->GetEntryLength(tmpGr,tmpEl) == 0 )
623             val = it->Value;
624       }
625
626       entry->SetValue( val ); // troubles expected when vr=SQ ...
627
628       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
629       {
630          gdcmVerboseMacro("GDCM_DICOMDIR_META ?!? should never print that");
631       }
632       si->AddEntry(entry);
633    }
634 }
635
636 /**
637  * \brief   CallStartMethod
638  */
639 void DicomDir::CallStartMethod()
640 {
641    Progress = 0.0f;
642    Abort    = false;
643    if( StartMethod )
644    {
645       StartMethod( StartArg );
646    }
647 }
648
649 /**
650  * \brief   CallProgressMethod
651  */
652 void DicomDir::CallProgressMethod()
653 {
654    if( ProgressMethod )
655    {
656       ProgressMethod( ProgressArg );
657    }
658 }
659
660 /**
661  * \brief   CallEndMethod
662  */
663 void DicomDir::CallEndMethod()
664 {
665    Progress = 1.0f;
666    if( EndMethod )
667    {
668       EndMethod( EndArg );
669    }
670 }
671
672 //-----------------------------------------------------------------------------
673 // Private
674 /**
675  * \brief Sets all fields to NULL
676  */
677 void DicomDir::Initialize()
678 {
679    StartMethod             = NULL;
680    ProgressMethod          = NULL;
681    EndMethod               = NULL;
682    StartMethodArgDelete    = NULL;
683    ProgressMethodArgDelete = NULL;
684    EndMethodArgDelete      = NULL;
685    StartArg                = NULL;
686    ProgressArg             = NULL;
687    EndArg                  = NULL;
688
689    Progress = 0.0;
690    Abort = false;
691
692    MetaElems = NULL;   
693 }
694
695 /**
696  * \brief create a 'DicomDir' from a DICOMDIR Header 
697  */
698 void DicomDir::CreateDicomDir()
699 {
700    // The SeqEntries of "Directory Record Sequence" are parsed. 
701    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
702    //  1 - we save the beginning iterator
703    //  2 - we continue to parse
704    //  3 - we find an other tag
705    //       + we create the object for the precedent tag
706    //       + loop to 1 -
707
708    // Directory record sequence
709    DocEntry *e = GetDocEntry(0x0004, 0x1220);
710    if ( !e )
711    {
712       gdcmVerboseMacro( "No Directory Record Sequence (0004,1220) found");
713       /// \todo FIXME: what to do when the parsed file IS NOT a DICOMDIR file ? 
714       return;         
715    }
716    
717    SeqEntry *s = dynamic_cast<SeqEntry *>(e);
718    if ( !s )
719    {
720       gdcmVerboseMacro( "Element (0004,1220) is not a Sequence ?!?");
721       return;
722    }
723
724    NewMeta();
725    
726    DocEntry *d;
727    std::string v;
728    SQItem *si;
729
730    SQItem *tmpSI=s->GetFirstSQItem();
731    while(tmpSI)
732    {
733       d = tmpSI->GetDocEntry(0x0004, 0x1430); // Directory Record Type
734       if ( ValEntry* valEntry = dynamic_cast<ValEntry *>(d) )
735       {
736          v = valEntry->GetValue();
737       }
738       else
739       {
740          gdcmVerboseMacro( "Not a ValEntry.");
741          continue;
742       }
743
744       if( v == "PATIENT " )
745       {
746          si = new DicomDirPatient(true);
747          if( !AddPatientToEnd( static_cast<DicomDirPatient *>(si)) )
748          {
749             delete si;
750             si = NULL;
751             gdcmErrorMacro( "Add PatientToEnd failed");
752          }
753       }
754       else if( v == "STUDY " )
755       {
756          si = new DicomDirStudy(true);
757          if( !AddStudyToEnd( static_cast<DicomDirStudy *>(si)) )
758          {
759             delete si;
760             si = NULL;
761             gdcmErrorMacro( "Add AddStudyToEnd failed");
762          }
763       }
764       else if( v == "SERIES" )
765       {
766          si = new DicomDirSerie(true);
767          if( !AddSerieToEnd( static_cast<DicomDirSerie *>(si)) )
768          {
769             delete si;
770             si = NULL;
771             gdcmErrorMacro( "Add AddSerieToEnd failed");
772          }
773       }
774       else if( v == "IMAGE " ) 
775       {
776          si = new DicomDirImage(true);
777          if( !AddImageToEnd( static_cast<DicomDirImage *>(si)) )
778          {
779             delete si;
780             si = NULL;
781             gdcmErrorMacro( "Add AddImageToEnd failed");
782          }
783       }
784       else
785       {
786          // It was not a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
787          // neither an 'IMAGE' SQItem. Skip to next item.
788          continue;
789       }
790       if( si )
791          MoveSQItem(si,tmpSI);
792
793       tmpSI=s->GetNextSQItem();
794    }
795    ClearEntry();
796 }
797
798 /**
799  * \brief  AddPatientToEnd 
800  * @param   dd SQ Item to enqueue to the DicomPatient chained List
801  */
802 bool DicomDir::AddPatientToEnd(DicomDirPatient *dd)
803 {
804    Patients.push_back(dd);
805    return true;
806 }
807
808 /**
809  * \brief  AddStudyToEnd 
810  * @param   dd SQ Item to enqueue to the DicomDirStudy chained List
811  */
812 bool DicomDir::AddStudyToEnd(DicomDirStudy *dd)
813 {
814    if( Patients.size() > 0 )
815    {
816       ListDicomDirPatient::iterator itp = Patients.end();
817       itp--;
818       (*itp)->AddStudy(dd);
819       return true;
820    }
821    return false;
822 }
823
824 /**
825  * \brief  AddSerieToEnd 
826  * @param   dd SQ Item to enqueue to the DicomDirSerie chained List
827  */
828 bool DicomDir::AddSerieToEnd(DicomDirSerie *dd)
829 {
830    if( Patients.size() > 0 )
831    {
832       ListDicomDirPatient::iterator itp = Patients.end();
833       itp--;
834
835       DicomDirStudy *study = (*itp)->GetLastStudy();
836       if( study )
837       {
838          study->AddSerie(dd);
839          return true;
840       }
841    }
842    return false;
843 }
844
845 /**
846  * \brief   AddImageToEnd
847  * @param   dd SQ Item to enqueue to the DicomDirImage chained List
848  */
849 bool DicomDir::AddImageToEnd(DicomDirImage *dd)
850 {
851    if( Patients.size() > 0 )
852    {
853       ListDicomDirPatient::iterator itp = Patients.end();
854       itp--;
855
856       DicomDirStudy *study = (*itp)->GetLastStudy();
857       if( study )
858       {
859          DicomDirSerie *serie = study->GetLastSerie();
860          if( serie )
861          {
862             serie->AddImage(dd);
863             return true;
864          }
865       }
866    }
867    return false;
868 }
869
870 /**
871  * \brief  for each Header of the chained list, add/update the Patient/Study/Serie/Image info 
872  * @param   path path of the root directory
873  * @param   list chained list of Headers
874  */
875 void DicomDir::SetElements(std::string const & path, VectDocument const &list)
876 {
877    ClearEntry();
878    ClearPatient();
879
880    std::string patPrevName         = "", patPrevID  = "";
881    std::string studPrevInstanceUID = "", studPrevID = "";
882    std::string serPrevInstanceUID  = "", serPrevID  = "";
883
884    std::string patCurName,         patCurID;
885    std::string studCurInstanceUID, studCurID;
886    std::string serCurInstanceUID,  serCurID;
887
888    bool first = true;
889    for( VectDocument::const_iterator it = list.begin();
890                                      it != list.end(); ++it )
891    {
892       // get the current file characteristics
893       patCurName         = (*it)->GetEntryValue(0x0010,0x0010);
894       patCurID           = (*it)->GetEntryValue(0x0010,0x0011);
895       studCurInstanceUID = (*it)->GetEntryValue(0x0020,0x000d);
896       studCurID          = (*it)->GetEntryValue(0x0020,0x0010);
897       serCurInstanceUID  = (*it)->GetEntryValue(0x0020,0x000e);
898       serCurID           = (*it)->GetEntryValue(0x0020,0x0011);
899
900       if( patCurName != patPrevName || patCurID != patPrevID || first )
901       {
902          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
903          first = true;
904       }
905
906       // if new Study Deal with 'STUDY' Elements   
907       if( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID 
908          || first )
909       {
910          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
911          first = true;
912       }
913
914       // if new Serie Deal with 'SERIE' Elements   
915       if( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID
916          || first )
917       {
918          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
919          first = true;
920       }
921       
922       // Always Deal with 'IMAGE' Elements  
923       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
924
925       patPrevName         = patCurName;
926       patPrevID           = patCurID;
927       studPrevInstanceUID = studCurInstanceUID;
928       studPrevID          = studCurID;
929       serPrevInstanceUID  = serCurInstanceUID;
930       serPrevID           = serCurID;
931       first = false;
932    }
933 }
934
935 /**
936  * \brief   Move the content of the source SQItem to the destination SQItem
937  *          Only DocEntry's are moved
938  * @param dst destination SQItem
939  * @param src source SQItem
940  */
941 void DicomDir::MoveSQItem(DocEntrySet *dst,DocEntrySet *src)
942 {
943    DocEntry *entry;
944
945    entry = src->GetFirstEntry();
946    while(entry)
947    {
948       src->RemoveEntryNoDestroy(entry);
949       dst->AddEntry(entry);
950       // we destroyed -> the current iterator is not longer valid
951       entry = src->GetFirstEntry();
952    }
953 }
954
955 /**
956  * \brief   compares two files
957  */
958 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
959 {
960    return *header1 < *header2;
961 }
962
963 //-----------------------------------------------------------------------------
964 // Print
965 /**
966  * \brief  Canonical Printer 
967  * @param   os ostream we want to print in
968  * @param indent Indentation string to be prepended during printing
969  */
970 void DicomDir::Print(std::ostream &os, std::string const & )
971 {
972    if( MetaElems )
973    {
974       MetaElems->SetPrintLevel(PrintLevel);
975       MetaElems->Print(os);   
976    }   
977    for(ListDicomDirPatient::iterator cc  = Patients.begin();
978                                      cc != Patients.end();
979                                    ++cc)
980    {
981      (*cc)->SetPrintLevel(PrintLevel);
982      (*cc)->Print(os);
983    }
984 }
985
986 //-----------------------------------------------------------------------------
987 } // end namespace gdcm