]> Creatis software - gdcm.git/blob - Testing/TestAllEntryVerify.cxx
* WARNING: ctest now depends more tightly on gdcmData. You should
[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
134 void ReferenceFileParser::Print()
135 {
136    for (MapFileValuesType::iterator i  = ProducedMap.begin();
137                                     i != ProducedMap.end();
138                                     ++i)
139    {
140       cout << Indent << "FileName: " << i->first << endl;
141       MapEntryValuesPtr KeyValues = i->second;
142       for (MapEntryValues::iterator j  = KeyValues->begin();
143                                     j != KeyValues->end();
144                                     ++j)
145       {
146          cout << Indent
147               << "  Key: " << j->first
148               << "  Value: " << j->second
149               << endl;
150       }
151       cout << Indent << endl;
152    }
153    cout << Indent << endl;
154 }
155
156 bool ReferenceFileParser::Check()
157 {
158    for (MapFileValuesType::iterator i  = ProducedMap.begin();
159                                     i != ProducedMap.end();
160                                     ++i)
161    {
162       string fileName = DataPath + i->first;
163       cout << Indent << "FileName: " << fileName << endl;
164       gdcmHeader* tested = new gdcmHeader( fileName.c_str(), false, true );
165       if( !tested->IsReadable() )
166       {
167         cerr << Indent << "Image not gdcm compatible:"
168              << fileName << endl;
169         delete tested;
170         return false;
171       }
172
173       MapEntryValuesPtr KeyValues = i->second;
174       for (MapEntryValues::iterator j  = KeyValues->begin();
175                                     j != KeyValues->end();
176                                     ++j)
177       {
178          string key = j->first;
179
180          string groupString  = key.substr( 0, 4 );
181          char* groupCharPtr;
182          groupCharPtr = new char(groupString.length() + 1);
183          strcpy( groupCharPtr, groupString.c_str() ); 
184
185          string groupElement = key.substr( key.find_first_of( "|" ) + 1, 4 );
186          char* groupElementPtr;
187          groupElementPtr = new char(groupElement.length() + 1);
188          strcpy( groupElementPtr, groupElement.c_str() ); 
189
190          uint16_t group   = axtoi( groupCharPtr );
191          uint16_t element = axtoi( groupElementPtr );
192
193          string testedValue = tested->GetEntryByNumber(group, element);
194          if ( testedValue != j->second )
195          {
196             cout << Indent << "Uncorrect value for key " << key << endl
197                  << Indent << "   read value " << testedValue << endl
198                  << Indent << "   reference value " << j->second << endl;
199             return false;
200          }
201       }
202       delete tested;
203       cout << Indent << endl;
204    }
205    cout << Indent << endl;
206 }
207
208 istream& ReferenceFileParser::eatwhite( istream& is )
209 {
210    char c;
211    while (is.get(c)) {
212       if (!isspace(c)) {
213          is.putback(c);
214          break;
215       }
216    }
217    return is;
218 }
219
220 void ReferenceFileParser::eatwhite( string& toClean )
221 {
222    while( toClean.find_first_of( " " ) == 0  )
223       toClean.erase( 0, toClean.find_first_of( " " ) + 1 );
224 }
225
226 string ReferenceFileParser::ExtractFirstString( string& toSplit )
227 {
228    std::string firstString;
229    eatwhite( toSplit );
230    if ( toSplit.find( " " ) == string::npos ) {
231       firstString = toSplit;
232       toSplit.erase();
233       return firstString;
234    }
235    firstString = toSplit.substr( 0, toSplit.find(" ") );
236    toSplit.erase( 0, toSplit.find(" ") + 1);
237    eatwhite( toSplit );
238    return firstString;
239 }
240
241 string ReferenceFileParser::ExtractValue( string& toSplit )
242    throw ( ParserException )
243 {
244    eatwhite( toSplit );
245    string::size_type beginPos = toSplit.find_first_of( '"' );
246    string::size_type   endPos = toSplit.find_last_of( '"' );
247
248    // Make sure we have at most to " in toSplit:
249    string noQuotes = toSplit.substr( beginPos + 1, endPos - beginPos - 1);
250    if ( noQuotes.find_first_of( '"' ) != string::npos )
251       throw ParserException( "more than two quote character" );
252
253    // No leading quote means this is not a value:
254    if ( beginPos == string::npos )
255    {
256       return string();
257    }
258
259    if ( ( endPos == string::npos ) || ( beginPos == endPos ) )
260       throw ParserException( "unmatched \" (quote character)" );
261
262    if ( beginPos != 0 )
263    {
264       ostringstream error;
265       error  << "leading character ["
266              << toSplit.substr(beginPos -1, 1)
267              << "] before opening \" ";
268       throw ParserException( error.str() );
269    }
270
271    // When they are some extra characters at end of value, it must
272    // be a space:
273    if (   ( endPos != toSplit.length() - 1 )
274        && ( toSplit.substr(endPos + 1, 1) != " " ) )
275    {
276       ostringstream error;
277       error  << "trailing character ["
278              << toSplit.substr(endPos + 1, 1)
279              << "] after value closing \" ";
280       throw ParserException( error.str() );
281    }
282
283    string value = toSplit.substr( beginPos + 1, endPos - beginPos - 1 );
284    toSplit.erase(  beginPos, endPos - beginPos + 1);
285    eatwhite( toSplit );
286    return value;
287 }
288
289 /// \brief   Checks the block syntax of the incoming ifstream. Checks that
290 ///          - no nested blocks are present
291 ///          - we encounter a matching succesion of "[" and "]"
292 ///          - when ifstream terminates the last block is closed.
293 ///          - a block is not opened and close on the same line
294 /// @param   from The incoming ifstream to be checked.
295 /// @return  True when incoming ifstream has a correct syntax, false otherwise.
296 /// \warning The underlying file pointer is not preseved.
297 void ReferenceFileParser::FirstPassReferenceFile() throw ( ParserException )
298 {
299    string line;
300    lineNumber = 1;
301    bool inBlock = false;
302    from.seekg( 0, ios::beg );
303
304    while ( ! from.eof() )
305    {
306       getline( from, line );
307
308       /// This is how we usually end the parsing because we hit EOF:
309       if ( ! from.good() )
310       {
311          if ( ! inBlock )
312             break;
313          else
314             throw ParserException( "Syntax error: EOF reached when in block.");
315       }
316
317       // Don't try to parse comments (weed out anything after first "#"):
318       if ( line.find_first_of( "#" ) != string::npos )
319       {
320          line.erase( line.find_first_of( "#" ) );
321       }
322
323       // Two occurences of opening blocks on a single line implies nested
324       // blocks which is illegal:
325       if ( line.find_first_of( "[" ) != line.find_last_of( "[" ) )
326       {
327          ostringstream error;
328          error << "Syntax error: nested block (open) in reference file"
329                << endl
330                << ParserException::GetIndent()
331                << "   at line " << lineNumber << endl;
332          throw ParserException( error.str() );
333       }
334
335       // Two occurences of closing blocks on a single line implies nested
336       // blocks which is illegal:
337       if ( line.find_first_of( "]" ) != line.find_last_of( "]" ) )
338       {
339          ostringstream error;
340          error << "Syntax error: nested block (close) in reference file"
341                << endl
342                << ParserException::GetIndent()
343                << "   at line " << lineNumber << endl;
344          throw ParserException( error.str() );
345       }
346
347       bool beginBlock ( line.find_first_of("[") != string::npos );
348       bool endBlock   ( line.find_last_of("]")  != string::npos );
349
350       // Opening and closing of block on same line:
351       if ( beginBlock && endBlock )
352       {
353          ostringstream error;
354          error << "Syntax error: opening and closing on block on same line "
355                << lineNumber++ << endl;
356          throw ParserException( error.str() );
357       }
358
359       // Illegal closing block when block not open:
360       if ( !inBlock && endBlock )
361       {
362          ostringstream error;
363          error << "Syntax error: unexpected end of block at line "
364                << lineNumber++ << endl;
365          throw ParserException( error.str() );
366       }
367   
368       // Uncommented line outside of block is not clean:
369       if ( !inBlock && !beginBlock )
370       {
371          cerr << Indent
372               << "Syntax warning: outside of block [] data at line "
373               << lineNumber++ << " not considered." << endl;
374          continue;
375       }
376
377       if ( inBlock && beginBlock )
378       {
379          ostringstream error;
380          error << "   Syntax error: illegal opening of nested block at line "
381                << lineNumber++ << endl;
382          throw ParserException( error.str() );
383       }
384
385       // Normal situation of opening block:
386       if ( beginBlock )
387       {
388          inBlock = true;
389          lineNumber++;
390          continue;
391       }
392
393       // Normal situation of closing block:
394       if ( endBlock )
395       {
396          inBlock = false;
397          lineNumber++;
398          continue;
399       }
400       // This line had no block delimiter
401       lineNumber++;
402    }
403
404    // We need rewinding:
405    from.clear();
406    from.seekg( 0, ios::beg );
407 }
408
409 ReferenceFileParser::ReferenceFileParser()
410 {
411    lineNumber = 1;
412    Indent = "      ";
413 }
414
415 bool ReferenceFileParser::Open( string& referenceFileName )
416 {
417    from.open( referenceFileName.c_str(), ios::in );
418    if ( !from.is_open() )
419    {
420       cerr << Indent << "Can't open reference file." << endl;
421    }
422    
423    try
424    {
425       FirstPassReferenceFile();
426       SecondPassReferenceFile();
427    }
428    catch ( ParserException except )
429    {
430       except.Print();
431       return false;
432    }
433
434    from.close();
435 }
436
437 void ReferenceFileParser::CleanUpLine( string& line )
438 {
439    // Cleanup from comments:
440    if ( line.find_first_of( "#" ) != string::npos )
441       line.erase( line.find_first_of( "#" ) );
442
443    // Cleanup everything after end block delimiter:
444    if ( line.find_last_of( "]" ) != string::npos )
445       line.erase( line.find_last_of( "]" ) + 1 );
446
447    // Cleanup leanding whites and skip empty lines:
448    eatwhite( line );
449 }
450
451 void ReferenceFileParser::HandleFileName( string& line )
452    throw ( ParserException )
453 {
454    if ( line.length() == 0 )
455       throw ParserException( "empty line on call of HandleFileName" );
456
457    if ( CurrentFileName.length() != 0 )
458       return;
459
460    CurrentFileName = ExtractFirstString(line);
461 }
462
463 void ReferenceFileParser::HandleKey( string& line )
464    throw ( ParserException )
465 {
466    if ( CurrentKey.length() != 0 )
467       return;
468
469    CurrentKey = ExtractFirstString(line);
470    if ( CurrentKey.find_first_of( "|" ) == string::npos )
471    {
472       ostringstream error;
473       error  << "uncorrect key:" << CurrentKey;
474       throw ParserException( error.str() );
475    }
476 }
477
478 bool ReferenceFileParser::HandleValue( string& line )
479    throw ( ParserException )
480 {
481    if ( line.length() == 0 )
482       throw ParserException( "empty line in HandleValue" );
483
484    if ( CurrentKey.length() == 0 )
485    {
486       cout << Indent << "No key present:" << CurrentKey << endl;
487       return false;
488    }
489    
490    string newCurrentValue = ExtractValue(line);
491    if ( newCurrentValue.length() == 0 )
492    {
493       ostringstream error;
494       error  << "missing value for key:" << CurrentKey;
495       throw ParserException( error.str() );
496    }
497
498    CurrentValue += newCurrentValue;
499    return true;
500 }
501
502 void ReferenceFileParser::ParseRegularLine(string& line)
503    throw ( ParserException )
504 {
505    if ( line.length() == 0 )
506       return;
507
508    // First thing is to get a filename:
509    HandleFileName( line );
510
511    if ( line.length() == 0 )
512       return;
513
514    // Second thing is to get a key:
515    HandleKey( line );
516        
517    if ( line.length() == 0 )
518       return;
519
520    // Third thing is to get a value:
521    if ( ! HandleValue( line ) )
522       return;
523
524    if ( CurrentKey.length() && CurrentValue.length() )
525    {
526       if ( ! AddKeyValuePairToMap( CurrentKey, CurrentValue ) )
527          throw ParserException( "adding to map of (key, value) failed" );
528       CurrentKey.erase();
529       CurrentValue.erase();
530    }
531 }
532
533 bool ReferenceFileParser::SecondPassReferenceFile()
534    throw ( ParserException )
535 {
536    gdcmTagKey key;
537    EntryValueType value;
538    string line;
539    bool inBlock = false;
540    lineNumber = 0;
541
542    while ( !from.eof() )
543    {
544       getline( from, line );
545       lineNumber++;
546
547       CleanUpLine( line );
548
549       // Empty lines don't require any treatement:
550       if ( line.length() == 0 )
551          continue;
552
553       bool beginBlock ( line.find_first_of("[") != string::npos );
554       bool endBlock   ( line.find_last_of("]")  != string::npos );
555
556       // Waiting for a block to be opened. Meanwhile, drop everything:
557       if ( !inBlock && !beginBlock )
558          continue;
559
560       if ( beginBlock )
561       {
562          inBlock = true;
563          line.erase( 0, line.find_first_of( "[" ) + 1 );
564          eatwhite( line );
565          CurrentMapEntryValuesPtr = new MapEntryValues();
566       }
567       else if ( endBlock )
568       {
569          line.erase( line.find_last_of( "]" ) );
570          eatwhite( line );
571          ParseRegularLine( line );
572          ProducedMap[CurrentFileName] = CurrentMapEntryValuesPtr;
573          inBlock = false;
574          CurrentFileName.erase();
575       }
576    
577       // Outside block lines are dropped:
578       if ( ! inBlock )
579          continue;
580
581       ParseRegularLine( line );
582    }
583 }
584
585 int TestAllEntryVerify(int argc, char* argv[]) 
586 {
587    if ( argc > 1 )
588    {
589       cerr << "   Usage: " << argv[0]
590                 << " (no arguments needed)." << endl;
591       return 1;
592    }
593    
594    cout << "   Description (Test::TestAllEntryVerify): "
595              << endl;
596    cout << "   For all images in gdcmData (and not blacklisted in "
597                 "Test/CMakeLists.txt)"
598              << endl;
599    cout << "   apply the following to each filename.xxx: "
600              << endl;
601    cout << "   step 1: parse the image (as gdcmHeader) and call"
602              << " IsReadable(). "
603              << endl;
604
605    string referenceDir = GDCM_DATA_ROOT;
606    referenceDir       += "/";
607    string referenceFilename = referenceDir + "TestAllEntryVerifyReference.txt";
608
609    ReferenceFileParser Parser;
610    Parser.Open(referenceFilename);
611    Parser.SetDataPath(referenceDir);
612    // Parser.Print();
613    Parser.Check();
614 /*
615    int i = 0;
616    while( gdcmDataImages[i] != 0 )
617    {
618       string filename = GDCM_DATA_ROOT;
619       filename += "/";  //doh!
620       filename += gdcmDataImages[i++];
621    
622       cout << "   Testing: " << filename << endl;
623
624       gdcmHeader* tested = new gdcmHeader( filename.c_str(), false, true );
625       if( !tested->GetHeader()->IsReadable() )
626       {
627         cout << "      Image not gdcm compatible:"
628                   << filename << endl;
629         delete tested;
630         return 1;
631       }
632
633       //////////////// Clean up:
634       delete tested;
635    }
636 */
637
638    return 0;
639 }