5 #include "gdcmHeader.h"
8 #include "gdcmDataImages.h"
12 typedef string EntryValueType; // same type as gdcmValEntry::value
13 typedef map< gdcmTagKey, EntryValueType > MapEntryValues;
14 typedef MapEntryValues* MapEntryValuesPtr;
15 typedef string FileNameType;
16 typedef map< FileNameType, MapEntryValuesPtr > MapFileValuesType;
18 struct ParserException
23 static string GetIndent() { return ParserException::Indent; }
24 ParserException( string ErrorMessage )
29 void Print() { cerr << Indent << error << endl; }
32 string ParserException::Indent = " ";
34 class ReferenceFileParser
36 bool AddKeyValuePairToMap( string& key, string& value );
38 istream& eatwhite(istream& is);
39 void eatwhite(string& toClean);
40 string ExtractFirstString(string& toSplit);
41 void CleanUpLine( string& line );
43 string ExtractValue(string& toSplit) throw ( ParserException );
44 void ParseRegularLine( string& line ) throw ( ParserException );
45 void FirstPassReferenceFile() throw ( ParserException );
46 bool SecondPassReferenceFile() throw ( ParserException );
47 void HandleFileName( string& line ) throw ( ParserException );
48 void HandleKey( string& line ) throw ( ParserException );
49 bool HandleValue( string& line ) throw ( ParserException );
50 static uint16_t axtoi( char* );
52 ReferenceFileParser();
53 bool Open( string& referenceFileName );
55 void SetDataPath(string&);
58 /// The directory containing the images to check:
61 /// The product of the parser:
62 MapFileValuesType ProducedMap;
64 /// The ifstream attached to the file we parse:
67 /// String prefixing every output
70 /// The current line position within the stream:
73 /// The currently parsed filename:
74 string CurrentFileName;
76 /// The currently parsed key:
79 /// The currently parsed value:
82 /// The current MapEntryValues pointer:
83 MapEntryValues* CurrentMapEntryValuesPtr;
87 /// http://community.borland.com/article/0,1410,17203,0.html
88 uint16_t ReferenceFileParser::axtoi(char *hexStg) {
89 int n = 0; // position in string
90 int m = 0; // position in digit[] to shift
91 int count; // loop index
92 int intValue = 0; // integer value of hex string
93 int digit[5]; // hold values to convert
97 if (hexStg[n] > 0x29 && hexStg[n] < 0x40 ) //if 0 to 9
98 digit[n] = hexStg[n] & 0x0f; //convert to int
99 else if (hexStg[n] >='a' && hexStg[n] <= 'f') //if a to f
100 digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
101 else if (hexStg[n] >='A' && hexStg[n] <= 'F') //if A to F
102 digit[n] = (hexStg[n] & 0x0f) + 9; //convert to int
110 // digit[n] is value of hex digit at position n
111 // (m << 2) is the number of positions to shift
112 // OR the bits into return value
113 intValue = intValue | (digit[n] << (m << 2));
114 m--; // adjust the position to set
115 n++; // next digit to process
120 void ReferenceFileParser::SetDataPath( string& inDataPath )
122 DataPath = inDataPath;
125 bool ReferenceFileParser::AddKeyValuePairToMap( string& key, string& value )
127 if ( !CurrentMapEntryValuesPtr )
129 if ( CurrentMapEntryValuesPtr->count(key) != 0 )
131 (*CurrentMapEntryValuesPtr)[key] = value;
134 void ReferenceFileParser::Print()
136 for (MapFileValuesType::iterator i = ProducedMap.begin();
137 i != ProducedMap.end();
140 cout << Indent << "FileName: " << i->first << endl;
141 MapEntryValuesPtr KeyValues = i->second;
142 for (MapEntryValues::iterator j = KeyValues->begin();
143 j != KeyValues->end();
147 << " Key: " << j->first
148 << " Value: " << j->second
151 cout << Indent << endl;
153 cout << Indent << endl;
156 bool ReferenceFileParser::Check()
158 for (MapFileValuesType::iterator i = ProducedMap.begin();
159 i != ProducedMap.end();
162 string fileName = DataPath + i->first;
163 cout << Indent << "FileName: " << fileName << endl;
164 gdcmHeader* tested = new gdcmHeader( fileName.c_str(), false, true );
165 if( !tested->IsReadable() )
167 cerr << Indent << "Image not gdcm compatible:"
173 MapEntryValuesPtr KeyValues = i->second;
174 for (MapEntryValues::iterator j = KeyValues->begin();
175 j != KeyValues->end();
178 string key = j->first;
180 string groupString = key.substr( 0, 4 );
182 groupCharPtr = new char(groupString.length() + 1);
183 strcpy( groupCharPtr, groupString.c_str() );
185 string groupElement = key.substr( key.find_first_of( "|" ) + 1, 4 );
186 char* groupElementPtr;
187 groupElementPtr = new char(groupElement.length() + 1);
188 strcpy( groupElementPtr, groupElement.c_str() );
190 uint16_t group = axtoi( groupCharPtr );
191 uint16_t element = axtoi( groupElementPtr );
193 string testedValue = tested->GetEntryByNumber(group, element);
194 if ( testedValue != j->second )
196 cout << Indent << "Uncorrect value for key " << key << endl
197 << Indent << " read value " << testedValue << endl
198 << Indent << " reference value " << j->second << endl;
203 cout << Indent << endl;
205 cout << Indent << endl;
208 istream& ReferenceFileParser::eatwhite( istream& is )
220 void ReferenceFileParser::eatwhite( string& toClean )
222 while( toClean.find_first_of( " " ) == 0 )
223 toClean.erase( 0, toClean.find_first_of( " " ) + 1 );
226 string ReferenceFileParser::ExtractFirstString( string& toSplit )
228 std::string firstString;
230 if ( toSplit.find( " " ) == string::npos ) {
231 firstString = toSplit;
235 firstString = toSplit.substr( 0, toSplit.find(" ") );
236 toSplit.erase( 0, toSplit.find(" ") + 1);
241 string ReferenceFileParser::ExtractValue( string& toSplit )
242 throw ( ParserException )
245 string::size_type beginPos = toSplit.find_first_of( '"' );
246 string::size_type endPos = toSplit.find_last_of( '"' );
248 // Make sure we have at most to " in toSplit:
249 string noQuotes = toSplit.substr( beginPos + 1, endPos - beginPos - 1);
250 if ( noQuotes.find_first_of( '"' ) != string::npos )
251 throw ParserException( "more than two quote character" );
253 // No leading quote means this is not a value:
254 if ( beginPos == string::npos )
259 if ( ( endPos == string::npos ) || ( beginPos == endPos ) )
260 throw ParserException( "unmatched \" (quote character)" );
265 error << "leading character ["
266 << toSplit.substr(beginPos -1, 1)
267 << "] before opening \" ";
268 throw ParserException( error.str() );
271 // When they are some extra characters at end of value, it must
273 if ( ( endPos != toSplit.length() - 1 )
274 && ( toSplit.substr(endPos + 1, 1) != " " ) )
277 error << "trailing character ["
278 << toSplit.substr(endPos + 1, 1)
279 << "] after value closing \" ";
280 throw ParserException( error.str() );
283 string value = toSplit.substr( beginPos + 1, endPos - beginPos - 1 );
284 toSplit.erase( beginPos, endPos - beginPos + 1);
289 /// \brief Checks the block syntax of the incoming ifstream. Checks that
290 /// - no nested blocks are present
291 /// - we encounter a matching succesion of "[" and "]"
292 /// - when ifstream terminates the last block is closed.
293 /// - a block is not opened and close on the same line
294 /// @param from The incoming ifstream to be checked.
295 /// @return True when incoming ifstream has a correct syntax, false otherwise.
296 /// \warning The underlying file pointer is not preseved.
297 void ReferenceFileParser::FirstPassReferenceFile() throw ( ParserException )
301 bool inBlock = false;
302 from.seekg( 0, ios::beg );
304 while ( ! from.eof() )
306 getline( from, line );
308 /// This is how we usually end the parsing because we hit EOF:
314 throw ParserException( "Syntax error: EOF reached when in block.");
317 // Don't try to parse comments (weed out anything after first "#"):
318 if ( line.find_first_of( "#" ) != string::npos )
320 line.erase( line.find_first_of( "#" ) );
323 // Two occurences of opening blocks on a single line implies nested
324 // blocks which is illegal:
325 if ( line.find_first_of( "[" ) != line.find_last_of( "[" ) )
328 error << "Syntax error: nested block (open) in reference file"
330 << ParserException::GetIndent()
331 << " at line " << lineNumber << endl;
332 throw ParserException( error.str() );
335 // Two occurences of closing blocks on a single line implies nested
336 // blocks which is illegal:
337 if ( line.find_first_of( "]" ) != line.find_last_of( "]" ) )
340 error << "Syntax error: nested block (close) in reference file"
342 << ParserException::GetIndent()
343 << " at line " << lineNumber << endl;
344 throw ParserException( error.str() );
347 bool beginBlock ( line.find_first_of("[") != string::npos );
348 bool endBlock ( line.find_last_of("]") != string::npos );
350 // Opening and closing of block on same line:
351 if ( beginBlock && endBlock )
354 error << "Syntax error: opening and closing on block on same line "
355 << lineNumber++ << endl;
356 throw ParserException( error.str() );
359 // Illegal closing block when block not open:
360 if ( !inBlock && endBlock )
363 error << "Syntax error: unexpected end of block at line "
364 << lineNumber++ << endl;
365 throw ParserException( error.str() );
368 // Uncommented line outside of block is not clean:
369 if ( !inBlock && !beginBlock )
372 << "Syntax warning: outside of block [] data at line "
373 << lineNumber++ << " not considered." << endl;
377 if ( inBlock && beginBlock )
380 error << " Syntax error: illegal opening of nested block at line "
381 << lineNumber++ << endl;
382 throw ParserException( error.str() );
385 // Normal situation of opening block:
393 // Normal situation of closing block:
400 // This line had no block delimiter
404 // We need rewinding:
406 from.seekg( 0, ios::beg );
409 ReferenceFileParser::ReferenceFileParser()
415 bool ReferenceFileParser::Open( string& referenceFileName )
417 from.open( referenceFileName.c_str(), ios::in );
418 if ( !from.is_open() )
420 cerr << Indent << "Can't open reference file." << endl;
425 FirstPassReferenceFile();
426 SecondPassReferenceFile();
428 catch ( ParserException except )
437 void ReferenceFileParser::CleanUpLine( string& line )
439 // Cleanup from comments:
440 if ( line.find_first_of( "#" ) != string::npos )
441 line.erase( line.find_first_of( "#" ) );
443 // Cleanup everything after end block delimiter:
444 if ( line.find_last_of( "]" ) != string::npos )
445 line.erase( line.find_last_of( "]" ) + 1 );
447 // Cleanup leanding whites and skip empty lines:
451 void ReferenceFileParser::HandleFileName( string& line )
452 throw ( ParserException )
454 if ( line.length() == 0 )
455 throw ParserException( "empty line on call of HandleFileName" );
457 if ( CurrentFileName.length() != 0 )
460 CurrentFileName = ExtractFirstString(line);
463 void ReferenceFileParser::HandleKey( string& line )
464 throw ( ParserException )
466 if ( CurrentKey.length() != 0 )
469 CurrentKey = ExtractFirstString(line);
470 if ( CurrentKey.find_first_of( "|" ) == string::npos )
473 error << "uncorrect key:" << CurrentKey;
474 throw ParserException( error.str() );
478 bool ReferenceFileParser::HandleValue( string& line )
479 throw ( ParserException )
481 if ( line.length() == 0 )
482 throw ParserException( "empty line in HandleValue" );
484 if ( CurrentKey.length() == 0 )
486 cout << Indent << "No key present:" << CurrentKey << endl;
490 string newCurrentValue = ExtractValue(line);
491 if ( newCurrentValue.length() == 0 )
494 error << "missing value for key:" << CurrentKey;
495 throw ParserException( error.str() );
498 CurrentValue += newCurrentValue;
502 void ReferenceFileParser::ParseRegularLine(string& line)
503 throw ( ParserException )
505 if ( line.length() == 0 )
508 // First thing is to get a filename:
509 HandleFileName( line );
511 if ( line.length() == 0 )
514 // Second thing is to get a key:
517 if ( line.length() == 0 )
520 // Third thing is to get a value:
521 if ( ! HandleValue( line ) )
524 if ( CurrentKey.length() && CurrentValue.length() )
526 if ( ! AddKeyValuePairToMap( CurrentKey, CurrentValue ) )
527 throw ParserException( "adding to map of (key, value) failed" );
529 CurrentValue.erase();
533 bool ReferenceFileParser::SecondPassReferenceFile()
534 throw ( ParserException )
537 EntryValueType value;
539 bool inBlock = false;
542 while ( !from.eof() )
544 getline( from, line );
549 // Empty lines don't require any treatement:
550 if ( line.length() == 0 )
553 bool beginBlock ( line.find_first_of("[") != string::npos );
554 bool endBlock ( line.find_last_of("]") != string::npos );
556 // Waiting for a block to be opened. Meanwhile, drop everything:
557 if ( !inBlock && !beginBlock )
563 line.erase( 0, line.find_first_of( "[" ) + 1 );
565 CurrentMapEntryValuesPtr = new MapEntryValues();
569 line.erase( line.find_last_of( "]" ) );
571 ParseRegularLine( line );
572 ProducedMap[CurrentFileName] = CurrentMapEntryValuesPtr;
574 CurrentFileName.erase();
577 // Outside block lines are dropped:
581 ParseRegularLine( line );
585 int TestAllEntryVerify(int argc, char* argv[])
589 cerr << " Usage: " << argv[0]
590 << " (no arguments needed)." << endl;
594 cout << " Description (Test::TestAllEntryVerify): "
596 cout << " For all images in gdcmData (and not blacklisted in "
597 "Test/CMakeLists.txt)"
599 cout << " apply the following to each filename.xxx: "
601 cout << " step 1: parse the image (as gdcmHeader) and call"
605 string referenceDir = GDCM_DATA_ROOT;
607 string referenceFilename = referenceDir + "TestAllEntryVerifyReference.txt";
609 ReferenceFileParser Parser;
610 Parser.Open(referenceFilename);
611 Parser.SetDataPath(referenceDir);
616 while( gdcmDataImages[i] != 0 )
618 string filename = GDCM_DATA_ROOT;
619 filename += "/"; //doh!
620 filename += gdcmDataImages[i++];
622 cout << " Testing: " << filename << endl;
624 gdcmHeader* tested = new gdcmHeader( filename.c_str(), false, true );
625 if( !tested->GetHeader()->IsReadable() )
627 cout << " Image not gdcm compatible:"
633 //////////////// Clean up: