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