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