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