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