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