]> Creatis software - gdcm.git/blob - src/gdcmDicomDir.cxx
To avoid warnings
[gdcm.git] / src / gdcmDicomDir.cxx
1 /*=========================================================================
2   
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDicomDir.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/11/07 10:14:44 $
7   Version:   $Revision: 1.172 $
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    SetStartMethod(NULL,NULL,NULL);
133    SetProgressMethod(NULL,NULL,NULL);
134    SetEndMethod(NULL,NULL,NULL);
135
136    ClearPatient();
137    if ( MetaElems )
138    {
139       MetaElems->Delete();
140    }
141 }
142
143 //-----------------------------------------------------------------------------
144 // Public
145
146 /**
147  * \brief   Loader. use SetFileName(fn) 
148  *                  or SetLoadMode(lm) + SetDirectoryName(dn)  before !  
149  * @return false if file cannot be open or no swap info was found,
150  *         or no tag was found.
151  */
152 bool DicomDir::Load( ) 
153 {
154    if (!ParseDir)
155    {
156       if ( ! this->Document::Load( ) )
157          return false;
158    }
159    return DoTheLoadingJob( );   
160 }
161
162 #ifndef GDCM_LEGACY_REMOVE
163 /* *
164  * \ brief   Loader. (DEPRECATED : kept not to break the API)
165  * @ param   fileName file to be open for parsing
166  * @ return false if file cannot be open or no swap info was found,
167  *         or no tag was found.
168  * @ deprecated use SetFileName(n) + Load() instead
169  */
170 bool DicomDir::Load(std::string const &fileName ) 
171 {
172    SetFileName(fileName);
173    return Load();
174 }
175
176 /// DEPRECATED : use SetDirectoryName(dname) instead
177 /* *
178  * \brief   Loader. (DEPRECATED : kept not to break the API)
179  * @param   paseDir Parse Dir
180  * @deprecated use SetDirectoryName(dname) instead
181  */
182 void DicomDir::SetParseDir(bool parseDir)
183 {
184    ParseDir = parseDir;
185 }
186 #endif
187
188 /**
189  * \brief   Does the Loading Job (internal use only)
190  * @return false if file cannot be open or no swap info was found,
191  *         or no tag was found.
192  */
193 bool DicomDir::DoTheLoadingJob( ) 
194 {
195    Progress = 0.0f;
196    Abort = false;
197
198    if (!ParseDir)
199    {
200    // Only if user passed a DICOMDIR
201    // ------------------------------
202       Fp = 0;
203       if (!Document::Load() )
204       {
205          return false;
206       }
207
208       if ( GetFirstEntry() == 0 ) // when user passed a Directory to parse
209       {
210          gdcmWarningMacro( "Entry HT empty for file: "<< GetFileName());
211          return false;
212       }
213       // Directory record sequence
214       DocEntry *e = GetDocEntry(0x0004, 0x1220);
215       if ( !e )
216       {
217          gdcmWarningMacro( "NO 'Directory record sequence' (0x0004,0x1220)"
218                           << " in file " << GetFileName());
219          return false;
220       }
221       else
222          CreateDicomDir();
223    }
224    else
225    {
226    // Only if user passed a root directory
227    // ------------------------------------
228       if ( GetFileName() == "." )
229       {
230          // user passed '.' as Name
231          // we get current directory name
232          char buf[2048];
233          const char *cwd = getcwd(buf, 2048);
234          if( cwd )
235            {
236            SetFileName( buf ); // will be converted into a string
237            }
238          else
239            {
240            gdcmErrorMacro( "Path was too long to fit on 2048 bytes" );
241            }
242       }
243       NewMeta();
244       gdcmDebugMacro( "Parse directory and create the DicomDir : " 
245                          << GetFileName() );
246       ParseDirectory();
247    }
248    return true;
249 }
250
251 /**
252  * \brief  This predicate, based on hopefully reasonable heuristics,
253  *         decides whether or not the current document was properly parsed
254  *         and contains the mandatory information for being considered as
255  *         a well formed and usable DicomDir.
256  * @return true when Document is the one of a reasonable DicomDir,
257  *         false otherwise. 
258  */
259 bool DicomDir::IsReadable()
260 {
261    if ( Filetype == Unknown )
262    {
263       gdcmErrorMacro( "Wrong filetype for " << GetFileName());
264       return false;
265    }
266    if ( !MetaElems )
267    {
268       gdcmWarningMacro( "Meta Elements missing in DicomDir");
269       return false;
270    }
271    if ( Patients.size() <= 0 )
272    {
273       gdcmWarningMacro( "NO Patient in DicomDir");
274       return false;
275    }
276
277    return true;
278 }
279
280 /**
281  * \brief   adds *the* Meta to a partially created DICOMDIR
282  */  
283 DicomDirMeta *DicomDir::NewMeta()
284 {
285    if ( MetaElems )
286       MetaElems->Delete();
287
288    DocEntry *entry = GetFirstEntry();
289    if ( entry )
290    { 
291       MetaElems = DicomDirMeta::New(true); // true = empty
292
293       entry = GetFirstEntry();
294       while( entry )
295       {
296          if ( dynamic_cast<SeqEntry *>(entry) )
297             break;
298
299          MetaElems->AddEntry(entry);
300          RemoveEntry(entry);
301
302          entry = GetFirstEntry();
303       }
304    }
305    else  // after root directory parsing
306    {
307       MetaElems = DicomDirMeta::New(false); // false = not empty
308    }
309    MetaElems->SetSQItemNumber(0); // To avoid further missprinting
310    return MetaElems;  
311 }
312
313 /**
314  * \brief   adds a new Patient (with the basic elements) to a partially created
315  *          DICOMDIR
316  */
317 DicomDirPatient *DicomDir::NewPatient()
318 {
319    DicomDirPatient *dd = DicomDirPatient::New();
320    AddPatientToEnd( dd );
321    return dd;
322 }
323
324 /**
325  * \brief   Remove all Patients
326  */
327 void DicomDir::ClearPatient()
328 {
329    for(ListDicomDirPatient::iterator cc = Patients.begin();
330                                      cc!= Patients.end();
331                                    ++cc)
332    {
333       (*cc)->Unregister();
334    }
335    Patients.clear();
336 }
337
338 /**
339  * \brief   Get the first entry while visiting the DicomDirPatients
340  * \return  The first DicomDirPatient if found, otherwhise NULL
341  */ 
342 DicomDirPatient *DicomDir::GetFirstPatient()
343 {
344    ItPatient = Patients.begin();
345    if ( ItPatient != Patients.end() )
346       return *ItPatient;
347    return NULL;
348 }
349
350 /**
351  * \brief   Get the next entry while visiting the DicomDirPatients
352  * \note : meaningfull only if GetFirstEntry already called
353  * \return  The next DicomDirPatient if found, otherwhise NULL
354  */
355 DicomDirPatient *DicomDir::GetNextPatient()
356 {
357    gdcmAssertMacro (ItPatient != Patients.end());
358
359    ++ItPatient;
360    if ( ItPatient != Patients.end() )
361       return *ItPatient;
362    return NULL;
363 }
364
365 /**
366  * \brief  fills the whole structure, starting from a root Directory
367  */
368 void DicomDir::ParseDirectory()
369 {
370    CreateDicomDirChainedList( GetFileName() );
371    CreateDicomDir();
372 }
373
374 void DicomDir::SetStartMethod( DicomDir::Method *method, void *arg )
375 {
376    SetStartMethod(method,arg,NULL);
377 }
378
379 void DicomDir::SetProgressMethod( DicomDir::Method *method, void *arg )
380 {
381    SetProgressMethod(method,arg,NULL);
382 }
383
384 void DicomDir::SetEndMethod( DicomDir::Method *method, void *arg )
385 {
386    SetEndMethod(method,arg,NULL);
387 }
388
389 /**
390  * \brief   Set the start method to call when the parsing of the
391  *          directory starts.
392  * @param   method Method to call
393  * @param   arg    Argument to pass to the method
394  * @param   argDelete    Argument 
395  * \warning In python : the arg parameter isn't considered
396  */
397 void DicomDir::SetStartMethod( DicomDir::Method *method, void *arg, 
398                                DicomDir::Method *argDelete )
399 {
400    if ( StartArg && StartMethodArgDelete )
401    {
402       StartMethodArgDelete( StartArg );
403    }
404
405    StartMethod          = method;
406    StartArg             = arg;
407    StartMethodArgDelete = argDelete;
408 }
409
410
411 /**
412  * \brief   Set the progress method to call when the parsing of the
413  *          directory progress
414  * @param   method Method to call
415  * @param   arg    Argument to pass to the method
416  * @param   argDelete    Argument  
417  * \warning In python : the arg parameter isn't considered
418  */
419 void DicomDir::SetProgressMethod( DicomDir::Method *method, void *arg, 
420                                   DicomDir::Method *argDelete )
421 {
422    if ( ProgressArg && ProgressMethodArgDelete )
423    {
424       ProgressMethodArgDelete( ProgressArg );
425    }
426
427    ProgressMethod          = method;
428    ProgressArg             = arg;
429    ProgressMethodArgDelete = argDelete;
430 }
431
432 /**
433  * \brief   Set the end method to call when the parsing of the directory ends
434  * @param   method Method to call
435  * @param   arg    Argument to pass to the method
436  * @param   argDelete    Argument 
437  * \warning In python : the arg parameter isn't considered
438  */
439 void DicomDir::SetEndMethod( DicomDir::Method *method, void *arg, 
440                              DicomDir::Method *argDelete )
441 {
442    if ( EndArg && EndMethodArgDelete )
443    {
444       EndMethodArgDelete( EndArg );
445    }
446
447    EndMethod          = method;
448    EndArg             = arg;
449    EndMethodArgDelete = argDelete;
450 }
451
452 /**
453  * \brief   Set the method to delete the argument
454  *          The argument is destroyed when the method is changed or when the
455  *          class is destroyed
456  * @param   method Method to call to delete the argument
457  */
458 void DicomDir::SetStartMethodArgDelete( DicomDir::Method *method ) 
459 {
460    StartMethodArgDelete = method;
461 }
462
463 /**
464  * \brief   Set the method to delete the argument
465  *          The argument is destroyed when the method is changed or when the 
466  *          class is destroyed          
467  * @param   method Method to call to delete the argument
468  */
469 void DicomDir::SetProgressMethodArgDelete( DicomDir::Method *method )
470 {
471    ProgressMethodArgDelete = method;
472 }
473
474 /**
475  * \brief   Set the method to delete the argument
476  *          The argument is destroyed when the method is changed or when
477  *          the class is destroyed
478  * @param   method Method to call to delete the argument
479  */
480 void DicomDir::SetEndMethodArgDelete( DicomDir::Method *method )
481 {
482    EndMethodArgDelete = method;
483 }
484
485 /**
486  * \brief    writes on disc a DICOMDIR
487  * \ warning does NOT add the missing elements in the header :
488  *           it's up to the user doing it !
489  * @param  fileName file to be written to 
490  * @return false only when fail to open
491  */
492  
493 bool DicomDir::Write(std::string const &fileName) 
494 {  
495    int i;
496    uint16_t sq[6] = { 0x0004, 0x1220, 0x5153, 0x0000, 0xffff, 0xffff };
497    uint16_t sqt[4]= { 0xfffe, 0xe0dd, 0xffff, 0xffff };
498
499    std::ofstream *fp = new std::ofstream(fileName.c_str(),  
500                                          std::ios::out | std::ios::binary);
501    if ( !fp ) 
502    {
503       gdcmWarningMacro("Failed to open(write) File: " << fileName.c_str());
504       return false;
505    }
506
507    char filePreamble[128];
508    memset(filePreamble, 0, 128);
509    fp->write(filePreamble, 128);
510    binary_write( *fp, "DICM");
511  
512    DicomDirMeta *ptrMeta = GetMeta();
513    ptrMeta->WriteContent(fp, ExplicitVR);
514    
515    // force writing 0004|1220 [SQ ], that CANNOT exist within DicomDirMeta
516    for(i=0;i<6;++i)
517    {
518       binary_write(*fp, sq[i]);
519    }
520         
521    for(ListDicomDirPatient::iterator cc  = Patients.begin();
522                                      cc != Patients.end();
523                                    ++cc )
524    {
525       (*cc)->WriteContent( fp, ExplicitVR );
526    }
527    
528    // force writing Sequence Delimitation Item
529    for(i=0;i<4;++i)
530    {
531       binary_write(*fp, sqt[i]);  // fffe e0dd ffff ffff 
532    }
533
534    fp->close();
535    delete fp;
536
537    return true;
538 }
539
540 /**
541  * \brief    Anonymize a DICOMDIR
542  * @return true 
543  */
544  
545 bool DicomDir::Anonymize() 
546 {
547    DataEntry *v;
548    // Something clever to be found to forge the Patient names
549    std::ostringstream s;
550    int i = 1;
551    for(ListDicomDirPatient::iterator cc = Patients.begin();
552                                      cc!= Patients.end();
553                                    ++cc)
554    {
555       s << i;
556       v = (*cc)->GetDataEntry(0x0010, 0x0010) ; // Patient's Name
557       if (v)
558       {
559          v->SetString(s.str());
560       }
561
562       v = (*cc)->GetDataEntry(0x0010, 0x0020) ; // Patient ID
563       if (v)
564       {
565          v->SetString(" ");
566       }
567
568       v = (*cc)->GetDataEntry(0x0010, 0x0030) ; // Patient's BirthDate
569       if (v)
570       {
571          v->SetString(" ");
572       }
573       s << "";
574       i++;
575    }
576    return true;
577 }
578
579 //-----------------------------------------------------------------------------
580 // Protected
581 /**
582  * \brief create a Document-like chained list from a root Directory 
583  * @param path entry point of the tree-like structure
584  */
585 void DicomDir::CreateDicomDirChainedList(std::string const &path)
586 {
587    CallStartMethod();
588    DirList dirList(path,1); // gets recursively the file list
589    unsigned int count = 0;
590    VectDocument list;
591    File *f;
592
593    DirListType fileList = dirList.GetFilenames();
594
595    for( DirListType::iterator it  = fileList.begin();
596                               it != fileList.end();
597                               ++it )
598    {
599       Progress = (float)(count+1)/(float)fileList.size();
600       CallProgressMethod();
601       if ( Abort )
602       {
603          break;
604       }
605
606       f = File::New( );
607       f->SetLoadMode(LoadMode); // we allow user not to load Sequences, 
608                                 //        or Shadow groups, or ......
609       f->SetFileName( it->c_str() );
610       f->Load( );
611
612       if ( f->IsReadable() )
613       {
614          // Add the file to the chained list:
615          list.push_back(f);
616          gdcmDebugMacro( "Readable " << it->c_str() );
617        }
618        else
619        {
620           f->Delete();
621        }
622        count++;
623    }
624    // sorts Patient/Study/Serie/
625    std::sort(list.begin(), list.end(), DicomDir::HeaderLessThan );
626    
627    std::string tmp = dirList.GetDirName();      
628    //for each File of the chained list, add/update the Patient/Study/Serie/Image info
629    SetElements(tmp, list);
630    CallEndMethod();
631
632    for(VectDocument::iterator itDoc=list.begin();
633        itDoc!=list.end();
634        ++itDoc)
635    {
636       dynamic_cast<File *>(*itDoc)->Delete();
637    }
638 }
639
640 /**
641  * \brief   CallStartMethod
642  */
643 void DicomDir::CallStartMethod()
644 {
645    Progress = 0.0f;
646    Abort    = false;
647    if ( StartMethod )
648    {
649       StartMethod( StartArg );
650    }
651 }
652
653 /**
654  * \brief   CallProgressMethod
655  */
656 void DicomDir::CallProgressMethod()
657 {
658    if ( ProgressMethod )
659    {
660       ProgressMethod( ProgressArg );
661    }
662 }
663
664 /**
665  * \brief   CallEndMethod
666  */
667 void DicomDir::CallEndMethod()
668 {
669    Progress = 1.0f;
670    if ( EndMethod )
671    {
672       EndMethod( EndArg );
673    }
674 }
675
676 //-----------------------------------------------------------------------------
677 // Private
678 /**
679  * \brief Sets all fields to NULL
680  */
681 void DicomDir::Initialize()
682 {
683    StartMethod             = NULL;
684    ProgressMethod          = NULL;
685    EndMethod               = NULL;
686    StartMethodArgDelete    = NULL;
687    ProgressMethodArgDelete = NULL;
688    EndMethodArgDelete      = NULL;
689    StartArg                = NULL;
690    ProgressArg             = NULL;
691    EndArg                  = NULL;
692
693    Progress = 0.0;
694    Abort = false;
695
696    MetaElems = NULL;   
697 }
698
699 /**
700  * \brief create a 'gdcm::DicomDir' from a DICOMDIR Header 
701  */
702 void DicomDir::CreateDicomDir()
703 {
704    // The SeqEntries of "Directory Record Sequence" are parsed. 
705    //  When a DicomDir tag ("PATIENT", "STUDY", "SERIE", "IMAGE") is found :
706    //  1 - we save the beginning iterator
707    //  2 - we continue to parse
708    //  3 - we find an other tag
709    //       + we create the object for the precedent tag
710    //       + loop to 1 -
711    gdcmDebugMacro("Create DicomDir");
712
713    // Directory record sequence
714    DocEntry *e = GetDocEntry(0x0004, 0x1220);
715    if ( !e )
716    {
717       gdcmWarningMacro( "No Directory Record Sequence (0004,1220) found");
718       return;         
719    }
720    
721    SeqEntry *s = dynamic_cast<SeqEntry *>(e);
722    if ( !s )
723    {
724       gdcmWarningMacro( "Element (0004,1220) is not a Sequence ?!?");
725       return;
726    }
727
728    NewMeta();
729    
730    DocEntry *d;
731    std::string v;
732    SQItem *si;
733
734    SQItem *tmpSI=s->GetFirstSQItem();
735    while(tmpSI)
736    {
737       d = tmpSI->GetDocEntry(0x0004, 0x1430); // Directory Record Type
738       if ( DataEntry *dataEntry = dynamic_cast<DataEntry *>(d) )
739       {
740          v = dataEntry->GetString();
741       }
742       else
743       {
744          gdcmWarningMacro( "(0004,1430) not a DataEntry ?!?");
745          continue;
746       }
747
748       // A decent DICOMDIR has much more images than series,
749       // more series than studies, and so on.
750       // This is the right order to preform the tests
751
752       if ( v == "IMAGE " ) 
753       {
754          si = DicomDirImage::New(true);
755          if ( !AddImageToEnd( static_cast<DicomDirImage *>(si)) )
756          {
757             si->Delete();
758             si = NULL;
759             gdcmErrorMacro( "Add AddImageToEnd failed");
760          }
761       }
762       else if ( v == "SERIES" )
763       {
764          si = DicomDirSerie::New(true);
765          if ( !AddSerieToEnd( static_cast<DicomDirSerie *>(si)) )
766          {
767             si->Delete();
768             si = NULL;
769             gdcmErrorMacro( "Add AddSerieToEnd failed");
770          }
771       }
772       else if ( v == "VISIT " )
773       {
774          si = DicomDirVisit::New(true);
775          if ( !AddVisitToEnd( static_cast<DicomDirVisit *>(si)) )
776          {
777             si->Delete();
778             si = NULL;
779             gdcmErrorMacro( "Add AddVisitToEnd failed");
780          }
781       }
782       else if ( v == "STUDY " )
783       {
784          si = DicomDirStudy::New(true);
785          if ( !AddStudyToEnd( static_cast<DicomDirStudy *>(si)) )
786          {
787             si->Delete();
788             si = NULL;
789             gdcmErrorMacro( "Add AddStudyToEnd failed");
790          }
791       }
792       else if ( v == "PATIENT " )
793       {
794          si = DicomDirPatient::New(true);
795          if ( !AddPatientToEnd( static_cast<DicomDirPatient *>(si)) )
796          {
797             si->Delete();
798             si = NULL;
799             gdcmErrorMacro( "Add PatientToEnd failed");
800          }
801       }
802       else
803       {
804          // It was neither a 'PATIENT', nor a 'STUDY', nor a 'SERIE',
805          // nor an 'IMAGE' SQItem. Skip to next item.
806          gdcmDebugMacro( " -------------------------------------------"
807          << "a non PATIENT/STUDY/SERIE/IMAGE SQItem was found : "
808          << v);
809
810         // FIXME : deal with other item types !
811         tmpSI=s->GetNextSQItem(); // To avoid infinite loop
812         continue;
813       }
814       if ( si )
815          si->MoveObject(tmpSI);  // New code : Copies the List
816
817       tmpSI=s->GetNextSQItem();
818    }
819    ClearEntry();
820 }
821
822 /**
823  * \brief  AddPatientToEnd 
824  * @param   dd SQ Item to enqueue to the DicomPatient chained List
825  */
826 bool DicomDir::AddPatientToEnd(DicomDirPatient *dd)
827 {
828    Patients.push_back(dd);
829    return true;
830 }
831
832 /**
833  * \brief  AddStudyToEnd 
834  * @param   dd SQ Item to enqueue to the DicomDirStudy chained List
835  */
836 bool DicomDir::AddStudyToEnd(DicomDirStudy *dd)
837 {
838    if ( Patients.size() > 0 )
839    {
840       ListDicomDirPatient::iterator itp = Patients.end();
841       itp--;
842       (*itp)->AddStudy(dd);
843       return true;
844    }
845    return false;
846 }
847
848 /**
849  * \brief  AddSerieToEnd 
850  * @param   dd SQ Item to enqueue to the DicomDirSerie chained List
851  */
852 bool DicomDir::AddSerieToEnd(DicomDirSerie *dd)
853 {
854    if ( Patients.size() > 0 )
855    {
856       ListDicomDirPatient::iterator itp = Patients.end();
857       itp--;
858
859       DicomDirStudy *study = (*itp)->GetLastStudy();
860       if ( study )
861       {
862          study->AddSerie(dd);
863          return true;
864       }
865    }
866    return false;
867 }
868
869 /**
870  * \brief  AddVisitToEnd 
871  * @param   dd SQ Item to enqueue to the DicomDirVisit chained List
872  */
873 bool DicomDir::AddVisitToEnd(DicomDirVisit *dd)
874 {
875    if ( Patients.size() > 0 )
876    {
877       ListDicomDirPatient::iterator itp = Patients.end();
878       itp--;
879
880       DicomDirStudy *study = (*itp)->GetLastStudy();
881       if ( study )
882       {
883          study->AddVisit(dd);
884          return true;
885       }
886    }
887    return false;
888 }
889 /**
890  * \brief   AddImageToEnd
891  * @param   dd SQ Item to enqueue to the DicomDirImage chained List
892  */
893 bool DicomDir::AddImageToEnd(DicomDirImage *dd)
894 {
895    if ( Patients.size() > 0 )
896    {
897       ListDicomDirPatient::iterator itp = Patients.end();
898       itp--;
899
900       DicomDirStudy *study = (*itp)->GetLastStudy();
901       if ( study )
902       {
903          DicomDirSerie *serie = study->GetLastSerie();
904          if ( serie )
905          {
906             serie->AddImage(dd);
907             return true;
908          }
909       }
910    }
911    return false;
912 }
913
914 /**
915  * \brief  for each Header of the chained list, 
916  *         add/update the Patient/Study/Serie/Image info 
917  * @param   path path of the root directory
918  * @param   list chained list of Headers
919  */
920 void DicomDir::SetElements(std::string const &path, VectDocument const &list)
921 {
922    ClearEntry();
923    ClearPatient();
924
925    std::string patPrevName         = "", patPrevID  = "";
926    std::string studPrevInstanceUID = "", studPrevID = "";
927    std::string serPrevInstanceUID  = "", serPrevID  = "";
928
929    std::string patCurName,         patCurID;
930    std::string studCurInstanceUID, studCurID;
931    std::string serCurInstanceUID,  serCurID;
932
933    bool first = true;
934    for( VectDocument::const_iterator it = list.begin();
935                                      it != list.end(); 
936                                    ++it )
937    {
938       // get the current file characteristics
939       patCurName         = (*it)->GetEntryString(0x0010,0x0010);
940       patCurID           = (*it)->GetEntryString(0x0010,0x0011);
941       studCurInstanceUID = (*it)->GetEntryString(0x0020,0x000d);
942       studCurID          = (*it)->GetEntryString(0x0020,0x0010);
943       serCurInstanceUID  = (*it)->GetEntryString(0x0020,0x000e);
944       serCurID           = (*it)->GetEntryString(0x0020,0x0011);
945
946       if ( patCurName != patPrevName || patCurID != patPrevID || first )
947       {
948          SetElement(path, GDCM_DICOMDIR_PATIENT, *it);
949          first = true;
950       }
951
952       // if new Study, deal with 'STUDY' Elements   
953       if ( studCurInstanceUID != studPrevInstanceUID || studCurID != studPrevID 
954          || first )
955       {
956          SetElement(path, GDCM_DICOMDIR_STUDY, *it);
957          first = true;
958       }
959
960       // if new Serie, deal with 'SERIE' Elements   
961       if ( serCurInstanceUID != serPrevInstanceUID || serCurID != serPrevID
962          || first )
963       {
964          SetElement(path, GDCM_DICOMDIR_SERIE, *it);
965       }
966       
967       // Always Deal with 'IMAGE' Elements  
968       SetElement(path, GDCM_DICOMDIR_IMAGE, *it);
969
970       patPrevName         = patCurName;
971       patPrevID           = patCurID;
972       studPrevInstanceUID = studCurInstanceUID;
973       studPrevID          = studCurID;
974       serPrevInstanceUID  = serCurInstanceUID;
975       serPrevID           = serCurID;
976       first = false;
977    }
978 }
979
980 /**
981  * \brief   adds to the HTable 
982  *          the Entries (Dicom Elements) corresponding to the given type
983  * @param   path full path file name (only used when type = GDCM_DICOMDIR_IMAGE
984  * @param   type DicomDirObject type to create (GDCM_DICOMDIR_PATIENT,
985  *          GDCM_DICOMDIR_STUDY, GDCM_DICOMDIR_SERIE ...)
986  * @param   header Header of the current file
987  */
988 void DicomDir::SetElement(std::string const &path, DicomDirType type,
989                           Document *header)
990 {
991    ListDicomDirElem elemList;
992    ListDicomDirElem::const_iterator it;
993    uint16_t tmpGr, tmpEl;
994    DictEntry *dictEntry;
995    DataEntry *entry;
996    std::string val;
997    SQItem *si;
998
999    switch( type )
1000    {
1001       case GDCM_DICOMDIR_IMAGE:
1002          elemList = Global::GetDicomDirElements()->GetDicomDirImageElements();
1003          si = DicomDirImage::New(true);
1004          if ( !AddImageToEnd(static_cast<DicomDirImage *>(si)) )
1005          {
1006             si->Delete();
1007             gdcmErrorMacro( "Add ImageToEnd failed");
1008          }
1009          break;
1010       case GDCM_DICOMDIR_SERIE:
1011          elemList = Global::GetDicomDirElements()->GetDicomDirSerieElements();
1012          si = DicomDirSerie::New(true);
1013          if ( !AddSerieToEnd(static_cast<DicomDirSerie *>(si)) )
1014          {
1015             si->Delete();
1016             gdcmErrorMacro( "Add SerieToEnd failed");
1017          }
1018          break;
1019       case GDCM_DICOMDIR_STUDY:
1020          elemList = Global::GetDicomDirElements()->GetDicomDirStudyElements();
1021          si = DicomDirStudy::New(true);
1022          if ( !AddStudyToEnd(static_cast<DicomDirStudy *>(si)) )
1023          {
1024             si->Delete();
1025             gdcmErrorMacro( "Add StudyToEnd failed");
1026          }
1027          break;
1028       case GDCM_DICOMDIR_PATIENT:
1029          elemList = Global::GetDicomDirElements()->GetDicomDirPatientElements();
1030          si = DicomDirPatient::New(true);
1031          if ( !AddPatientToEnd(static_cast<DicomDirPatient *>(si)) )
1032          {
1033             si->Delete();
1034             gdcmErrorMacro( "Add PatientToEnd failed");
1035          }
1036          break;
1037       case GDCM_DICOMDIR_META:
1038          if ( MetaElems )
1039          {
1040             MetaElems->Delete();
1041             gdcmErrorMacro( "MetaElements already exist, they will be destroyed");
1042          }
1043          elemList = Global::GetDicomDirElements()->GetDicomDirMetaElements();
1044          MetaElems = DicomDirMeta::New(true);
1045          si = MetaElems;
1046          break;
1047       default:
1048          return;
1049    }
1050    
1051    // FIXME : troubles found when it's a SeqEntry
1052       
1053    // removed all the seems-to-be-useless stuff about Referenced Image Sequence
1054    // to avoid further troubles
1055    // imageElem 0008 1140 "" // Referenced Image Sequence
1056    // imageElem fffe e000 "" // 'no length' item : length to be set to 0xffffffff later
1057    // imageElem 0008 1150 "" // Referenced SOP Class UID    : to be set/forged later
1058    // imageElem 0008 1155 "" // Referenced SOP Instance UID : to be set/forged later
1059    // imageElem fffe e00d "" // Item delimitation : length to be set to ZERO later
1060  
1061    std::string referencedVal;
1062    // for all the relevant elements found in their own spot of the DicomDir.dic
1063    for( it = elemList.begin(); it != elemList.end(); ++it)
1064    {
1065       tmpGr     = it->Group;
1066       tmpEl     = it->Elem;
1067       dictEntry = GetPubDict()->GetEntry(tmpGr, tmpEl);
1068
1069       entry     = DataEntry::New( dictEntry ); 
1070       entry->SetOffset(0); // just to avoid further missprinting
1071
1072       if ( header )
1073       {
1074          // NULL when we Build Up (ex nihilo) a DICOMDIR
1075          //   or when we add the META elems
1076  
1077             val = header->GetEntryString(tmpGr, tmpEl); 
1078       }
1079       else
1080       {
1081          val = GDCM_UNFOUND;
1082       }
1083
1084       if ( val == GDCM_UNFOUND) 
1085       {       
1086          if ( tmpGr == 0x0004 ) // never present in File !     
1087          {
1088             switch (tmpEl)
1089            {
1090            case 0x1130: // File-set ID
1091               // force to the *end* File Name
1092               val = Util::GetName( path );
1093               break;
1094       
1095            case 0x1500: // Only used for image    
1096                if ( header->GetFileName().substr(0, path.length()) != path )
1097                {
1098                   gdcmWarningMacro( "The base path of file name is incorrect");
1099                   val = header->GetFileName();
1100                }
1101                else
1102                {
1103                   val = &(header->GetFileName().c_str()[path.length()+1]);
1104                }
1105        break;
1106     
1107        case 0x1510:  // Referenced SOP Class UID in File
1108           referencedVal = header->GetEntryString(0x0008, 0x0016);
1109           // FIXME : probabely something to check
1110           val = referencedVal;
1111           break;
1112        
1113        case 0x1511: // Referenced SOP Instance UID in File
1114           referencedVal = header->GetEntryString(0x0008, 0x0018);
1115           // FIXME : probabely something to check
1116           val = referencedVal;
1117           break;
1118     
1119        case 0x1512: // Referenced Transfer Syntax UID in File
1120           referencedVal = header->GetEntryString(0x0002, 0x0010);
1121           // FIXME : probabely something to check
1122           val = referencedVal;
1123           break;
1124     
1125        default :
1126          val = it->Value;   
1127     } 
1128          }
1129       }
1130       else
1131       {
1132          if ( header->GetEntryLength(tmpGr,tmpEl) == 0 )
1133             val = it->Value;
1134       }
1135
1136 /* FIX later the pb of creating the 'Implementation Version Name'!
1137
1138       if (val == GDCM_UNFOUND)
1139          val = "";
1140
1141       if ( tmpGr == 0x0002 && tmpEl == 0x0013)
1142       { 
1143          // 'Implementation Version Name'
1144          std::string val = "GDCM ";
1145          val += Util::GetVersion();
1146       }
1147 */ 
1148       entry->SetString( val ); // troubles expected when vr=SQ ...
1149
1150       if ( type == GDCM_DICOMDIR_META ) // fusible : should never print !
1151       {
1152          gdcmDebugMacro("GDCM_DICOMDIR_META ?!? should never print that");
1153       }
1154       si->AddEntry(entry);
1155       entry->Delete();
1156    }
1157 }
1158
1159 /**
1160  * \brief   Move the content of the source SQItem to the destination SQItem
1161  *          Only DocEntry's are moved
1162  * @param dst destination SQItem
1163  * @param src source SQItem
1164  */
1165 void DicomDir::MoveSQItem(DocEntrySet *dst,DocEntrySet *src)
1166
1167    DocEntry *entry;
1168
1169    entry = src->GetFirstEntry();
1170    while(entry)
1171    {
1172       src->RemoveEntry(entry);
1173       dst->AddEntry(entry);
1174       // we destroyed -> the current iterator is not longer valid
1175       entry = src->GetFirstEntry();
1176    }
1177 }
1178
1179 /**
1180  * \brief   compares two files
1181  */
1182 bool DicomDir::HeaderLessThan(Document *header1, Document *header2)
1183 {
1184    return *header1 < *header2;
1185 }
1186
1187 //-----------------------------------------------------------------------------
1188 // Print
1189 /**
1190  * \brief  Canonical Printer 
1191  * @param   os ostream we want to print in
1192  * @param indent Indentation string to be prepended during printing
1193  */
1194 void DicomDir::Print(std::ostream &os, std::string const & )
1195 {
1196    if ( MetaElems )
1197    {
1198       MetaElems->SetPrintLevel(PrintLevel);
1199       MetaElems->Print(os);   
1200    }   
1201    for(ListDicomDirPatient::iterator cc  = Patients.begin();
1202                                      cc != Patients.end();
1203                                    ++cc)
1204    {
1205      (*cc)->SetPrintLevel(PrintLevel);
1206      (*cc)->Print(os);
1207    }
1208 }
1209
1210 //-----------------------------------------------------------------------------
1211 } // end namespace gdcm