mpxplugins/serviceplugins/playlistplugins/m3uplaylistplugin/src/mpxm3uplaylistimporter.cpp
changeset 0 ff3acec5bc43
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mpxplugins/serviceplugins/playlistplugins/m3uplaylistplugin/src/mpxm3uplaylistimporter.cpp	Thu Dec 17 08:45:05 2009 +0200
@@ -0,0 +1,811 @@
+/*
+* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
+* All rights reserved.
+* This component and the accompanying materials are made available
+* under the terms of "Eclipse Public License v1.0"
+* which accompanies this distribution, and is available
+* at the URL "http://www.eclipse.org/legal/epl-v10.html".
+*
+* Initial Contributors:
+* Nokia Corporation - initial contribution.
+*
+* Contributors:
+*
+* Description:  Imports a m3u playlist file
+*
+*/
+
+
+// INCLUDE FILES
+#include <bautils.h>
+#include <utf.h>
+#include <charconv.h>
+#include <mpxlog.h>
+#include <mpxmediaarray.h>
+#include <mpxattribute.h>
+#include <mpxmediageneraldefs.h>
+#include <mpxmediacontainerdefs.h>
+#include "mpxm3uplaylistdefs.h"
+#include "mpxm3uplaylistimporter.h"
+
+
+// ============================ CONSTANTS ==============================
+const TUint KUnicodeBOM = 0xFEFF;
+const TInt KMinimumConfidenceRequired = 60;
+const TInt KPathStartingChars = 3;
+
+// ============================ MEMBER FUNCTIONS ==============================
+// ----------------------------------------------------------------------------
+// Constructor. 
+// ----------------------------------------------------------------------------
+EXPORT_C CMPXM3uPlaylistImporter::CMPXM3uPlaylistImporter(
+            RFs* aFs,
+            MMPXPlaylistPluginObserver* aObserver,
+            const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>& aTopCharacterSet,
+            const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>& aAvailableCharacterSet,
+            TRequestStatus& aStatus ) :
+    CActive( EPriorityStandard ),
+    iFs( aFs ),
+    iObserver( aObserver ),
+    iCallerStatus( &aStatus ),
+    iEndLineNumber( KMPXM3UNumOfLinesToProcess ),
+    iCurrentLineNumber( 0 ),
+    iMoreToDo( ETrue ),
+    iEndOfFile( EFalse ),
+    iAutoEncodingInvalidItems( 0 ),
+    iState( EMPXM3UReadBufferWithAutoDetectEncoding ),
+    iTopCharacterSet( aTopCharacterSet ),
+    iAvailableCharacterSet( aAvailableCharacterSet )
+    {
+    CActiveScheduler::Add( this );
+    }
+
+// ----------------------------------------------------------------------------
+// 2nd phase constructor
+// ----------------------------------------------------------------------------
+EXPORT_C void CMPXM3uPlaylistImporter::ConstructL( const TDesC& aPlaylistUri )
+    {
+    MPX_DEBUG2("CMPXM3uPlaylistImporter::ConstructL(%S) entering", &aPlaylistUri);
+
+    __ASSERT_DEBUG(aPlaylistUri.Length() != 0, User::Leave(KErrArgument));
+    iPlaylistFilePath.Set(aPlaylistUri);
+
+    iAutoEncodingPlaylistArray = CMPXMediaArray::NewL();
+    
+    *iCallerStatus = KRequestPending;
+
+    TRequestStatus* status = &iStatus;
+    *status = KRequestPending;
+    User::RequestComplete(status, KErrNone);   
+    SetActive();
+    
+    MPX_DEBUG1("CMPXM3uPlaylistImporter::ConstructL() exiting");
+    }
+    
+// ----------------------------------------------------------------------------
+// Two-phased constructor. 
+// ----------------------------------------------------------------------------
+EXPORT_C CMPXM3uPlaylistImporter* CMPXM3uPlaylistImporter::NewL(
+            RFs* aFs,
+            MMPXPlaylistPluginObserver* aObserver,
+            const TDesC& aPlaylistUri,
+            const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>& aTopCharacterSet,
+            const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>& aAvailableCharacterSet,
+            TRequestStatus& aStatus )            
+    {
+    CMPXM3uPlaylistImporter* self =
+        new(ELeave)CMPXM3uPlaylistImporter(
+            aFs, aObserver, aTopCharacterSet, aAvailableCharacterSet, aStatus );
+    CleanupStack::PushL( self );
+    self->ConstructL( aPlaylistUri );
+    CleanupStack::Pop( self );
+    return self;
+    }
+
+// ----------------------------------------------------------------------------
+// Destructor. 
+// ----------------------------------------------------------------------------
+//
+EXPORT_C CMPXM3uPlaylistImporter::~CMPXM3uPlaylistImporter()
+    {
+    Cancel();
+    Cleanup();
+    }
+
+// ----------------------------------------------------------------------------
+// Handles request completion event
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXM3uPlaylistImporter::RunL()
+    {
+    MPX_DEBUG1("CMPXM3uPlaylistImporter::RunL");
+    
+    if ( iMoreToDo && iStatus.Int() == KErrNone )
+        {
+        DoTaskStep();
+        SetActive();
+        }
+    else
+        {
+        User::RequestComplete( iCallerStatus, iStatus.Int() );        
+        NotifyClient(iStatus.Int());      
+        Cleanup();                    
+        }
+    }
+    
+// ----------------------------------------------------------------------------
+// Implements cancellation of an outstanding request.
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXM3uPlaylistImporter::DoCancel()
+    {
+    MPX_DEBUG1("CMPXM3uPlaylistImporter::DoCancel");
+
+    TInt error( KErrCancel );
+    
+    // notify client that the request has been cancelled
+    NotifyClient(error);
+
+    Cleanup();
+    
+    if ( iCallerStatus )
+        {
+        User::RequestComplete( iCallerStatus, error );
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// Performs one step of the task
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXM3uPlaylistImporter::DoTaskStep()
+    {
+    MPX_DEBUG1("CMPXM3uPlaylistImporter::DoTaskStep()");
+    
+    TRequestStatus* status = &iStatus;
+    *status = KRequestPending;
+
+    TInt error( KErrNone );
+    
+    MPX_TRAP( error, DoTaskStepL() );
+
+    User::RequestComplete( status, error );
+    }
+        
+// ----------------------------------------------------------------------------
+// Performs one step of the task. Leaves when an error is encountered
+// ----------------------------------------------------------------------------
+//
+EXPORT_C void CMPXM3uPlaylistImporter::DoTaskStepL()
+    {
+    MPX_DEBUG1("CMPXM3uPlaylistImporter::DoTaskStepL()");
+    
+    switch( iState )
+        {        
+        case EMPXM3UReadBufferWithAutoDetectEncoding:
+            {
+            ReadPlaylistFileToBufferL();
+            iState = EMPXM3UParseWithAutoDetectEncoding;
+            }
+            break;
+            
+        case EMPXM3UParseWithAutoDetectEncoding:
+            {
+            ParsePlaylistBufferL(
+                *iAutoEncodingPlaylistArray, iAutoEncodingInvalidItems );
+            
+            // If at the moment, we know that there is at least one error parsing
+            // with auto detect encoding, we don't need to proceed until end of
+            // file anymore, this playlist file is concluded to be corrupted
+            if ( iAutoEncodingInvalidItems > 0 )
+                {
+                delete iAutoEncodingPlaylistArray;
+                iAutoEncodingPlaylistArray = NULL;
+                
+                User::Leave(KErrCorrupt);
+                }
+
+            // we've finished parsing with auto detect encoding we will return
+            // the playlist parsed with auto encoding
+            else if ( iEndOfFile )
+                {               
+                iState = EMPXM3UComposePlaylistMedia;
+                }
+            }       
+            break;
+
+        case EMPXM3UComposePlaylistMedia:
+            {
+            ComposePlaylistL();
+            iMoreToDo = EFalse;
+            }
+            break;
+            
+        default:
+            {
+            User::Leave(KErrAbort);
+            }
+            break;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistImporter::ReadPlaylistFileToBufferL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistImporter::ReadPlaylistFileToBufferL()
+    {
+    MPX_DEBUG2("Before reading playlist to buffer: heap size = %d", User::Heap().Size());    
+
+    delete iBuffer;
+    iBuffer = NULL;
+    iBufferPtr.Set(KNullDesC);    
+
+    //
+    // leave with KErrNotFound if the playlist file does not exist
+    //    
+    if (!BaflUtils::FileExists(*iFs, iPlaylistFilePath))
+        {
+        User::Leave(KErrNotFound);
+        }
+    
+    TEntry entry;
+    User::LeaveIfError(iFs->Entry(iPlaylistFilePath, entry));
+    
+    HBufC* buffer = HBufC::NewLC(entry.iSize);
+    TPtr ptr = buffer->Des();
+
+    HBufC8* buf8 = HBufC8::NewLC(entry.iSize);
+    TPtr8 ptr8 = buf8->Des();
+
+    // Read the first KPlaylistSampleLength bytes of the file
+    TInt sampleLength(KPlaylistSampleLength);
+    if (sampleLength > entry.iSize)
+        {
+        sampleLength = entry.iSize;
+        }
+    User::LeaveIfError(iFs->ReadFileSection(
+                            iPlaylistFilePath, 0, ptr8, sampleLength));
+
+    // auto detect character encoding
+    TUint charSetId(0);
+    TInt error = DetectCharacterSetL(*buf8, iTopCharacterSet, charSetId);
+    MPX_DEBUG3("encoding detected using top character set is 0x%x, error %d", charSetId, error);
+    
+    // when we fail to detect the encoding, use all available character set in the
+    // system to try again. If that also fails, abandon the operation.
+    if (error)
+        {
+        User::LeaveIfError(DetectCharacterSetL(*buf8, iAvailableCharacterSet, charSetId));
+        MPX_DEBUG2("encoding detected using available character set is 0x%x", charSetId);        
+        }
+
+    // read the whole file if the sample taken isn't the whole file
+    if (sampleLength != entry.iSize)
+        {
+        User::LeaveIfError(iFs->ReadFileSection(
+                                iPlaylistFilePath, 0, ptr8, entry.iSize));
+        }
+   
+    // perform character conversion using the selected encoding
+    TInt state(CCnvCharacterSetConverter::KStateDefault);
+    TInt numOfUnconvertibleChars(0);
+    CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC();
+    charSetConv->PrepareToConvertToOrFromL(charSetId, iAvailableCharacterSet, *iFs);
+    TInt retVal = charSetConv->ConvertToUnicode(ptr, *buf8, state, numOfUnconvertibleChars);
+    User::LeaveIfError(retVal);
+
+    // try again if the character set wasn't detected using the whole file
+    if ((retVal > 0 || numOfUnconvertibleChars > 0) && (sampleLength != entry.iSize))
+        {
+        MPX_DEBUG4("retVal = %d, numOfUnconvertibleChars = %d, entry.iSize = %d",
+                    retVal, numOfUnconvertibleChars, entry.iSize);
+        numOfUnconvertibleChars = 0;
+        retVal = 0;
+        User::LeaveIfError(DetectCharacterSetL(*buf8, iAvailableCharacterSet, charSetId));
+        charSetConv->PrepareToConvertToOrFromL(charSetId, iAvailableCharacterSet, *iFs);
+        retVal = charSetConv->ConvertToUnicode(ptr, *buf8, state, numOfUnconvertibleChars);
+        }
+        
+    if (retVal > 0 || numOfUnconvertibleChars > 0)
+        {
+        MPX_DEBUG3("Unable to find character encoding for the playlist file. retVal = %d, numOfUnconvertibleChars = %d",
+                    retVal, numOfUnconvertibleChars);        
+        User::Leave(KErrNotSupported);
+        }
+    
+    // remove the byte order mark (BOM) character prepended at the beginning
+    // of the stream if encoded with unicode as per Unicode section 2.4
+    if ((charSetId == KCharacterSetIdentifierUnicodeLittle ||
+         charSetId == KCharacterSetIdentifierUnicodeBig) &&
+        ptr.Length() > 0 &&
+        ptr[0] == KUnicodeBOM)
+        {
+        ptr.Delete(0,1);
+        }
+        
+    iBuffer = buffer;
+    iBufferPtr.Set(*iBuffer);
+    
+    CleanupStack::PopAndDestroy(2, buf8); // charSetConv & buf8    
+    CleanupStack::Pop(buffer);
+        
+    // brand new buffer which hasn't been read, reset iCurrentLineNumber and
+    // iEndLineNumber, and iEndOfFile
+    iCurrentLineNumber = 0;
+    iEndLineNumber = KMPXM3UNumOfLinesToProcess;
+    iEndOfFile = EFalse;
+
+    MPX_DEBUG2("After reading playlist to buffer: heap size = %d", User::Heap().Size());        
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistImporter::DetectCharacterSetL
+// copied and revised based on CMetaDataParserID3v1::DetectCharacterSetL version 4
+// -----------------------------------------------------------------------------
+//
+TInt CMPXM3uPlaylistImporter::DetectCharacterSetL(
+            const TDesC8& aSample,
+            const CArrayFix<CCnvCharacterSetConverter::SCharacterSet>& aCharacterSet,
+            TUint& aCharSetId)
+	{
+	// CCnvCharacterSetConverter::ConvertibleToCharSetL hangs if sample is too big
+	if (aSample.Size() > KPlaylistMaxSampleLength)
+	    {
+	    User::Leave(KErrNotSupported);
+	    }
+	    
+	TInt confidence(0);
+	TInt highestConfidence(0);
+	TUint charSetId(0);
+	TUint highestConfidencecharSetId(0);
+
+	CCnvCharacterSetConverter* charSetConv = CCnvCharacterSetConverter::NewLC();
+	TInt count = aCharacterSet.Count();
+	for ( TInt i=0; i < count; i++)
+		{
+		charSetId = aCharacterSet.At(i).Identifier();
+		charSetConv->ConvertibleToCharSetL(confidence, charSetId, aCharacterSet, aSample);
+		if ( confidence > highestConfidence )
+			{
+			highestConfidence = confidence;
+			highestConfidencecharSetId = charSetId;
+			}
+		}
+	CleanupStack::PopAndDestroy(charSetConv);
+	MPX_DEBUG3("CMPXM3uPlaylistImporter::DetectCharacterSetL :-> Confidence[%d] CharSetId[0x%x]",
+   		confidence, aCharSetId);
+	if ( highestConfidence == 0 || highestConfidence < KMinimumConfidenceRequired )
+		{
+		return KErrNotFound;
+		}
+	else
+		{
+		aCharSetId = highestConfidencecharSetId;
+		return KErrNone;
+		}
+	}
+	
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistPlugin::ParsePlaylistBufferL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistImporter::ParsePlaylistBufferL(
+            CMPXMediaArray& aPlaylist,
+            TInt& aInvalidItemCount)
+    {
+    // Read and process all the lines in the file
+    //
+    // the order of the following conditions is important. ReadNextLineL
+    // should be called last to avoid skipping one line
+    while (iCurrentLineNumber < iEndLineNumber &&
+           aPlaylist.Count() < KMPXM3UPlaylistMaxItemCount &&
+           ReadNextLineL())
+        {
+        ProcessLineL(aPlaylist, aInvalidItemCount);  
+        }
+
+    if ( aPlaylist.Count() == KMPXM3UPlaylistMaxItemCount )
+        {
+        User::Leave(KErrOverflow);
+        }
+
+    //
+    // haven't finished processing all lines in the file, but have processed
+    // KMPXM3UNumOfLinesToProcess number of lines. Set up iEndLineNumber for
+    // the next iteration
+    //        
+    if ( !iEndOfFile && iCurrentLineNumber == iEndLineNumber )
+        {
+        iEndLineNumber += KMPXM3UNumOfLinesToProcess;
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistPlugin::ReadNextLineL
+// -----------------------------------------------------------------------------
+//
+TBool CMPXM3uPlaylistImporter::ReadNextLineL()
+    {
+    // iBuffer should exist when this function is called
+    __ASSERT_DEBUG(iBuffer, User::Leave(KErrBadDescriptor));
+
+    if (!iBufferPtr.Length())
+        {
+        // File end reached.
+        iEndOfFile = ETrue;
+        return EFalse;
+        }
+
+    delete iLine;
+    iLine = NULL;
+
+    // Try to find line change
+    TInt offset = iBufferPtr.FindF(KMPXM3ULineChange);
+  
+    if (offset == KErrNotFound)
+        {
+        // No line change was found --> last line had no line change
+        iLine = iBufferPtr.AllocL();
+        // Set iBufferPtr to the end of buffer
+        iBufferPtr.Set(iBufferPtr.Right(0)); //magic
+        }
+    else
+        {
+        // Found line change
+        TInt length(offset);
+        if ((offset > KMPXM3UNoOffset) && 
+            (iBufferPtr[length - 1] == KMPXM3UCarriageReturn)) // magic
+            {
+            --length;
+            }
+
+        iLine = iBufferPtr.Left(length).AllocL();
+
+        // Move past the line feed
+        iBufferPtr.Set(iBufferPtr.Mid(++offset));
+        }
+
+    // Remove leading and trailing space characters from iLine's data.
+    TPtr ptr = iLine->Des();
+    ptr.Trim();
+
+    iCurrentLineNumber++;
+    return ETrue;
+    }
+    
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistImporter::ProcessLineL()
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistImporter::ProcessLineL(
+            CMPXMediaArray& aPlaylist,
+            TInt& aInvalidItemCount)
+    {
+    if ( iCurrentLineNumber == 1 ) // first line
+        {
+        // Check whether the file is in the extented format
+        TInt offset = iLine->Find(KMPXM3UTagExtm3u);
+        if (offset == KErrNotFound || offset != KMPXM3UNoOffset ||
+            iLine->Length() != KMPXM3UTagExtm3u().Length())
+            {
+            // The file is not in the extented format
+            iExtendedFormat = EFalse;
+            }
+        else
+            {
+            // The file is in the extented format
+            iExtendedFormat = ETrue;
+            return;
+            }        
+        }
+
+    if (!iItem)
+        {
+        iItem = CMPXMedia::NewL();
+        iItem->SetTObjectValueL(KMPXMediaGeneralType, EMPXItem);
+        iItem->SetTObjectValueL(KMPXMediaGeneralCategory, EMPXSong);
+        }
+    
+    // Parse line and then decide what to do with it
+    switch (ParseLineL(iItem, aInvalidItemCount))
+        {
+        case EMPXM3UPlaylistLineTypeExtinf:
+            // Continue to next round
+            break;
+            
+        case EMPXM3UPlaylistLineTypePath:
+            {
+            // Line was a path => add item to playlist
+            aPlaylist.AppendL(iItem);
+            iItem = NULL; // item now owned by aPlaylist
+            }
+            break; 
+                       
+        case EMPXM3UPlaylistLineTypeNotSupported:
+        case EMPXM3UPlaylistLineTypeCorrupted:
+        default:
+            {
+            // Line has unsupported extension tag or undefined has error
+            // occurred -> delete item
+            delete iItem;
+            iItem = NULL;
+            }
+            break;
+        }
+    }
+    
+// -----------------------------------------------------------------------------
+// CMPXM3uPlaylistImporter::ParseLineL
+// -----------------------------------------------------------------------------
+//
+TInt CMPXM3uPlaylistImporter::ParseLineL(
+            CMPXMedia* aItem,
+            TInt& aInvalidItemCount)
+    {
+    // There should be always aItem & iLine when this function is called
+    __ASSERT_DEBUG(aItem && iLine, User::Leave(KErrAbort));
+
+    if (!iLine->Length())
+        {
+        // Empty line => line is invalid
+        return EMPXM3UPlaylistLineTypeNotSupported;
+        }
+
+    if (iExtendedFormat)
+        {
+        // File is in the extented format => check whether there is extented
+        // info in this line.
+        TInt offset = iLine->Find(KMPXM3UTagExtinf);
+        if (offset != KErrNotFound && offset == KMPXM3UNoOffset)
+            {
+            // Extented info found
+            aItem->SetTextValueL(KMPXMediaGeneralTitle, KNullDesC);
+
+            offset = iLine->Find(KMPXM3UPoint);
+
+            if(offset != KErrNotFound)
+                {
+
+                // Title and length found from extented info
+                // Parse length
+                TLex16 lex(iLine->Mid(KMPXM3UTagExtinf().Length(),
+                                offset - KMPXM3UTagExtinf().Length()));
+                TInt length;
+                if (lex.Val(length))
+                    {
+                    // Error has occurred => legth is set to be ignored
+                    length = KMPXM3UIgnoreTimeEntry;
+                    }
+
+                aItem->SetTObjectValueL(KMPXMediaGeneralDuration, length);
+                MPX_DEBUG2("    duration %d", length);
+
+                // Parse title
+                HBufC* title = iLine->Mid(offset + 1).AllocLC();
+                aItem->SetTextValueL(KMPXMediaGeneralTitle, *title);
+                MPX_DEBUG2("    title %S", title);
+                CleanupStack::PopAndDestroy( title );
+
+                return EMPXM3UPlaylistLineTypeExtinf; // line type extinf
+                }    
+            }
+        }
+
+    // File is not in the extented format or supported info not found from this
+    // line.
+    switch (iLine->Find(KMPXM3UTagExt))
+        {
+        case KMPXM3UNoOffset:
+            // Unsupported extended info tag found from this line
+            return EMPXM3UPlaylistLineTypeNotSupported;
+            
+        case KErrNotFound:
+        default:
+            // Extended info not found from the beginning of line => line is
+            // a path.
+            {
+            // Get absolute path
+            TInt error(KErrNone);
+            HBufC* uri = ParseAbsolutePathLC(*iLine, error);
+        
+            if (error)
+                {
+                if (error == KErrPathNotFound)
+                    {
+                    TUint flag(KMPXMediaGeneralFlagsSetOrUnsetBit | KMPXMediaGeneralFlagsIsInvalid);
+                    aItem->SetTObjectValueL(KMPXMediaGeneralFlags, flag);
+                    }
+                else
+                    {
+                    if( uri )
+                        {
+                        CleanupStack::PopAndDestroy( uri );
+                        }
+                    
+                    ++aInvalidItemCount;
+                                    
+                    // All other errors are considered to mean playlist is
+                    // corrupt.
+                    return EMPXM3UPlaylistLineTypeCorrupted;
+                    }
+                }
+
+            aItem->SetTextValueL(KMPXMediaGeneralUri, *uri);
+            MPX_DEBUG2("    uri %S", uri);
+                
+            // if title isn't supplied by the m3u file, extract file name from
+            // URI as the title
+            if (!aItem->IsSupported(KMPXMediaGeneralTitle))
+                {
+                TParsePtrC parser(*uri);
+                TPtrC title = parser.Name();
+                aItem->SetTextValueL(KMPXMediaGeneralTitle, title);
+                MPX_DEBUG2("    title %S", &title);
+                }
+
+            CleanupStack::PopAndDestroy( uri );
+            
+            return EMPXM3UPlaylistLineTypePath; // line type path
+            }
+        }
+    }
+
+// -----------------------------------------------------------------------------
+// CMPlayerM3UPlaylistParser::ParseAbsolutePathL
+// -----------------------------------------------------------------------------
+//
+HBufC* CMPXM3uPlaylistImporter::ParseAbsolutePathLC(
+            const TDesC& aPath,
+            TInt& aError)
+    {
+    HBufC* path = NULL;
+    
+    TBool isAbsolute( EFalse );
+    
+    if (aPath.Length() > KPathStartingChars && 
+        !aPath.Mid(1, 2).CompareF(KMPXM3UAbsPath)) // magic: the 2nd and 3rd chars
+                                               // are always ":\"
+                                               // for absolute paths
+        {
+        isAbsolute = ETrue;
+        }
+
+    if (aPath.Length() > KMaxFileName) // Test if path is too long
+        {
+        aError = KErrCorrupt;
+        }
+    else if ( isAbsolute )
+        {
+        aError = KErrNone;
+        aError = iFs->IsValidName(aPath) ? KErrNone : KErrBadName;
+        path = aPath.AllocLC();
+        }
+    else
+        {
+        // Given path could be relative => create absolute path and test it
+        // Playlist file path
+        TParse playlistPath;
+        playlistPath.Set(iPlaylistFilePath, NULL, NULL);
+        // Path to the folder, where playlist file is located to
+        TPtrC currentFolder = playlistPath.DriveAndPath();
+        // Create absolute path
+        path = HBufC::NewLC(currentFolder.Length() + aPath.Length());
+
+        TPtr tmpPtr(path->Des());
+        tmpPtr = currentFolder;
+        tmpPtr += aPath;
+
+        aError = iFs->IsValidName(*path) ? KErrNone : KErrBadName;
+        }
+    
+    // It is possible that a song exists in the filesystem but isn't added to
+    // the database because it's not a supported type. If such song is included
+    // in a playlist, it will be added to the database when the playlist is added.
+    // Because of this, we cannot rely on whether the song exists in the database
+    // to conclude whether the song is a broken link. We need to check for file
+    // existence here. For the unsupported songs included in the playlist, they
+    // will then be marked as corrupted when user initiates playback of those
+    // songs.
+    if (!aError &&
+        !BaflUtils::FileExists(*iFs, *path))
+        {
+        aError = KErrPathNotFound;
+        }
+
+    return path;
+    }
+    
+// -----------------------------------------------------------------------------
+// CMPlayerM3UPlaylistParser::ComposePlaylistL
+// -----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistImporter::ComposePlaylistL()
+    {
+    //
+    // instantiate a CMPXMedia that represent the playlist which will
+    // contain the CMPXMediaArray
+    //
+    iPlaylist = CMPXMedia::NewL();
+    
+    // set playlist title
+    TParsePtrC parser(iPlaylistFilePath);
+    iPlaylist->SetTextValueL(KMPXMediaGeneralTitle, parser.Name());
+    
+    // set playlist URI
+    iPlaylist->SetTextValueL(KMPXMediaGeneralUri, iPlaylistFilePath);
+        
+    // set type
+    iPlaylist->SetTObjectValueL(KMPXMediaGeneralType, EMPXItem);
+        
+    // set category
+    iPlaylist->SetTObjectValueL(KMPXMediaGeneralCategory, EMPXPlaylist);        
+
+    // set playlist array
+    iPlaylist->SetCObjectValueL(KMPXMediaArrayContents, iAutoEncodingPlaylistArray);
+        
+    // set array acount
+    iPlaylist->SetTObjectValueL(KMPXMediaArrayCount, iAutoEncodingPlaylistArray->Count());
+        
+    // playlist makes a copy of the array, we can now free the medias
+    // array
+    delete iAutoEncodingPlaylistArray;
+    iAutoEncodingPlaylistArray = NULL;
+    }
+    
+// ----------------------------------------------------------------------------
+// Cleanup. 
+// ----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistImporter::Cleanup()
+    {
+    delete iBuffer;
+    iBuffer = NULL;
+    
+    delete iLine;
+    iLine = NULL;   
+
+    delete iItem;
+    iItem = NULL;
+   
+    delete iAutoEncodingPlaylistArray;
+    iAutoEncodingPlaylistArray = NULL;
+    
+    delete iPlaylist;
+    iPlaylist = NULL;
+    }
+
+// ----------------------------------------------------------------------------
+// Handles notifications to the client
+// ----------------------------------------------------------------------------
+//
+void CMPXM3uPlaylistImporter::NotifyClient( TInt aError )
+    {
+    MPX_DEBUG3("CMPXM3uPlaylistImporter::NotifyClient - iAutoEncodingInvalidItems=%d error=%d",
+        iAutoEncodingInvalidItems, aError);
+        
+    if ( iObserver )
+        {
+        if (aError)
+            {
+            // we don't need the playlist media to be passed back to the client
+            // in case of an error, delete it now
+            delete iAutoEncodingPlaylistArray;
+            iAutoEncodingPlaylistArray = NULL;
+
+            // to-do: change HandlePlaylistL to HandlePlaylist
+            TRAP_IGNORE(iObserver->HandlePlaylistL( NULL, aError, ETrue ));
+            }
+        else
+            {
+            // notify client. return the playlist media
+            CMPXMedia* playlist = iPlaylist;
+            iPlaylist = NULL; // client takes over the ownership
+            
+            // to-do: change HandlePlaylistL to HandlePlaylist
+            TRAP_IGNORE(iObserver->HandlePlaylistL( playlist, aError, ETrue ));
+            }
+        }        
+    }
+        
+// End of file