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