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