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