]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
Add Anonymize method (not yet fully checked)
[gdcm.git] / src / gdcmDicomDir.cxx
1 /*=========================================================================
2   
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDicomDir.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/03/09 19:29:38 $
7   Version:   $Revision: 1.136 $
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 //    (see also PS 4.3, 2004, page 50 for Entity-Relationship model)
59 //
60 //  Directory Record Type      Directory Record Types which may be included
61 //                                in the next lower-level directory Entity
62 //
63 // (Root directory Entity)     PATIENT
64 //
65 // PATIENT                     STUDY
66 //
67 // STUDY                       SERIES, VISIT, RESULTS, STUDY COMPONENT
68 //
69 // SERIES                      IMAGE, OVERLAYS, MODALITY LUT, VOI LUT,
70 //                             CURVE, STORED PRINT, RT DOSE, RT STRUCTURE SET
71 //                             RT PLAN, RT TREAT RECORD, PRESENTATION, WAVEFORM,
72 //                             SR DOCUMENT, KEY OBJECT DOC, SPECTROSCOPY,
73 //                             RAW DATA, REGISTRATION, FIDUCIAL
74 // IMAGE
75 // OVERLAY
76 // MODALITY LUT
77 // VOI LUT
78 // CURVE
79 // STORED PRINT
80 // RT DOSE
81 // RT STRUCTURE SET
82 // RT PLAN
83 // RT TREAT RECORD
84 // PRESENTATION
85 // WAVEFORM
86 // SR DOCUMENT
87 // KEY OBJECT DOC
88 // SPECTROSCOPY
89 // RAW DATA
90 // REGISTRATION
91 // FIDUCIAL
92 // 
93 // ----------------------
94 // The current gdcm version only deals with :
95 //
96 // (Root directory Entity)     PATIENT
97 // PATIENT                     STUDY
98 // STUDY                       SERIES 
99 // SERIES                      IMAGE
100 // IMAGE                       /
101 //
102 // DicomDir::CreateDicomDir will have to be completed
103 // Treelike structure management will have to be upgraded
104 // ----------------------------------------------------------------------------
105     
106 namespace gdcm 
107 {
108 //-----------------------------------------------------------------------------
109 // Constructor / Destructor
110 /**
111  * \brief   Constructor : creates an empty DicomDir
112  */
113 DicomDir::DicomDir()
114          :Document( )
115 {
116    Initialize();  // sets all private fields to NULL
117    NewMeta();
118 }
119
120 /**
121  * \brief Constructor Parses recursively the directory and creates the DicomDir
122  *        or uses an already built DICOMDIR, depending on 'parseDir' value.
123  * @param fileName  name 
124  *                      - of the root directory (parseDir = true)
125  *                      - of the DICOMDIR       (parseDir = false)
126  * @param parseDir boolean
127  *                      - true if user passed an entry point 
128  *                        and wants to explore recursively the directories
129  *                      - false if user passed an already built DICOMDIR file
130  *                        and wants to use it 
131  */
132 DicomDir::DicomDir(std::string const &fileName, bool parseDir ):
133    Document( fileName )
134 {
135    // At this step, Document constructor is already executed,
136    // whatever user passed (a root directory or a DICOMDIR)
137    // and whatever the value of parseDir was.
138    // (nothing is cheked in Document constructor, to avoid overhead)
139
140    Initialize();  // sets all private fields to NULL
141
142    // if user passed a root directory, sure we didn't get anything
143
144    if ( GetFirstEntry() == 0 ) // when user passed a Directory to parse
145    {
146       if (!parseDir)
147          gdcmWarningMacro( "Entry HT empty for file: "<<fileName);
148
149    // Only if user passed a root directory
150    // ------------------------------------
151       if ( fileName == "." )
152       {
153          // user passed '.' as Name
154          // we get current directory name
155          char dummy[1000];
156          getcwd(dummy, (size_t)1000);
157          SetFileName( dummy ); // will be converted into a string
158       }
159
160       if ( parseDir ) // user asked for a recursive parsing of a root directory
161       {
162          NewMeta();
163
164          gdcmWarningMacro( "Parse directory and create the DicomDir");
165          ParseDirectory();
166       }
167       /*
168       else
169       {
170          //  user may just call ParseDirectory() *after* constructor
171       }
172       */
173    }
174    // Only if user passed a DICOMDIR
175    // ------------------------------
176    else 
177    {
178       // Directory record sequence
179       DocEntry *e = GetDocEntry(0x0004, 0x1220);
180       if ( !e )
181       {
182          gdcmWarningMacro( "NO 'Directory record sequence' (0x0004,0x1220)"
183                           << " in file " << fileName);
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  * @param  fileName file to be written to 
431  * @return false only when fail to open
432  */
433  
434 bool DicomDir::WriteDicomDir(std::string const &fileName) 
435 {  
436    int i;
437    uint16_t sq[4] = { 0x0004, 0x1220, 0xffff, 0xffff };
438    uint16_t sqt[4]= { 0xfffe, 0xe0dd, 0xffff, 0xffff };
439
440    std::ofstream *fp = new std::ofstream(fileName.c_str(),  
441                                          std::ios::out | std::ios::binary);
442    if( !fp ) 
443    {
444       gdcmWarningMacro("Failed to open(write) File: " << fileName.c_str());
445       return false;
446    }
447
448    char filePreamble[128];
449    memset(filePreamble, 0, 128);
450    fp->write(filePreamble, 128);
451    binary_write( *fp, "DICM");
452  
453    DicomDirMeta *ptrMeta = GetMeta();
454    ptrMeta->WriteContent(fp, ExplicitVR);
455    
456    // force writing 0004|1220 [SQ ], that CANNOT exist within DicomDirMeta
457    for(i=0;i<4;++i)
458    {
459       binary_write(*fp, sq[i]);
460    }
461         
462    for(ListDicomDirPatient::iterator cc  = Patients.begin();
463                                      cc != Patients.end();
464                                    ++cc )
465    {
466       (*cc)->WriteContent( fp, ExplicitVR );
467    }
468    
469    // force writing Sequence Delimitation Item
470    for(i=0;i<4;++i)
471    {
472       binary_write(*fp, sqt[i]);  // fffe e0dd ffff ffff 
473    }
474
475    fp->close();
476    delete fp;
477
478    return true;
479 }
480
481 /**
482  * \brief    Anonymize a DICOMDIR
483  * @return true 
484  */
485  
486 bool DicomDir::AnonymizeDicomDir() 
487 {
488    ValEntry *v;
489    // Something clever to be found to forge the Patient names
490    std::ostringstream s;
491    int i = 1;
492    for(ListDicomDirPatient::iterator cc = Patients.begin();
493                                      cc!= Patients.end();
494                                    ++cc)
495    {
496       s << i;
497       v = (*cc)->GetValEntry(0x0010, 0x0010) ; // Patient's Name
498       if (v)
499       {
500          v->SetValue(s.str());
501       }
502
503       v = (*cc)->GetValEntry(0x0010, 0x0020) ; // Patient ID
504       if (v)
505       {
506          v->SetValue(" ");
507       }
508
509       v = (*cc)->GetValEntry(0x0010, 0x0030) ; // Patient's BirthDate
510       if (v)
511       {
512          v->SetValue(" ");
513       }
514       s << "";
515       i++;
516    }
517    return true;
518 }
519 //-----------------------------------------------------------------------------
520 // Protected
521 /**
522  * \brief create a Document-like chained list from a root Directory 
523  * @param path entry point of the tree-like structure
524  */
525 void DicomDir::CreateDicomDirChainedList(std::string const &path)
526 {
527    CallStartMethod();
528    DirList dirList(path,1); // gets recursively the file list
529    unsigned int count = 0;
530    VectDocument list;
531    File *header;
532
533    DirListType fileList = dirList.GetFilenames();
534
535    for( DirListType::iterator it  = fileList.begin();
536                               it != fileList.end();
537                               ++it )
538    {
539       Progress = (float)(count+1)/(float)fileList.size();
540       CallProgressMethod();
541       if( Abort )
542       {
543          break;
544       }
545
546       header = new File( it->c_str() );
547       if( !header )
548       {
549          gdcmWarningMacro( "Failure in new gdcm::File " << it->c_str() );
550          continue;
551       }
552       
553       if( header->IsReadable() )
554       {
555          // Add the file to the chained list:
556          list.push_back(header);
557          gdcmWarningMacro( "Readable " << it->c_str() );
558        }
559        else
560        {
561           delete header;
562        }
563        count++;
564    }
565    // sorts Patient/Study/Serie/
566    std::sort(list.begin(), list.end(), DicomDir::HeaderLessThan );
567    
568    std::string tmp = dirList.GetDirName();      
569    //for each File of the chained list, add/update the Patient/Study/Serie/Image info
570    SetElements(tmp, list);
571    CallEndMethod();
572
573    for(VectDocument::iterator itDoc=list.begin();
574        itDoc!=list.end();
575        ++itDoc)
576    {
577       delete dynamic_cast<File *>(*itDoc);
578    }
579 }
580
581 /**
582  * \brief   CallStartMethod
583  */
584 void DicomDir::CallStartMethod()
585 {
586    Progress = 0.0f;
587    Abort    = false;
588    if( StartMethod )
589    {
590       StartMethod( StartArg );
591    }
592 }
593
594 /**
595  * \brief   CallProgressMethod
596  */
597 void DicomDir::CallProgressMethod()
598 {
599    if( ProgressMethod )
600    {
601       ProgressMethod( ProgressArg );
602    }
603 }
604
605 /**
606  * \brief   CallEndMethod
607  */
608 void DicomDir::CallEndMethod()
609 {
610    Progress = 1.0f;
611    if( EndMethod )
612    {
613       EndMethod( EndArg );
614    }
615 }
616
617 //-----------------------------------------------------------------------------
618 // Private
619 /**
620  * \brief Sets all fields to NULL
621  */
622 void DicomDir::Initialize()
623 {
624    StartMethod             = NULL;
625    ProgressMethod          = NULL;
626    EndMethod               = NULL;
627    StartMethodArgDelete    = NULL;
628    ProgressMethodArgDelete = NULL;
629    EndMethodArgDelete      = NULL;
630    StartArg                = NULL;
631    ProgressArg             = NULL;
632    EndArg                  = NULL;
633
634    Progress = 0.0;
635    Abort = false;
636
637    MetaElems = NULL;   
638 }
639
640 /**
641  * \brief create a 'DicomDir' from a DICOMDIR Header 
642  */
643 void DicomDir::CreateDicomDir()
644 {
645    // The SeqEntries of "Directory Record Sequence" are parsed. 
646    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
647    //  1 - we save the beginning iterator
648    //  2 - we continue to parse
649    //  3 - we find an other tag
650    //       + we create the object for the precedent tag
651    //       + loop to 1 -
652
653    // Directory record sequence
654    DocEntry *e = GetDocEntry(0x0004, 0x1220);
655    if ( !e )
656    {
657       gdcmWarningMacro( "No Directory Record Sequence (0004,1220) found");
658       return;         
659    }
660    
661    SeqEntry *s = dynamic_cast<SeqEntry *>(e);
662    if ( !s )
663    {
664       gdcmWarningMacro( "Element (0004,1220) is not a Sequence ?!?");
665       return;
666    }
667
668    NewMeta();
669    
670    DocEntry *d;
671    std::string v;
672    SQItem *si;
673
674    SQItem *tmpSI=s->GetFirstSQItem();
675    while(tmpSI)
676    {
677       d = tmpSI->GetDocEntry(0x0004, 0x1430); // Directory Record Type
678       if ( ValEntry* valEntry = dynamic_cast<ValEntry *>(d) )
679       {
680          v = valEntry->GetValue();
681       }
682       else
683       {
684          gdcmWarningMacro( "(0004,1430) not a ValEntry ?!?");
685          continue;
686       }
687
688       if( v == "PATIENT " )
689       {
690          si = new DicomDirPatient(true);
691          if( !AddPatientToEnd( static_cast<DicomDirPatient *>(si)) )
692          {
693             delete si;
694             si = NULL;
695             gdcmErrorMacro( "Add PatientToEnd failed");
696          }
697       }
698       else if( v == "STUDY " )
699       {
700          si = new DicomDirStudy(true);
701          if( !AddStudyToEnd( static_cast<DicomDirStudy *>(si)) )
702          {
703             delete si;
704             si = NULL;
705             gdcmErrorMacro( "Add AddStudyToEnd failed");
706          }
707       }
708       else if( v == "SERIES" )
709       {
710          si = new DicomDirSerie(true);
711          if( !AddSerieToEnd( static_cast<DicomDirSerie *>(si)) )
712          {
713             delete si;
714             si = NULL;
715             gdcmErrorMacro( "Add AddSerieToEnd failed");
716          }
717       }
718       else if( v == "IMAGE " ) 
719       {
720          si = new DicomDirImage(true);
721          if( !AddImageToEnd( static_cast<DicomDirImage *>(si)) )
722          {
723             delete si;
724             si = NULL;
725             gdcmErrorMacro( "Add AddImageToEnd failed");
726          }
727       }
728       else
729       {
730          // It was not a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
731          // neither an 'IMAGE' SQItem. Skip to next item.
732          continue;
733       }
734       if( si )
735          MoveSQItem(si,tmpSI);
736
737       tmpSI=s->GetNextSQItem();
738    }
739    ClearEntry();
740 }
741
742 /**
743  * \brief  AddPatientToEnd 
744  * @param   dd SQ Item to enqueue to the DicomPatient chained List
745  */
746 bool DicomDir::AddPatientToEnd(DicomDirPatient *dd)
747 {
748    Patients.push_back(dd);
749    return true;
750 }
751
752 /**
753  * \brief  AddStudyToEnd 
754  * @param   dd SQ Item to enqueue to the DicomDirStudy chained List
755  */
756 bool DicomDir::AddStudyToEnd(DicomDirStudy *dd)
757 {
758    if( Patients.size() > 0 )
759    {
760       ListDicomDirPatient::iterator itp = Patients.end();
761       itp--;
762       (*itp)->AddStudy(dd);
763       return true;
764    }
765    return false;
766 }
767
768 /**
769  * \brief  AddSerieToEnd 
770  * @param   dd SQ Item to enqueue to the DicomDirSerie chained List
771  */
772 bool DicomDir::AddSerieToEnd(DicomDirSerie *dd)
773 {
774    if( Patients.size() > 0 )
775    {
776       ListDicomDirPatient::iterator itp = Patients.end();
777       itp--;
778
779       DicomDirStudy *study = (*itp)->GetLastStudy();
780       if( study )
781       {
782          study->AddSerie(dd);
783          return true;
784       }
785    }
786    return false;
787 }
788
789 /**
790  * \brief   AddImageToEnd
791  * @param   dd SQ Item to enqueue to the DicomDirImage chained List
792  */
793 bool DicomDir::AddImageToEnd(DicomDirImage *dd)
794 {
795    if( Patients.size() > 0 )
796    {
797       ListDicomDirPatient::iterator itp = Patients.end();
798       itp--;
799
800       DicomDirStudy *study = (*itp)->GetLastStudy();
801       if( study )
802       {
803          DicomDirSerie *serie = study->GetLastSerie();
804          if( serie )
805          {
806             serie->AddImage(dd);
807             return true;
808          }
809       }
810    }
811    return false;
812 }
813
814 /**
815  * \brief  for each Header of the chained list, 
816  *         add/update the Patient/Study/Serie/Image info 
817  * @param   path path of the root directory
818  * @param   list chained list of Headers
819  */
820 void DicomDir::SetElements(std::string const &path, VectDocument const &list)
821 {
822    ClearEntry();
823    ClearPatient();
824
825    std::string patPrevName         = "", patPrevID  = "";
826    std::string studPrevInstanceUID = "", studPrevID = "";
827    std::string serPrevInstanceUID  = "", serPrevID  = "";
828
829    std::string patCurName,         patCurID;
830    std::string studCurInstanceUID, studCurID;
831    std::string serCurInstanceUID,  serCurID;
832
833    bool first = true;
834    for( VectDocument::const_iterator it = list.begin();
835                                      it != list.end(); 
836                                    ++it )
837    {
838       // get the current file characteristics
839       patCurName         = (*it)->GetEntryValue(0x0010,0x0010);
840       patCurID           = (*it)->GetEntryValue(0x0010,0x0011);
841       studCurInstanceUID = (*it)->GetEntryValue(0x0020,0x000d);
842       studCurID          = (*it)->GetEntryValue(0x0020,0x0010);
843       serCurInstanceUID  = (*it)->GetEntryValue(0x0020,0x000e);
844       serCurID           = (*it)->GetEntryValue(0x0020,0x0011);
845
846       if( patCurName != patPrevName || patCurID != patPrevID || first )
847       {
848          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
849          first = true;
850       }
851
852       // if new Study Deal with 'STUDY' Elements   
853       if( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID 
854          || first )
855       {
856          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
857          first = true;
858       }
859
860       // if new Serie Deal with 'SERIE' Elements   
861       if( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID
862          || first )
863       {
864          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
865       }
866       
867       // Always Deal with 'IMAGE' Elements  
868       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
869
870       patPrevName         = patCurName;
871       patPrevID           = patCurID;
872       studPrevInstanceUID = studCurInstanceUID;
873       studPrevID          = studCurID;
874       serPrevInstanceUID  = serCurInstanceUID;
875       serPrevID           = serCurID;
876       first = false;
877    }
878 }
879
880 /**
881  * \brief   adds to the HTable 
882  *          the Entries (Dicom Elements) corresponding to the given type
883  * @param   path full path file name (only used when type = GDCM_DICOMDIR_IMAGE
884  * @param   type DicomDirObject type to create (GDCM_DICOMDIR_PATIENT,
885  *          GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
886  * @param   header Header of the current file
887  */
888 void DicomDir::SetElement(std::string const &path, DicomDirType type,
889                           Document *header)
890 {
891    ListDicomDirElem elemList;
892    ListDicomDirElem::const_iterator it;
893    uint16_t tmpGr, tmpEl;
894    DictEntry *dictEntry;
895    ValEntry *entry;
896    std::string val;
897    SQItem *si;
898
899    switch( type )
900    {
901       case GDCM_DICOMDIR_IMAGE:
902          elemList = Global::GetDicomDirElements()->GetDicomDirImageElements();
903          si = new DicomDirImage(true);
904          if( !AddImageToEnd(static_cast<DicomDirImage *>(si)) )
905          {
906             delete si;
907             gdcmErrorMacro( "Add ImageToEnd failed");
908          }
909          break;
910       case GDCM_DICOMDIR_SERIE:
911          elemList = Global::GetDicomDirElements()->GetDicomDirSerieElements();
912          si = new DicomDirSerie(true);
913          if( !AddSerieToEnd(static_cast<DicomDirSerie *>(si)) )
914          {
915             delete si;
916             gdcmErrorMacro( "Add SerieToEnd failed");
917          }
918          break;
919       case GDCM_DICOMDIR_STUDY:
920          elemList = Global::GetDicomDirElements()->GetDicomDirStudyElements();
921          si = new DicomDirStudy(true);
922          if( !AddStudyToEnd(static_cast<DicomDirStudy *>(si)) )
923          {
924             delete si;
925             gdcmErrorMacro( "Add StudyToEnd failed");
926          }
927          break;
928       case GDCM_DICOMDIR_PATIENT:
929          elemList = Global::GetDicomDirElements()->GetDicomDirPatientElements();
930          si = new DicomDirPatient(true);
931          if( !AddPatientToEnd(static_cast<DicomDirPatient *>(si)) )
932          {
933             delete si;
934             gdcmErrorMacro( "Add PatientToEnd failed");
935          }
936          break;
937       case GDCM_DICOMDIR_META:
938          elemList = Global::GetDicomDirElements()->GetDicomDirMetaElements();
939          si = new DicomDirMeta(true);
940          if( MetaElems )
941          {
942             delete MetaElems;
943             gdcmErrorMacro( "MetaElements already exist, they will be destroyed");
944          }
945          MetaElems = static_cast<DicomDirMeta *>(si);
946          break;
947       default:
948          return;
949    }
950    // removed all the seems-to-be-useless stuff about Referenced Image Sequence
951    // to avoid further troubles
952    // imageElem 0008 1140 "" // Referenced Image Sequence
953    // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
954    // imageElem 0008 1150 "" // Referenced SOP Class UID    : to be set/forged later
955    // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
956    // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
957  
958    // FIXME : troubles found when it's a SeqEntry
959
960    // for all the relevant elements found in their own spot of the DicomDir.dic
961    for( it = elemList.begin(); it != elemList.end(); ++it)
962    {
963       tmpGr     = it->Group;
964       tmpEl     = it->Elem;
965       dictEntry = GetPubDict()->GetEntry(tmpGr, tmpEl);
966
967       entry     = new ValEntry( dictEntry ); // Be sure it's never a BinEntry !
968
969       entry->SetOffset(0); // just to avoid further missprinting
970
971       if( header )
972       {
973          // NULL when we Build Up (ex nihilo) a DICOMDIR
974          //   or when we add the META elems
975          val = header->GetEntryValue(tmpGr, tmpEl);
976       }
977       else
978       {
979          val = GDCM_UNFOUND;
980       }
981
982       if( val == GDCM_UNFOUND) 
983       {
984          if( tmpGr == 0x0004 && tmpEl == 0x1130 ) // File-set ID
985          {
986            // force to the *end* File Name
987            val = Util::GetName( path );
988          }
989          else if( tmpGr == 0x0004 && tmpEl == 0x1500 ) // Only used for image
990          {
991             if( header->GetFileName().substr(0, path.length()) != path )
992             {
993                gdcmWarningMacro( "The base path of file name is incorrect");
994                val = header->GetFileName();
995             }
996             else
997             {
998                val = &(header->GetFileName().c_str()[path.length()]);
999             }
1000          }
1001          else
1002          {
1003             val = it->Value;
1004          }
1005       }
1006       else
1007       {
1008          if ( header->GetEntryLength(tmpGr,tmpEl) == 0 )
1009             val = it->Value;
1010       }
1011
1012       entry->SetValue( val ); // troubles expected when vr=SQ ...
1013
1014       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
1015       {
1016          gdcmWarningMacro("GDCM_DICOMDIR_META ?!? should never print that");
1017       }
1018       si->AddEntry(entry);
1019    }
1020 }
1021
1022 /**
1023  * \brief   Move the content of the source SQItem to the destination SQItem
1024  *          Only DocEntry's are moved
1025  * @param dst destination SQItem
1026  * @param src source SQItem
1027  */
1028 void DicomDir::MoveSQItem(DocEntrySet *dst,DocEntrySet *src)
1029 {
1030    DocEntry *entry;
1031
1032    entry = src->GetFirstEntry();
1033    while(entry)
1034    {
1035       src->RemoveEntryNoDestroy(entry);
1036       dst->AddEntry(entry);
1037       // we destroyed -> the current iterator is not longer valid
1038       entry = src->GetFirstEntry();
1039    }
1040 }
1041
1042 /**
1043  * \brief   compares two files
1044  */
1045 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
1046 {
1047    return *header1 < *header2;
1048 }
1049
1050 //-----------------------------------------------------------------------------
1051 // Print
1052 /**
1053  * \brief  Canonical Printer 
1054  * @param   os ostream we want to print in
1055  * @param indent Indentation string to be prepended during printing
1056  */
1057 void DicomDir::Print(std::ostream &os, std::string const & )
1058 {
1059    if( MetaElems )
1060    {
1061       MetaElems->SetPrintLevel(PrintLevel);
1062       MetaElems->Print(os);   
1063    }   
1064    for(ListDicomDirPatient::iterator cc  = Patients.begin();
1065                                      cc != Patients.end();
1066                                    ++cc)
1067    {
1068      (*cc)->SetPrintLevel(PrintLevel);
1069      (*cc)->Print(os);
1070    }
1071 }
1072
1073 //-----------------------------------------------------------------------------
1074 } // end namespace gdcm