#include "headers.hxx" #include "CSVDSReader.hpp" #include "resource.h" #include "global.hpp" #include #include CSVDSReader::CSVDSReader():file(INVALID_HANDLE_VALUE) { } HRESULT CSVDSReader::read( const wchar_t *fileName_, const long *locales) { LOG_FUNCTION(CSVDSReader::read); localeOffsets.clear(); propertyPositions.clear(); fileName=fileName_; HRESULT hr=S_OK; do { // fill localeOffsets and property positions if(!FS::FileExists(fileName)) { error=fileName; hr=Win32ToHresult(ERROR_FILE_NOT_FOUND); break; } hr=FS::CreateFile(fileName,file,GENERIC_READ,FILE_SHARE_READ); BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); do { AnsiString unicodeId; hr=FS::Read(file, 2, unicodeId); BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); if (unicodeId[0]!='\xFF' || unicodeId[1]!='\xFE') { error = String::format(IDS_INVALID_CSV_UNICODE_ID, fileName.c_str()); hr=E_FAIL; break; } hr=parseProperties(); BREAK_ON_FAILED_HRESULT(hr); hr=parseLocales(locales); BREAK_ON_FAILED_HRESULT(hr); } while(0); if (FAILED(hr)) { CloseHandle(file); file=INVALID_HANDLE_VALUE; break; } } while(0); LOG_HRESULT(hr); return hr; } // Decode first line of the file building propertyPositions // Expects file to be in the first valid file character (after // the unicode identifier) HRESULT CSVDSReader::parseProperties() { LOG_FUNCTION(CSVDSReader::parseProperties); ASSERT(file!=INVALID_HANDLE_VALUE); HRESULT hr=S_OK; do { String csvLine; hr=ReadLine(file,csvLine); BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); StringList tokens; size_t token_count = csvLine.tokenize(back_inserter(tokens),L","); ASSERT(token_count == tokens.size()); StringList::iterator begin=tokens.begin(); StringList::iterator end=tokens.end(); long count=0; while( begin != end ) { propertyPositions[begin->to_upper()]=count++; begin++; } } while(0); LOG_HRESULT(hr); return hr; } // Fill localeOffsets with the starting position of all locales // Expects file to be in the second line // Expects the locale order to be the same as the one // found in the file HRESULT CSVDSReader::parseLocales(const long *locales) { LOG_FUNCTION(CSVDSReader::parseLocales); ASSERT(file!=INVALID_HANDLE_VALUE); HRESULT hr=S_OK; do { long count=0; bool flagEof=false; while(locales[count]!=0 && !flagEof) { long locale=locales[count]; String localeStr=String::format(L"CN=%1!3x!,", locale); LARGE_INTEGER pos; hr = WinGetVLFilePointer(file, &pos); BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); String csvLine; hr=ReadLine(file,csvLine); if(hr==EOF_HRESULT) { flagEof=true; hr=S_OK; } BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); if(csvLine.length() > localeStr.length()) { csvLine.erase(localeStr.size()+1); if( localeStr.icompare(&csvLine[1])==0 ) { localeOffsets[locale]=pos; count++; } } } BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); if(locales[count]!=0) { error=String::format(IDS_MISSING_LOCALES,fileName.c_str()); hr=E_FAIL; break; } } while(0); LOG_HRESULT(hr); return hr; } // get the csv value starting with value to XPValue // returns S_FALSE if no value is found HRESULT CSVDSReader::getCsvValue( const long locale, const wchar_t *object, const wchar_t *property, const String &value, String &XPValue) const { LOG_FUNCTION(CSVDSReader::getCsvValue); HRESULT hr=S_OK; XPValue.erase(); bool found=false; do { StringList values; hr=getCsvValues(locale,object,property,values); BREAK_ON_FAILED_HRESULT(hr); StringList::const_iterator begin,end; begin=values.begin(); end=values.end(); while(begin!=end && !found) { if (_wcsnicmp(begin->c_str(),value.c_str(),value.length())==0) { XPValue=*begin; found=true; } begin++; } } while(0); if (!found) { hr=S_FALSE; } LOG_HRESULT(hr); return hr; } HRESULT CSVDSReader::getCsvValues( const long locale, const wchar_t *object, const wchar_t *property, StringList &values) const { LOG_FUNCTION(CSVDSReader::getCsvValues); // seek on locale // read sequentially until find object // call parseLine on the line found to retrieve values ASSERT(file!=INVALID_HANDLE_VALUE); HRESULT hr=S_OK; do { String propertyString(property); mapOfPositions::const_iterator propertyPos = propertyPositions.find(propertyString.to_upper()); if (propertyPos==propertyPositions.end()) { error=String::format(IDS_PROPERTY_NOT_FOUND_IN_CSV, property, fileName.c_str()); hr=E_FAIL; break; } String csvLine; hr=getObjectLine(locale,object,csvLine); BREAK_ON_FAILED_HRESULT(hr); hr=parseLine(csvLine.c_str(),propertyPos->second,values); BREAK_ON_FAILED_HRESULT(hr); } while(0); LOG_HRESULT(hr); return hr; } // starting from the locale offset // finds the object and returns its line in csvLine HRESULT CSVDSReader::getObjectLine( const long locale, const wchar_t *object, String &csvLine ) const { LOG_FUNCTION(CSVDSReader::getObjectLine); ASSERT(file!=INVALID_HANDLE_VALUE); HRESULT hr=S_OK; do { mapOfOffsets::const_iterator offset = localeOffsets.find(locale); // locale must have been passed to read ASSERT(offset!=localeOffsets.end()); String objectStr; objectStr=String::format(L"CN=%1,CN=%2!3x!",object,locale); hr=Win::SetFilePointerEx(file,offset->second,0,FILE_BEGIN); BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); // first line is the container properties and since we want the // properties of an object we will ignore it bool flagEof=false; hr=ReadLine(file,csvLine); if(hr==EOF_HRESULT) { flagEof=true; hr=S_OK; } BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); bool found=false; while(!found && !flagEof) { hr=ReadLine(file,csvLine); if(hr==EOF_HRESULT) { flagEof=true; hr=S_OK; } BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); if(csvLine.length() > objectStr.length()) { String auxComp=csvLine.substr(1,objectStr.length()); if( auxComp.icompare(objectStr)==0 ) { found=true; } } } BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); if(!found) { error = String::format( IDS_OBJECT_NOT_FOUND_IN_CSV, object, locale, fileName.c_str() ); hr=E_FAIL; break; } } while(0); LOG_HRESULT(hr); return hr; } //Get the values from the line at the position HRESULT CSVDSReader::parseLine( const wchar_t *line, const long position, StringList &values) const { LOG_FUNCTION(CSVDSReader::parseLine); ASSERT(line!=NULL); ASSERT(file!=INVALID_HANDLE_VALUE); HRESULT hr=S_OK; do { long pos=0; const wchar_t *csr=line; const wchar_t *sBegin=line; size_t count=0; while(pos<=position && csr!=NULL && *csr!=0) { while(*csr==L' ' || *csr==L'\t') csr++; // The goal of both 'if' and 'else' is setting sBegin and count // and leaving csr after the next comma if (*csr==L'"') { sBegin=csr+1; csr=wcschr(sBegin,L'"'); if(csr==NULL) { error=String::format(IDS_QUOTES_NOT_CLOSED,fileName.c_str()); break; } count=csr-sBegin; csr=wcschr(csr+1,L','); if(csr!=NULL) csr++; } else { sBegin=csr; csr=wcschr(sBegin,L','); if(csr!=NULL) { count=csr-sBegin; csr++; } else { count=wcslen(sBegin); } } pos++; } BREAK_ON_FAILED_HRESULT(hr); String sProp(sBegin,count); values.clear(); size_t token_count = sProp.tokenize(back_inserter(values),L";"); ASSERT(token_count == values.size()); } while(0); LOG_HRESULT(hr); return hr; } HRESULT CSVDSReader::writeHeader(HANDLE fileOut) const { LOG_FUNCTION(CSVDSReader::writeHeader); HRESULT hr=S_OK; do { char suId[3]={'\xFF','\xFE',0}; //uId solves ambiguous Write AnsiString uId(suId); hr=FS::Write(fileOut,uId); BREAK_ON_FAILED_HRESULT(hr); // 2 to skip the unicode identifier LARGE_INTEGER pos; pos.QuadPart=2; hr=Win::SetFilePointerEx(file,pos,0,FILE_BEGIN); BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); String csvLine; hr=ReadLine(file,csvLine); // We are breaking for EOF_HRESULT too, since // there should be more lines in the csv BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); hr=FS::WriteLine(fileOut,csvLine); BREAK_ON_FAILED_HRESULT(hr); } while(0); LOG_HRESULT(hr); return hr; } HRESULT CSVDSReader::makeLocalesCsv( HANDLE fileOut, const long *locales) const { LOG_FUNCTION(CSVDSReader::makeLocalesCsv); HRESULT hr=S_OK; ASSERT(file!=INVALID_HANDLE_VALUE); ASSERT(fileOut!=INVALID_HANDLE_VALUE); do { LARGE_INTEGER posStartOut; hr = WinGetVLFilePointer(fileOut, &posStartOut); BREAK_ON_FAILED_HRESULT(hr); if (posStartOut.QuadPart==0) { hr=writeHeader(fileOut); BREAK_ON_FAILED_HRESULT(hr); } long count=0; String csvLoc; while(locales[count]!=0) { long locale=locales[count]; mapOfOffsets::const_iterator offset; offset = localeOffsets.find(locale); // locale must have been passed to read ASSERT(offset!=localeOffsets.end()); hr=Win::SetFilePointerEx(file,offset->second,0,FILE_BEGIN); BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); String localeStr=String::format(L"CN=%1!3x!,", locale); bool flagEof=false; String csvLine; hr=ReadLine(file,csvLine); if(hr==EOF_HRESULT) { flagEof=true; hr=S_OK; } BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); do { hr=FS::WriteLine(fileOut,csvLine); BREAK_ON_FAILED_HRESULT(hr); hr=ReadLine(file,csvLine); if(hr==EOF_HRESULT) { flagEof=true; hr=S_OK; } BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); size_t posComma=csvLine.find(L","); if(posComma!=string::npos) { csvLoc=csvLine.substr(posComma+1,localeStr.length()); } else { csvLoc.erase(); } } while( !flagEof && !csvLoc.empty() && ( csvLoc.icompare(localeStr) == 0 ) ); count++; } // while(locales[count]!=0) BREAK_ON_FAILED_HRESULT(hr); } while(0); LOG_HRESULT(hr); return hr; } HRESULT CSVDSReader::makeObjectsCsv( HANDLE fileOut, const setOfObjects &objects) const { LOG_FUNCTION(CSVDSReader::makeObjectsCsv); HRESULT hr=S_OK; ASSERT(file!=INVALID_HANDLE_VALUE); do { LARGE_INTEGER posStartOut; hr = WinGetVLFilePointer(fileOut, &posStartOut); BREAK_ON_FAILED_HRESULT(hr); if (posStartOut.QuadPart==0) { hr=writeHeader(fileOut); BREAK_ON_FAILED_HRESULT(hr); } setOfObjects::const_iterator begin,end; begin=objects.begin(); end=objects.end(); while(begin!=end) { String csvLine; hr=getObjectLine( begin->second, begin->first.c_str(), csvLine); BREAK_ON_FAILED_HRESULT(hr); hr=FS::WriteLine(fileOut,csvLine); BREAK_ON_FAILED_HRESULT(hr); begin++; } BREAK_ON_FAILED_HRESULT(hr); } while(0); LOG_HRESULT(hr); return hr; }