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