1 /*=========================================================================
4 Module: $RCSfile: TestAllEntryVerify.cxx,v $
6 Date: $Date: 2004/11/16 04:28:20 $
7 Version: $Revision: 1.14 $
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.
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.
17 =========================================================================*/
18 #include "gdcmHeader.h"
26 #include "gdcmDataImages.h"
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;
34 struct ParserException
37 static std::string Indent;
39 static std::string GetIndent() { return ParserException::Indent; }
40 ParserException( std::string ErrorMessage )
45 void Print() { std::cerr << Indent << error << std::endl; }
48 std::string ParserException::Indent = " ";
50 class ReferenceFileParser
52 bool AddKeyValuePairToMap( std::string& key, std::string& value );
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 );
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* );
68 ReferenceFileParser();
69 bool Open( std::string& referenceFileName );
71 void SetDataPath(std::string&);
74 /// The directory containing the images to check:
77 /// The product of the parser:
78 MapFileValuesType ProducedMap;
80 /// The ifstream attached to the file we parse:
83 /// String prefixing every output
86 /// The current line position within the stream:
89 /// The currently parsed filename:
90 std::string CurrentFileName;
92 /// The currently parsed key:
93 std::string CurrentKey;
95 /// The currently parsed value:
96 std::string CurrentValue;
98 /// The current MapEntryValues pointer:
99 MapEntryValues* CurrentMapEntryValuesPtr;
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
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
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
136 void ReferenceFileParser::SetDataPath( std::string& inDataPath )
138 DataPath = inDataPath;
141 bool ReferenceFileParser::AddKeyValuePairToMap( std::string& key, std::string& value )
143 if ( !CurrentMapEntryValuesPtr )
145 if ( CurrentMapEntryValuesPtr->count(key) != 0 )
147 (*CurrentMapEntryValuesPtr)[key] = value;
152 void ReferenceFileParser::Print()
154 for (MapFileValuesType::iterator i = ProducedMap.begin();
155 i != ProducedMap.end();
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();
165 << " Key: " << j->first
166 << " Value: " << j->second
169 std::cout << Indent << std::endl;
171 std::cout << Indent << std::endl;
174 bool ReferenceFileParser::Check()
176 for (MapFileValuesType::iterator i = ProducedMap.begin();
177 i != ProducedMap.end();
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() )
185 std::cerr << Indent << "Image not gdcm compatible:"
186 << fileName << std::endl;
191 MapEntryValuesPtr KeyValues = i->second;
192 for (MapEntryValues::iterator j = KeyValues->begin();
193 j != KeyValues->end();
196 std::string key = j->first;
198 std::string groupString = key.substr( 0, 4 );
200 groupCharPtr = new char(groupString.length() + 1);
201 strcpy( groupCharPtr, groupString.c_str() );
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() );
208 uint16_t group = axtoi( groupCharPtr );
209 uint16_t element = axtoi( groupElementPtr );
211 std::string testedValue = tested->GetEntryByNumber(group, element);
212 if ( testedValue != j->second )
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)
219 std::cout << Indent << "Uncorrect value for key "
221 << Indent << " read value ["
222 << testedValue << "]" << std::endl
223 << Indent << " reference value ["
224 << j->second << "]" << std::endl;
230 std::cout << Indent << " OK" << std::endl;
232 std::cout << Indent << std::endl;
236 std::istream& ReferenceFileParser::eatwhite( std::istream& is )
248 void ReferenceFileParser::eatwhite( std::string& toClean )
250 while( toClean.find_first_of( " " ) == 0 )
251 toClean.erase( 0, toClean.find_first_of( " " ) + 1 );
254 std::string ReferenceFileParser::ExtractFirstString( std::string& toSplit )
256 std::string firstString;
258 if ( toSplit.find( " " ) == std::string::npos ) {
259 firstString = toSplit;
263 firstString = toSplit.substr( 0, toSplit.find(" ") );
264 toSplit.erase( 0, toSplit.find(" ") + 1);
269 std::string ReferenceFileParser::ExtractValue( std::string& toSplit )
270 throw ( ParserException )
273 std::string::size_type beginPos = toSplit.find_first_of( '"' );
274 std::string::size_type endPos = toSplit.find_last_of( '"' );
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" );
281 // No leading quote means this is not a value:
282 if ( beginPos == std::string::npos )
284 return std::string();
287 if ( ( endPos == std::string::npos ) || ( beginPos == endPos ) )
288 throw ParserException( "unmatched \" (quote character)" );
292 std::ostringstream error;
293 error << "leading character ["
294 << toSplit.substr(beginPos -1, 1)
295 << "] before opening \" ";
296 throw ParserException( error.str() );
299 // When they are some extra characters at end of value, it must
301 if ( ( endPos != toSplit.length() - 1 )
302 && ( toSplit.substr(endPos + 1, 1) != " " ) )
304 std::ostringstream error;
305 error << "trailing character ["
306 << toSplit.substr(endPos + 1, 1)
307 << "] after value closing \" ";
308 throw ParserException( error.str() );
311 std::string value = toSplit.substr( beginPos + 1, endPos - beginPos - 1 );
312 toSplit.erase( beginPos, endPos - beginPos + 1);
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 )
329 bool inBlock = false;
330 from.seekg( 0, std::ios::beg );
332 while ( ! from.eof() )
334 std::getline( from, line );
336 /// This is how we usually end the parsing because we hit EOF:
342 throw ParserException( "Syntax error: EOF reached when in block.");
345 // Don't try to parse comments (weed out anything after first "#"):
346 if ( line.find_first_of( "#" ) != std::string::npos )
348 line.erase( line.find_first_of( "#" ) );
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( "[" ) )
355 std::ostringstream error;
356 error << "Syntax error: nested block (open) in reference file"
358 << ParserException::GetIndent()
359 << " at line " << lineNumber << std::endl;
360 throw ParserException( error.str() );
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( "]" ) )
367 std::ostringstream error;
368 error << "Syntax error: nested block (close) in reference file"
370 << ParserException::GetIndent()
371 << " at line " << lineNumber << std::endl;
372 throw ParserException( error.str() );
375 bool beginBlock ( line.find_first_of("[") != std::string::npos );
376 bool endBlock ( line.find_last_of("]") != std::string::npos );
378 // Opening and closing of block on same line:
379 if ( beginBlock && endBlock )
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() );
387 // Illegal closing block when block not open:
388 if ( !inBlock && endBlock )
390 std::ostringstream error;
391 error << "Syntax error: unexpected end of block at line "
392 << lineNumber++ << std::endl;
393 throw ParserException( error.str() );
396 // Uncommented line outside of block is not clean:
397 if ( !inBlock && !beginBlock )
402 if ( inBlock && beginBlock )
404 std::ostringstream error;
405 error << " Syntax error: illegal opening of nested block at line "
406 << lineNumber++ << std::endl;
407 throw ParserException( error.str() );
410 // Normal situation of opening block:
418 // Normal situation of closing block:
425 // This line had no block delimiter
429 // We need rewinding:
431 from.seekg( 0, std::ios::beg );
434 ReferenceFileParser::ReferenceFileParser()
440 bool ReferenceFileParser::Open( std::string& referenceFileName )
442 from.open( referenceFileName.c_str(), std::ios::in );
443 if ( !from.is_open() )
445 std::cerr << Indent << "Can't open reference file." << std::endl;
450 FirstPassReferenceFile();
451 SecondPassReferenceFile();
453 catch ( ParserException except )
463 void ReferenceFileParser::CleanUpLine( std::string& line )
465 // Cleanup from comments:
466 if ( line.find_first_of( "#" ) != std::string::npos )
467 line.erase( line.find_first_of( "#" ) );
469 // Cleanup everything after end block delimiter:
470 if ( line.find_last_of( "]" ) != std::string::npos )
471 line.erase( line.find_last_of( "]" ) + 1 );
473 // Cleanup leading whites and skip empty lines:
477 void ReferenceFileParser::HandleFileName( std::string& line )
478 throw ( ParserException )
480 if ( line.length() == 0 )
481 throw ParserException( "empty line on call of HandleFileName" );
483 if ( CurrentFileName.length() != 0 )
486 CurrentFileName = ExtractFirstString(line);
489 void ReferenceFileParser::HandleKey( std::string& line )
490 throw ( ParserException )
492 if ( CurrentKey.length() != 0 )
495 CurrentKey = ExtractFirstString(line);
496 if ( CurrentKey.find_first_of( "|" ) == std::string::npos )
498 std::ostringstream error;
499 error << "uncorrect key:" << CurrentKey;
500 throw ParserException( error.str() );
504 bool ReferenceFileParser::HandleValue( std::string& line )
505 throw ( ParserException )
507 if ( line.length() == 0 )
508 throw ParserException( "empty line in HandleValue" );
510 if ( CurrentKey.length() == 0 )
512 std::cout << Indent << "No key present:" << CurrentKey << std::endl;
516 std::string newCurrentValue = ExtractValue(line);
517 if ( newCurrentValue.length() == 0 )
519 std::cout << Indent << "Warning: empty value for key:"
520 << CurrentKey << std::endl;
523 CurrentValue += newCurrentValue;
527 void ReferenceFileParser::ParseRegularLine( std::string& line)
528 throw ( ParserException )
530 if ( line.length() == 0 )
533 // First thing is to get a filename:
534 HandleFileName( line );
536 if ( line.length() == 0 )
539 // Second thing is to get a key:
542 if ( line.length() == 0 )
545 // Third thing is to get a value:
546 if ( ! HandleValue( line ) )
549 if ( CurrentKey.length() && CurrentValue.length() )
551 if ( ! AddKeyValuePairToMap( CurrentKey, CurrentValue ) )
552 throw ParserException( "adding to map of (key, value) failed" );
554 CurrentValue.erase();
558 bool ReferenceFileParser::SecondPassReferenceFile()
559 throw ( ParserException )
562 EntryValueType value;
564 bool inBlock = false;
567 while ( !from.eof() )
569 std::getline( from, line );
574 // Empty lines don't require any treatement:
575 if ( line.length() == 0 )
578 bool beginBlock ( line.find_first_of("[") != std::string::npos );
579 bool endBlock ( line.find_last_of("]") != std::string::npos );
581 // Waiting for a block to be opened. Meanwhile, drop everything:
582 if ( !inBlock && !beginBlock )
588 line.erase( 0, line.find_first_of( "[" ) + 1 );
590 CurrentMapEntryValuesPtr = new MapEntryValues();
594 line.erase( line.find_last_of( "]" ) );
596 ParseRegularLine( line );
597 ProducedMap[CurrentFileName] = CurrentMapEntryValuesPtr;
599 CurrentFileName.erase();
602 // Outside block lines are dropped:
606 ParseRegularLine( line );
611 int TestAllEntryVerify(int argc, char* argv[])
615 std::cerr << " Usage: " << argv[0]
616 << " (no arguments needed)." << std::endl;
620 std::string referenceDir = GDCM_DATA_ROOT;
622 std::string referenceFilename = referenceDir + "TestAllEntryVerifyReference.txt";
624 std::cout << " Description (Test::TestAllEntryVerify): "
626 std::cout << " For all images (not blacklisted in gdcm/Test/CMakeLists.txt)"
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"
635 std::cout << " entry matches the tag encountered at parsing step 1."
636 << std::endl << std::endl;
638 ReferenceFileParser Parser;
639 if ( !Parser.Open(referenceFilename) )
641 std::cout << " Corrupted reference file name: "
642 << referenceFilename << std::endl;
645 Parser.SetDataPath(referenceDir);
647 if ( Parser.Check() )