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