]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
* Add the Copy method in all datas
[gdcm.git] / src / gdcmDicomDir.cxx
1 /*=========================================================================
2   
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDicomDir.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/11/29 12:48:45 $
7   Version:   $Revision: 1.176 $
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 {
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  * \brief Copies all the attributes from an other DocEntrySet 
440  * @param set entry to copy from
441  * @remarks The contained DocEntries a not copied, only referenced
442  */
443 void DicomDir::Copy(DocEntrySet *set)
444 {
445    // Remove all previous childs
446    ClearPatient();
447
448    Document::Copy(set);
449
450    DicomDir *dd = dynamic_cast<DicomDir *>(set);
451    if( dd )
452    {
453       if(MetaElems)
454          MetaElems->Unregister();
455       MetaElems = dd->MetaElems;
456       if(MetaElems)
457          MetaElems->Register();
458
459       Patients = dd->Patients;
460       for(ItPatient = Patients.begin();ItPatient != Patients.end();++ItPatient)
461          (*ItPatient)->Register();
462    }
463 }
464
465 //-----------------------------------------------------------------------------
466 // Protected
467 /**
468  * \brief create a Document-like chained list from a root Directory 
469  * @param path entry point of the tree-like structure
470  */
471 void DicomDir::CreateDicomDirChainedList(std::string const &path)
472 {
473    CallStartMethod();
474    DirList dirList(path,1); // gets recursively the file list
475    unsigned int count = 0;
476    VectDocument list;
477    File *f;
478
479    DirListType fileList = dirList.GetFilenames();
480
481    for( DirListType::iterator it  = fileList.begin();
482                               it != fileList.end();
483                               ++it )
484    {
485       Progress = (float)(count+1)/(float)fileList.size();
486       CallProgressMethod();
487       if ( Abort )
488       {
489          break;
490       }
491
492       f = File::New( );
493       f->SetLoadMode(LoadMode); // we allow user not to load Sequences, 
494                                 //        or Shadow groups, or ......
495       f->SetFileName( it->c_str() );
496       f->Load( );
497
498       if ( f->IsReadable() )
499       {
500          // Add the file to the chained list:
501          list.push_back(f);
502          gdcmDebugMacro( "Readable " << it->c_str() );
503        }
504        else
505        {
506           f->Delete();
507        }
508        count++;
509    }
510    // sorts Patient/Study/Serie/
511    std::sort(list.begin(), list.end(), DicomDir::HeaderLessThan );
512    
513    std::string tmp = dirList.GetDirName();      
514    //for each File of the chained list, add/update the Patient/Study/Serie/Image info
515    SetElements(tmp, list);
516    CallEndMethod();
517
518    for(VectDocument::iterator itDoc=list.begin();
519        itDoc!=list.end();
520        ++itDoc)
521    {
522       dynamic_cast<File *>(*itDoc)->Delete();
523    }
524 }
525
526 /**
527  * \brief   CallStartMethod
528  */
529 void DicomDir::CallStartMethod()
530 {
531    Progress = 0.0f;
532    Abort    = false;
533    CommandManager::ExecuteCommand(this,CMD_STARTPROGRESS);
534 }
535
536 /**
537  * \brief   CallProgressMethod
538  */
539 void DicomDir::CallProgressMethod()
540 {
541    CommandManager::ExecuteCommand(this,CMD_PROGRESS);
542 }
543
544 /**
545  * \brief   CallEndMethod
546  */
547 void DicomDir::CallEndMethod()
548 {
549    Progress = 1.0f;
550    CommandManager::ExecuteCommand(this,CMD_ENDPROGRESS);
551 }
552
553 //-----------------------------------------------------------------------------
554 // Private
555 /**
556  * \brief Sets all fields to NULL
557  */
558 void DicomDir::Initialize()
559 {
560    Progress = 0.0;
561    Abort = false;
562
563    MetaElems = NULL;   
564 }
565
566 /**
567  * \brief create a 'gdcm::DicomDir' from a DICOMDIR Header 
568  */
569 void DicomDir::CreateDicomDir()
570 {
571    // The SeqEntries of "Directory Record Sequence" are parsed. 
572    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
573    //  1 - we save the beginning iterator
574    //  2 - we continue to parse
575    //  3 - we find an other tag
576    //       + we create the object for the precedent tag
577    //       + loop to 1 -
578    gdcmDebugMacro("Create DicomDir");
579
580    // Directory record sequence
581    DocEntry *e = GetDocEntry(0x0004, 0x1220);
582    if ( !e )
583    {
584       gdcmWarningMacro( "No Directory Record Sequence (0004,1220) found");
585       return;         
586    }
587    
588    SeqEntry *s = dynamic_cast<SeqEntry *>(e);
589    if ( !s )
590    {
591       gdcmWarningMacro( "Element (0004,1220) is not a Sequence ?!?");
592       return;
593    }
594
595    NewMeta();
596    
597    DocEntry *d;
598    std::string v;
599    SQItem *si;
600
601    SQItem *tmpSI=s->GetFirstSQItem();
602    while(tmpSI)
603    {
604       d = tmpSI->GetDocEntry(0x0004, 0x1430); // Directory Record Type
605       if ( DataEntry *dataEntry = dynamic_cast<DataEntry *>(d) )
606       {
607          v = dataEntry->GetString();
608       }
609       else
610       {
611          gdcmWarningMacro( "(0004,1430) not a DataEntry ?!?");
612          continue;
613       }
614
615       // A decent DICOMDIR has much more images than series,
616       // more series than studies, and so on.
617       // This is the right order to preform the tests
618
619       if ( v == "IMAGE " ) 
620       {
621          si = DicomDirImage::New(true);
622          if ( !AddImageToEnd( static_cast<DicomDirImage *>(si)) )
623          {
624             si->Delete();
625             si = NULL;
626             gdcmErrorMacro( "Add AddImageToEnd failed");
627          }
628       }
629       else if ( v == "SERIES" )
630       {
631          si = DicomDirSerie::New(true);
632          if ( !AddSerieToEnd( static_cast<DicomDirSerie *>(si)) )
633          {
634             si->Delete();
635             si = NULL;
636             gdcmErrorMacro( "Add AddSerieToEnd failed");
637          }
638       }
639       else if ( v == "VISIT " )
640       {
641          si = DicomDirVisit::New(true);
642          if ( !AddVisitToEnd( static_cast<DicomDirVisit *>(si)) )
643          {
644             si->Delete();
645             si = NULL;
646             gdcmErrorMacro( "Add AddVisitToEnd failed");
647          }
648       }
649       else if ( v == "STUDY " )
650       {
651          si = DicomDirStudy::New(true);
652          if ( !AddStudyToEnd( static_cast<DicomDirStudy *>(si)) )
653          {
654             si->Delete();
655             si = NULL;
656             gdcmErrorMacro( "Add AddStudyToEnd failed");
657          }
658       }
659       else if ( v == "PATIENT " )
660       {
661          si = DicomDirPatient::New(true);
662          if ( !AddPatientToEnd( static_cast<DicomDirPatient *>(si)) )
663          {
664             si->Delete();
665             si = NULL;
666             gdcmErrorMacro( "Add PatientToEnd failed");
667          }
668       }
669       else
670       {
671          // It was neither a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
672          // nor an 'IMAGE' SQItem. Skip to next item.
673          gdcmDebugMacro( " -------------------------------------------"
674          << "a non PATIENT/STUDY/SERIE/IMAGE SQItem was found : "
675          << v);
676
677         // FIXME : deal with other item types !
678         tmpSI=s->GetNextSQItem(); // To avoid infinite loop
679         continue;
680       }
681       if ( si )
682          si->Copy(tmpSI);
683
684       tmpSI=s->GetNextSQItem();
685    }
686    ClearEntry();
687 }
688
689 /**
690  * \brief  AddPatientToEnd 
691  * @param   dd SQ Item to enqueue to the DicomPatient chained List
692  */
693 bool DicomDir::AddPatientToEnd(DicomDirPatient *dd)
694 {
695    Patients.push_back(dd);
696    return true;
697 }
698
699 /**
700  * \brief  AddStudyToEnd 
701  * @param   dd SQ Item to enqueue to the DicomDirStudy chained List
702  */
703 bool DicomDir::AddStudyToEnd(DicomDirStudy *dd)
704 {
705    if ( Patients.size() > 0 )
706    {
707       ListDicomDirPatient::iterator itp = Patients.end();
708       itp--;
709       (*itp)->AddStudy(dd);
710       return true;
711    }
712    return false;
713 }
714
715 /**
716  * \brief  AddSerieToEnd 
717  * @param   dd SQ Item to enqueue to the DicomDirSerie chained List
718  */
719 bool DicomDir::AddSerieToEnd(DicomDirSerie *dd)
720 {
721    if ( Patients.size() > 0 )
722    {
723       ListDicomDirPatient::iterator itp = Patients.end();
724       itp--;
725
726       DicomDirStudy *study = (*itp)->GetLastStudy();
727       if ( study )
728       {
729          study->AddSerie(dd);
730          return true;
731       }
732    }
733    return false;
734 }
735
736 /**
737  * \brief  AddVisitToEnd 
738  * @param   dd SQ Item to enqueue to the DicomDirVisit chained List
739  */
740 bool DicomDir::AddVisitToEnd(DicomDirVisit *dd)
741 {
742    if ( Patients.size() > 0 )
743    {
744       ListDicomDirPatient::iterator itp = Patients.end();
745       itp--;
746
747       DicomDirStudy *study = (*itp)->GetLastStudy();
748       if ( study )
749       {
750          study->AddVisit(dd);
751          return true;
752       }
753    }
754    return false;
755 }
756 /**
757  * \brief   AddImageToEnd
758  * @param   dd SQ Item to enqueue to the DicomDirImage chained List
759  */
760 bool DicomDir::AddImageToEnd(DicomDirImage *dd)
761 {
762    if ( Patients.size() > 0 )
763    {
764       ListDicomDirPatient::iterator itp = Patients.end();
765       itp--;
766
767       DicomDirStudy *study = (*itp)->GetLastStudy();
768       if ( study )
769       {
770          DicomDirSerie *serie = study->GetLastSerie();
771          if ( serie )
772          {
773             serie->AddImage(dd);
774             return true;
775          }
776       }
777    }
778    return false;
779 }
780
781 /**
782  * \brief  for each Header of the chained list, 
783  *         add/update the Patient/Study/Serie/Image info 
784  * @param   path path of the root directory
785  * @param   list chained list of Headers
786  */
787 void DicomDir::SetElements(std::string const &path, VectDocument const &list)
788 {
789    ClearEntry();
790    ClearPatient();
791
792    std::string patPrevName         = "", patPrevID  = "";
793    std::string studPrevInstanceUID = "", studPrevID = "";
794    std::string serPrevInstanceUID  = "", serPrevID  = "";
795
796    std::string patCurName,         patCurID;
797    std::string studCurInstanceUID, studCurID;
798    std::string serCurInstanceUID,  serCurID;
799
800    bool first = true;
801    for( VectDocument::const_iterator it = list.begin();
802                                      it != list.end(); 
803                                    ++it )
804    {
805       // get the current file characteristics
806       patCurName         = (*it)->GetEntryString(0x0010,0x0010);
807       patCurID           = (*it)->GetEntryString(0x0010,0x0011);
808       studCurInstanceUID = (*it)->GetEntryString(0x0020,0x000d);
809       studCurID          = (*it)->GetEntryString(0x0020,0x0010);
810       serCurInstanceUID  = (*it)->GetEntryString(0x0020,0x000e);
811       serCurID           = (*it)->GetEntryString(0x0020,0x0011);
812
813       if ( patCurName != patPrevName || patCurID != patPrevID || first )
814       {
815          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
816          first = true;
817       }
818
819       // if new Study, deal with 'STUDY' Elements   
820       if ( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID 
821          || first )
822       {
823          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
824          first = true;
825       }
826
827       // if new Serie, deal with 'SERIE' Elements   
828       if ( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID
829          || first )
830       {
831          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
832       }
833       
834       // Always Deal with 'IMAGE' Elements  
835       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
836
837       patPrevName         = patCurName;
838       patPrevID           = patCurID;
839       studPrevInstanceUID = studCurInstanceUID;
840       studPrevID          = studCurID;
841       serPrevInstanceUID  = serCurInstanceUID;
842       serPrevID           = serCurID;
843       first = false;
844    }
845 }
846
847 /**
848  * \brief   adds to the HTable 
849  *          the Entries (Dicom Elements) corresponding to the given type
850  * @param   path full path file name (only used when type = GDCM_DICOMDIR_IMAGE
851  * @param   type DicomDirObject type to create (GDCM_DICOMDIR_PATIENT,
852  *          GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
853  * @param   header Header of the current file
854  */
855 void DicomDir::SetElement(std::string const &path, DicomDirType type,
856                           Document *header)
857 {
858    ListDicomDirElem elemList;
859    ListDicomDirElem::const_iterator it;
860    uint16_t tmpGr, tmpEl;
861    DictEntry *dictEntry;
862    DataEntry *entry;
863    std::string val;
864    SQItem *si;
865
866    switch( type )
867    {
868       case GDCM_DICOMDIR_IMAGE:
869          elemList = Global::GetDicomDirElements()->GetDicomDirImageElements();
870          si = DicomDirImage::New(true);
871          if ( !AddImageToEnd(static_cast<DicomDirImage *>(si)) )
872          {
873             si->Delete();
874             gdcmErrorMacro( "Add ImageToEnd failed");
875          }
876          break;
877       case GDCM_DICOMDIR_SERIE:
878          elemList = Global::GetDicomDirElements()->GetDicomDirSerieElements();
879          si = DicomDirSerie::New(true);
880          if ( !AddSerieToEnd(static_cast<DicomDirSerie *>(si)) )
881          {
882             si->Delete();
883             gdcmErrorMacro( "Add SerieToEnd failed");
884          }
885          break;
886       case GDCM_DICOMDIR_STUDY:
887          elemList = Global::GetDicomDirElements()->GetDicomDirStudyElements();
888          si = DicomDirStudy::New(true);
889          if ( !AddStudyToEnd(static_cast<DicomDirStudy *>(si)) )
890          {
891             si->Delete();
892             gdcmErrorMacro( "Add StudyToEnd failed");
893          }
894          break;
895       case GDCM_DICOMDIR_PATIENT:
896          elemList = Global::GetDicomDirElements()->GetDicomDirPatientElements();
897          si = DicomDirPatient::New(true);
898          if ( !AddPatientToEnd(static_cast<DicomDirPatient *>(si)) )
899          {
900             si->Delete();
901             gdcmErrorMacro( "Add PatientToEnd failed");
902          }
903          break;
904       case GDCM_DICOMDIR_META:
905          if ( MetaElems )
906          {
907             MetaElems->Delete();
908             gdcmErrorMacro( "MetaElements already exist, they will be destroyed");
909          }
910          elemList = Global::GetDicomDirElements()->GetDicomDirMetaElements();
911          MetaElems = DicomDirMeta::New(true);
912          si = MetaElems;
913          break;
914       default:
915          return;
916    }
917    
918    // FIXME : troubles found when it's a SeqEntry
919       
920    // removed all the seems-to-be-useless stuff about Referenced Image Sequence
921    // to avoid further troubles
922    // imageElem 0008 1140 "" // Referenced Image Sequence
923    // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
924    // imageElem 0008 1150 "" // Referenced SOP Class UID    : to be set/forged later
925    // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
926    // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
927  
928    std::string referencedVal;
929    // for all the relevant elements found in their own spot of the DicomDir.dic
930    for( it = elemList.begin(); it != elemList.end(); ++it)
931    {
932       tmpGr     = it->Group;
933       tmpEl     = it->Elem;
934       dictEntry = GetPubDict()->GetEntry(tmpGr, tmpEl);
935
936       entry     = DataEntry::New( dictEntry ); 
937       entry->SetOffset(0); // just to avoid further missprinting
938
939       if ( header )
940       {
941          // NULL when we Build Up (ex nihilo) a DICOMDIR
942          //   or when we add the META elems
943  
944             val = header->GetEntryString(tmpGr, tmpEl); 
945       }
946       else
947       {
948          val = GDCM_UNFOUND;
949       }
950
951       if ( val == GDCM_UNFOUND) 
952       {       
953          if ( tmpGr == 0x0004 ) // never present in File !     
954          {
955             switch (tmpEl)
956            {
957            case 0x1130: // File-set ID
958               // force to the *end* File Name
959               val = Util::GetName( path );
960               break;
961       
962            case 0x1500: // Only used for image    
963                if ( header->GetFileName().substr(0, path.length()) != path )
964                {
965                   gdcmWarningMacro( "The base path of file name is incorrect");
966                   val = header->GetFileName();
967                }
968                else
969                {
970                   val = &(header->GetFileName().c_str()[path.length()+1]);
971                }
972        break;
973     
974        case 0x1510:  // Referenced SOP Class UID in File
975           referencedVal = header->GetEntryString(0x0008, 0x0016);
976           // FIXME : probabely something to check
977           val = referencedVal;
978           break;
979        
980        case 0x1511: // Referenced SOP Instance UID in File
981           referencedVal = header->GetEntryString(0x0008, 0x0018);
982           // FIXME : probabely something to check
983           val = referencedVal;
984           break;
985     
986        case 0x1512: // Referenced Transfer Syntax UID in File
987           referencedVal = header->GetEntryString(0x0002, 0x0010);
988           // FIXME : probabely something to check
989           val = referencedVal;
990           break;
991     
992        default :
993          val = it->Value;   
994     } 
995          }
996       }
997       else
998       {
999          if ( header->GetEntryLength(tmpGr,tmpEl) == 0 )
1000             val = it->Value;
1001       }
1002
1003 /* FIX later the pb of creating the 'Implementation Version Name'!
1004
1005       if (val == GDCM_UNFOUND)
1006          val = "";
1007
1008       if ( tmpGr == 0x0002 && tmpEl == 0x0013)
1009       { 
1010          // 'Implementation Version Name'
1011          std::string val = "GDCM ";
1012          val += Util::GetVersion();
1013       }
1014 */ 
1015       entry->SetString( val ); // troubles expected when vr=SQ ...
1016
1017       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
1018       {
1019          gdcmDebugMacro("GDCM_DICOMDIR_META ?!? should never print that");
1020       }
1021       si->AddEntry(entry);
1022       entry->Delete();
1023    }
1024 }
1025
1026 /**
1027  * \brief   Move the content of the source SQItem to the destination SQItem
1028  *          Only DocEntry's are moved
1029  * @param dst destination SQItem
1030  * @param src source SQItem
1031  */
1032 void DicomDir::MoveSQItem(DocEntrySet *dst,DocEntrySet *src)
1033
1034    DocEntry *entry;
1035
1036    entry = src->GetFirstEntry();
1037    while(entry)
1038    {
1039       src->RemoveEntry(entry);
1040       dst->AddEntry(entry);
1041       // we destroyed -> the current iterator is not longer valid
1042       entry = src->GetFirstEntry();
1043    }
1044 }
1045
1046 /**
1047  * \brief   compares two files
1048  */
1049 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
1050 {
1051    return *header1 < *header2;
1052 }
1053
1054 //-----------------------------------------------------------------------------
1055 // Print
1056 /**
1057  * \brief  Canonical Printer 
1058  * @param   os ostream we want to print in
1059  * @param indent Indentation string to be prepended during printing
1060  */
1061 void DicomDir::Print(std::ostream &os, std::string const & )
1062 {
1063    if ( MetaElems )
1064    {
1065       MetaElems->SetPrintLevel(PrintLevel);
1066       MetaElems->Print(os);   
1067    }   
1068    for(ListDicomDirPatient::iterator cc  = Patients.begin();
1069                                      cc != Patients.end();
1070                                    ++cc)
1071    {
1072      (*cc)->SetPrintLevel(PrintLevel);
1073      (*cc)->Print(os);
1074    }
1075 }
1076
1077 //-----------------------------------------------------------------------------
1078 } // end namespace gdcm