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