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