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