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