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