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