]> Creatis software - gdcm.git/blob - src/gdcmDataEntry.cxx
Remove useless accesses to the Dicom Dictionnary std::map
[gdcm.git] / src / gdcmDataEntry.cxx
1 /*=========================================================================
2                                                                                 
3   Program:   gdcm
4   Module:    $RCSfile: gdcmDataEntry.cxx,v $
5   Language:  C++
6   Date:      $Date: 2006/04/11 16:03:26 $
7   Version:   $Revision: 1.36 $
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 #include "gdcmDataEntry.h"
20 #include "gdcmVR.h"
21 #include "gdcmTS.h"
22 #include "gdcmGlobal.h"
23 #include "gdcmUtil.h"
24 #include "gdcmDebug.h"
25
26 #include <fstream>
27
28 #if defined(__BORLANDC__)
29  #include <mem.h> // for memcpy
30  #include <stdlib.h> // for atof
31  #include <ctype.h> // for isdigit
32 #endif
33
34 namespace gdcm 
35 {
36 //-----------------------------------------------------------------------------
37 #define MAX_SIZE_PRINT_ELEMENT_VALUE 0x7fffffff
38 uint32_t DataEntry::MaxSizePrintEntry = MAX_SIZE_PRINT_ELEMENT_VALUE;
39
40 //-----------------------------------------------------------------------------
41 // Constructor / Destructor
42 /**
43  * \brief   Constructor for a given DictEntry
44  * @param   e Pointer to existing dictionary entry
45  */
46 DataEntry::DataEntry(uint16_t group,uint16_t elem,
47                                      VRKey const &vr) 
48             : DocEntry(group,elem,vr)
49 {
50    State = STATE_LOADED;
51    Flag = FLAG_NONE;
52
53    BinArea = 0;
54    SelfArea = true;
55 }
56
57 /**
58  * \brief   Constructor for a given DocEntry
59  * @param   e Pointer to existing Doc entry
60  */
61 DataEntry::DataEntry(DocEntry *e)
62             //: DocEntry(e->GetDictEntry())
63             : DocEntry(e->GetGroup(),e->GetElement(), e->GetVR()  )
64 {
65    Flag = FLAG_NONE;
66    BinArea = 0;
67    SelfArea = true;
68
69    Copy(e);
70 }
71
72 /**
73  * \brief   Canonical destructor.
74  */
75 DataEntry::~DataEntry ()
76 {
77    DeleteBinArea();
78 }
79
80 //-----------------------------------------------------------------------------
81 // Print
82
83 //-----------------------------------------------------------------------------
84 // Public
85 /**
86  * \brief Sets the value (non string) of the current DataEntry
87  * @param area area
88  * @param self self
89  */
90 void DataEntry::SetBinArea( uint8_t *area, bool self )  
91
92    DeleteBinArea();
93
94    BinArea = area;
95    SelfArea = self;
96
97    State = STATE_LOADED;
98 }
99 /**
100  * \brief Inserts the value (non string) into the current DataEntry
101  * @param area area
102  * @param length length 
103  */
104 void DataEntry::CopyBinArea( uint8_t *area, uint32_t length )
105 {
106    DeleteBinArea();
107
108    uint32_t lgh = length + length%2;
109    SetLength(lgh);
110
111    if( area && length > 0 )
112    {
113       NewBinArea();
114       memcpy(BinArea,area,length);
115       if( length!=lgh )
116          BinArea[length]=0;
117
118       State = STATE_LOADED;
119    }
120 }
121
122 /**
123  * \brief Inserts the elementary (non string) value into the current DataEntry
124  * @param id index of the elementary value to be set
125  * @param val value, passed as a double 
126  */
127 void DataEntry::SetValue(const uint32_t &id, const double &val)
128 {
129    if( !BinArea )
130       NewBinArea();
131    State = STATE_LOADED;
132
133    if( id > GetValueCount() )
134    {
135       gdcmErrorMacro("Index (" << id << ")is greater than the data size");
136       return;
137    }
138
139    const VRKey &vr = GetVR();
140    if( vr == "US" || vr == "SS" )
141    {
142       uint16_t *data = (uint16_t *)BinArea;
143       data[id] = (uint16_t)val;
144    }
145    else if( vr == "UL" || vr == "SL" )
146    {
147       uint32_t *data = (uint32_t *)BinArea;
148       data[id] = (uint32_t)val;
149    }
150    else if( vr == "FL" )
151    {
152       float *data = (float *)BinArea;
153       data[id] = (float)val;
154    }
155    else if( vr == "FD" )
156    {
157       double *data = (double *)BinArea;
158       data[id] = (double)val;
159    }
160    else if( Global::GetVR()->IsVROfStringRepresentable(vr) )
161    {
162       gdcmErrorMacro("SetValue on String representable not implemented yet");
163    }
164    else
165    {
166       BinArea[id] = (uint8_t)val;
167    }
168 }
169 /**
170  * \brief returns, as a double (?!?) one of the values 
171  *      (when entry is multivaluated), identified by its index.
172  *      Returns 0.0 if index is wrong
173  * @param id id
174  */
175 double DataEntry::GetValue(const uint32_t &id) const
176 {
177    if( !BinArea )
178    {
179       if (GetLength() != 0) // avoid stupid messages
180    /// \todo warn the user there was a problem !
181          gdcmErrorMacro("BinArea not set " << std::hex 
182                      << GetGroup() << " " << GetElement() 
183                      << " Can't get the value");
184       return 0.0;
185    }
186
187    uint32_t count = GetValueCount();
188    if( id > count )
189    {
190       gdcmErrorMacro("Index (" << id << ") is greater than the data size");
191       return 0.0;
192    }
193
194    /// \todo FIX the API : user *knows* that entry contains a US
195    ///               and he receives a double ?!?
196    
197    const VRKey &vr = GetVR();
198    /// \todo FIX the API : user *knows* that entry contains a US,
199    ///       the method is supposed to return a double
200    ///       but sends a US ?!? 
201    if( vr == "US" || vr == "SS" )
202       return ((uint16_t *)BinArea)[id];
203    else if( vr == "UL" || vr == "SL" )
204       return ((uint32_t *)BinArea)[id];
205    else if( vr == "FL" )
206       return ((float *)BinArea)[id];
207    else if( vr == "FD" )
208       return ((double *)BinArea)[id];
209    else if( Global::GetVR()->IsVROfStringRepresentable(vr) )
210    {
211       // this is for VR = "DS", ...
212       if( GetLength() )
213       {
214          // Don't use std::string to accelerate processing
215          double val;
216          char *tmp = new char[GetLength()+1];
217          memcpy(tmp,BinArea,GetLength());
218          tmp[GetLength()]=0;
219
220          if( count == 0 )
221          {
222             val = atof(tmp);
223          }
224          else
225          {
226             count = id;
227             char *beg = tmp;
228             for(uint32_t i=0;i<GetLength();i++)
229             {
230                if( tmp[i] == '\\' )
231                {
232                   if( count == 0 )
233                   {
234                      tmp[i] = 0;
235                      break;
236                   }
237                   else
238                   {
239                      count--;
240                      beg = &(tmp[i+1]);
241                   }
242                }
243             }
244             val = atof(beg);
245          }
246
247          delete[] tmp;
248          return val;
249       }
250       else 
251          return 0.0;
252    }
253    else
254       return BinArea[id];
255 }
256
257 /**
258  * \brief Checks if the multiplicity of the value follows Dictionary VM
259  */
260 bool DataEntry::IsValueCountValid() /*const*/
261 {
262   uint32_t vm;
263   const std::string &strVM = GetVM();
264   uint32_t vc = GetValueCount();
265   bool valid = vc == 0;
266   if( valid )
267     return true;
268   
269   // FIXME : what shall we do with VM = "2-n", "3-n", etc
270   
271   if( strVM == "1-n" )
272   {
273     // make sure there is at least one ??? FIXME
274     valid = vc >= 1;
275   }
276   else
277   {
278     std::istringstream os;
279     os.str( strVM );
280     os >> vm;
281     // Two cases:
282     // vm respects the one from the dict
283     // vm is 0 (we need to check if this element is allowed to be empty) FIXME
284
285     // note  (JPR)
286     // ----    
287     // Entries whose type is 1 are mandatory, with a mandatory value.
288     // Entries whose type is 1c are mandatory-inside-a-Sequence,
289     //                          with a mandatory value.
290     // Entries whose type is 2 are mandatory, with an optional value.
291     // Entries whose type is 2c are mandatory-inside-a-Sequence,
292     //                          with an optional value.
293     // Entries whose type is 3 are optional.
294
295     // case vc == 0 is only applicable for 'type 2' entries.
296     // Problem : entry type may depend on the modality and/or the Sequence
297     //           it's embedded in !
298     //          (Get the information in the 'Conformance Statements' ...)  
299     valid = vc == vm;
300   }
301   return valid;
302 }
303
304 /**
305  * \brief returns the number of elementary values
306  */ 
307 uint32_t DataEntry::GetValueCount( ) const
308 {
309    const VRKey &vr = GetVR();
310    if( vr == "US" || vr == "SS" )
311       return GetLength()/sizeof(uint16_t);
312    else if( vr == "UL" || vr == "SL" )
313       return GetLength()/sizeof(uint32_t);
314    else if( vr == "FL" || vr == "OF" )
315       return GetLength()/4 ; // FL has a *4* length! sizeof(float);
316    else if( vr == "FD" )
317       return GetLength()/8;  // FD has a *8* length! sizeof(double);
318    else if( Global::GetVR()->IsVROfStringRepresentable(vr) )
319    {
320       // Some element in DICOM are allowed to be empty
321       if( !GetLength() ) 
322          return 0;
323       // Don't use std::string to accelerate processing
324       uint32_t count = 1;
325       for(uint32_t i=0;i<GetLength();i++)
326       {
327          if( BinArea[i] == '\\')
328             count++;
329       }
330       return count;
331    }
332    return GetLength();
333 }
334
335 /**
336  * \brief Gets a std::vector <double> holding the value(s) of a DS DataEntry
337  * @param valueVector std::vector <double> of value(s)
338  * \return false if VR not "DS" or DataEntry empty
339  */
340  bool DataEntry::GetDSValue(std::vector <double> &valueVector)
341  {
342     /// \todo rewrite the whole method, in order *not to use* std::string !
343     std::vector<std::string> tokens;
344     
345     if (GetVR() != "DS") // never trust a user !
346        return false;    
347        
348     Util::Tokenize ( GetString().c_str(), tokens, "\\" );
349         
350     int nbValues= tokens.size();
351     if (nbValues == 0)
352        return false;
353                
354     for (int loop=0; loop<nbValues; loop++) 
355        valueVector.push_back(atof(tokens[loop].c_str()));
356     
357     return true;  
358  }
359  
360 /**
361  * \brief Sets the 'value' of a DataEntry, passed as a std::string
362  * @param value string representation of the value to be set
363  */ 
364 void DataEntry::SetString(std::string const &value)
365 {
366    DeleteBinArea();
367
368    const VRKey &vr = GetVR();
369    if ( vr == "US" || vr == "SS" )
370    {
371       std::vector<std::string> tokens;
372       Util::Tokenize (value, tokens, "\\");
373       SetLength(tokens.size()*sizeof(uint16_t));
374       NewBinArea();
375
376       uint16_t *data = (uint16_t *)BinArea;
377       for (unsigned int i=0; i<tokens.size();i++)
378          data[i] = atoi(tokens[i].c_str());
379       tokens.clear();
380    }
381    else if ( vr == "UL" || vr == "SL" )
382    {
383       std::vector<std::string> tokens;
384       Util::Tokenize (value, tokens, "\\");
385       SetLength(tokens.size()*sizeof(uint32_t));
386       NewBinArea();
387
388       uint32_t *data = (uint32_t *)BinArea;
389       for (unsigned int i=0; i<tokens.size();i++)
390          data[i] = atoi(tokens[i].c_str());
391       tokens.clear();
392    }
393    else if ( vr == "FL" )
394    {
395       std::vector<std::string> tokens;
396       Util::Tokenize (value, tokens, "\\");
397       SetLength(tokens.size()*sizeof(float));
398       NewBinArea();
399
400       float *data = (float *)BinArea;
401       for (unsigned int i=0; i<tokens.size();i++)
402          data[i] = (float)atof(tokens[i].c_str());
403       tokens.clear();
404    }
405    else if ( vr == "FD" )
406    {
407       std::vector<std::string> tokens;
408       Util::Tokenize (value, tokens, "\\");
409       SetLength(tokens.size()*sizeof(double));
410       NewBinArea();
411
412       double *data = (double *)BinArea;
413       for (unsigned int i=0; i<tokens.size();i++)
414          data[i] = atof(tokens[i].c_str());
415       tokens.clear();
416    }
417    else
418    {
419       //if( value.size() > 0 )  // when user sets a string to 0, *do* the job.
420       {
421          size_t l =  value.size();    
422          SetLength(l + l%2);
423          NewBinArea();
424          memcpy(BinArea, value.c_str(), l);
425          if (l%2)
426             BinArea[l] = '\0';
427       }      
428    }
429    State = STATE_LOADED;
430 }
431 /**
432  * \brief   returns as a string (when possible) the value of the DataEntry
433  */
434 std::string const &DataEntry::GetString() const
435 {
436    static std::ostringstream s;
437    const VRKey &vr = GetVR();
438
439    s.str("");
440    StrArea="";
441
442    if( !BinArea )
443       return StrArea;
444       
445    // When short integer(s) are stored, convert the following (n * 2) characters 
446    // as a displayable string, the values being separated by a back-slash
447
448    if( vr == "US" || vr == "SS" )
449    {
450       uint16_t *data=(uint16_t *)BinArea;
451
452       for (unsigned int i=0; i < GetValueCount(); i++)
453       {
454          if( i!=0 )
455             s << '\\';
456          s << data[i];
457       }
458       StrArea=s.str();
459    }
460    // See above comment on multiple short integers (mutatis mutandis).
461    else if( vr == "UL" || vr == "SL" )
462    {
463       uint32_t *data=(uint32_t *)BinArea;
464
465       for (unsigned int i=0; i < GetValueCount(); i++)
466       {
467          if( i!=0 )
468             s << '\\';
469          s << data[i];
470       }
471       StrArea=s.str();
472    }
473    else if( vr == "FL" )
474    {
475       float *data=(float *)BinArea;
476
477       for (unsigned int i=0; i < GetValueCount(); i++)
478       {
479          if( i!=0 )
480             s << '\\';
481          s << data[i];
482       }
483       StrArea=s.str();
484    }
485    else if( vr == "FD" )
486    {
487       double *data=(double *)BinArea;
488
489       for (unsigned int i=0; i < GetValueCount(); i++)
490       {
491          if( i!=0 )
492             s << '\\';
493          s << data[i];
494       }
495       StrArea=s.str();
496    }
497    else
498    {
499       StrArea.append((const char *)BinArea,GetLength());
500       // to avoid gdcm to propagate oddities in lengthes
501       if ( GetLength()%2)
502          StrArea.append(" ",1);  
503    }
504    return StrArea;
505 }
506
507 /**
508  * \brief Copies all the attributes from an other DocEntry 
509  * @param doc entry to copy from
510  * @remarks The content BinArea is copied too
511  */
512 void DataEntry::Copy(DocEntry *doc)
513 {
514    DocEntry::Copy(doc);
515
516    DataEntry *entry = dynamic_cast<DataEntry *>(doc);
517    if ( entry )
518    {
519       State = entry->State;
520       Flag = entry->Flag;
521       CopyBinArea(entry->BinArea,entry->GetLength());
522    }
523 }
524
525 /**
526  * \brief   Writes the 'value' area of a DataEntry
527  * @param fp already open ofstream pointer
528  * @param filetype type of the file (ACR, ImplicitVR, ExplicitVR, ...)
529  */
530 void DataEntry::WriteContent(std::ofstream *fp, FileType filetype)
531
532    DocEntry::WriteContent(fp, filetype);
533
534    if ( GetGroup() == 0xfffe )
535    {
536       return; //delimitors have NO value
537    }
538    
539    // --> We only deal with Little Endian writting.
540    // --> forget Big Endian Transfer Syntax writting!
541    //     Next DICOM version will give it up ...
542  
543    // WARNING - For Implicit VR private element,
544    //           we have *no choice* but considering them as
545    //           something like 'OB' values.
546    //           we rewrite them as we found them on disc.
547    //           Some trouble will occur if element was 
548    //           *actually* OW, if image was produced 
549    //           on Big endian based processor, read and writen 
550    //           on Little endian based processor
551    //           and, later on, somebody needs
552    //           this 'OW' Implicit VR private element (?!?)
553    //           (Same stuff, mutatis mutandis, for Little/Big)
554  
555    // 8/16 bits Pixels problem should be solved automatiquely,
556    // since we ensure the VR (OB vs OW) is conform to Pixel size.
557         
558    uint8_t *data = BinArea; //safe notation
559    size_t l = GetLength(); 
560 //   gdcmDebugMacro("in DataEntry::WriteContent " << GetKey() << " AtomicLength: "
561 //              << Global::GetVR()->GetAtomicElementLength(this->GetVR() ) // << " BinArea in :" << &BinArea
562 //             );
563    if (BinArea) // the binArea was *actually* loaded
564    {
565 #if defined(GDCM_WORDS_BIGENDIAN) || defined(GDCM_FORCE_BIGENDIAN_EMULATION)
566       unsigned short vrLgth = 
567                         Global::GetVR()->GetAtomicElementLength(this->GetVR());
568       unsigned int i;
569       switch(vrLgth)
570       {
571          case 1:
572          {
573             binary_write (*fp, data, l );           
574             break;
575          }     
576          case 2:
577          {
578             uint16_t *data16 = (uint16_t *)data;
579             for(i=0;i<l/vrLgth;i++)
580                binary_write( *fp, data16[i]);
581             break;
582          }
583          case 4:
584          {
585             uint32_t *data32 = (uint32_t *)data;
586             for(i=0;i<l/vrLgth;i++)
587                binary_write( *fp, data32[i]);
588             break;
589          }
590          case 8:
591          {
592             double *data64 = (double *)data;
593             for(i=0;i<l/vrLgth;i++)
594                binary_write( *fp, data64[i]);
595             break;
596          }
597       }
598 #else
599    binary_write (*fp, data, l );
600 #endif //GDCM_WORDS_BIGENDIAN
601
602    }
603    else
604    {
605       // nothing was loaded, but we need to skip space on disc     
606       if (l != 0)
607       {
608       //  --> WARNING : nothing is written; 
609       //  --> the initial data (on the the source image) is lost
610       //  --> user is *not* informed !      
611          gdcmDebugMacro ("Nothing was loaded, but we need to skip space on disc. "
612                       << "Length =" << l << " for " << GetKey() );   
613          fp->seekp(l, std::ios::cur); // At Write time, for unloaded elems
614       }
615    }
616    // to avoid gdcm to propagate oddities
617    // (length was already modified)  
618    if (l%2)
619       fp->seekp(1, std::ios::cur);  // At Write time, for non even length elems
620 }
621
622 /**
623  * \brief   Compute the full length of the elementary DataEntry (not only value
624  *          length) depending on the VR.
625  */
626 uint32_t DataEntry::ComputeFullLength()
627 {
628    return GetFullLength();
629 }
630
631 //-----------------------------------------------------------------------------
632 // Protected
633 /// \brief Creates a DataEntry owned BinArea (remove previous one if any)
634 void DataEntry::NewBinArea( )
635 {
636    DeleteBinArea();
637    if( GetLength() > 0 )
638       BinArea = new uint8_t[GetLength()];
639    SelfArea = true;
640 }
641 /// \brief Removes the BinArea, if owned by the DataEntry
642 void DataEntry::DeleteBinArea(void)
643 {
644    if (BinArea && SelfArea)
645    {
646       delete[] BinArea;
647       BinArea = NULL;
648    }
649 }
650
651 //-----------------------------------------------------------------------------
652 // Private
653
654 //-----------------------------------------------------------------------------
655 // Print
656 /**
657  * \brief   Prints a DataEntry (Dicom entry)
658  * @param   os ostream we want to print in
659  * @param indent Indentation string to be prepended during printing
660  */
661 void DataEntry::Print(std::ostream &os, std::string const & )
662 {
663    os << "D ";
664    DocEntry::Print(os);
665
666    uint16_t g = GetGroup();
667    if (g == 0xfffe) // delimiters have NO value
668    {          
669       return; // just to avoid identing all the remaining code 
670    }
671
672    std::ostringstream s;
673    TSAtr v;
674
675    if( BinArea )
676    {
677       v = GetString();
678       const VRKey &vr = GetVR();
679
680       if( vr == "US" || vr == "SS" || vr == "UL" || vr == "SL" 
681        || vr == "FL" || vr == "FD")
682          s << " [" << GetString() << "]";
683       else
684       { 
685          if(Global::GetVR()->IsVROfStringRepresentable(vr))
686          {
687             // replace non printable characters by '.'
688             std::string cleanString = Util::CreateCleanString(v);
689             if ( cleanString.length() <= GetMaxSizePrintEntry()
690               || PrintLevel >= 3
691               || IsNotLoaded() )
692            // FIXME : when IsNotLoaded(), you create a Clean String ?!?
693            // FIXME : PrintLevel<2 *does* print the values 
694            //        (3 is only for extra offsets printing)
695            // What do you wanted to do ? JPR
696             {
697                s << " [" << cleanString << "]";
698             }
699             else
700             {
701                s << " [gdcm::too long for print (" << cleanString.length() << ") ]";
702             }
703          }
704          else
705          {
706             // A lot of Private elements (with no VR) contain actually 
707             // only printable characters;
708             // Let's deal with them as is they were VR std::string representable
709     
710             if ( Util::IsCleanArea( GetBinArea(), GetLength()  ) )
711             {
712                // FIXME : since the 'Area' *is* clean, just use
713                //         a 'CreateString' method, to save CPU time.
714                std::string cleanString = 
715                      Util::CreateCleanString( BinArea,GetLength()  );
716                s << " [" << cleanString << "]";
717             }
718             else
719             {
720                s << " [" << GDCM_BINLOADED << ";"
721                << "length = " << GetLength() << "]";
722             }
723          }
724       }
725    }
726    else
727    {
728       if( IsNotLoaded() )
729          s << " [" << GDCM_NOTLOADED << "]";
730       else if( IsUnfound() )
731          s << " [" << GDCM_UNFOUND << "]";
732       else if( IsUnread() )
733          s << " [" << GDCM_UNREAD << "]";
734       else if ( GetLength() == 0 )
735          s << " []";
736    }
737
738    if( IsPixelData() )
739       s << " (" << GDCM_PIXELDATA << ")";
740
741    // Display the UID value (instead of displaying only the rough code)
742    // First 'clean' trailing character (space or zero) 
743    if(BinArea)
744    {
745       const uint16_t &gr = GetGroup();
746       const uint16_t &elt = GetElement();
747       TS *ts = Global::GetTS();
748
749       if (gr == 0x0002)
750       {
751          // Any more to be displayed ?
752          if ( elt == 0x0010 || elt == 0x0002 )
753          {
754             if ( v.length() != 0 )  // for brain damaged headers
755             {
756                if ( ! isdigit((unsigned char)v[v.length()-1]) )
757                {
758                   v.erase(v.length()-1, 1);
759                }
760             }
761             s << "  ==>\t[" << ts->GetValue(v) << "]";
762          }
763       }
764       else if (gr == 0x0008)
765       {
766          if ( elt == 0x0016 || elt == 0x1150 )
767          {
768             if ( v.length() != 0 )  // for brain damaged headers
769             {
770                if ( ! isdigit((unsigned char)v[v.length()-1]) )
771                {
772                   v.erase(v.length()-1, 1);
773                }
774             }
775             s << "  ==>\t[" << ts->GetValue(v) << "]";
776          }
777       }
778       else if (gr == 0x0004)
779       {
780          if ( elt == 0x1510 || elt == 0x1512  )
781          {
782             if ( v.length() != 0 )  // for brain damaged headers  
783             {
784                if ( ! isdigit((unsigned char)v[v.length()-1]) )
785                {
786                   v.erase(v.length()-1, 1);  
787                }
788             }
789             s << "  ==>\t[" << ts->GetValue(v) << "]";
790          }
791       }
792    }
793
794    os << s.str();
795 }
796
797 //-----------------------------------------------------------------------------
798 } // end namespace gdcm
799