]> Creatis software - gdcm.git/blob - src/gdcmElValSet.cxx
* src/gdcmUtil.[cxx|h] : change the _CreateCleanString return value from
[gdcm.git] / src / gdcmElValSet.cxx
1 // gdcmElValSet.cxx
2
3 #ifdef _MSC_VER
4 // 'identifier' : class 'type' needs to have dll-interface to be used by
5 // clients of class 'type2'
6 #pragma warning ( disable : 4251 )
7 // 'identifier' : identifier was truncated to 'number' characters in the
8 // debug information
9 #pragma warning ( disable : 4786 )
10 #endif //_MSC_VER
11
12 #include <sstream>
13 #include "gdcmUtil.h"
14 #include "gdcmElValSet.h"
15 #include "gdcmTS.h"
16 using namespace std;
17
18 gdcmElValSet::~gdcmElValSet() {
19    for (TagElValueHT::iterator tag = tagHt.begin(); tag != tagHt.end(); ++tag) {
20       gdcmElValue* EntryToDelete = tag->second;
21       if ( EntryToDelete )
22          delete EntryToDelete;
23    }
24    tagHt.clear();
25    // Since Add() adds symetrical in both tagHt and NameHt we can
26    // assume all the pointed gdcmElValues are already cleaned-up when
27    // we cleaned tagHt.
28    NameHt.clear();
29 }
30
31 TagElValueHT & gdcmElValSet::GetTagHt(void) {
32         return tagHt;
33 }
34
35 /**
36  * \ingroup gdcmElValSet
37  * \brief   
38  * @param   newElValue
39  * @return  
40  */
41 void gdcmElValSet::Add(gdcmElValue * newElValue) {
42         tagHt [newElValue->GetKey()]  = newElValue;
43         NameHt[newElValue->GetName()] = newElValue;
44 }
45
46 /**
47  * \ingroup gdcmElValSet
48  * \brief   Checks if a given Dicom element exists
49  * \        within a ElValSet
50  * @param   Group
51  * @param   Elem
52  * @return  
53  */
54 int gdcmElValSet::CheckIfExistByNumber(guint16 Group, guint16 Elem ) {
55         string key = TranslateToKey(Group, Elem );
56         return (tagHt.count(key));
57 }
58
59 /**
60  * \ingroup gdcmElValSet
61  * \brief   
62  */
63 void gdcmElValSet::Print(ostream & os) {
64
65    size_t o;
66    short int g, e;
67    TSKey v;
68    string d2;
69    gdcmTS * ts = gdcmGlobal::GetTS();
70    
71    for (TagElValueHT::iterator tag = tagHt.begin();
72            tag != tagHt.end();
73            ++tag){
74       g = tag->second->GetGroup();
75       e = tag->second->GetElement();
76       v = tag->second->GetValue();
77       o = tag->second->GetOffset();
78       d2 = _CreateCleanString(v);  // replace non printable characters by '.'
79                  
80       os << tag->first << ": ";
81       os << " lgr : " << tag->second->GetLength();
82       os << ", Offset : " << o;
83       os << " x(" << hex << o << dec << ") ";
84       os << "\t[" << tag->second->GetVR()    << "]";
85       os << "\t[" << tag->second->GetName()  << "]";       
86       os << "\t[" << d2 << "]";
87       
88       // Display the UID value (instead of displaying the rough code)  
89       if (g == 0x0002) {  // Some more to be displayed ?
90          if ( (e == 0x0010) || (e == 0x0002) )     
91             os << "  ==>\t[" << ts->GetValue(v) << "]";   
92       } else {
93          if (g == 0x0008) {
94             if ( (e == 0x0016) || (e == 0x1150)  )         
95                os << "  ==>\t[" << ts->GetValue(v) << "]"; 
96          }
97       }              
98       os << endl;
99    }
100
101
102 /**
103  * \ingroup gdcmElValSet
104  * \brief   
105  */
106 void gdcmElValSet::PrintByName(ostream & os) {
107    for (TagElValueNameHT::iterator tag = NameHt.begin();
108           tag != NameHt.end();
109           ++tag){
110       os << tag->first << ": ";
111       os << "[" << tag->second->GetValue() << "]";
112       os << "[" << tag->second->GetKey()   << "]";
113       os << "[" << tag->second->GetVR()    << "]" << endl;
114    }
115 }
116
117 /**
118  * \ingroup gdcmElValSet
119  * \brief   
120  * @param   group 
121  * @param   element 
122  * @return  
123  */
124 gdcmElValue* gdcmElValSet::GetElementByNumber(guint16 group, guint16 element) {
125    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
126    if ( ! tagHt.count(key))
127       return (gdcmElValue*)0;
128    return tagHt.find(key)->second;
129 }
130
131 /**
132  * \ingroup gdcmElValSet
133  * \brief   
134  * @return  
135  */
136 gdcmElValue* gdcmElValSet::GetElementByName(string TagName) {
137    if ( ! NameHt.count(TagName))
138       return (gdcmElValue*)0;
139    return NameHt.find(TagName)->second;
140 }
141
142 /**
143  * \ingroup gdcmElValSet
144  * \brief   
145  * @param   group 
146  * @param   element 
147  * @return  
148  */
149 string gdcmElValSet::GetElValueByNumber(guint16 group, guint16 element) {
150    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
151    if ( ! tagHt.count(key))
152       return GDCM_UNFOUND;
153    return tagHt.find(key)->second->GetValue();
154 }
155
156 /**
157  * \ingroup gdcmElValSet
158  * \brief   
159  * @return  
160  */
161 string gdcmElValSet::GetElValueByName(string TagName) {
162    if ( ! NameHt.count(TagName))
163       return GDCM_UNFOUND;
164    return NameHt.find(TagName)->second->GetValue();
165 }
166
167 /**
168  * \ingroup gdcmElValSet
169  * \brief   
170  * @param   content
171  * @param   group 
172  * @param   element 
173  * @return  
174  */
175 int gdcmElValSet::SetElValueByNumber(string content,
176                                      guint16 group, guint16 element) {
177    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
178    if ( ! tagHt.count(key))
179       return 0;
180    tagHt[key]->SetValue(content);       
181    string vr = tagHt[key]->GetVR();
182    guint32 lgr;
183
184    if( (vr == "US") || (vr == "SS") ) 
185       lgr = 2;
186    else if( (vr == "UL") || (vr == "SL") )
187       lgr = 4;
188    else
189       lgr = content.length();      
190    tagHt[key]->SetLength(lgr); 
191    return 1;
192 }
193
194 /**
195  * \ingroup gdcmElValSet
196  * \brief   
197  * @param   content
198  * @param   TagName
199  * @return  
200  */
201 int gdcmElValSet::SetElValueByName(string content, string TagName) {
202    if ( ! NameHt.count(TagName))
203       return 0;
204    NameHt[TagName]->SetValue(content);
205    string vr = NameHt[TagName]->GetVR();
206    guint32 lgr;
207
208    if( (vr == "US") || (vr == "SS") ) 
209       lgr = 2;
210    else if( (vr == "UL") || (vr == "SL") )
211       lgr = 4;     
212    else 
213       lgr = content.length();
214            
215 // TODO : WARNING: le cas de l'element des pixels (7fe0,0010) n'est pas traite
216 // par SetElValueByName
217 // il faudra utiliser SetElValueByNumber
218            
219    NameHt[TagName]->SetLength(lgr);
220    return 1;            
221 }
222
223 /**
224  * \ingroup gdcmElValSet
225  * \brief   Generate a free TagKey i.e. a TagKey that is not present
226  *          in the TagHt dictionary.
227  * @param   group The generated tag must belong to this group.  
228  * @return  The element of tag with given group which is fee.
229  */
230 guint32 gdcmElValSet::GenerateFreeTagKeyInGroup(guint16 group) {
231    for (guint32 elem = 0; elem < UINT32_MAX; elem++) {
232       TagKey key = gdcmDictEntry::TranslateToKey(group, elem);
233       if (tagHt.count(key) == 0)
234          return elem;
235    }
236    return UINT32_MAX;
237 }
238
239 /**
240  * \ingroup gdcmElValSet
241  * \brief   
242  * @param   area
243  * @param   group 
244  * @param   element 
245  * @return  
246  */
247 int gdcmElValSet::SetVoidAreaByNumber(void * area,
248                                       guint16 group, guint16 element) {
249    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
250    if ( ! tagHt.count(key))
251       return 0;
252    tagHt[key]->SetVoidArea(area);        
253    return 1 ;           
254 }
255
256 /**
257  * \ingroup gdcmElValSet
258  * \brief   
259  * @param   length
260  * @param   group 
261  * @param   element 
262  * @return  int acts as a boolean
263  */
264 int gdcmElValSet::SetElValueLengthByNumber(guint32 length,
265                                            guint16 group, guint16 element) {
266    TagKey key = gdcmDictEntry::TranslateToKey(group, element);
267    if ( ! tagHt.count(key))
268       return 0;
269    tagHt[key]->SetLength(length);        
270    return 1 ;           
271 }
272 /**
273  * \ingroup gdcmElValSet
274  * \brief   
275  * @param   length
276  * @param   TagName
277  * @return  
278  */
279 int gdcmElValSet::SetElValueLengthByName(guint32 length, string TagName) {
280    if ( ! NameHt.count(TagName))
281       return 0;
282    NameHt.find(TagName)->second->SetLength(length);      
283    return 1 ;           
284 }
285
286 /**
287  * \ingroup gdcmElValSet
288  * \brief   Re-computes the length of a ACR-NEMA/Dicom group from a DcmHeader
289  * @param   SkipSequence TRUE if we don't want to write Sequences (ACR-NEMA Files)
290  * @param   type
291  */
292 void gdcmElValSet::UpdateGroupLength(bool SkipSequence, FileType type) {
293    guint16 gr, el;
294    string vr;
295    
296    gdcmElValue *elem;
297    char trash[10];
298    string str_trash;
299    
300    GroupKey key;
301    GroupHT groupHt;  // to hold the length of each group
302    TagKey tk;
303    // remember :
304    // typedef std::map<GroupKey, int> GroupHT;
305    
306    gdcmElValue *elemZ;
307   
308    // for each Tag in the DCM Header
309    
310    for (TagElValueHT::iterator tag2 = tagHt.begin(); 
311         tag2 != tagHt.end();
312         ++tag2){
313
314       elem  = tag2->second;
315       gr = elem->GetGroup();
316       el = elem->GetElement();
317       vr = elem->GetVR(); 
318                  
319       sprintf(trash, "%04x", gr);
320       key = trash;              // generate 'group tag'
321       
322       // if the caller decided not to take SEQUENCEs into account 
323       // e.g : he wants to write an ACR-NEMA File 
324                 
325       if (SkipSequence && vr == "SQ") continue;
326       
327          // Still unsolved problem :
328          // we cannot find the 'Sequence Delimitation Item'
329          // since it's at the end of the Hash Table
330          // (fffe,e0dd) 
331              
332          // pas SEQUENCE en ACR-NEMA
333          // WARNING : 
334          // --> la descente a l'interieur' des SQ 
335          // devrait etre faite avec une liste chainee, pas avec une HTable...
336             
337       if ( groupHt.count(key) == 0) { // we just read the first elem of a given group
338          if (el == 0x0000) {          // the first elem is 0x0000
339             groupHt[key] = 0;         // initialize group length 
340          } else {
341             groupHt[key] = 2 + 2 + 4 + elem->GetLength(); // non 0x0000 first group elem
342          } 
343       } else {   // any elem but the first    
344          if (type == ExplicitVR) {
345             if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
346                groupHt[key] +=  4; // explicit VR AND OB, OW, SQ : 4 more bytes
347             }
348          }
349          groupHt[key] += 2 + 2 + 4 + elem->GetLength(); 
350       } 
351    }
352
353    unsigned short int gr_bid;
354   
355    for (GroupHT::iterator g = groupHt.begin(); // for each group we found
356         g != groupHt.end();
357         ++g){ 
358       // FIXME: g++ -Wall -Wstrict-prototypes reports on following line:
359       //        warning: unsigned int format, different type arg
360       sscanf(g->first.c_str(),"%x",&gr_bid);
361       tk = g->first + "|0000";                  // generate the element full tag
362                      
363       if ( tagHt.count(tk) == 0) {              // if element 0x0000 not found
364          gdcmDictEntry * tagZ = new gdcmDictEntry(gr_bid, 0x0000, "UL");       
365          elemZ = new gdcmElValue(tagZ);
366          elemZ->SetLength(4);
367          Add(elemZ);                            // create it
368       } else {
369          elemZ=GetElementByNumber(gr_bid, 0x0000);
370       }     
371       sprintf(trash ,"%d",g->second);
372       str_trash=trash;
373       elemZ->SetValue(str_trash);
374    }   
375 }
376
377 /**
378  * \ingroup gdcmElValSet
379  * \brief   
380  * @param   type
381  * @param   _fp 
382  * @return  
383  */
384 void gdcmElValSet::WriteElements(FileType type, FILE * _fp) {
385    guint16 gr, el;
386    guint32 lgr;
387    const char * val;
388    string vr;
389    guint32 val_uint32;
390    guint16 val_uint16;
391    
392    vector<string> tokens;
393
394    void *ptr;
395
396    // Tout ceci ne marche QUE parce qu'on est sur un proc Little Endian 
397    // restent à tester les echecs en écriture (apres chaque fwrite)
398
399    for (TagElValueHT::iterator tag2=tagHt.begin();
400         tag2 != tagHt.end();
401         ++tag2){
402
403       gr =  tag2->second->GetGroup();
404       el =  tag2->second->GetElement();
405       lgr = tag2->second->GetLength();
406       val = tag2->second->GetValue().c_str();
407       vr =  tag2->second->GetVR();
408       
409      // cout << "Tag "<< hex << gr << " " << el << "\n";
410
411       if ( type == ACR ) { 
412          if (gr < 0x0008)   continue; // ignore pure DICOM V3 groups
413          if (gr %2)         continue; // ignore shadow groups
414          if (vr == "SQ" )   continue; // ignore Sequences
415          if (gr == 0xfffe ) continue; // ignore delimiters
416       } 
417
418       fwrite ( &gr,(size_t)2 ,(size_t)1 ,_fp);  //group
419       fwrite ( &el,(size_t)2 ,(size_t)1 ,_fp);  //element
420
421       if ( (type == ExplicitVR) && (gr <= 0x0002) ) {
422          // EXPLICIT VR
423          guint16 z=0, shortLgr;
424          fwrite (vr.c_str(),(size_t)2 ,(size_t)1 ,_fp);
425
426          if ( (vr == "OB") || (vr == "OW") || (vr == "SQ") ) {
427             fwrite ( &z,  (size_t)2 ,(size_t)1 ,_fp);
428             fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
429
430          } else {
431             shortLgr=lgr;
432             fwrite ( &shortLgr,(size_t)2 ,(size_t)1 ,_fp);
433          }
434       } else { // IMPLICIT VR
435          fwrite ( &lgr,(size_t)4 ,(size_t)1 ,_fp);
436       }
437
438       if (vr == "US" || vr == "SS") {
439          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
440          Tokenize (tag2->second->GetValue(), tokens, "\\");
441          for (unsigned int i=0; i<tokens.size();i++) {
442             val_uint16 = atoi(tokens[i].c_str());
443             ptr = &val_uint16;
444             fwrite ( ptr,(size_t)2 ,(size_t)1 ,_fp);
445          }
446          tokens.clear();
447          continue;
448       }
449       if (vr == "UL" || vr == "SL") {
450          tokens.erase(tokens.begin(),tokens.end()); // clean any previous value
451          Tokenize (tag2->second->GetValue(), tokens, "\\");
452          for (unsigned int i=0; i<tokens.size();i++) {
453             val_uint32 = atoi(tokens[i].c_str());
454             ptr = &val_uint32;
455             fwrite ( ptr,(size_t)4 ,(size_t)1 ,_fp);
456          }
457          tokens.clear();
458          continue;
459       }     
460       // Pixels are never loaded in the element !
461       if ((gr == 0x7fe0) && (el == 0x0010) ) break;
462
463       fwrite ( val,(size_t)lgr ,(size_t)1 ,_fp); // Elem value
464    }
465 }
466
467 /**
468  * \ingroup gdcmElValSet
469  * \brief   
470  * @param   _fp
471  * @param   type
472  * @return  
473  */
474 int gdcmElValSet::Write(FILE * _fp, FileType type) {
475
476    if (type == ImplicitVR) {
477       string implicitVRTransfertSyntax = "1.2.840.10008.1.2";
478       SetElValueByNumber(implicitVRTransfertSyntax, 0x0002, 0x0010);
479       
480       //FIXME Refer to standards on page 21, chapter 6.2 "Value representation":
481       //      values with a VR of UI shall be padded with a single trailing null
482       //      Dans le cas suivant on doit pader manuellement avec un 0
483       
484       SetElValueLengthByNumber(18, 0x0002, 0x0010);
485    }  
486         // Question :
487         // Comment pourrait-on savoir si le DcmHeader vient d'un fichier DicomV3 ou non ,
488         // (FileType est un champ de gdcmHeader ...)
489         // WARNING : Si on veut ecrire du DICOM V3 a partir d'un DcmHeader ACR-NEMA
490         // no way
491         
492    if (type == ExplicitVR) {
493       string explicitVRTransfertSyntax = "1.2.840.10008.1.2.1";
494       SetElValueByNumber(explicitVRTransfertSyntax, 0x0002, 0x0010);
495       // See above comment 
496       SetElValueLengthByNumber(20, 0x0002, 0x0010);
497    }
498
499    if ( (type == ImplicitVR) || (type == ExplicitVR) )
500       UpdateGroupLength(false,type);
501    if ( type == ACR)
502       UpdateGroupLength(true,ACR);
503
504    WriteElements(type, _fp);
505    return(1);
506 }