|
|
#include "headers.hxx"
#include "CSVDSReader.hpp"
#include "resourceDspecup.h"
#include "constants.hpp"
#include "global.hpp"
#include <stdio.h>
#include <crtdbg.h>
CSVDSReader::CSVDSReader():file(INVALID_HANDLE_VALUE) { canCallGetNext=false; }
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 = String::format(IDS_COULD_NOT_FIND_FILE, fileName.c_str()); hr=E_FAIL; 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); // We are breaking for EOF_HRESULT too, since
// there should be more lines in the csv
BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName);
hr = WinGetVLFilePointer(file, &startPosition); 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]=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 inValue to outValue
// returns S_FALSE if no value is found
HRESULT CSVDSReader::getCsvValue ( const long locale, const wchar_t *object, const wchar_t *property, const String &inValue, String &outValue ) const { LOG_FUNCTION(CSVDSReader::getCsvValue);
HRESULT hr=S_OK; outValue.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(),inValue.c_str(),inValue.length())==0) { outValue=*begin; found=true; } begin++; } } while(0);
if (!found) { hr=S_FALSE; }
LOG_HRESULT(hr); return hr; }
// return all values for a property in a given locale/object
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 getPropertyValues 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); 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); mapOfProperties allValues; hr=getPropertyValues(csvLine,allValues); BREAK_ON_FAILED_HRESULT(hr); values=allValues[property]; } 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; }
HRESULT CSVDSReader::writeHeader(HANDLE fileOut) const { LOG_FUNCTION(CSVDSReader::writeHeader); ASSERT(fileOut!=INVALID_HANDLE_VALUE);
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;
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);
// We know that the first line matches even if it ends with EOF
hr=FS::WriteLine(fileOut,csvLine); BREAK_ON_FAILED_HRESULT(hr); bool newContainer=false; while ( !flagEof && !newContainer ) { hr=ReadLine(file,csvLine); if(hr==EOF_HRESULT) { flagEof=true; hr=S_OK; } BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName); // We will deal with the line even if it ends with EOF
size_t posComma=csvLine.find(L","); if(posComma!=String::npos) { String csvLoc=csvLine.substr(posComma+1,localeStr.length()); if (csvLoc.icompare(localeStr) == 0) { hr=FS::WriteLine(fileOut,csvLine); BREAK_ON_FAILED_HRESULT(hr); } else { newContainer=true; } } else { newContainer=true; } }; 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); 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); } 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; }
// auxiliar for getPropertyValues.
// It is out of the class because it can be used elesewhere
String unquote(const String &src) { String ret=src; ret.strip(String::BOTH); size_t len=ret.size(); if(len>=2 && ret[0]==L'"' && ret[len-1]==L'"') { ret=ret.substr(1,len-2); } return ret; }
// extract from line the value of all properties
HRESULT CSVDSReader::getPropertyValues ( const String &line, mapOfProperties &properties ) const { LOG_FUNCTION(CSVDSReader::getPropertyValues);
HRESULT hr=S_OK; ASSERT(file!=INVALID_HANDLE_VALUE); ASSERT(!line.empty()); do { StringVector objValues; const wchar_t *csr=line.c_str(); const wchar_t *start=csr;
while(*csr!=0) { if (*csr==L',') { objValues.push_back(unquote(String(start,csr))); csr++; start=csr; } else if (*csr==L'"') { // We are only advancing up to after the next quote
csr++; while(*csr!=L'"' && *csr!=0) csr++; if (*csr==0) { error=String::format(IDS_QUOTES_NOT_CLOSED,fileName.c_str()); hr=E_FAIL; break; } csr++; } else { csr++; } } BREAK_ON_FAILED_HRESULT(hr); objValues.push_back(unquote(String(start,csr))); if (objValues.size()!=propertyPositions.size()) { error=String::format ( IDS_WRONG_NUMBER_OF_PROPERTIES, objValues.size(), propertyPositions.size(), line.c_str(), fileName.c_str() );; hr=E_FAIL; break; }
properties.clear(); mapOfPositions::iterator current=propertyPositions.begin(); mapOfPositions::iterator end=propertyPositions.end(); while(current!=end) { String &propValue=objValues[current->second];
StringList values; if (!propValue.empty()) { size_t cnt = propValue.tokenize(back_inserter(values),L";"); ASSERT(cnt == values.size()); } properties[current->first]=values; current++; } BREAK_ON_FAILED_HRESULT(hr); } while(0); LOG_HRESULT(hr); return hr; }
// Sets the file pointer at the begining so that the next call to
// getNextObject will retrieve the first object.
// I did not take the usual getFirstObject approach, because
// I want to deal want to do something like:
// do
// {
// hr=getNextObject(loc,obj,prop)
// BREAK_ON_FAILED_HRESULT(hr);
// if(hr==S_FALSE) flagEof=true;
// if (loc==0) continue; // line is empty
// // deal with line here
// } while(!flagEOF)
HRESULT CSVDSReader::initializeGetNext() const { LOG_FUNCTION(CSVDSReader::initializeGetNext);
HRESULT hr=S_OK; ASSERT(file!=INVALID_HANDLE_VALUE); do { hr=Win::SetFilePointerEx(file,startPosition,0,FILE_BEGIN); BREAK_ON_FAILED_HRESULT(hr); canCallGetNext=true; } while(0); LOG_HRESULT(hr);
return hr; }
// Get first object in the csv file returning it's name, locale
// and values for the properties in properties
// Returns S_FALSE for no more objects
HRESULT CSVDSReader::getNextObject ( long &locale, String &object, mapOfProperties &properties ) const { LOG_FUNCTION(CSVDSReader::getNextObject);
HRESULT hr=S_OK; ASSERT(file!=INVALID_HANDLE_VALUE); ASSERT(canCallGetNext);
locale=0; object.erase();
bool flagEOF=false; do { String csvLine; hr=ReadLine(file,csvLine); if(hr==EOF_HRESULT) { flagEOF=true; if(csvLine.size()==0) { // we are done with success and EOF
break; } hr=S_OK; } BREAK_ON_FAILED_HRESULT_ERROR(hr,fileName);
size_t pos1stComma=csvLine.find(L','); ASSERT(pos1stComma!=String::npos); ASSERT(pos1stComma > 4);
object=csvLine.substr(4,pos1stComma - 4);
size_t pos2ndComma = csvLine.find(L',',pos1stComma+1); ASSERT(pos2ndComma!=String::npos); ASSERT(pos2ndComma > pos1stComma + 4); String strLocale=csvLine.substr ( pos1stComma + 4, pos2ndComma - pos1stComma - 4 );
if (strLocale.icompare(L"DisplaySpecifiers")==0) { // This is a container line.
// The object that we got is actually the locale
// and we have no object
strLocale=object; object.erase(); } String::ConvertResult result=strLocale.convert(locale,16); ASSERT(result==String::CONVERT_SUCCESSFUL);
hr=getPropertyValues(csvLine,properties); } while(0);
if(flagEOF) { hr=S_FALSE; canCallGetNext=false; }
LOG_HRESULT(hr); return hr;
}
|