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