csvfileloader.cpp

来自「series60 应用程序开发的源代码 series60 应用程序开发的源代码」· C++ 代码 · 共 186 行

CPP
186
字号
/**
*
* @brief Class to load a list of elements from a comma separated value (csv file
*
* Copyright (c) EMCC Software Ltd 2003
* @version 1.0
*/

// INCLUDE FILES

// Class include
#include "csvfileloader.h"

// User includes
#include "csvfileloaderobserver.h"
#include "elementlist.h"

// ================= MEMBER FUNCTIONS =======================

_LIT8(KTxtLineDelimiter, "\n");    // To delimit lines within the CSV file--might not necessarily be just \n!

CCsvFileLoader::CCsvFileLoader(RFs& aFs, CElementList& aElementList, MCsvFileLoaderObserver& aObserver)
 :    CActive(EPriorityStandard),
    iFs(aFs),
    iElementList(aElementList),
    iObserver(aObserver)
{
}

CCsvFileLoader::~CCsvFileLoader()
{
    Cancel();            // All AOs should cancel when they are deleted
    iFile.Close();
    iTimeWaster.Close();
}

void CCsvFileLoader::ConstructL(const TDesC& aFileName)
{
    iFileName = aFileName;
    User::LeaveIfError(iFile.Open(iFs, iFileName, EFileRead));
    User::LeaveIfError(iTimeWaster.CreateLocal());
    CActiveScheduler::Add(this);    // Not done automatically!
}

CCsvFileLoader* CCsvFileLoader::NewLC(RFs& aFs, CElementList& aElementList, MCsvFileLoaderObserver& aObserver,
    const TDesC& aFileName)
{
    CCsvFileLoader* self = new (ELeave) CCsvFileLoader(aFs, aElementList, aObserver);
    CleanupStack::PushL(self);
    self->ConstructL(aFileName);
    return self;
}

CCsvFileLoader* CCsvFileLoader::NewL(RFs& aFs, CElementList& aElementList, MCsvFileLoaderObserver& aObserver,
    const TDesC& aFileName)
{
    CCsvFileLoader* self = CCsvFileLoader::NewLC(aFs, aElementList, aObserver, aFileName);
    CleanupStack::Pop(self);
    return self;
}

void CCsvFileLoader::DoCancel()
{
    if (iWastingTime || !iHaveTriedToLoad)
    {
        iTimeWaster.Cancel();
    }
    //Cannot cancel a file read!
}

void CCsvFileLoader::Start()
{
    iFilePos = 0;                    //Just in case this isn't the first time round!
    iHaveTriedToLoad = EFalse;        //I want to start with a random wait first rather than a load (for the purposes of
                                    //this example). Hence I need a way of telling RunL() that the reason there's nothing
                                    //in the buffer first time round is that we haven't tried to load anything yet!

    //Wait for a randomish amount of time before doing anything else
    TInt delay = (iFileName.Size() % 10) * 100000;    //"random" enough!
    iTimeWaster.After(iStatus, delay);
    SetActive();
}

void CCsvFileLoader::FillReadBufferL()
{
    iHaveTriedToLoad = ETrue;            // Only check readbuffer size in RunL() if we've tried loading before!
    // Seek to current file position. We're not bothered if this is past the EOF, 
    // in that case Read() returns a zero-sized descriptor via iReadBuffer(), tested in RunL().
    User::LeaveIfError(iFile.Seek(ESeekStart, iFilePos));
                                        
    //R ead from the file into the buffer. iStatus will be completed by the fileserver once done.
    iFile.Read(iReadBuffer, iReadBuffer.MaxSize(), iStatus);
    
    SetActive();
}

TPtrC8 CCsvFileLoader::ExtractToNewLineL(const TDesC8& aBuffer) const
{
    //Search for the line delimiter within the buffer
    TInt foundPos = aBuffer.Find(KTxtLineDelimiter);
    if (foundPos < 0)
    {
        //End of line not found--malformed csv file?
        User::Leave(KErrGeneral);
    }

    //return an immutable pointer descriptor to the leftmost portion up to the delimiter location
    return aBuffer.Left(foundPos);
}

void CCsvFileLoader::RunL()
//RunL is called on completion of the iStatus member, i.e. when the asynchronous request has finished
//(whether successfully or not)
{
    if (!iHaveTriedToLoad)
    {
        //Fill the read buffer if we haven't yet tried to do so...
        FillReadBufferL();
        return;
    }

    if ((iStatus.Int() != KErrNone) || (iReadBuffer.Size() == 0))
    {
        //An error has occurred, or the buffer contained no data hence reading is complete. In either case,
        //return the error code (which will be KErrNone if reading has completed) to the observer.
        iObserver.NotifyLoadCompleted(iStatus.Int(), *this);
        return;
    }
    
    //This code is added simply to demonstrate the asynchronous behaviour more clearly, since without it
    //the file loading will happen so quickly that no other active object of the same or lower priority
    //than this is ever likely to get a look in...
    //Comment it out if you like. If so, you'll notice that the first instance of this class added to the
    //scheduler will "hog" it, executing every time around the scheduler's wait loop until it is fully
    //complete. Pausing a random amount of time allows other active objects to have a look in whilst this
    //instance awaits completion of the timer.
    if (iWastingTime)
    {
        //Just wait a randomish amount of time
        TInt delay = (iFilePos % 10) * 100000;
        iTimeWaster.After(iStatus, delay);
        SetActive();
        iWastingTime = EFalse;    //Don't waste time next time around!
        return;
    }

    //Extract and convert the buffer
    TPtrC8 elementBuf8 = ExtractToNewLineL(iReadBuffer);
    HBufC* elementBuf16 = HBufC::NewLC(elementBuf8.Length());    //elementBuf16 left on cleanup stack
    TPtr elementPtr16 = elementBuf16->Des();
    elementPtr16.Copy(elementBuf8);        //Copy the 8 bit buffer to the 16 bit buffer, with alternate zero padding
                                        //(i.e. convert from ASCII to UNICODE!)
    //Read the element from the buffer
    iElementList.AppendL(*elementBuf16);    //(could equally well have used elementPtr16)

    //Report the element that's just been loaded to the observer 
    TInt lastElementLoadedIndex = iElementList.NumberOfElements() - 1;    //-1 since zero based index!
    iObserver.NotifyElementLoaded(lastElementLoadedIndex);

    //Increment the file position
    iFilePos += elementBuf16->Length() + KTxtLineDelimiter().Length();    //Start next read from after the newline
                                                                        //NB the use of overloaded () operator
                                                                        //to cast a TLitC to a TDesC

    //Re-issue the request
    FillReadBufferL();

    //Cleanup
    CleanupStack::PopAndDestroy(elementBuf16);

    //Waste some time next time round before processing the file input (see comment earlier)...
    iWastingTime = ETrue;    
}

TInt CCsvFileLoader::RunError(TInt aError)
//This function is called if a leave occurs during RunL(). aError is the leave code. If the leave is
//unhandled, then RunError() should return the leavecode for handling by the active scheduler,
//otherwise it must return KErrNone
{
    //Notify the observer of the error
    iObserver.NotifyLoadCompleted(aError, *this);

    return KErrNone;    //KErrNone indicates we have dealt with the error so the scheduler won't panic 
}
// End of File

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?