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