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