]> Creatis software - gdcm.git/blob - Testing/TestAllEntryVerify.cxx
ENH: Improve string manipulation. I now inforce the notion of 'DicomString'
[gdcm.git] / Testing / TestAllEntryVerify.cxx
1 #include "gdcmHeader.h"
2
3 #include <map>
4 #include <list>
5 #include <fstream>
6 #include <iostream>
7
8 //Generated file:
9 #include "gdcmDataImages.h"
10
11 typedef std::string EntryValueType;   // same type as ValEntry::value
12 typedef std::map< gdcm::TagKey, EntryValueType > MapEntryValues;
13 typedef MapEntryValues* MapEntryValuesPtr;
14 typedef std::string FileNameType;
15 typedef std::map< FileNameType, MapEntryValuesPtr > MapFileValuesType;
16
17 struct ParserException
18 {
19    std::string error;
20    static std::string Indent;
21
22    static std::string GetIndent() { return ParserException::Indent; }
23    ParserException( std::string ErrorMessage )
24    {
25       error = ErrorMessage;
26       Indent = "      ";
27    }
28    void Print() { std::cerr << Indent << error << std::endl; }
29 };
30
31 std::string ParserException::Indent = "      ";
32
33 class ReferenceFileParser
34 {
35    bool AddKeyValuePairToMap( std::string& key, std::string& value );
36
37    std::istream& eatwhite(std::istream& is);
38    void eatwhite(std::string& toClean);
39    std::string ExtractFirstString(std::string& toSplit);
40    void CleanUpLine( std::string& line );
41
42    std::string ExtractValue(std::string& toSplit)  throw ( ParserException );
43    void ParseRegularLine( std::string& line ) throw ( ParserException );
44    void FirstPassReferenceFile()         throw ( ParserException );
45    bool SecondPassReferenceFile()        throw ( ParserException );
46    void HandleFileName( std::string& line )   throw ( ParserException );
47    void HandleKey( std::string& line )        throw ( ParserException );
48    bool HandleValue( std::string& line )      throw ( ParserException );
49    static uint16_t axtoi( char* );
50 public:
51    ReferenceFileParser();
52    bool Open( std::string& referenceFileName );
53    void Print();
54    void SetDataPath(std::string&);
55    bool Check();
56 private:
57    /// The directory containing the images to check:
58    std::string DataPath;
59
60    /// The product of the parser:
61    MapFileValuesType ProducedMap;
62
63    /// The ifstream attached to the file we parse:
64    std::ifstream from;
65
66    /// String prefixing every output
67    std::string Indent;
68
69    /// The current line position within the stream:
70    int lineNumber;
71
72    /// The currently parsed filename:
73    std::string CurrentFileName;
74
75    /// The currently parsed key:
76    std::string CurrentKey;
77
78    /// The currently parsed value:
79    std::string CurrentValue;
80
81    /// The current MapEntryValues pointer:
82    MapEntryValues* CurrentMapEntryValuesPtr;
83 };
84
85 /// As gotten from:
86 /// http://community.borland.com/article/0,1410,17203,0.html
87 uint16_t ReferenceFileParser::axtoi(char *hexStg) {
88   int n = 0;         // position in string
89   int m = 0;         // position in digit[] to shift
90   int count;         // loop index
91   int intValue = 0;  // integer value of hex string
92   int digit[5];      // hold values to convert
93   while (n < 4) {
94      if (hexStg[n]=='\0')
95         break;
96      if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9
97         digit[n] = hexStg[n] & 0x0f;            //convert to int
98      else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f
99         digit[n] = (hexStg[n] & 0x0f) + 9;      //convert to int
100      else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F
101         digit[n] = (hexStg[n] & 0x0f) + 9;      //convert to int
102      else break;
103     n++;
104   }
105   count = n;
106   m = n - 1;
107   n = 0;
108   while(n < count) {
109      // digit[n] is value of hex digit at position n
110      // (m << 2) is the number of positions to shift
111      // OR the bits into return value
112      intValue = intValue | (digit[n] << (m << 2));
113      m--;   // adjust the position to set
114      n++;   // next digit to process
115   }
116   return intValue;
117 }
118
119 void ReferenceFileParser::SetDataPath( std::string& inDataPath )
120 {
121    DataPath = inDataPath;
122 }
123
124 bool ReferenceFileParser::AddKeyValuePairToMap( std::string& key, std::string& value )
125 {
126    if ( !CurrentMapEntryValuesPtr )
127       return false;
128    if ( CurrentMapEntryValuesPtr->count(key) != 0 )
129       return false;
130    (*CurrentMapEntryValuesPtr)[key] = value;
131    
132    return true; //??
133 }
134
135 void ReferenceFileParser::Print()
136 {
137    for (MapFileValuesType::iterator i  = ProducedMap.begin();
138                                     i != ProducedMap.end();
139                                     ++i)
140    {
141       std::cout << Indent << "FileName: " << i->first << std::endl;
142       MapEntryValuesPtr KeyValues = i->second;
143       for (MapEntryValues::iterator j  = KeyValues->begin();
144                                     j != KeyValues->end();
145                                     ++j)
146       {
147          std::cout << Indent
148               << "  Key: " << j->first
149               << "  Value: " << j->second
150               << std::endl;
151       }
152       std::cout << Indent << std::endl;
153    }
154    std::cout << Indent << std::endl;
155 }
156
157 bool ReferenceFileParser::Check()
158 {
159    for (MapFileValuesType::iterator i  = ProducedMap.begin();
160                                     i != ProducedMap.end();
161                                     ++i)
162    {
163       std::string fileName = DataPath + i->first;
164       std::cout << Indent << "FileName: " << fileName << std::endl;
165       gdcm::Header* tested = new gdcm::Header( fileName.c_str() );
166       if( !tested->IsReadable() )
167       {
168         std::cerr << Indent << "Image not gdcm compatible:"
169              << fileName << std::endl;
170         delete tested;
171         return false;
172       }
173
174       MapEntryValuesPtr KeyValues = i->second;
175       for (MapEntryValues::iterator j  = KeyValues->begin();
176                                     j != KeyValues->end();
177                                     ++j)
178       {
179          std::string key = j->first;
180
181          std::string groupString  = key.substr( 0, 4 );
182          char* groupCharPtr;
183          groupCharPtr = new char(groupString.length() + 1);
184          strcpy( groupCharPtr, groupString.c_str() ); 
185
186          std::string groupElement = key.substr( key.find_first_of( "|" ) + 1, 4 );
187          char* groupElementPtr;
188          groupElementPtr = new char(groupElement.length() + 1);
189          strcpy( groupElementPtr, groupElement.c_str() ); 
190
191          uint16_t group   = axtoi( groupCharPtr );
192          uint16_t element = axtoi( groupElementPtr );
193
194          std::string testedValue = tested->GetEntryByNumber(group, element);
195          if ( testedValue != j->second )
196          {
197             // Oops make sure this is only the \0 that differ
198             if( testedValue[j->second.size()] != '\0' ||
199                 strncmp(testedValue.c_str(), 
200                         j->second.c_str(), j->second.size()) != 0)
201             {
202                std::cout << Indent << "Uncorrect value for key " 
203                          << key << std::endl
204                          << Indent << "   read value      [" 
205                          << testedValue << "]" << std::endl
206                          << Indent << "   reference value [" 
207                          << j->second << "]" << std::endl;
208             return false;
209             }
210          }
211       }
212       delete tested;
213       std::cout << Indent << "  OK" << std::endl;
214    }
215    std::cout << Indent << std::endl;
216    return true;
217 }
218
219 std::istream& ReferenceFileParser::eatwhite( std::istream& is )
220 {
221    char c;
222    while (is.get(c)) {
223       if (!isspace(c)) {
224          is.putback(c);
225          break;
226       }
227    }
228    return is;
229 }
230
231 void ReferenceFileParser::eatwhite( std::string& toClean )
232 {
233    while( toClean.find_first_of( " " ) == 0  )
234       toClean.erase( 0, toClean.find_first_of( " " ) + 1 );
235 }
236
237 std::string ReferenceFileParser::ExtractFirstString( std::string& toSplit )
238 {
239    std::string firstString;
240    eatwhite( toSplit );
241    if ( toSplit.find( " " ) == std::string::npos ) {
242       firstString = toSplit;
243       toSplit.erase();
244       return firstString;
245    }
246    firstString = toSplit.substr( 0, toSplit.find(" ") );
247    toSplit.erase( 0, toSplit.find(" ") + 1);
248    eatwhite( toSplit );
249    return firstString;
250 }
251
252 std::string ReferenceFileParser::ExtractValue( std::string& toSplit )
253    throw ( ParserException )
254 {
255    eatwhite( toSplit );
256    std::string::size_type beginPos = toSplit.find_first_of( '"' );
257    std::string::size_type   endPos = toSplit.find_last_of( '"' );
258
259    // Make sure we have at most two " in toSplit:
260    std::string noQuotes = toSplit.substr( beginPos + 1, endPos - beginPos - 1);
261    if ( noQuotes.find_first_of( '"' ) != std::string::npos )
262       throw ParserException( "more than two quote character" );
263
264    // No leading quote means this is not a value:
265    if ( beginPos == std::string::npos )
266    {
267       return std::string();
268    }
269
270    if ( ( endPos == std::string::npos ) || ( beginPos == endPos ) )
271       throw ParserException( "unmatched \" (quote character)" );
272
273    if ( beginPos != 0 )
274    {
275       std::ostringstream error;
276       error  << "leading character ["
277              << toSplit.substr(beginPos -1, 1)
278              << "] before opening \" ";
279       throw ParserException( error.str() );
280    }
281
282    // When they are some extra characters at end of value, it must
283    // be a space:
284    if (   ( endPos != toSplit.length() - 1 )
285        && ( toSplit.substr(endPos + 1, 1) != " " ) )
286    {
287       std::ostringstream error;
288       error  << "trailing character ["
289              << toSplit.substr(endPos + 1, 1)
290              << "] after value closing \" ";
291       throw ParserException( error.str() );
292    }
293
294    std::string value = toSplit.substr( beginPos + 1, endPos - beginPos - 1 );
295    toSplit.erase(  beginPos, endPos - beginPos + 1);
296    eatwhite( toSplit );
297    return value;
298 }
299
300 /// \brief   Checks the block syntax of the incoming ifstream. Checks that
301 ///          - no nested blocks are present
302 ///          - we encounter a matching succesion of "[" and "]"
303 ///          - when ifstream terminates the last block is closed.
304 ///          - a block is not opened and close on the same line
305 /// @param   from The incoming ifstream to be checked.
306 /// @return  True when incoming ifstream has a correct syntax, false otherwise.
307 /// \warning The underlying file pointer is not preseved.
308 void ReferenceFileParser::FirstPassReferenceFile() throw ( ParserException )
309 {
310    std::string line;
311    lineNumber = 1;
312    bool inBlock = false;
313    from.seekg( 0, std::ios::beg );
314
315    while ( ! from.eof() )
316    {
317       std::getline( from, line );
318
319       /// This is how we usually end the parsing because we hit EOF:
320       if ( ! from.good() )
321       {
322          if ( ! inBlock )
323             break;
324          else
325             throw ParserException( "Syntax error: EOF reached when in block.");
326       }
327
328       // Don't try to parse comments (weed out anything after first "#"):
329       if ( line.find_first_of( "#" ) != std::string::npos )
330       {
331          line.erase( line.find_first_of( "#" ) );
332       }
333
334       // Two occurences of opening blocks on a single line implies nested
335       // blocks which is illegal:
336       if ( line.find_first_of( "[" ) != line.find_last_of( "[" ) )
337       {
338          std::ostringstream error;
339          error << "Syntax error: nested block (open) in reference file"
340                << std::endl
341                << ParserException::GetIndent()
342                << "   at line " << lineNumber << std::endl;
343          throw ParserException( error.str() );
344       }
345
346       // Two occurences of closing blocks on a single line implies nested
347       // blocks which is illegal:
348       if ( line.find_first_of( "]" ) != line.find_last_of( "]" ) )
349       {
350          std::ostringstream error;
351          error << "Syntax error: nested block (close) in reference file"
352                << std::endl
353                << ParserException::GetIndent()
354                << "   at line " << lineNumber << std::endl;
355          throw ParserException( error.str() );
356       }
357
358       bool beginBlock ( line.find_first_of("[") != std::string::npos );
359       bool endBlock   ( line.find_last_of("]")  != std::string::npos );
360
361       // Opening and closing of block on same line:
362       if ( beginBlock && endBlock )
363       {
364          std::ostringstream error;
365          error << "Syntax error: opening and closing on block on same line "
366                << lineNumber++ << std::endl;
367          throw ParserException( error.str() );
368       }
369
370       // Illegal closing block when block not open:
371       if ( !inBlock && endBlock )
372       {
373          std::ostringstream error;
374          error << "Syntax error: unexpected end of block at line "
375                << lineNumber++ << std::endl;
376          throw ParserException( error.str() );
377       }
378   
379       // Uncommented line outside of block is not clean:
380       if ( !inBlock && !beginBlock )
381       {
382          continue;
383       }
384
385       if ( inBlock && beginBlock )
386       {
387          std::ostringstream error;
388          error << "   Syntax error: illegal opening of nested block at line "
389                << lineNumber++ << std::endl;
390          throw ParserException( error.str() );
391       }
392
393       // Normal situation of opening block:
394       if ( beginBlock )
395       {
396          inBlock = true;
397          lineNumber++;
398          continue;
399       }
400
401       // Normal situation of closing block:
402       if ( endBlock )
403       {
404          inBlock = false;
405          lineNumber++;
406          continue;
407       }
408       // This line had no block delimiter
409       lineNumber++;
410    }
411
412    // We need rewinding:
413    from.clear();
414    from.seekg( 0, std::ios::beg );
415 }
416
417 ReferenceFileParser::ReferenceFileParser()
418 {
419    lineNumber = 1;
420    Indent = "      ";
421 }
422
423 bool ReferenceFileParser::Open( std::string& referenceFileName )
424 {
425    from.open( referenceFileName.c_str(), std::ios::in );
426    if ( !from.is_open() )
427    {
428       std::cerr << Indent << "Can't open reference file." << std::endl;
429    }
430    
431    try
432    {
433       FirstPassReferenceFile();
434       SecondPassReferenceFile();
435    }
436    catch ( ParserException except )
437    {
438       except.Print();
439       return false;
440    }
441
442    from.close();
443    return true; //??
444 }
445
446 void ReferenceFileParser::CleanUpLine( std::string& line )
447 {
448    // Cleanup from comments:
449    if ( line.find_first_of( "#" ) != std::string::npos )
450       line.erase( line.find_first_of( "#" ) );
451
452    // Cleanup everything after end block delimiter:
453    if ( line.find_last_of( "]" ) != std::string::npos )
454       line.erase( line.find_last_of( "]" ) + 1 );
455
456    // Cleanup leading whites and skip empty lines:
457    eatwhite( line );
458 }
459
460 void ReferenceFileParser::HandleFileName( std::string& line )
461    throw ( ParserException )
462 {
463    if ( line.length() == 0 )
464       throw ParserException( "empty line on call of HandleFileName" );
465
466    if ( CurrentFileName.length() != 0 )
467       return;
468
469    CurrentFileName = ExtractFirstString(line);
470 }
471
472 void ReferenceFileParser::HandleKey( std::string& line )
473    throw ( ParserException )
474 {
475    if ( CurrentKey.length() != 0 )
476       return;
477
478    CurrentKey = ExtractFirstString(line);
479    if ( CurrentKey.find_first_of( "|" ) == std::string::npos )
480    {
481       std::ostringstream error;
482       error  << "uncorrect key:" << CurrentKey;
483       throw ParserException( error.str() );
484    }
485 }
486
487 bool ReferenceFileParser::HandleValue( std::string& line )
488    throw ( ParserException )
489 {
490    if ( line.length() == 0 )
491       throw ParserException( "empty line in HandleValue" );
492
493    if ( CurrentKey.length() == 0 )
494    {
495       std::cout << Indent << "No key present:" << CurrentKey << std::endl;
496       return false;
497    }
498    
499    std::string newCurrentValue = ExtractValue(line);
500    if ( newCurrentValue.length() == 0 )
501    {
502       std::cout << Indent << "Warning: empty value for key:"
503                      << CurrentKey << std::endl;
504    }
505
506    CurrentValue += newCurrentValue;
507    return true;
508 }
509
510 void ReferenceFileParser::ParseRegularLine( std::string& line)
511    throw ( ParserException )
512 {
513    if ( line.length() == 0 )
514       return;
515
516    // First thing is to get a filename:
517    HandleFileName( line );
518
519    if ( line.length() == 0 )
520       return;
521
522    // Second thing is to get a key:
523    HandleKey( line );
524        
525    if ( line.length() == 0 )
526       return;
527
528    // Third thing is to get a value:
529    if ( ! HandleValue( line ) )
530       return;
531
532    if ( CurrentKey.length() && CurrentValue.length() )
533    {
534       if ( ! AddKeyValuePairToMap( CurrentKey, CurrentValue ) )
535          throw ParserException( "adding to map of (key, value) failed" );
536       CurrentKey.erase();
537       CurrentValue.erase();
538    }
539 }
540
541 bool ReferenceFileParser::SecondPassReferenceFile()
542    throw ( ParserException )
543 {
544    gdcm::TagKey key;
545    EntryValueType value;
546    std::string line;
547    bool inBlock = false;
548    lineNumber = 0;
549
550    while ( !from.eof() )
551    {
552       std::getline( from, line );
553       lineNumber++;
554
555       CleanUpLine( line );
556
557       // Empty lines don't require any treatement:
558       if ( line.length() == 0 )
559          continue;
560
561       bool beginBlock ( line.find_first_of("[") != std::string::npos );
562       bool endBlock   ( line.find_last_of("]")  != std::string::npos );
563
564       // Waiting for a block to be opened. Meanwhile, drop everything:
565       if ( !inBlock && !beginBlock )
566          continue;
567
568       if ( beginBlock )
569       {
570          inBlock = true;
571          line.erase( 0, line.find_first_of( "[" ) + 1 );
572          eatwhite( line );
573          CurrentMapEntryValuesPtr = new MapEntryValues();
574       }
575       else if ( endBlock )
576       {
577          line.erase( line.find_last_of( "]" ) );
578          eatwhite( line );
579          ParseRegularLine( line );
580          ProducedMap[CurrentFileName] = CurrentMapEntryValuesPtr;
581          inBlock = false;
582          CurrentFileName.erase();
583       }
584    
585       // Outside block lines are dropped:
586       if ( ! inBlock )
587          continue;
588
589       ParseRegularLine( line );
590    }
591    return true; //??
592 }
593
594 int TestAllEntryVerify(int argc, char* argv[]) 
595 {
596    if ( argc > 1 )
597    {
598       std::cerr << "   Usage: " << argv[0]
599                 << " (no arguments needed)." << std::endl;
600       return 1;
601    }
602
603    std::string referenceDir = GDCM_DATA_ROOT;
604    referenceDir       += "/";
605    std::string referenceFilename = referenceDir + "TestAllEntryVerifyReference.txt";
606    
607    std::cout << "   Description (Test::TestAllEntryVerify): "
608         << std::endl;
609    std::cout << "   For all images (not blacklisted in gdcm/Test/CMakeLists.txt)"
610         << std::endl;
611    std::cout << "   encountered in directory: " << GDCM_DATA_ROOT << std::endl;
612    std::cout << "   apply the following tests : "<< std::endl;
613    std::cout << "   step 1: parse the image and call IsReadable(). "  << std::endl;
614    std::cout << "   step 2: look for the entry corresponding to the image" << std::endl;
615    std::cout << "           in the reference file: " << referenceFilename << std::endl;
616    std::cout << "   step 3: check that each reference tag value listed for this"
617         << std::endl;
618    std::cout << "           entry matches the tag encountered at parsing step 1."
619         << std::endl << std::endl;
620
621    ReferenceFileParser Parser;
622    if ( !Parser.Open(referenceFilename) )
623    {
624       std::cout << "   Corrupted reference file name: "
625            << referenceFilename << std::endl;
626       return 1;
627    }
628    Parser.SetDataPath(referenceDir);
629    // Parser.Print();
630    if ( Parser.Check() )
631       return 0;
632    return 1;
633 }