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