00001 #ifndef __CSVFILE_CPP__
00002 #define __CSVFILE_CPP__
00003
00004 #include <loaders/csv_file.h>
00005 #include <utilities/testing_utilities.h>
00006
00007 #ifdef ENABLE_AUTOTESTING
00008 #include <utilities/binary_data.h>
00009 #endif
00010
00011 namespace nitro
00012 {
00013 std::size_t CSV_FILE_READ_BUFFER_SIZE( 1024 );
00014
00015 const std::size_t START_FIELD( 1 );
00016 const std::size_t READ_DATA( 2 );
00017 const std::size_t END_QUOTED_FIELD( 3 );
00018 const std::size_t ADD_FIELD( 4 );
00019 const std::size_t TRY_READ_NEXT( 5 );
00020 const std::size_t QUOTE_WAS_FOUND( 6 );
00021 const std::size_t END_RECORD( 7 );
00022
00023 CSVReadable::~CSVReadable()
00024 {
00025 try
00026 {
00027 }
00028 catch( ... )
00029 {
00030 }
00031 }
00032
00033 CSVWritable::~CSVWritable()
00034 {
00035 try
00036 {
00037 }
00038 catch( ... )
00039 {
00040 }
00041 }
00042
00043 void CSVFile::OpenFile( const char * FilePath )
00044 {
00045 try
00046 {
00047 FileStream.Open( FilePath , nitro::FA_FILE_BINARY | nitro::FA_FILE_READ | nitro::FA_FILE_WRITE );
00048 }
00049 catch( nitro::exception e )
00050 {
00051 throw( nitro::exception( std::string( "CSVFile::OpenFile( const char * FilePath )::" ) + e.what() , e.code() ) );
00052 }
00053 catch( ... )
00054 {
00055 throw( nitro::exception( std::string( "CSVFile::OpenFile( const char * FilePath )::An error occured" ) , 1 ) );
00056 }
00057 }
00058
00059 void CSVFile::OpenFile( const std::string & FilePath )
00060 {
00061 try
00062 {
00063 OpenFile( FilePath.c_str() );
00064 }
00065 catch( nitro::exception e )
00066 {
00067 throw( nitro::exception( std::string( "CSVFile::OpenFile( const std::string & FilePath )::" ) + e.what() , e.code() ) );
00068 }
00069 catch( ... )
00070 {
00071 throw( nitro::exception( std::string( "CSVFile::OpenFile( const std::string & FilePath )::An error occured" ) , 1 ) );
00072 }
00073 }
00074
00075 bool CSVFile::ReadRecord( CSVReadable * Readable , const std::size_t MODE )
00076 {
00077 try
00078 {
00079 std::size_t FilePosition;
00080 std::size_t ReadBytes;
00081
00082
00083 const std::size_t ReadBufferSize( CSV_FILE_READ_BUFFER_SIZE );
00084
00085 char * Buffer( new char[ ReadBufferSize ] );
00086
00087 bool QFieldStart( false );
00088 bool FieldStart( false );
00089 std::size_t FieldStartCursor( 0 );
00090 std::size_t State( START_FIELD );
00091 std::size_t FieldRawSize( 0 );
00092
00093 do
00094 {
00095 FilePosition = FileStream.Tell();
00096
00097 ReadBytes = FileStream.Read( Buffer , ReadBufferSize );
00098
00099 for( std::size_t i( 0 ) ; i < ReadBufferSize + 1 && i < ReadBytes + 1 ; i++ )
00100 {
00101 if( ReadBytes == 0 || ( ReadBytes < ReadBufferSize && i == ReadBytes ) )
00102 {
00103 if( QFieldStart == true )
00104 {
00105 throw( nitro::exception( "Illegal sintax (quote was not found)" , 1 ) );
00106 }
00107 if( FieldStart == true )
00108 {
00109 State = ADD_FIELD;
00110 }
00111 }
00112 else
00113 {
00114 if( i == ReadBufferSize || i == ReadBytes )
00115 {
00116 continue;
00117 }
00118 }
00119
00120 if( State == START_FIELD )
00121 {
00122 FieldRawSize = 0;
00123 if( ( Buffer[ i ] >= 0x20 && Buffer[ i ] <= 0x21 ) ||
00124 ( Buffer[ i ] >= 0x23 && Buffer[ i ] <= 0x2b ) ||
00125 ( Buffer[ i ] >= 0x2d && Buffer[ i ] <= 0x7e ) || Buffer[ i ] == ',' )
00126 {
00127 FieldStart = true;
00128 FieldStartCursor = FilePosition + i;
00129 State = READ_DATA;
00130 }
00131
00132 if( Buffer[ i ] == '"' )
00133 {
00134 FieldRawSize++;
00135 QFieldStart = true;
00136 FieldStartCursor = FilePosition + i;
00137 State = READ_DATA;
00138 continue;
00139 }
00140 }
00141
00142 if( State == READ_DATA )
00143 {
00144 FieldRawSize++;
00145 if( Buffer[ i ] == '"' && QFieldStart == false )
00146 {
00147 throw( nitro::exception( "Illegal sintax (quote was found)" , 1 ) );
00148 }
00149 if( Buffer[ i ] == '\r' && QFieldStart == false )
00150 {
00151 FieldRawSize--;
00152 State = ADD_FIELD;
00153 }
00154 if( Buffer[ i ] == '\n' && QFieldStart == false )
00155 {
00156 throw( nitro::exception( "Illegal sintax (LF was found)" , 1 ) );
00157 }
00158 if( Buffer[ i ] == '"' && QFieldStart == true )
00159 {
00160 if( i < ReadBytes - 1 )
00161 {
00162 State = QUOTE_WAS_FOUND;
00163 continue;
00164 }
00165 else
00166 {
00167 State = ADD_FIELD;
00168 }
00169 }
00170 if( Buffer[ i ] == ',' && FieldStart == true )
00171 {
00172 FieldRawSize--;
00173 State = ADD_FIELD;
00174 }
00175 }
00176
00177 if( State == QUOTE_WAS_FOUND )
00178 {
00179 if( Buffer[ i ] == '"' )
00180 {
00181 FieldRawSize++;
00182 State = READ_DATA;
00183 continue;
00184 }
00185 if( Buffer[ i ] == ',' || Buffer[ i ] == '\r' )
00186 {
00187 State = ADD_FIELD;
00188 }
00189 else
00190 {
00191 throw( nitro::exception( "Can't end quoted field (1)'" , 1 ) );
00192 }
00193 }
00194
00195 if( State == END_QUOTED_FIELD )
00196 {
00197 if( Buffer[ i ] == ',' )
00198 {
00199 State = ADD_FIELD;
00200 }
00201 else
00202 {
00203 throw( nitro::exception( "Can't end quoted field (2)'" , 1 ) );
00204 }
00205 }
00206
00207 if( State == ADD_FIELD )
00208 {
00209 if( FieldRawSize > 0 )
00210 {
00211 nitro::BinaryData Data;
00212 std::size_t Decreaser( 0 );
00213
00214 if( QFieldStart == true )
00215 {
00216 Decreaser = 1;
00217 }
00218
00219 if( FieldStartCursor < FilePosition )
00220 {
00221
00222 char * FieldValue( new char [ FieldRawSize - 2 * Decreaser ] );
00223
00224 FileStream.Seek( FieldStartCursor + Decreaser , FA_FILE_BEGIN );
00225
00226 FileStream.Read( FieldValue , FieldRawSize - 2 * Decreaser );
00227
00228 Data.AppendData( FieldValue , FieldRawSize - 2 * Decreaser );
00229
00230 FileStream.Seek( FilePosition + ReadBufferSize , FA_FILE_BEGIN );
00231
00232 delete [] FieldValue;
00233 }
00234 else
00235 {
00236
00237 std::size_t BufferOffset( FieldStartCursor + Decreaser - FilePosition );
00238
00239 Data.AppendData( Buffer + BufferOffset , FieldRawSize - 2 * Decreaser );
00240 }
00241
00242 if( MODE & CSV_AUTO_ESCAPE )
00243 {
00244 nitro::BinaryData::ReplaceBuffer( Data , ( const char * )"\"\"" , ( const std::size_t )2 , ( const char * )"\"" , ( const std::size_t )1 );
00245 }
00246
00247 Readable->AddField( Data.GetBuffer() , Data.GetBufferLength() );
00248 }
00249 else
00250 {
00251 Readable->AddField( "" , 0 );
00252 }
00253
00254 QFieldStart = false;
00255 FieldStart = false;
00256 State = TRY_READ_NEXT;
00257 }
00258
00259 if( State == TRY_READ_NEXT )
00260 {
00261 if( Buffer[ i ] == ',' )
00262 {
00263 State = START_FIELD;
00264 }
00265 if( Buffer[ i ] == '\r' )
00266 {
00267 State = END_RECORD;
00268 continue;
00269 }
00270 }
00271
00272 if( State == END_RECORD )
00273 {
00274 if( Buffer[ i ] == '\n' )
00275 {
00276
00277 State = START_FIELD;
00278 FileStream.Seek( FilePosition + i + 1 , FA_FILE_BEGIN );
00279
00280 delete [] Buffer;
00281
00282 return( false );
00283 }
00284 else
00285 {
00286 throw( nitro::exception( "Can't find LF symbol" , 1 ) );
00287 }
00288 }
00289 }
00290 }
00291 while( ReadBytes == ReadBufferSize );
00292
00293 delete [] Buffer;
00294
00295 return( true );
00296 }
00297 catch( nitro::exception e )
00298 {
00299 throw( nitro::exception( std::string( "CSVFile::ReadRecord( CSVReadable * Readable , const std::size_t MODE /* = 0 */ )::" ) + e.what() , e.code() ) );
00300 }
00301 catch( ... )
00302 {
00303 throw( nitro::exception( std::string( "CSVFile::ReadRecord( CSVReadable * Readable , const std::size_t MODE /* = 0 */ )::An error occured" ) , 1 ) );
00304 }
00305 }
00306
00307 void CSVFile::ReadHeader( const std::size_t MODE )
00308 {
00309 try
00310 {
00311 ReadRecord( Header , MODE );
00312 }
00313 catch( nitro::exception e )
00314 {
00315 throw( nitro::exception( std::string( "CSVFile::ReadHeader( const std::size_t MODE /* = 0 */ )::" ) + e.what() , e.code() ) );
00316 }
00317 catch( ... )
00318 {
00319 throw( nitro::exception( std::string( "CSVFile::ReadHeader( const std::size_t MODE /* = 0 */ )::An error occured" ) , 1 ) );
00320 }
00321 }
00322
00323 std::size_t CSVFile::GetHeaderItemsCount( void )
00324 {
00325 try
00326 {
00327 return( Header.size() );
00328 }
00329 catch( nitro::exception e )
00330 {
00331 throw( nitro::exception( std::string( "CSVFile::GetHeaderItemsCount( void )::" ) + e.what() , e.code() ) );
00332 }
00333 catch( ... )
00334 {
00335 throw( nitro::exception( std::string( "CSVFile::GetHeaderItemsCount( void )::An error occured" ) , 1 ) );
00336 }
00337 }
00338
00339 nitro::BinaryData & CSVFile::GetHeaderItem( std::size_t i )
00340 {
00341 try
00342 {
00343 return( Header[ i ] );
00344 }
00345 catch( nitro::exception e )
00346 {
00347 throw( nitro::exception( std::string( "CSVFile::GetHeaderItem( std::size_t i )::" ) + e.what() , e.code() ) );
00348 }
00349 catch( ... )
00350 {
00351 throw( nitro::exception( std::string( "CSVFile::GetHeaderItem( std::size_t i )::An error occured" ) , 1 ) );
00352 }
00353 }
00354
00355 void CSVFile::AppendRecord( CSVWritable * Writable , const std::size_t MODE )
00356 {
00357 try
00358 {
00359 std::size_t FilePosition( FileStream.Tell() );
00360
00361
00362
00363 std::size_t BytesCount( 0 );
00364
00365
00366
00367
00368 FileStream.Seek( 0 , FA_FILE_END );
00369 std::size_t EndOfFilePosition( FileStream.Tell() );
00370 FileStream.Seek( EndOfFilePosition - 2 , FA_FILE_BEGIN );
00371
00372 char EndOfFile[ 2 ];
00373
00374 FileStream.Read( EndOfFile , 2 );
00375
00376 bool PrependCRLF( false );
00377 if( EndOfFile[ 0 ] != '\r' || EndOfFile[ 1 ] != '\n' )
00378 {
00379 PrependCRLF = true;
00380 BytesCount += 2;
00381 }
00382
00383 const char * Buffer;
00384 std::size_t BufferSize;
00385 int Tmp;
00386
00387
00388
00389 for( std::size_t i( 0 ) ; i < Writable->GetFieldCount() ; i++ )
00390 {
00391 Writable->GetField( i , Buffer , BufferSize , MODE );
00392
00393 BytesCount += BufferSize;
00394
00395 if( MODE & CSV_AUTO_ESCAPE )
00396 {
00397 Tmp = nitro::BinaryData::GetOccuranceCount( Buffer , BufferSize , "\"" , 1 );
00398 if( Tmp != 0 )
00399 {
00400 BytesCount += 2 + Tmp;
00401 }
00402 }
00403 }
00404
00405
00406
00407 BytesCount += Writable->GetFieldCount() - 1;
00408
00409
00410
00411 BytesCount += 2;
00412
00413 nitro::BinaryData Data;
00414
00415 if( PrependCRLF )
00416 {
00417 Data.Reserve( BytesCount + 2 );
00418 Data.AppendData( "\r\n" );
00419 }
00420 else
00421 {
00422 Data.Reserve( BytesCount );
00423 }
00424
00425 for( std::size_t i( 0 ) ; i < Writable->GetFieldCount() ; i++ )
00426 {
00427 Writable->GetField( i , Buffer , BufferSize , MODE );
00428
00429 Tmp = 0;
00430 if( MODE & CSV_AUTO_ESCAPE )
00431 {
00432 Tmp = nitro::BinaryData::GetOccuranceCount( Buffer , BufferSize , "\"" , 1 );
00433 }
00434
00435 if( Tmp != 0 )
00436 {
00437 Data.AppendData( '"' );
00438 }
00439
00440 for( std::size_t j( 0 ) ; j < BufferSize ; j++ )
00441 {
00442 if( Buffer[ j ] == '"' && ( MODE & CSV_AUTO_ESCAPE ) )
00443 {
00444 Data.AppendData( '"' );
00445 }
00446 Data.AppendData( Buffer[ j ] );
00447 }
00448
00449 if( Tmp != 0 )
00450 {
00451 Data.AppendData( '"' );
00452 }
00453
00454 if( i < Writable->GetFieldCount() - 1 )
00455 {
00456 Data.AppendData( ',' );
00457 }
00458 else
00459 {
00460 Data.AppendData( "\r\n" , 2 );
00461 }
00462 }
00463
00464 FileStream.Write( Data.GetBuffer() , Data.GetBufferLength() );
00465
00466 FileStream.Seek( FilePosition , FA_FILE_BEGIN );
00467 }
00468 catch( nitro::exception e )
00469 {
00470 throw( nitro::exception( std::string( "CSVFile::AppendRecord( CSVWritable * Writable , const std::size_t MODE /* = 0 */ )::" ) + e.what() , e.code() ) );
00471 }
00472 catch( ... )
00473 {
00474 throw( nitro::exception( std::string( "CSVFile::AppendRecord( CSVWritable * Writable , const std::size_t MODE /* = 0 */ )::An error occured" ) , 1 ) );
00475 }
00476 }
00477
00478 void CSVFile::CloseFile( void )
00479 {
00480 try
00481 {
00482 FileStream.Close();
00483 }
00484 catch( nitro::exception e )
00485 {
00486 throw( nitro::exception( std::string( "CSVFile::CloseFile( void )::" ) + e.what() , e.code() ) );
00487 }
00488 catch( ... )
00489 {
00490 throw( nitro::exception( std::string( "CSVFile::CloseFile( void )::An error occured" ) , 1 ) );
00491 }
00492 }
00493
00494 CSVFile::~CSVFile()
00495 {
00496 try
00497 {
00498 CloseFile();
00499 }
00500 catch( ... )
00501 {
00502 }
00503 }
00504
00505 void CSVFile::SetReadBufferSize( std::size_t ReadBufferSize )
00506 {
00507 CSV_FILE_READ_BUFFER_SIZE = ReadBufferSize;
00508 }
00509
00510
00511
00512
00513 BEGIN_TESTING_SECTION()
00514
00515 ENABLE_CLASS_TESTING( CSVFile )
00516
00517 CLASS_MEMBER_FUNCTION_TESTING_1( CSVFile , aliasOpenFile , tstOpenFile , const char * , void , NO_RET )
00518 CLASS_MEMBER_FUNCTION_TESTING_0( CSVFile , CloseFile , tstCloseFile , void , NO_RET )
00519
00520 #ifdef ENABLE_AUTOTESTING
00521 std::vector< std::vector< nitro::BinaryData > > CSVRecords;
00522 std::vector< nitro::BinaryData > Fields;
00523 nitro::BinaryData Field;
00524 NITRO_EXPORTING bool tstReadRecord( const char * ObjectName , std::size_t MODE )
00525 {
00526 class Reader : public CSVReadable{
00527 public:
00528 void AddField( const char * Buffer , const std::size_t & BufferSize )
00529 {
00530 Fields.push_back( nitro::BinaryData() );
00531
00532 char * NewBuffer( new char[ BufferSize + 1 ] );
00533
00534 memset( NewBuffer , 0 , BufferSize + 1 );
00535 memcpy( NewBuffer , Buffer , BufferSize );
00536
00537 Fields.back().AppendData( NewBuffer , BufferSize + 1 );
00538
00539 delete [] NewBuffer;
00540 }
00541 };
00542
00543 Reader r;
00544
00545 return( CSVFileGetter( ObjectName ).ReadRecord( ( CSVReadable * ) & r , MODE ) );
00546 }
00547
00548 NITRO_EXPORTING void tstAppendRecord( const char * ObjectName , std::size_t MODE )
00549 {
00550 try
00551 {
00552 class Writer : public CSVWritable{
00553 public:
00554 std::size_t GetFieldCount( void )
00555 {
00556 return( Fields.size() );
00557 }
00558
00559 void GetField( const std::size_t & i , const char * & Buffer , std::size_t & BufferSize , const std::size_t & )
00560 {
00561 Buffer = Fields[ i ].GetBuffer();
00562
00563 BufferSize = Fields[ i ].GetBufferLength();
00564 }
00565 };
00566
00567 Writer w;
00568
00569 CSVFileGetter( ObjectName ).AppendRecord( ( CSVWritable * ) & w , MODE );
00570 }
00571 catch( nitro::exception e )
00572 {
00573 std::cout<<e.what()<<std::endl;
00574 }
00575 catch( ... )
00576 {
00577 std::cout<<"An error occured"<<std::endl;
00578 }
00579 }
00580
00581 NITRO_EXPORTING void tstAppendRecordTpl( const char * ObjectName , std::size_t MODE )
00582 {
00583 try
00584 {
00585 CSVFileGetter( ObjectName ).AppendRecord( Fields , MODE );
00586 }
00587 catch( nitro::exception e )
00588 {
00589 std::cout<<e.what()<<std::endl;
00590 }
00591 catch( ... )
00592 {
00593 std::cout<<"An error occured"<<std::endl;
00594 }
00595 }
00596
00597 NITRO_EXPORTING void tstPushFields( void )
00598 {
00599 CSVRecords.push_back( Fields );
00600 }
00601
00602 NITRO_EXPORTING void tstAppendAllRecords( const char * ObjectName , std::size_t MODE )
00603 {
00604 try
00605 {
00606 CSVFileGetter( ObjectName ).AppendAllRecords( CSVRecords , MODE );
00607 }
00608 catch( nitro::exception e )
00609 {
00610 std::cout<<e.what()<<std::endl;
00611 }
00612 catch( ... )
00613 {
00614 std::cout<<"An error occured"<<std::endl;
00615 }
00616 }
00617
00618 NITRO_EXPORTING void tstReadAllRecords( const char * ObjectName , std::size_t MODE )
00619 {
00620 try
00621 {
00622 std::vector< std::vector< nitro::BinaryData > > CSVRecords;
00623
00624 CSVFileGetter( ObjectName ).ReadAllRecords( CSVRecords , MODE );
00625
00626 Fields = CSVRecords.back();
00627 }
00628 catch( nitro::exception e )
00629 {
00630 std::cout<<e.what()<<std::endl;
00631 }
00632 catch( ... )
00633 {
00634 std::cout<<"An error occured"<<std::endl;
00635 }
00636 }
00637
00638 NITRO_EXPORTING int tstGetCount( void )
00639 {
00640 return( ( int )( Fields.size() ) );
00641 }
00642
00643 NITRO_EXPORTING const char * tstGetField( int i )
00644 {
00645 Field = Fields[ i ];
00646 Field.AppendData( "\0" , 1 );
00647
00648 return( Field.GetBuffer() );
00649 }
00650
00651 NITRO_EXPORTING void tstSetField( std::size_t i , const char * Data , std::size_t DataSize )
00652 {
00653 if( i >= Fields.size() )
00654 {
00655 Fields.resize( i + 1 );
00656 }
00657
00658 Fields[ i ].Release();
00659 Fields[ i ].AppendData( Data , DataSize );
00660 }
00661
00662 NITRO_EXPORTING void tstReadRecordTpl( const char * ObjectName , std::size_t MODE )
00663 {
00664 try
00665 {
00666 CSVFileGetter( ObjectName ).ReadRecord< std::vector< nitro::BinaryData > >( Fields , MODE );
00667 }
00668 catch( nitro::exception e )
00669 {
00670 std::cout<<e.what()<<std::endl;
00671 }
00672 catch( ... )
00673 {
00674 std::cout<<"An error occured"<<std::endl;
00675 }
00676 }
00677 #endif
00678
00679
00680
00681 FUNCTION_TESTING_1( CSVFile::SetReadBufferSize , tstSetReadBufferSize , std::size_t , void , NO_RET )
00682
00683 END_TESTING_SECTION()
00684 }
00685
00686 #endif