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