]> Creatis software - gdcm.git/blob - src/gdcmDocEntrySet.cxx
Add the GetEntryForcedAsciiValue method to deal with Private Implicit VR Tags,
[gdcm.git] / src / gdcmDocEntrySet.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDocEntrySet.cxx,v $
5   Language:  C++
6   Date:      $Date: 2005/09/06 15:28:49 $
7   Version:   $Revision: 1.59 $
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 #include "gdcmDocEntrySet.h"
19
20 #include "gdcmDebug.h"
21 #include "gdcmCommon.h"
22 #include "gdcmDictSet.h"
23 #include "gdcmGlobal.h"
24 #include "gdcmDocEntry.h"
25 #include "gdcmSeqEntry.h"
26 #include "gdcmValEntry.h"
27 #include "gdcmBinEntry.h"
28 #include "gdcmUtil.h"
29
30 namespace gdcm 
31 {
32 //-----------------------------------------------------------------------------
33 // Constructor / Destructor
34 DocEntrySet::DocEntrySet() 
35
36    PreviousDocEntry = 0;
37 }
38 //-----------------------------------------------------------------------------
39 // Public
40 /**
41  * \brief   Get the "std::string representable" value of the Dicom entry
42  * @param   group  Group number of the searched tag.
43  * @param   elem Element number of the searched tag.
44  * @return  Corresponding element value when it exists,
45  *          and the string GDCM_UNFOUND otherwise.
46  */
47 std::string DocEntrySet::GetEntryValue(uint16_t group, uint16_t elem)
48 {
49    ContentEntry *entry = dynamic_cast<ContentEntry *>(GetDocEntry(group,elem));
50    if ( entry )
51       return entry->GetValue();
52    return GDCM_UNFOUND;
53 }
54
55 /**
56  * \brief   Gets (from Header) a 'non string' element value 
57  * @param group   group number of the Entry 
58  * @param elem  element number of the Entry
59  * @return Pointer to the 'non string' area
60  */
61 void *DocEntrySet::GetEntryBinArea(uint16_t group, uint16_t elem) 
62 {
63    BinEntry *entry = GetBinEntry(group, elem);
64    if ( entry )
65       return entry->GetBinArea();
66    return 0;
67 }
68
69 /**
70  * \brief   Return the value of the BinEntry if it's "std::string representable"
71  * @param   group  Group number of the searched tag.
72  * @param   elem Element number of the searched tag.
73  * @return  Corresponding element value when it's "std::string representable"
74  *          and the string GDCM_NOTASCII otherwise.
75  */
76 std::string DocEntrySet::GetEntryForcedAsciiValue(uint16_t group, uint16_t elem)
77 {
78    DocEntry *d = GetDocEntry(group,elem);
79    if ( !d )
80       return GDCM_UNFOUND;
81
82    if (ValEntry *v = dynamic_cast<ValEntry *>(d))
83       return v->GetValue();
84
85    if (BinEntry *b = dynamic_cast<BinEntry *>(d))
86    {
87       uint8_t *a = b->GetBinArea();
88       if (!b)
89          return GDCM_NOTLOADED;
90          // TODO : unify those two methods.
91       if (Util::IsCleanArea(a, b->GetLength()) )
92          return  Util::CreateCleanString(a, b->GetLength());
93    }
94    return GDCM_NOTASCII;
95 }
96
97 /**
98  * \brief   Searches within Header Entries (Dicom Elements) parsed with 
99  *          the public and private dictionaries 
100  *          for the value length of a given tag..
101  * @param   group  Group number of the searched tag.
102  * @param   elem Element number of the searched tag.
103  * @return  Corresponding element length; -2 if not found
104  */
105 int DocEntrySet::GetEntryLength(uint16_t group, uint16_t elem)
106 {
107    DocEntry *entry = GetDocEntry(group, elem);
108    if ( entry )
109       return entry->GetLength();
110    return -1;
111 }
112
113 /**
114  * \brief   Searches within Header Entries (Dicom Elements) parsed with 
115  *          the public [and private dictionaries] 
116  *          for the element value representation of a given tag..
117  *          Obtaining the VR (Value Representation) might be needed by caller
118  *          to convert the string typed content to caller's native type 
119  *          (think of C++ vs Python). The VR is actually of a higher level
120  *          of semantics than just the native C++ type.
121  * @param   group  Group number of the searched tag.
122  * @param   elem Element number of the searched tag.
123  * @return  Corresponding element value representation when it exists,
124  *          and the string GDCM_UNFOUND ("gdcm::Unfound") otherwise.
125  */
126 std::string DocEntrySet::GetEntryVR(uint16_t group, uint16_t elem)
127 {
128    DocEntry *entry = GetDocEntry(group, elem);
129    if ( entry )
130       return entry->GetVR();
131    return GDCM_UNFOUND;
132 }
133
134 /**
135  * \brief  Same as \ref Document::GetDocEntry except it only
136  *         returns a result when the corresponding entry is of type
137  *         ValEntry.
138  * @param   group  Group number of the searched Dicom Element 
139  * @param   elem Element number of the searched Dicom Element  
140  * @return When present, the corresponding ValEntry. 
141  */
142 ValEntry *DocEntrySet::GetValEntry(uint16_t group, uint16_t elem)
143 {
144    DocEntry *currentEntry = GetDocEntry(group, elem);
145    if ( !currentEntry )
146       return NULL;
147
148    return dynamic_cast<ValEntry*>(currentEntry);
149 }
150
151 /**
152  * \brief  Same as \ref Document::GetDocEntry except it only
153  *         returns a result when the corresponding entry is of type
154  *         BinEntry.
155  * @param   group  Group number of the searched Dicom Element
156  * @param   elem Element number of the searched Dicom Element
157  * @return When present, the corresponding BinEntry. 
158  */
159 BinEntry *DocEntrySet::GetBinEntry(uint16_t group, uint16_t elem)
160 {
161    DocEntry *currentEntry = GetDocEntry(group, elem);
162    if ( !currentEntry )
163    {
164       gdcmWarningMacro( "No corresponding BinEntry " << std::hex << group <<
165                          "," << elem);
166       return NULL;
167    }
168
169    return dynamic_cast<BinEntry*>(currentEntry);
170 }
171
172 /**
173  * \brief  Same as \ref Document::GetDocEntry except it only
174  *         returns a result when the corresponding entry is of type
175  *         SeqEntry.
176  * @param   group  Group number of the searched Dicom Element 
177  * @param   elem Element number of the searched Dicom Element  
178  * @return When present, the corresponding SeqEntry. 
179  */
180 SeqEntry *DocEntrySet::GetSeqEntry(uint16_t group, uint16_t elem)
181 {
182    DocEntry *currentEntry = GetDocEntry(group, elem);
183    if ( !currentEntry )
184    {
185       gdcmWarningMacro( "No corresponding SeqEntry " << std::hex << group <<
186                         "," << elem);
187       return NULL;
188    }
189
190    return dynamic_cast<SeqEntry*>(currentEntry);
191 }
192
193 /**
194  * \brief   Accesses an existing DocEntry (i.e. a Dicom Element)
195  *          through it's (group, element) and modifies it's content with
196  *          the given value.
197  * @param   content new value (string) to substitute with
198  * @param   group  group number of the Dicom Element to modify
199  * @param   elem element number of the Dicom Element to modify
200  */
201 bool DocEntrySet::SetValEntry(std::string const &content, 
202                               uint16_t group, uint16_t elem) 
203 {
204    ValEntry *entry = GetValEntry(group, elem);
205    if (!entry )
206    {
207       gdcmWarningMacro( "No corresponding ValEntry " << std::hex << group <<
208                          "," << elem << " element (try promotion first).");
209       return false;
210    }
211    return SetValEntry(content,entry);
212 }
213
214 /**
215  * \brief   Accesses an existing DocEntry (i.e. a Dicom Element)
216  *          through it's (group, element) and modifies it's content with
217  *          the given value.
218  * @param   content new value (void*  -> uint8_t*) to substitute with
219  * @param   lgth new value length
220  * @param   group  group number of the Dicom Element to modify
221  * @param   elem element number of the Dicom Element to modify
222  */
223 bool DocEntrySet::SetBinEntry(uint8_t *content, int lgth, 
224                               uint16_t group, uint16_t elem) 
225 {
226    BinEntry *entry = GetBinEntry(group, elem);
227    if (!entry )
228    {
229       gdcmWarningMacro( "No corresponding ValEntry " << std::hex << group <<
230                         "," << elem << " element (try promotion first).");
231       return false;
232    }
233
234    return SetBinEntry(content,lgth,entry);
235
236
237 /**
238  * \brief   Accesses an existing DocEntry (i.e. a Dicom Element)
239  *          and modifies it's content with the given value.
240  * @param  content new value (string) to substitute with
241  * @param  entry Entry to be modified
242  */
243 bool DocEntrySet::SetValEntry(std::string const &content, ValEntry *entry)
244 {
245    if (entry)
246    {
247       entry->SetValue(content);
248       return true;
249    }
250    return false;
251 }
252
253 /**
254  * \brief   Accesses an existing BinEntry (i.e. a Dicom Element)
255  *          and modifies it's content with the given value.
256  * @param   content new value (void*  -> uint8_t*) to substitute with
257  * @param  entry Entry to be modified 
258  * @param  lgth new value length
259  */
260 bool DocEntrySet::SetBinEntry(uint8_t *content, int lgth, BinEntry *entry)
261 {
262    if (entry)
263    {
264       entry->SetBinArea(content);  
265       entry->SetLength(lgth);
266       entry->SetValue(GDCM_BINLOADED);
267       return true;
268    }
269    return false;
270 }
271
272 /**
273  * \brief   Modifies the value of a given Doc Entry (Dicom Element)
274  *          when it exists. Create it with the given value when unexistant.
275  * @param   value (string) Value to be set
276  * @param   group   Group number of the Entry 
277  * @param   elem  Element number of the Entry
278  * @param   vr  V(alue) R(epresentation) of the Entry -if private Entry-
279  * \return  pointer to the modified/created Header Entry (NULL when creation
280  *          failed).
281  */ 
282 ValEntry *DocEntrySet::InsertValEntry(std::string const &value, 
283                                       uint16_t group, uint16_t elem,
284                                       TagName const &vr )
285 {
286    ValEntry *valEntry = 0;
287    DocEntry *currentEntry = GetDocEntry( group, elem );
288    
289    if (currentEntry)
290    {
291       valEntry = dynamic_cast<ValEntry *>(currentEntry);
292
293       // Verify the VR
294       if ( valEntry )
295          if ( valEntry->GetVR()!=vr )
296             valEntry = NULL;
297
298       // if currentEntry doesn't correspond to the requested valEntry
299       if ( !valEntry)
300       {
301          if ( !RemoveEntry(currentEntry) )
302          {
303             gdcmWarningMacro( "Removal of previous DocEntry failed.");
304
305             return NULL;
306          }
307       }
308    }
309
310    // Create a new valEntry if necessary
311    if ( !valEntry )
312    {
313       valEntry = NewValEntry( group, elem, vr );
314
315       if ( !AddEntry(valEntry) )
316       {
317          gdcmWarningMacro("AddEntry failed although this is a creation.");
318
319          delete valEntry;
320          return NULL;
321       }
322    }
323
324    // Set the binEntry value
325    SetValEntry(value, valEntry); // The std::string value
326    return valEntry;
327 }
328
329 /**
330  * \brief   Modifies the value of a given Header Entry (Dicom Element)
331  *          when it exists. Create it with the given value when unexistant.
332  *          A copy of the binArea is made to be kept in the Document.
333  * @param   binArea (binary) value to be set
334  * @param   lgth length of the Bin Area we want to set
335  * @param   group   Group number of the Entry 
336  * @param   elem  Element number of the Entry
337  * @param   vr  V(alue) R(epresentation) of the Entry -if private Entry-
338  * \return  pointer to the modified/created Header Entry (NULL when creation
339  *          failed).
340  */
341 BinEntry *DocEntrySet::InsertBinEntry(uint8_t *binArea, int lgth, 
342                                       uint16_t group, uint16_t elem,
343                                       TagName const &vr )
344 {
345    BinEntry *binEntry = 0;
346    DocEntry *currentEntry = GetDocEntry( group, elem );
347
348    // Verify the currentEntry
349    if (currentEntry)
350    {
351       binEntry = dynamic_cast<BinEntry *>(currentEntry);
352
353       // Verify the VR
354       if ( binEntry )
355          if ( binEntry->GetVR()!=vr )
356             binEntry = NULL;
357
358       // if currentEntry doesn't correspond to the requested valEntry
359       if ( !binEntry)
360       {
361          if ( !RemoveEntry(currentEntry) )
362          {
363             gdcmWarningMacro( "Removal of previous DocEntry failed.");
364
365             return NULL;
366          }
367       }
368    }
369
370    // Create a new binEntry if necessary
371    if ( !binEntry)
372    {
373       binEntry = NewBinEntry(group, elem, vr);
374
375       if ( !AddEntry(binEntry) )
376       {
377          gdcmWarningMacro( "AddEntry failed although this is a creation.");
378
379          delete binEntry;
380          return NULL;
381       }
382    }
383
384    // Set the binEntry value
385    uint8_t *tmpArea;
386    if ( lgth>0 && binArea )
387    {
388       tmpArea = new uint8_t[lgth];
389       memcpy(tmpArea,binArea,lgth);
390    }
391    else
392    {
393       tmpArea = 0;
394    }
395    if ( !SetBinEntry(tmpArea,lgth,binEntry) )
396    {
397       if ( tmpArea )
398       {
399          delete[] tmpArea;
400       }
401    }
402
403    return binEntry;
404 }  
405
406 /**
407  * \brief   Modifies the value of a given Doc Entry (Dicom Element)
408  *          when it exists. Creates it when unexistant.
409  * @param   group   Group number of the Entry 
410  * @param   elem  Element number of the Entry
411  * \return  pointer to the modified/created SeqEntry (NULL when creation
412  *          failed).
413  */
414 SeqEntry *DocEntrySet::InsertSeqEntry(uint16_t group, uint16_t elem)
415 {
416    SeqEntry *seqEntry = 0;
417    DocEntry *currentEntry = GetDocEntry( group, elem );
418
419    // Verify the currentEntry
420    if ( currentEntry )
421    {
422       seqEntry = dynamic_cast<SeqEntry *>(currentEntry);
423
424       // Verify the VR
425       if ( seqEntry )
426          seqEntry = NULL;
427
428       // if currentEntry doesn't correspond to the requested seqEntry
429       if ( !seqEntry )
430       {
431          if (!RemoveEntry(currentEntry))
432          {
433             gdcmWarningMacro( "Removal of previous DocEntry failed.");
434
435             return NULL;
436          }
437       }
438    }
439    // Create a new seqEntry if necessary
440    if ( !seqEntry )
441    {
442       seqEntry = NewSeqEntry(group, elem);
443
444       if ( !AddEntry(seqEntry) )
445       {
446          gdcmWarningMacro( "AddEntry failed although this is a creation.");
447
448          delete seqEntry;
449          return NULL;
450       }
451    }
452
453    // TODO : Find a trick to insert a SequenceDelimitationItem 
454    //       in the SeqEntry, at the end.
455    return seqEntry;
456
457  
458 /**
459  * \brief   Checks if a given Dicom Element exists within the H table
460  * @param   group   Group number of the searched Dicom Element 
461  * @param   elem  Element number of the searched Dicom Element 
462  * @return true is found
463  */
464 bool DocEntrySet::CheckIfEntryExist(uint16_t group, uint16_t elem )
465 {
466    return GetDocEntry(group,elem)!=NULL;
467 }
468
469 /**
470  * \brief   Build a new Val Entry from all the low level arguments. 
471  *          Check for existence of dictionary entry, and build
472  *          a default one when absent.
473  * @param   group Group number   of the new Entry
474  * @param   elem  Element number of the new Entry
475  * @param   vr    V(alue) R(epresentation) of the new Entry 
476  */
477 ValEntry *DocEntrySet::NewValEntry(uint16_t group,uint16_t elem,
478                                    TagName const &vr) 
479 {
480    DictEntry *dictEntry = GetDictEntry(group, elem, vr);
481    gdcmAssertMacro(dictEntry);
482
483    ValEntry *newEntry = new ValEntry(dictEntry);
484    if (!newEntry) 
485    {
486       gdcmWarningMacro( "Failed to allocate ValEntry");
487       return 0;
488    }
489    return newEntry;
490 }
491
492
493 /**
494  * \brief   Build a new Bin Entry from all the low level arguments. 
495  *          Check for existence of dictionary entry, and build
496  *          a default one when absent.
497  * @param   group Group   number of the new Entry
498  * @param   elem  Element number of the new Entry
499  * @param   vr    V(alue) R(epresentation) of the new Entry 
500  */
501 BinEntry *DocEntrySet::NewBinEntry(uint16_t group, uint16_t elem,
502                                    TagName const &vr) 
503 {
504    DictEntry *dictEntry = GetDictEntry(group, elem, vr);
505    gdcmAssertMacro(dictEntry);
506
507    BinEntry *newEntry = new BinEntry(dictEntry);
508    if (!newEntry) 
509    {
510       gdcmWarningMacro( "Failed to allocate BinEntry");
511       return 0;
512    }
513    return newEntry;
514 }
515
516 /**
517  * \brief   Build a new Seq Entry from all the low level arguments. 
518  *          Check for existence of dictionary entry, and build
519  *          a default one when absent.
520  * @param   group Group   number of the new Entry
521  * @param   elem  Element number of the new Entry
522  */
523 SeqEntry* DocEntrySet::NewSeqEntry(uint16_t group, uint16_t elem) 
524 {
525    DictEntry *dictEntry = GetDictEntry(group, elem, "SQ");
526    gdcmAssertMacro(dictEntry);
527
528    SeqEntry *newEntry = new SeqEntry( dictEntry );
529    if (!newEntry)
530    {
531       gdcmWarningMacro( "Failed to allocate SeqEntry");
532       return 0;
533    }
534    return newEntry;
535 }
536
537 /**
538  * \brief   Request a new virtual dict entry to the dict set
539  * @param   group Group   number of the underlying DictEntry
540  * @param   elem  Element number of the underlying DictEntry
541  * @param   vr    V(alue) R(epresentation) of the underlying DictEntry
542  * @param   vm    V(alue) M(ultiplicity)   of the underlying DictEntry
543  * @param   name   english name
544  */
545 DictEntry* DocEntrySet::NewVirtualDictEntry( uint16_t group, uint16_t elem,
546                                              TagName const &vr,
547                                              TagName const &vm,
548                                              TagName const &name )
549 {
550    return Global::GetDicts()->NewVirtualDictEntry(group,elem,vr,vm,name);
551 }
552
553 //-----------------------------------------------------------------------------
554 // Protected
555 /**
556  * \brief   Searches [both] the public [and the shadow dictionary (when they
557  *          exist)] for the presence of the DictEntry with given
558  *          group and element. The public dictionary has precedence on the
559  *          shadow one(s), if any.
560  * @param   group  Group number of the searched DictEntry
561  * @param   elem Element number of the searched DictEntry
562  * @return  Corresponding DictEntry when it exists, NULL otherwise.
563  */
564 DictEntry *DocEntrySet::GetDictEntry(uint16_t group,uint16_t elem) 
565 {
566    DictEntry *found = 0;
567    Dict *pubDict = Global::GetDicts()->GetDefaultPubDict();
568    if (!pubDict) 
569    {
570       gdcmWarningMacro( "We SHOULD have a default dictionary");
571    }
572    else
573    {
574       found = pubDict->GetEntry(group, elem);  
575    }
576    return found;
577 }
578
579 /**
580  * \brief   Searches [both] the public [and the shadow dictionary (when they
581  *          exist)] for the presence of the DictEntry with given
582  *          group and element, and create a new virtual DictEntry if necessary
583  * @param   group  group number of the searched DictEntry
584  * @param   elem element number of the searched DictEntry
585  * @param   vr V(alue) R(epresentation) to use, if necessary 
586  * @return  Corresponding DictEntry when it exists, NULL otherwise.
587  */
588 DictEntry *DocEntrySet::GetDictEntry(uint16_t group, uint16_t elem,
589                                      TagName const &vr)
590 {
591    DictEntry *dictEntry = GetDictEntry(group,elem);
592    DictEntry *goodEntry = dictEntry;
593    std::string goodVR = vr;
594
595    if (elem == 0x0000) goodVR="UL";
596
597    if ( goodEntry )
598    {
599       if ( goodVR != goodEntry->GetVR()
600         && goodVR != GDCM_UNKNOWN )
601       {
602          goodEntry = NULL;
603       }
604    }
605
606    // Create a new virtual DictEntry if necessary
607    if (!goodEntry)
608    {
609       if (dictEntry)
610       {
611          goodEntry = NewVirtualDictEntry(group, elem, goodVR, "FIXME", 
612                                          dictEntry->GetName() );
613       }
614       else
615       {
616          goodEntry = NewVirtualDictEntry(group, elem, goodVR);
617       }
618    }
619    return goodEntry;
620 }
621
622 //-----------------------------------------------------------------------------
623 // Private
624
625 //-----------------------------------------------------------------------------
626 // Print
627
628 //-----------------------------------------------------------------------------
629 } // end namespace gdcm