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