]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
* src/gdcmSQItem[h|cxx] : rename some methods to have coherent names
[gdcm.git] / src / gdcmDicomDir.cxx
1 /*=========================================================================
2   
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDicomDir.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/01/20 11:26:17 $
7   Version:   $Revision: 1.109 $
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 "gdcmHeader.h"
31 #include "gdcmSeqEntry.h"
32 #include "gdcmSQItem.h"
33 #include "gdcmValEntry.h"
34
35 #include <fstream>
36 #include <string>
37 #include <algorithm>
38 #include <sys/types.h>
39
40 #ifdef _MSC_VER
41 #   define getcwd _getcwd
42 #endif
43
44 #if defined(_MSC_VER) || defined(__BORLANDC__)
45 #   include <direct.h>
46 #else
47 #   include <unistd.h>
48 #endif
49
50 namespace gdcm 
51 {
52
53 //-----------------------------------------------------------------------------
54 //  For full DICOMDIR description, see:
55 //  PS 3.3-2003, pages 731-750
56 //-----------------------------------------------------------------------------
57 // Constructor / Destructor
58
59 /**
60  * \brief   Constructor : creates an empty DicomDir
61  */
62 DicomDir::DicomDir()
63    :Document( )
64 {
65    Initialize();  // sets all private fields to NULL
66    NewMeta();
67 }
68
69 /**
70  * \brief Constructor Parses recursively the directory and creates the DicomDir
71  *        or uses an already built DICOMDIR, depending on 'parseDir' value.
72  * @param fileName        name 
73  *                      - of the root directory (parseDir = true)
74  *                      - of the DICOMDIR       (parseDir = false)
75  * @param parseDir boolean
76  *                      - true if user passed an entry point 
77  *                        and wants to explore recursively the directories
78  *                      - false if user passed an already built DICOMDIR file
79  *                        and wants to use it 
80  */
81 DicomDir::DicomDir(std::string const &fileName, bool parseDir ):
82    Document( fileName )
83 {
84    // At this step, Document constructor is already executed,
85    // whatever user passed (a root directory or a DICOMDIR)
86    // and whatever the value of parseDir was.
87    // (nothing is cheked in Document constructor, to avoid overhead)
88
89    Initialize();  // sets all private fields to NULL
90
91    // if user passed a root directory, sure we didn't get anything
92    if ( TagHT.begin() == TagHT.end() ) // when user passed a Directory to parse
93    {
94       if (!parseDir)
95          gdcmVerboseMacro( "Entry HT empty for file: "<<fileName);
96
97    // Only if user passed a root directory
98    // ------------------------------------
99       if ( fileName == "." )
100       {
101          // user passed '.' as Name
102          // we get current directory name
103          char dummy[1000];
104          getcwd(dummy, (size_t)1000);
105          SetFileName( dummy ); // will be converted into a string
106       }
107
108       if ( parseDir ) // user asked for a recursive parsing of a root directory
109       {
110          NewMeta();
111
112          gdcmVerboseMacro( "Parse directory and create the DicomDir");
113          ParseDirectory();
114       }
115       else
116       {
117          /// \todo if parseDir == false, it should be tagged as an error
118          // NO ! user may just call ParseDirectory() *after* constructor
119       }
120    }
121    // Only if user passed a DICOMDIR
122    // ------------------------------
123    else 
124    {
125       // Directory record sequence
126       DocEntry *e = GetDocEntry(0x0004, 0x1220);
127       if ( !e )
128       {
129          gdcmVerboseMacro( "NO 'Directory record sequence' (0x0004,0x1220)"
130                           << " in file " << fileName);
131          /// \todo FIXME : what do we do when the parsed file IS NOT a
132          ///       DICOMDIR file ?         
133       }
134       else
135          CreateDicomDir();
136    }
137 }
138
139 /**
140  * \brief  Canonical destructor 
141  */
142 DicomDir::~DicomDir() 
143 {
144    SetStartMethod(NULL);
145    SetProgressMethod(NULL);
146    SetEndMethod(NULL);
147
148    for(ListDicomDirPatient::iterator cc = Patients.begin();
149                                      cc!= Patients.end();
150                                    ++cc)
151    {
152       delete *cc;
153    }
154    if ( MetaElems )
155    {
156       delete MetaElems;
157    }
158 }
159
160 //-----------------------------------------------------------------------------
161 // Print
162 /**
163  * \brief  Canonical Printer 
164  */
165 void DicomDir::Print(std::ostream &os, std::string const & )
166 {
167    if( MetaElems )
168    {
169       MetaElems->SetPrintLevel(PrintLevel);
170       MetaElems->Print(os);   
171    }   
172    for(ListDicomDirPatient::iterator cc  = Patients.begin();
173                                      cc != Patients.end();
174                                    ++cc)
175    {
176      (*cc)->SetPrintLevel(PrintLevel);
177      (*cc)->Print(os);
178    }
179 }
180
181 //-----------------------------------------------------------------------------
182 // Public
183 /**
184  * \brief  This predicate, based on hopefully reasonable heuristics,
185  *         decides whether or not the current header 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    Header *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 Header( it->c_str() );
431       if( !header )
432       {
433          gdcmVerboseMacro( "Failure in new Header " << it->c_str() );
434          continue;
435       }
436       
437       if( header->IsReadable() )
438       {
439          // Add the file header 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 Header 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<Header *>(*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    MetaElems = new DicomDirMeta();
475   
476    if ( TagHT.begin() != TagHT.end() ) // after Document Parsing
477    { 
478       TagDocEntryHT::iterator lastOneButSequence = TagHT.end();
479       lastOneButSequence --;
480       // ALL the 'out of Sequence' Tags belong to Meta Elems
481       // (we skip 0004|1220 [Directory record sequence] )
482       for ( TagDocEntryHT::iterator cc  = TagHT.begin(); 
483                                     cc != lastOneButSequence;
484                                    ++cc)
485       {
486          MetaElems->AddEntry( cc->second );
487       }
488    }
489    else  // after root directory parsing
490    {
491       ListDicomDirMetaElem const &elemList = 
492          Global::GetDicomDirElements()->GetDicomDirMetaElements();
493       MetaElems->FillObject(elemList);
494    }
495    MetaElems->SetSQItemNumber(0); // To avoid further missprinting
496    return MetaElems;  
497 }
498
499 /**
500  * \brief   adds a new Patient (with the basic elements) to a partially created DICOMDIR
501  */
502 DicomDirPatient *DicomDir::NewPatient()
503 {
504    ListDicomDirPatientElem::const_iterator it;
505    uint16_t tmpGr,tmpEl;
506    DictEntry *dictEntry;
507    ValEntry *entry;
508
509    ListDicomDirPatientElem const & elemList =
510       Global::GetDicomDirElements()->GetDicomDirPatientElements(); 
511    DicomDirPatient *p = new DicomDirPatient();
512
513    // for all the DicomDirPatient Elements      
514    for( it = elemList.begin(); it != elemList.end(); ++it ) 
515    {
516       tmpGr     = it->Group;
517       tmpEl     = it->Elem;
518       dictEntry = GetPubDict()->GetDictEntry(tmpGr, tmpEl);
519       entry     = new ValEntry( dictEntry );
520       entry->SetOffset(0); // just to avoid further missprinting
521       entry->SetValue( it->Value );
522
523       p->AddEntry( entry );
524    }
525
526    AddPatientToEnd( p );
527    return p;
528 }
529
530 /**
531  * \brief   adds to the HTable 
532  *          the Entries (Dicom Elements) corresponding to the given type
533  * @param   path full path file name (only used when type = GDCM_DICOMDIR_IMAGE
534  * @param   type DicomDirObject type to create (GDCM_DICOMDIR_PATIENT,
535  *          GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
536  * @param   header Header of the current file
537  */
538 void DicomDir::SetElement(std::string const &path, DicomDirType type,
539                           Document *header)
540 {
541    ListDicomDirElem elemList; //FIXME this is going to be a by copy operation
542    ListDicomDirElem::const_iterator it;
543    uint16_t tmpGr, tmpEl;
544    DictEntry *dictEntry;
545    ValEntry *entry;
546    std::string val;
547    SQItem *si;
548
549    switch( type )
550    {
551       case GDCM_DICOMDIR_IMAGE:
552          elemList = Global::GetDicomDirElements()->GetDicomDirImageElements();
553          si = new DicomDirImage();
554          if( !AddImageToEnd(static_cast<DicomDirImage *>(si)) )
555          {
556             delete si;
557             gdcmErrorMacro( "Add ImageToEnd failed");
558          }
559          break;
560       case GDCM_DICOMDIR_SERIE:
561          elemList = Global::GetDicomDirElements()->GetDicomDirSerieElements();
562          si = new DicomDirSerie();
563          if( !AddSerieToEnd(static_cast<DicomDirSerie *>(si)) )
564          {
565             delete si;
566             gdcmErrorMacro( "Add SerieToEnd failed");
567          }
568          break;
569       case GDCM_DICOMDIR_STUDY:
570          elemList = Global::GetDicomDirElements()->GetDicomDirStudyElements();
571          si = new DicomDirStudy();
572          if( !AddStudyToEnd(static_cast<DicomDirStudy *>(si)) )
573          {
574             delete si;
575             gdcmErrorMacro( "Add StudyToEnd failed");
576          }
577          break;
578       case GDCM_DICOMDIR_PATIENT:
579          elemList = Global::GetDicomDirElements()->GetDicomDirPatientElements();
580          si = new DicomDirPatient();
581          if( !AddPatientToEnd(static_cast<DicomDirPatient *>(si)) )
582          {
583             delete si;
584             gdcmErrorMacro( "Add PatientToEnd failed");
585          }
586          break;
587       case GDCM_DICOMDIR_META:
588          elemList = Global::GetDicomDirElements()->GetDicomDirMetaElements();
589          si = new DicomDirMeta();
590          if( MetaElems )
591          {
592             delete MetaElems;
593             gdcmErrorMacro( "MetaElements already exist, they will be destroyed");
594          }
595          MetaElems = static_cast<DicomDirMeta *>(si);
596          break;
597       default:
598          return;
599    }
600    // removed all the seems-to-be-useless stuff about Referenced Image Sequence
601    // to avoid further troubles
602    // imageElem 0008 1140 "" // Referenced Image Sequence
603    // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
604    // imageElem 0008 1150 "" // Referenced SOP Class UID    : to be set/forged later
605    // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
606    // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
607    // for all the relevant elements found in their own spot of the DicomDir.dic
608    // FIXME : troubles found when it's a SeqEntry
609
610    for( it = elemList.begin(); it != elemList.end(); ++it)
611    {
612       tmpGr     = it->Group;
613       tmpEl     = it->Elem;
614       dictEntry = GetPubDict()->GetDictEntry(tmpGr, tmpEl);
615
616       entry     = new ValEntry( dictEntry ); // Be sure it's never a BinEntry !
617
618       entry->SetOffset(0); // just to avoid further missprinting
619
620       if( header )
621       {
622          // NULL when we Build Up (ex nihilo) a DICOMDIR
623          //   or when we add the META elems
624          val = header->GetEntry(tmpGr, tmpEl);
625       }
626       else
627       {
628          val = GDCM_UNFOUND;
629       }
630
631       if( val == GDCM_UNFOUND) 
632       {
633          if( tmpGr == 0x0004 && tmpEl == 0x1130 ) // File-set ID
634          {
635            // force to the *end* File Name
636            val = Util::GetName( path );
637          }
638          else if( tmpGr == 0x0004 && tmpEl == 0x1500 ) // Only used for image
639          {
640             if( header->GetFileName().substr(0, path.length()) != path )
641             {
642                gdcmVerboseMacro( "The base path of file name is incorrect");
643                val = header->GetFileName();
644             }
645             else
646             {
647                val = &(header->GetFileName().c_str()[path.length()]);
648             }
649          }
650          else
651          {
652             val = it->Value;
653          }
654       }
655       else
656       {
657          if ( header->GetEntryLength(tmpGr,tmpEl) == 0 )
658             val = it->Value;
659       }
660
661       entry->SetValue( val ); // troubles expected when vr=SQ ...
662
663       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
664       {
665          gdcmVerboseMacro("GDCM_DICOMDIR_META ?!? should never print that");
666       }
667       si->AddEntry(entry);
668    }
669 }
670
671 //-----------------------------------------------------------------------------
672 /**
673  * \brief   CallStartMethod
674  */
675 void DicomDir::CallStartMethod()
676 {
677    Progress = 0.0f;
678    Abort    = false;
679    if( StartMethod )
680    {
681       StartMethod( StartArg );
682    }
683 }
684
685 //-----------------------------------------------------------------------------
686 /**
687  * \brief   CallProgressMethod
688  */
689 void DicomDir::CallProgressMethod()
690 {
691    if( ProgressMethod )
692    {
693       ProgressMethod( ProgressArg );
694    }
695 }
696
697 //-----------------------------------------------------------------------------
698 /**
699  * \brief   CallEndMethod
700  */
701 void DicomDir::CallEndMethod()
702 {
703    Progress = 1.0f;
704    if( EndMethod )
705    {
706       EndMethod( EndArg );
707    }
708 }
709
710 //-----------------------------------------------------------------------------
711 // Private
712 /**
713  * \brief Sets all fields to NULL
714  */
715 void DicomDir::Initialize()
716 {
717    StartMethod             = NULL;
718    ProgressMethod          = NULL;
719    EndMethod               = NULL;
720    StartMethodArgDelete    = NULL;
721    ProgressMethodArgDelete = NULL;
722    EndMethodArgDelete      = NULL;
723    StartArg                = NULL;
724    ProgressArg             = NULL;
725    EndArg                  = NULL;
726
727    Progress = 0.0;
728    Abort = false;
729
730    MetaElems = NULL;   
731 }
732
733 /**
734  * \brief create a 'DicomDir' from a DICOMDIR Header 
735  */
736 void DicomDir::CreateDicomDir()
737 {
738    // The list is parsed. 
739    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
740    //  1 - we save the beginning iterator
741    //  2 - we continue to parse
742    //  3 - we find an other tag
743    //       + we create the object for the precedent tag
744    //       + loop to 1 -
745
746    // Directory record sequence
747    DocEntry *e = GetDocEntry(0x0004, 0x1220);
748    if ( !e )
749    {
750       gdcmVerboseMacro( "NO Directory record sequence (0x0004,0x1220)");
751       /// \todo FIXME: what to do when the parsed file IS NOT a DICOMDIR file ? 
752       return;         
753    }
754    
755    SeqEntry *s = dynamic_cast<SeqEntry *>(e);
756    if ( !s )
757    {
758       gdcmVerboseMacro( "No SeqEntry present");
759       // useless : (0x0004,0x1220) IS a Sequence !
760       return;
761    }
762
763    NewMeta();
764
765    //ListSQItem listItems = s->GetSQItems();
766    
767    DocEntry *d;
768    std::string v;
769    SQItem *si;
770
771    SQItem *tmpSI=s->GetFirstSQItem();
772    while(tmpSI)
773    {
774       d = tmpSI->GetDocEntry(0x0004, 0x1430); // Directory Record Type
775       if ( ValEntry* valEntry = dynamic_cast<ValEntry *>(d) )
776       {
777          v = valEntry->GetValue();
778       }
779       else
780       {
781          gdcmVerboseMacro( "Not a ValEntry.");
782          continue;
783       }
784
785       if( v == "PATIENT " )
786       {
787          si = new DicomDirPatient();
788          if( !AddPatientToEnd( static_cast<DicomDirPatient *>(si)) )
789          {
790             delete si;
791             si = NULL;
792             gdcmErrorMacro( "Add PatientToEnd failed");
793          }
794       }
795       else if( v == "STUDY " )
796       {
797          si = new DicomDirStudy();
798          if( !AddStudyToEnd( static_cast<DicomDirStudy *>(si)) )
799          {
800             delete si;
801             si = NULL;
802             gdcmErrorMacro( "Add AddStudyToEnd failed");
803          }
804       }
805       else if( v == "SERIES" )
806       {
807          si = new DicomDirSerie();
808          if( !AddSerieToEnd( static_cast<DicomDirSerie *>(si)) )
809          {
810             delete si;
811             si = NULL;
812             gdcmErrorMacro( "Add AddSerieToEnd failed");
813          }
814       }
815       else if( v == "IMAGE " ) 
816       {
817          si = new DicomDirImage();
818          if( !AddImageToEnd( static_cast<DicomDirImage *>(si)) )
819          {
820             delete si;
821             si = NULL;
822             gdcmErrorMacro( "Add AddImageToEnd failed");
823          }
824       }
825       else
826       {
827          // It was not a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
828          // neither an 'IMAGE' SQItem. Skip to next item.
829          continue;
830       }
831
832       if( si )
833          MoveSQItem(si,tmpSI);
834
835       tmpSI=s->GetNextSQItem();
836    }
837    TagHT.clear();
838 }
839
840 /**
841  * \brief  AddPatientToEnd 
842  * @param   dd SQ Item to enqueue to the DicomPatient chained List
843  */
844 bool DicomDir::AddPatientToEnd(DicomDirPatient *dd)
845 {
846    Patients.push_back(dd);
847    return true;
848 }
849
850 /**
851  * \brief  AddStudyToEnd 
852  * @param   dd SQ Item to enqueue to the DicomDirStudy chained List
853  */
854 bool DicomDir::AddStudyToEnd(DicomDirStudy *dd)
855 {
856    if( Patients.size() > 0 )
857    {
858       ListDicomDirPatient::iterator itp = Patients.end();
859       itp--;
860       (*itp)->AddStudy(dd);
861       return true;
862    }
863    return false;
864 }
865
866 /**
867  * \brief  AddSerieToEnd 
868  * @param   dd SQ Item to enqueue to the DicomDirSerie chained List
869  */
870 bool DicomDir::AddSerieToEnd(DicomDirSerie *dd)
871 {
872    if( Patients.size() > 0 )
873    {
874       ListDicomDirPatient::iterator itp = Patients.end();
875       itp--;
876
877       DicomDirStudy *study = (*itp)->GetLastStudy();
878       if( study )
879       {
880          study->AddSerie(dd);
881          return true;
882       }
883 /*      if( (*itp)->GetDicomDirStudies().size() > 0 )
884       {
885          ListDicomDirStudy::const_iterator itst = 
886             (*itp)->GetDicomDirStudies().end();
887          itst--;
888          (*itst)->AddSerie(dd);
889          return true;
890       }*/
891    }
892    return false;
893 }
894
895 /**
896  * \brief   AddImageToEnd
897  * @param   dd SQ Item to enqueue to the DicomDirImage chained List
898  */
899 bool DicomDir::AddImageToEnd(DicomDirImage *dd)
900 {
901    if( Patients.size() > 0 )
902    {
903       ListDicomDirPatient::iterator itp = Patients.end();
904       itp--;
905
906       DicomDirStudy *study = (*itp)->GetLastStudy();
907       if( study )
908       {
909          DicomDirSerie *serie = study->GetLastSerie();
910          if( serie )
911          {
912             serie->AddImage(dd);
913             return true;
914          }
915       }
916 /*      if( (*itp)->GetDicomDirStudies().size() > 0 )
917       {
918          ListDicomDirStudy::const_iterator itst = 
919             (*itp)->GetDicomDirStudies().end();
920          itst--;
921
922          if( (*itst)->GetDicomDirSeries().size() > 0 )
923          {
924             ListDicomDirSerie::const_iterator its = (*itst)->GetDicomDirSeries().end();
925             its--;
926             (*its)->AddImage(dd);
927             return true;
928          }
929       }*/
930    }
931    return false;
932 }
933
934 /**
935  * \brief  for each Header of the chained list, add/update the Patient/Study/Serie/Image info 
936  * @param   path path of the root directory
937  * @param   list chained list of Headers
938  */
939 void DicomDir::SetElements(std::string const & path, VectDocument const &list)
940 {
941    TagHT.clear();
942    Patients.clear();
943
944    std::string patPrevName         = "", patPrevID  = "";
945    std::string studPrevInstanceUID = "", studPrevID = "";
946    std::string serPrevInstanceUID  = "", serPrevID  = "";
947
948    std::string patCurName,         patCurID;
949    std::string studCurInstanceUID, studCurID;
950    std::string serCurInstanceUID,  serCurID;
951
952    bool first = true;
953    for( VectDocument::const_iterator it = list.begin();
954                                      it != list.end(); ++it )
955    {
956       // get the current file characteristics
957       patCurName         = (*it)->GetEntry(0x0010,0x0010);
958       patCurID           = (*it)->GetEntry(0x0010,0x0011);
959       studCurInstanceUID = (*it)->GetEntry(0x0020,0x000d);
960       studCurID          = (*it)->GetEntry(0x0020,0x0010);
961       serCurInstanceUID  = (*it)->GetEntry(0x0020,0x000e);
962       serCurID           = (*it)->GetEntry(0x0020,0x0011);
963
964       if( patCurName != patPrevName || patCurID != patPrevID || first )
965       {
966          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
967          first = true;
968       }
969
970       // if new Study Deal with 'STUDY' Elements   
971       if( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID 
972          || first )
973       {
974          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
975          first = true;
976       }
977
978       // if new Serie Deal with 'SERIE' Elements   
979       if( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID
980          || first )
981       {
982          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
983          first = true;
984       }
985       
986       // Always Deal with 'IMAGE' Elements  
987       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
988
989       patPrevName         = patCurName;
990       patPrevID           = patCurID;
991       studPrevInstanceUID = studCurInstanceUID;
992       studPrevID          = studCurID;
993       serPrevInstanceUID  = serCurInstanceUID;
994       serPrevID           = serCurID;
995       first = false;
996    }
997 }
998
999 /**
1000  * \brief   Move the content of the source SQItem to the destination SQItem
1001  *          Only DocEntry's are moved
1002  * @param dst destination SQItem
1003  * @param src source SQItem
1004  */
1005 void DicomDir::MoveSQItem(SQItem *dst,SQItem *src)
1006 {
1007    DocEntry *entry;
1008
1009    entry = src->GetFirstEntry();
1010    while(entry)
1011    {
1012       src->RemoveEntryNoDestroy(entry);
1013       dst->AddEntry(entry);
1014       // we destroyed -> the current iterator is not longer valid
1015       entry = src->GetFirstEntry();
1016    }
1017 }
1018
1019 /**
1020  * \brief   compares two dgcmHeaders
1021  */
1022 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
1023 {
1024    return *header1 < *header2;
1025 }
1026
1027 } // end namespace gdcm
1028
1029 //-----------------------------------------------------------------------------