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