upnpharvester/cdssync/cdssynclib/src/cdssyncimplsql.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 17 Sep 2010 08:31:21 +0300
changeset 32 3785f754ee62
parent 0 7f85d04be362
permissions -rw-r--r--
Revision: 201035 Kit: 201037

/*
* Copyright (c) 2008 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:      Cds Sync Implementation
*
*/






#include <hash.h>
#include <xml/matchdata.h>
#include "cmsqlmainfactory.h"
#include "mcmsqlmain.h"
#include "cmsqlbaseitem.h"
#include "cmsqlgenericitem.h"
#include "cmsqlaudioitem.h"
#include "cmsqlimageitem.h"
#include "cmsqlvideoitem.h"
#include "cmsqlpropertyitem.h"
#include "cmsqlresolutionpropertyitem.h"

#include "cdssyncimplsql.h"
#include "cdssyncsqlao.h"
#include "msdebug.h"

// Constants
_LIT8( KXmlMimeType, "text/xml" );
_LIT8( KSymbian, "Symbian");
_LIT8( KSemicolon, ";");

_LIT8( KXmlCdsDefaultNamespace, 
       "urn:schemas-upnp-org:metadata-1-0/DIDL-Lite/");
_LIT8( KXmlCdsDcNamespace,"http://purl.org/dc/elements/1.1/");
_LIT8( KXmlCdsUpnpNamespace,"urn:schemas-upnp-org:metadata-1-0/upnp/");

_LIT8( KAlbum,      "album");
_LIT8( KArtist,     "artist");
_LIT8( KClass,      "class");
_LIT8( KDescription,"description");
_LIT8( KDate,       "date");
_LIT8( KGenre,      "genre");
_LIT8( KId,         "id");
_LIT8( KItem,       "item");
_LIT8( KRes,        "res");
_LIT8( KTitle,      "title");
_LIT8( KAlbumArtUri, "albumArtURI");

_LIT8( KBitrate,        "bitrate");
_LIT8( KDuration,       "duration");
_LIT8( KProtocolInfo,   "protocolInfo");
_LIT8( KResolution,     "resolution");
_LIT8( KSize,           "size");

_LIT8( KAudioItem,      "audioItem");
_LIT8( KImageItem,      "imageItem");
_LIT8( KVideoItem,      "videoItem");
_LIT8( KAudioBroadCastItem, "audioItem.audioBroadcast");
_LIT8( KVideoBroadCastItem, "videoItem.videoBroadcast");
_LIT8( KDlnaPn,         "DLNA.ORG_PN=" );

const TInt KAlbumIndex =        0;
const TInt KArtistIndex =       1;
const TInt KClassIndex =        2;
const TInt KGenreIndex =        3;
const TInt KResolutionIndex =   4;
const TInt KUpnpProfileIndex =  5;

const TCmMetadataField KMetadataTypes[] = 
    {
    ECmAlbum, ECmArtist, ECmUpnpClass, ECmGenre, ECmResolution, ECmProfileId
    };

const TInt KMetadataTypeCount = 6;

const TInt KParseChunkSize = 100 * KKilo; // 100 kB

const TInt KCdsSyncMaxBufLength = 512;

const TInt KCdsSyncPriority = CActive::EPriorityIdle; 

// --------------------------------------------------------------------------
// CItemResource::NewLC()
// --------------------------------------------------------------------------
CItemResource* CItemResource::NewLC()
    {
    CItemResource* self=new (ELeave) CItemResource();
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }


// --------------------------------------------------------------------------
// CItemResource::~CItemResource()
// --------------------------------------------------------------------------
CItemResource::~CItemResource() // destruct - virtual, so no export
    {
    delete iDuration;
    delete iBitrate;
    delete iSize;    
    delete iResolution;    
    delete iProtocol;    
    delete iUri;    
    }

// --------------------------------------------------------------------------
// CItemResource::CItemResource()
// --------------------------------------------------------------------------
CItemResource::CItemResource()
    {
    }

// --------------------------------------------------------------------------
// CItemResource::ConstructL()
// --------------------------------------------------------------------------
void CItemResource::ConstructL()
    {
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::NewL()
// --------------------------------------------------------------------------
CCdsSyncImpl* CCdsSyncImpl::NewL()
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::NewL"));
    CCdsSyncImpl* self = NewLC();
    CleanupStack::Pop( self );
    return self;
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::NewLC()
// --------------------------------------------------------------------------
CCdsSyncImpl* CCdsSyncImpl::NewLC()
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::NewLC"));

    CCdsSyncImpl* self = new (ELeave) CCdsSyncImpl();
    CleanupStack::PushL( self );
    self->ConstructL();
    return self;
    }


// --------------------------------------------------------------------------
// CCdsSyncImpl::~CCdsSyncImpl()
// --------------------------------------------------------------------------
CCdsSyncImpl::~CCdsSyncImpl() 
    {
    
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::~CCdsSyncImpl"));

    if ( IsActive() )
        {
        Cancel();
        }
        
    // iCurrentContent is owned and needs to be deleted here before it is set
    // to NULL at CleanItemData-method.
    if ( iCurrentContent )
        {
        delete iCurrentContent;    
        }    
    CleanItemData();
    
    iState = ECdsSyncIdle;
    if ( iBackground )
        {
        delete iBackground;    
        }
    
    if ( iParser )
        {
        delete iParser;    
        }
    
    if ( iCurrentDocument )
        {
        delete iCurrentDocument;    
        }
    
    iItemsInDb.ResetAndDestroy();
    iItemsToAdd.ResetAndDestroy();
    for ( TInt i = 0; i < iNames.Count(); i++ )
        {
        iNames[ i ]->ResetAndDestroy();
        }
    iNames.ResetAndDestroy();
    iFs.Close();
    
    if ( iSqlAo )
        {
        delete iSqlAo;    
        }
    
    if ( iMetadataDb )
        {
        iMetadataDb->Close();    
        }    
    }


// --------------------------------------------------------------------------
// CCdsSyncImpl::CCdsSyncImpl()
// --------------------------------------------------------------------------
CCdsSyncImpl::CCdsSyncImpl() : 
    CActive( EPriorityStandard ),
    iHashOrder( CCmSqlBaseItem::CompareByHash ),
    iNameOrder( CCmSqlPropertyItem::CompareItemsByName )
    {
    CActiveScheduler::Add( this );
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::ConstructL() 
// --------------------------------------------------------------------------
void CCdsSyncImpl::ConstructL() 
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::ConstructL"));

    CMatchData *matchData = CMatchData::NewL();
    CleanupStack::PushL( matchData );
    matchData->SetMimeTypeL( KXmlMimeType );
    User::LeaveIfError( iFs.Connect() );
    matchData->SetVariantL( KSymbian );

    iParser  = CParser::NewL( *matchData, *this );
    CleanupStack::PopAndDestroy( matchData );
    
    iBackground = CIdle::NewL( KCdsSyncPriority );
    iMetadataDb = CCmSqlMainFactory::NewCmSqlMainL( iFs );

    }


// --------------------------------------------------------------------------
// CCdsSyncImpl::ResetL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::ResetL() 
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::ResetL"));
    LOG(_L("[Cds Sync]\t cleaning objects.."));
    iBackground->Cancel();
    if( iSqlAo && iSqlAo->IsActive() && iMetadataDb )
        {
        iMetadataDb->CancelAsyncOperation();
        }
  
    delete iSqlAo; 
    iSqlAo = NULL;
    
    iItemsInDb.ResetAndDestroy();
    iItemsToAdd.ResetAndDestroy();
    for ( TInt i = 0; i < iNames.Count(); i++ )
        {
        iNames[ i ]->ResetAndDestroy();
        }
    iNames.ResetAndDestroy();
    CleanItemData();           
    }


// --------------------------------------------------------------------------
// CCdsSyncImpl::InitL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::InitL( RPointerArray<HBufC8>& aSourceDataArray,
                                   const TInt& aDeviceId, 
                                   MCdsSyncObserver& aObserver,
                                   TInt aAddGranularity )
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::InitL"));
    iSearchIndex = 0;
    ResetL();
    
    LOG(_L("[Cds Sync]\t initializing variables.."));
    iObserver = &aObserver;
    iSourceDataArray = &aSourceDataArray;
    iSourceDataComplete = EFalse;
    iState = ECdsSyncInitializing;
    iUnchangedItemCount = 0;
        
    LOG(_L("[Cds Sync]\t creating ao.."));
    iSqlAo = CCdsSyncSqlAo::NewL( 
        *iMetadataDb, *this, iItemsToAdd, 
        iItemsInDb, aAddGranularity );
    
    LOG(_L("[Cds Sync]\t Requesting existing metadata.."));
    iMetadataDb->SetMsId( aDeviceId );
    iMetadataDb->GetItemsL( iItemsInDb, iSqlAo->iStatus );
    
    iSqlAo->iState = CCdsSyncSqlAo::ECdsSyncSqlAoInitializing;
    iSqlAo->Activate();
        
    LOG(_L("[Cds Sync]\t done."));
    }


// --------------------------------------------------------------------------
// CCdsSyncImpl::RunL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::RunL()
    {
    TRACE( Print( _L("[Cds Sync]\t CCdsSyncImpl::RunL,\
    iStatus %d iState %d"), iStatus.Int(), (TInt)iState ) );
    
    switch ( iState )
        {
        case ECdsSyncInitializing:
            {
            TInt namesCount = iNames.Count();
            TRACE( Print( 
                _L("[Cds Sync]\t iNames array size is %d out of %d"),
                namesCount, 
                KMetadataTypeCount ) );
            
            if ( namesCount == KMetadataTypeCount )
                {
                // all arrays ready, sort them
                iItemsInDb.Sort( iHashOrder );
                for ( TInt i = 0; i < KMetadataTypeCount; i++ )
                    {
                    iNames[ i ]->Sort( iNameOrder );
                    }
                iState = ECdsSyncReadyToParse;
                iSqlAo->iState = CCdsSyncSqlAo::ECdsSyncSqlAoIdle;
                NotifySourceDataAddedL();
                }
            else 
                {
                RPointerArray<CCmSqlPropertyItem>* nameArray =
                    new (ELeave) RPointerArray<CCmSqlPropertyItem>();
                
                iMetadataDb->GetPropertyValuesL( 
                    *nameArray,
                    iSqlAo->iStatus,
                    KMetadataTypes[namesCount]
                );
                iSqlAo->Activate();
                iNames.AppendL( nameArray );
                }
            break;
            }
        default: 
            {
            TRACE( Print( _L("[Cds Sync]\t RunL iState is \
            not ECdsSyncInitializing" ) ));
            break;
            }
        }
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::DoCancel()
// --------------------------------------------------------------------------
void CCdsSyncImpl::DoCancel()
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::DoCancel"));
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::NotifySourceDataAddedL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::NotifySourceDataAddedL( 
    TBool aSourceDataComplete ) 
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::NotifySourceDataAdded"));
    
    if ( aSourceDataComplete )
        {
        iSourceDataComplete = ETrue;
        }
    
    if ( iState == ECdsSyncReadyToParse ) 
        {
        if ( iSourceDataArray->Count() )
            {
            iCurrentDocument = ( *iSourceDataArray )[ 0 ];
            iSourceDataArray->Remove( 0 );
            iChunkIndex = 0;
            iParser->ParseBeginL();
            iBackground->Start( 
                TCallBack( CCdsSyncImpl::BackgroundParseL, this) );
            iState = ECdsSyncParsing;
            }
        else if ( iSourceDataComplete ) 
            {
            TRACE( Print( _L
            ("[Cds Sync]\t parsing complete, %d items to add"), 
                iItemsToAdd.Count() ));
            TRACE( Print( _L("[Cds Sync]\t and %d items to remove"), 
                iItemsInDb.Count() ));
     
            iState = ECdsSyncIdle;
            
            RemoveUnchangedItems( );
            
            iSqlAo->NotifyItemsAddedL( ETrue );
            }
        }
    }
    
// --------------------------------------------------------------------------
// CCdsSyncImpl::ProgressL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::ProgressL( TInt aItemCount )
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::ProgressL"));
    iObserver->ProgressL( aItemCount );
    }


// --------------------------------------------------------------------------
// CCdsSyncImpl::ChunkCount()
// --------------------------------------------------------------------------
TInt CCdsSyncImpl::ChunkCount()
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::ChunkCount"));
    TInt chunkCount = iSourceDataArray->Count();
    if ( iCurrentDocument )
        {
        chunkCount++;
        }
    return chunkCount;
    }

// --------------------------------------------------------------------------
// Increment successfully processed number 
// --------------------------------------------------------------------------
//
TInt CCdsSyncImpl::ProcessedItemCount()
	{
	return iProcessedItems;
	}
	
// --------------------------------------------------------------------------
// Increment chuch number ( search index )
// --------------------------------------------------------------------------
//
void CCdsSyncImpl::SetSearchIndex( const TInt aSearchIndex )
    {
    iSearchIndex = aSearchIndex;
    } 
        
// --------------------------------------------------------------------------
// CCdsSyncImpl::ChunkCompleteL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::ChunkCompleteL()
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::ChunkCompleteL"));
    iObserver->ChunkCompleteL();
    iProcessedItems = 0;
    }
    
// --------------------------------------------------------------------------
// CCdsSyncImpl::OperationsCompleteL()
// --------------------------------------------------------------------------
#ifdef _DEBUG
void CCdsSyncImpl::OperationsCompleteL( TInt aErrCode ) 
#else // _DEBUG
void CCdsSyncImpl::OperationsCompleteL( TInt /*aErrCode*/ ) 
#endif // _DEBUG
    {
    TRACE( Print( _L
    ("[Cds Sync]\t CCdsSyncImpl::OperationsCompleteL (err %d)"), 
        aErrCode ));
    
    if ( iState == ECdsSyncInitializing )
        {
        RunL();
        }
    else 
        {
        iObserver->SyncCompleteL();    
        iProcessedItems = 0;  
        }
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::BackgroundParseL()
// --------------------------------------------------------------------------
TInt CCdsSyncImpl::BackgroundParseL( TAny* aCdsSync )
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::BackgroundParseL"));
    
    return ((CCdsSyncImpl*)aCdsSync)->DoBackgroundParseL();
    }
 

// --------------------------------------------------------------------------
// CCdsSyncImpl::DoBackgroundParseL()
// --------------------------------------------------------------------------
TInt CCdsSyncImpl::DoBackgroundParseL()
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::DoBackgroundParseL"));
    if ( iCurrentDocument ) 
        {

    #ifdef _DEBUG
        iHashTime = 0;
        TTime timeBefore;
        timeBefore.HomeTime();
    #endif
        
        TBool parseMore = 
            ( iCurrentDocument->Length() - iChunkIndex ) > KParseChunkSize;
        TPtrC8 parseChunk = 
            parseMore ?
            iCurrentDocument->Mid( iChunkIndex, KParseChunkSize ) :
            iCurrentDocument->Mid( iChunkIndex );
        
        iUnchangedItemCount = 0;
            
        // in case of leave that is caused by out of memory
        TRAPD( error, iParser->ParseL( parseChunk ) );
    	if ( error != KErrNone )
        	{
        	TRACE( Print( _L("[Cds Sync]\t Parse error = %d"), error ));
        	}
        
    #ifdef _DEBUG
        TTime timeAfter;
        timeAfter.HomeTime();
        TRACE( Print( _L
            ("[Cds Sync]\t parsing of %d bytes of XML took %ld microsec"),
            parseChunk.Size(), 
            timeAfter.MicroSecondsFrom( timeBefore ).Int64() ) );
        TRACE( Print( _L
            ("[Cds Sync]\t of which hash comparison took %ld microsec"), 
            iHashTime));
    #endif

        if ( parseMore ) 
            {
            iChunkIndex += KParseChunkSize;
            }
        else 
            {
            // in case of leave that is caused by out of memory
            TRAPD( err, iParser->ParseEndL() );
    		if ( err != KErrNone )
        		{
        		TRACE( Print( _L("[Cds Sync]\t \
        		ParseEndL error = %d"), err ));
        		}
            iState = ECdsSyncReadyToParse;
            delete iCurrentDocument; iCurrentDocument = NULL;
            // check if there's more to parse and trap the leave
            TRAPD( errOne, NotifySourceDataAddedL() );
            if ( errOne != KErrNone )
                {
                TRACE( Print( _L("[Cds Sync]\t NotifySourceDataAdded \
                error = %d"), errOne ));
                }
            ChunkCompleteL();
            }
        iSqlAo->NotifyItemsAddedL();
        
        return parseMore;
        
        }
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::BackgroundParseL END"));
    return EFalse;
    }
  

// --------------------------------------------------------------------------
// CCdsSyncImpl::FindAttribute()
// --------------------------------------------------------------------------
TInt CCdsSyncImpl::FindAttribute( const TDesC8& aName, 
                                  const TDesC8& aPref, 
                                  const RAttributeArray& aAttributes ) const
    {
    TInt returnvalue = KErrNotFound;
    for ( TInt i = 0; i < aAttributes.Count(); i++ ) 
        {
        if ( ( aAttributes[i].Attribute().LocalName().
               DesC().Compare( aName ) == 0) &&
             ( aAttributes[i].Attribute().Prefix().
               DesC().Compare( aPref ) == 0) )
            {
            returnvalue = i;
            // break out from the loop
            i = aAttributes.Count();
            }
        }
    return returnvalue;
    }


// --------------------------------------------------------------------------
// CCdsSyncImpl::ParseResolution()
// --------------------------------------------------------------------------
void CCdsSyncImpl::ParseResolution( const TDesC8& aDes,
                                    TUint& aWidth,
                                    TUint& aHeight ) const
    {
    TLex8 lex( aDes );
    if ( lex.Val( aWidth ) != KErrNone ) 
        {
        aWidth = 0;
        }
    if ( lex.Get() != 'x' || lex.Val( aHeight ) != KErrNone ) 
        {
        aHeight = 0;
        }
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::ParseUint()
// --------------------------------------------------------------------------
TUint CCdsSyncImpl::ParseUint( const TDesC8& aDes ) const
    {
    TUint res = 0;
    TLex8 lex( aDes );
    if ( lex.Val( res ) != KErrNone ) 
        {
        res = 0;
        }
    return res;
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::ParseInt64()
// --------------------------------------------------------------------------
TInt64 CCdsSyncImpl::ParseInt64( const TDesC8& aDes ) const
    {
    TInt64 res = 0;
    TLex8 lex( aDes );
    if ( lex.Val( res ) != KErrNone )
        {
        res = 0;
        }
    return res;
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::ParseTime()
// --------------------------------------------------------------------------
TTime CCdsSyncImpl::ParseTime( const TDesC8& aDes ) const 
    {
    TUint year = 0;
    TUint month = 1;
    TUint day = 1;
    TLex8 lex;
    
    TInt dashpos = aDes.Find( _L8("-") );
    TInt dashpos2 = aDes.Mid( dashpos + 1 ).Find( _L8("-") ) + dashpos + 1;
    
    lex = aDes.Left(4);
    if ( lex.Val( year ) != KErrNone )
        {
        year = 0;
        }        
    if ( dashpos2-dashpos > 1 ) 
        {
        lex = aDes.Mid( dashpos + 1,dashpos2 - dashpos - 1 );
        if ( lex.Val(month) != KErrNone )
            {
            month = 1;
            }
            
        }
    if (aDes.Length() - dashpos2 > 1) 
        {
        lex = aDes.Mid(dashpos2 + 1);
        if ( lex.Val(day) != KErrNone ) 
            {
            day = 1;
            }            
        }
    TDateTime time;
    if ( time.Set(year, TMonth(month-1), day-1, 0,0,0,0) != KErrNone )
        {
        return TTime( TDateTime(0, TMonth(0), 0, 0,0,0,0) );
        }
    else
        {
        return TTime( time );
        }
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::ParseDuration()
// --------------------------------------------------------------------------
TReal CCdsSyncImpl::ParseDuration( const TDesC8& aDes ) const
    {
    TInt multiplier = 1;
    TInt plusminuspos = aDes.Find( _L8("-") );
    if ( plusminuspos == KErrNotFound ) 
        {
        plusminuspos = aDes.Find( _L8("+") );
        }
    else 
        {
        multiplier = -1;
        }
        
    TInt hourminutepos = aDes.Find( _L8(":") );
    TInt minutesecondpos = aDes.Mid( hourminutepos + 1 ).
         Find( _L8(":") ) + hourminutepos + 1;
    TInt dotpos = aDes.Find( _L8(".") );
    TInt slashpos = aDes.Find( _L8("/") );

    TInt hours = 0; TInt minutes = 0; 
    TReal seconds = 0; TReal f0 = 0; TReal f1=1;    
    TLex8 lex;
    
    if ( hourminutepos - plusminuspos > 1 ) 
        {
        lex = aDes.Mid( plusminuspos + 1, hourminutepos - plusminuspos - 1 );
        if ( lex.Val( hours ) != KErrNone )
            {
            hours = 0;
            }            
        }
    if ( minutesecondpos - hourminutepos > 1 ) 
        {
        lex = aDes.Mid( hourminutepos + 1, 
                        minutesecondpos - hourminutepos - 1 );
        if ( lex.Val(minutes) != KErrNone ) 
            {
            minutes = 0;
            }
        }
    if ( (dotpos == KErrNotFound || slashpos == KErrNotFound) 
          && minutesecondpos != KErrNotFound ) 
        {
        lex = aDes.Mid( minutesecondpos + 1 );
        if ( lex.Val(seconds) != KErrNone )
            {
            seconds = 0;
            }            
        } 
    else if (slashpos - dotpos > 1 && dotpos - minutesecondpos > 1 
            && aDes.Length() - slashpos > 1) 
        {
        lex = aDes.Mid( minutesecondpos + 1, dotpos - minutesecondpos - 1 );
        if ( lex.Val( seconds ) != KErrNone ) 
            {
            seconds = 0;
            }
        lex = aDes.Mid( dotpos + 1, slashpos - dotpos - 1 );
        if ( lex.Val(f0) != KErrNone ) 
            {
            f0 = 0;
            }            
        lex = aDes.Mid( slashpos + 1 );
        if ( lex.Val(f1) != KErrNone ) 
            {
            f1 = 1;
            }            
        }
    
    return multiplier*(hours * 3600 + minutes * 60 + seconds + f0/f1);
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::CalculateHashL()
// --------------------------------------------------------------------------
HBufC8* CCdsSyncImpl::CalculateHashL() const
    {
    CSHA1* sha1 = CSHA1::NewL();
    CleanupStack::PushL( sha1 );
    sha1->Reset();
    
    if ( iArtist ) 
        {
        sha1->Update( *iArtist );    
        }
    else 
        {
        sha1->Update( KNullDesC8() );
        }
    
    if ( iAlbum ) 
        {
        sha1->Update( *iAlbum );    
        }
    else 
        {
        sha1->Update( KNullDesC8() );
        }
    
    if ( iTitle ) 
        {
        sha1->Update( *iTitle );
        }
    else 
        {
        sha1->Update( KNullDesC8() );
        }
    
    if ( iClass ) 
        {
        sha1->Update( *iClass );
        }
    else 
        {
        sha1->Update( KNullDesC8() );
        }
    
    if ( iGenre ) 
        {
        sha1->Update( *iGenre );
        }
    else 
        {
        sha1->Update( KNullDesC8() );
        }
        
    if ( iDate ) 
        {
        sha1->Update( *iDate );
        }
    else 
        {
        sha1->Update( KNullDesC8() );
        }
        
    if( iAlbumArtUri )
        {
        sha1->Update( *iAlbumArtUri );
        }
    else 
        {
        sha1->Update( KNullDesC8() );
        }        
    
    for ( TInt i = 0; i < iResources.Count(); i++ ) 
        {
        CItemResource* res = iResources[i];
        
        if ( res->iBitrate ) 
            {
            sha1->Update( *res->iBitrate );    
            }
        else 
            {
            sha1->Update( KNullDesC8() );
            }
        
        if ( res->iSize ) 
            {
            sha1->Update( *res->iSize );
            }
        else 
            {
            sha1->Update( KNullDesC8() );
            }
        
        
        if ( res->iResolution ) 
            {
            sha1->Update( *res->iResolution );
            }
        else 
            {
            sha1->Update( KNullDesC8() );    
            }
        
        
        if ( res->iDuration ) 
            {
            sha1->Update( *res->iDuration );
            }
        else 
            {
            sha1->Update( KNullDesC8() );    
            }
        
        if ( res->iProtocol ) 
            {
            sha1->Update( *res->iProtocol );
            }
        else 
            {
            sha1->Update( KNullDesC8() );
            }
        
        if ( res->iUri ) 
            {
            sha1->Update( *res->iUri );
            }
        else 
            {
            sha1->Update( KNullDesC8() );            
            }
        }

    // get the final hash value.
    TPtrC8 hash = sha1->Final();
    
    // create an object that can be returned and copy hash value there.
    HBufC8* retval = hash.AllocL();

    // delete SHA1 object.
    CleanupStack::PopAndDestroy( sha1 );
    sha1 = NULL;
    
    // return 
    return retval;
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::EscapeHashLC()
// --------------------------------------------------------------------------
HBufC* CCdsSyncImpl::EscapeHashLC( const TDesC8& aHash ) const
    {
    HBufC* escapedHash = HBufC::NewLC( 40 );
    TPtr ptr = escapedHash->Des();
    for ( TInt i=0; i < 20; i++ ) 
        {
        if ( aHash[i] == 0 ) 
            {
            ptr.Append( _L("\\0") );
            } 
        else if ( aHash[i] == '\\' )
            {
            ptr.Append( _L("\\\\") );
            } 
        else
            {
            ptr.Append( aHash[i] );
            }
        }
    return escapedHash;    
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::GetPropertyIdL()
// --------------------------------------------------------------------------
TInt64 CCdsSyncImpl::GetPropertyIdL( TInt aMetadataIndex,
                                     const TDesC8& aValue ) const
    {
    TInt64 id = 0;
    
    TCmMetadataField metadataType = KMetadataTypes[ aMetadataIndex ];
    
    CCmSqlPropertyItem* property = NULL;
    if ( metadataType == ECmResolution )
        {
        property = CCmSqlResolutionPropertyItem::NewLC();
        }
    else 
        {
        property = CCmSqlPropertyItem::NewLC();
        }
        
    
    property->SetNameL( aValue );
    CCmSqlPropertyItemArray* properties = iNames[ aMetadataIndex ];
    TInt index = properties->FindInOrder( property, iNameOrder );

    
    if ( index == KErrNotFound )
        {
        if ( metadataType == ECmResolution ) 
            {
            TUint width = 0;
            TUint height = 0;
            ParseResolution( aValue, width, height );
            
            CCmSqlResolutionPropertyItem* resolutionProperty = 
                static_cast<CCmSqlResolutionPropertyItem*> ( property );
            resolutionProperty->SetWidth( width );
            resolutionProperty->SetHeight( height );
            resolutionProperty->SetPixelCount( width * height );
            }
        
        property->SetStatus( EFalse );
        
        iMetadataDb->SyncAddPropertyItemL( 
            *property, metadataType );
        properties->InsertInOrder( property, iNameOrder );
        id = property->Id();
        CleanupStack::Pop( property );
        }
    else 
        {
        id = (*properties)[ index ]->Id();
        CleanupStack::PopAndDestroy( property );
        }

    return id;
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::CleanItemData()
// --------------------------------------------------------------------------
void CCdsSyncImpl::CleanItemData() 
    {
    iCurrentContent = NULL;

    delete iItemId; 
    iItemId = NULL;

    delete iArtist; 
    iArtist = NULL;

    delete iAlbum; 
    iAlbum = NULL;

    delete iTitle; 
    iTitle = NULL;

    delete iClass; 
    iClass = NULL;

    delete iGenre; 
    iGenre = NULL;

    delete iDate; 
    iDate = NULL;

    delete iDescription; 
    iDescription = NULL;
    
    delete iAlbumArtUri;
    iAlbumArtUri = NULL;

    iResources.ResetAndDestroy();
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::OnStartDocumentL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnStartDocumentL( const RDocumentParameters&/*aDocParam*/, 
                                     TInt aErrorCode ) 
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::OnStartDocumentL"));

    if ( aErrorCode ) 
        {
        iObserver->SyncErrorL( aErrorCode );
        }
        
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::OnEndDocumentL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnEndDocumentL( TInt aErrorCode ) 
    {
    LOG(_L("[Cds Sync]\t CCdsSyncImpl::OnEndDocumentL"));

    if (aErrorCode) 
        {
        iObserver->SyncErrorL( aErrorCode );
        }
        

    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::OnStartElementL(
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnStartElementL( const RTagInfo& aElement, 
                                    const RAttributeArray& aAttributes,
                                    TInt aErrorCode )
    {
    
    if ( aErrorCode ) 
        {
        iObserver->SyncErrorL( aErrorCode );
        }
        
    delete iCurrentContent;
    iCurrentContent = NULL;
        
    const TDesC8& name = aElement.LocalName().DesC();
    const TDesC8& uri = aElement.Uri().DesC();

    if ( !uri.Compare( KXmlCdsDefaultNamespace ) )
        {
        if ( !name.Compare( KItem ) ) // <item>
            {
            TInt id = FindAttribute( KId, _L8(""), aAttributes );
            if ( id != KErrNotFound) 
                {
                CleanItemData();
                iItemId = aAttributes[ id ].Value().DesC().AllocL();
                }
            }
        else if ( !name.Compare( KRes ) ) // <res>
            {
            
            CItemResource* res = CItemResource::NewLC();
            
            for (TInt i = 0; i < aAttributes.Count(); i++) 
                {
                
                const TDesC8& attrName = 
                    aAttributes[i].Attribute().LocalName().DesC();
                HBufC8* attrValue = aAttributes[i].Value().DesC().AllocL();
                
                if ( !attrName.Compare( KBitrate ) )  
                    {
                    res->iBitrate = attrValue; 
                    } 
                else if ( !attrName.Compare( KSize ) )  
                    {
                    res->iSize = attrValue; 
                    } 
                else if ( !attrName.Compare( KDuration ) )  
                    {
                    res->iDuration = attrValue; 
                    } 
                else if ( !attrName.Compare( KResolution ) )  
                    {
                    res->iResolution = attrValue; 
                    } 
                else if ( !attrName.Compare( KProtocolInfo ) )  
                    {
                    res->iProtocol = attrValue; 
                    } 
                }
            iResources.AppendL( res );
            CleanupStack::Pop( res );
            }     
        }
    }  

// --------------------------------------------------------------------------
// CCdsSyncImpl::OnEndElementL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnEndElementL( const RTagInfo& aElement, 
                                  TInt aErrorCode )
    {
    if ( aErrorCode ) 
        {
        iObserver->SyncErrorL( aErrorCode );
        }
        
    const TDesC8& name = aElement.LocalName().DesC();
    const TDesC8& uri = aElement.Uri().DesC();
    
    if ( !uri.Compare( KXmlCdsDefaultNamespace ) ) 
        // element from DIDL-Lite namespace
        {
        if ( !name.Compare( KRes ) && iCurrentContent ) // </res>
            {
            // remove "http://" and IP from URI
            TInt httpPos = iCurrentContent->Find( _L8("http://") );
            if ( httpPos != KErrNotFound ) 
                {
                TInt ipLength = 
                    iCurrentContent->Mid( httpPos + 7 ).Find(_L8("/"));
                if ( ipLength != KErrNotFound ) 
                    {
                    iCurrentContent->Des().Delete(0, httpPos + 7 + ipLength);
                    } 
                } 
            iResources[ iResources.Count() - 1 ]->iUri = iCurrentContent;
            }
        
        else if ( !name.Compare( KItem )) // </item>
            {
            // calc hash and construct item here
            HBufC8* hash = CalculateHashL();
            CleanupStack::PushL( hash );
            HBufC* escapedHash = EscapeHashLC( *hash );
            CleanupStack::Pop( escapedHash );

            CleanupStack::PopAndDestroy( hash );
            hash = NULL;
            CleanupStack::PushL( escapedHash );
            
            CCmSqlBaseItem* baseItem = CCmSqlBaseItem::NewLC();
            baseItem->SetHashL( *escapedHash );
            TInt index = iItemsInDb.FindInOrder( baseItem, iHashOrder );
            CleanupStack::PopAndDestroy( baseItem );
            
            if ( index == KErrNotFound ) // new or modified item
                {
                CCmSqlGenericItem* item = NULL;
                
                // find relevant resources
                CItemResource* httpRes = NULL;
                CItemResource* internalRes = NULL;
                for ( TInt i=0; i < iResources.Count(); i++ )
                    {
                    HBufC8* protocol = iResources[ i ]->iProtocol;
                    if ( protocol )
                        {
                        if ( !httpRes && 
                              protocol->Find(_L8("http-get:")) 
                                != KErrNotFound )
                            {
                            httpRes = iResources[ i ];
                            }
                        else if ( !internalRes &&
                                   protocol->Find(_L8("internal:")) 
                                    != KErrNotFound )
                            {
                            internalRes = iResources[ i ];
                            }
                        }
                    }
                
                if( iClass && iClass->Find( KAudioBroadCastItem ) != 
                    KErrNotFound )
                    {
                    // create audio item and set audio specific properties
                    CCmSqlAudioItem* audioItem = CCmSqlAudioItem::NewLC();
                    audioItem->SetMediaType( ECmAudioBroadCast );
                    if ( iGenre ) 
                        {
                        audioItem->SetGenreId( 
                            GetPropertyIdL( KGenreIndex, *iGenre ) );                        
                        }
                    item = audioItem;
                    CleanupStack::Pop( audioItem );
                    }
                    
                else if( iClass && iClass->Find( KVideoBroadCastItem ) != 
                    KErrNotFound  )
                    {
                    // create video item and set image specific properties
                    CCmSqlVideoItem* videoItem = CCmSqlVideoItem::NewLC();
                    videoItem->SetMediaType( ECmVideoBroadCast );
                    if ( iGenre ) 
                        {
                        videoItem->SetGenreId( 
                            GetPropertyIdL( KGenreIndex, *iGenre ) );                        
                        }
                    item = videoItem;
                    CleanupStack::Pop( videoItem );                    
                    }
                    
                else if ( iClass && 
                          iClass->Find( KAudioItem ) != KErrNotFound )
                    {
                    // create audio item and set audio specific properties
                    CCmSqlAudioItem* audioItem = CCmSqlAudioItem::NewLC();
                    audioItem->SetMediaType( ECmAudio );
                    if ( iAlbum ) 
                        {
                        audioItem->SetAlbumId( 
                            GetPropertyIdL( KAlbumIndex, *iAlbum ) );                        
                        }
                    if ( iArtist ) 
                        {
                        audioItem->SetArtistId( 
                            GetPropertyIdL( KArtistIndex, *iArtist ) );                        
                        }
                    if ( iGenre ) 
                        {
                        audioItem->SetGenreId( 
                            GetPropertyIdL( KGenreIndex, *iGenre ) );                        
                        }
                    if( iAlbumArtUri )
                        {
                        audioItem->SetAlbumArtUriL( *iAlbumArtUri );
                        }
                    if ( httpRes && httpRes->iDuration )
                        {
                        audioItem->SetDuration( 
                            (TInt) ParseDuration( *httpRes->iDuration ) );
                        }
                    if ( httpRes && httpRes->iBitrate )
                        {
                        audioItem->SetBitrate( 
                            ParseUint( *httpRes->iBitrate ) );
                        }
                    item = audioItem;
                    CleanupStack::Pop( audioItem );
                    }
                
                else if ( iClass && 
                          iClass->Find( KImageItem ) != KErrNotFound )
                    {
                    // create image item and set image specific properties
                    CCmSqlImageItem* imageItem = CCmSqlImageItem::NewLC();
                    imageItem->SetMediaType( ECmImage );
                    
                    if ( iDescription )
                        {
                        imageItem->SetDescriptionL( *iDescription );
                        }
                    if ( internalRes && internalRes->iResolution )
                        {
                        imageItem->SetResolutionId(
                            GetPropertyIdL( KResolutionIndex, 
                                *internalRes->iResolution ));
                        }
                    item = imageItem;
                    CleanupStack::Pop( imageItem );
                    }
                else if ( iClass && 
                          iClass->Find( KVideoItem ) != KErrNotFound )
                    {
                    // create video item and set image specific properties
                    CCmSqlVideoItem* videoItem = CCmSqlVideoItem::NewLC();
                    videoItem->SetMediaType( ECmVideo );
                    if ( iGenre ) 
                        {
                        videoItem->SetGenreId( 
                            GetPropertyIdL( KGenreIndex, *iGenre ) );                        
                        }
                    item = videoItem;
                    CleanupStack::Pop( videoItem );
                    }

                if ( item )
                    {
                    CleanupStack::PushL( item );
                    // Parsing dlna profile id from protocol info
                    ParseProfileIdL( *item, *httpRes );
                    ParseProfileIdL( *item, *internalRes );
                    // set general properties
                    item->SetHashL( *escapedHash );
                    item->SetCdsIdL( *iItemId );
                    if ( iClass ) 
                        {
                        item->SetUpnpclassId( 
                            GetPropertyIdL( KClassIndex, *iClass ) );                        
                        }
                    if ( iTitle )
                        {
                        item->SetTitleL( *iTitle );
                        }
                    if ( iDate )
                        {
                        item->SetDate( ParseTime( *iDate ) );
                        }
                    if ( httpRes && httpRes->iUri )
                        {
                        item->SetUriL( *httpRes->iUri );
                        }
                    if ( httpRes && httpRes->iSize )
                        {
                        item->SetSize( ParseUint( *httpRes->iSize ) );
                        }
                    else if ( internalRes && internalRes->iSize ) 
                        {
                        item->SetSize( ParseUint( *internalRes->iSize ) );
                        }
                    TTime currentTime;
                    currentTime.HomeTime();
                    item->SetHarvestDate( currentTime );
                    item->SetSearchId( iSearchIndex );
                    iItemsToAdd.AppendL( item ); // transfer ownership
                    ProgressL(1);
                    iProcessedItems++;
                    CleanupStack::Pop( item );
                    }
                else
                    {
                    LOG(_L("[Cds Sync]\t item == NULL "));
                    }                    
                }
            else // unchanged item
                {
                CCmSqlBaseItem* itemToRemove = iItemsInDb[ index ];
                iItemsInDb.Remove( index );
                ProgressL(1);
                iProcessedItems++;
                delete itemToRemove;
                }
            CleanItemData();
            CleanupStack::PopAndDestroy( escapedHash );
            }
        }
    
    else if ( !uri.Compare( KXmlCdsDcNamespace ) ) 
        // element from dc namespace
        {
        if ( !name.Compare( KTitle ) ) // </title>
            {
            iTitle = iCurrentContent;
            }
        else if ( !name.Compare( KDate ) ) // </date>
            {
            iDate = iCurrentContent;
            }
        else if ( !name.Compare( KDescription ) ) // </description>
            {
            iDescription = iCurrentContent;
            }          
        }
    
    else if ( !uri.Compare( KXmlCdsUpnpNamespace ) ) 
        // element from UPnP namespace
        {
        if ( !name.Compare( KAlbum ) ) // </album>
            {
            iAlbum = iCurrentContent;
            }
        else if ( !name.Compare( KArtist ) ) // </artist>
            {
            iArtist = iCurrentContent;
            }
        else if ( !name.Compare( KClass ) ) // </class>
            {
            iClass = iCurrentContent;
            }
        else if ( !name.Compare( KGenre ) ) // </genre>
            {
            iGenre = iCurrentContent;
            }
        else if ( !name.Compare( KAlbumArtUri ) ) // </albumarturi>
            {
            // Parse uri removes ip and port for iCurrentContent
            ParseUri();
            iAlbumArtUri = iCurrentContent;
            }              
        }
    else
        {
        delete iCurrentContent;
        }        
    
    iCurrentContent = NULL;
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::OnContentL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnContentL( const TDesC8& aBytes, TInt aErrorCode )
    {
    if ( aErrorCode ) 
        {
        iObserver->SyncErrorL( aErrorCode );
        }                
    if ( iCurrentContent )
        {
        if( iCurrentContent->Length() + aBytes.Length() < 
            KCdsSyncMaxBufLength )
            {
            iCurrentContent->Des().Append( aBytes );
            }
        else
            {
            iCurrentContent = iCurrentContent->ReAllocL( 
                iCurrentContent->Length() + aBytes.Length() );
            iCurrentContent->Des().Append( aBytes );                
            }
        }
    else 
        {
        iCurrentContent = HBufC8::NewL( KCdsSyncMaxBufLength );
        iCurrentContent->Des().Append( aBytes );
        }        
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::OnStartPrefixMappingL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnStartPrefixMappingL( const RString& /* aPrefix */, 
                                          const RString& /* aUri */, 
                                          TInt aErrorCode ) 
    {
    if (aErrorCode) 
        {
        iObserver->SyncErrorL( aErrorCode );
        }
        
    
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::OnEndPrefixMappingL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnEndPrefixMappingL( const RString& /* aPrefix */, 
                                        TInt aErrorCode )
    {
    if (aErrorCode) 
        {
        iObserver->SyncErrorL( aErrorCode );
        }
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::OnIgnorableWhiteSpaceL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnIgnorableWhiteSpaceL( const TDesC8& /* aBytes */, 
                                           TInt aErrorCode ) 
    {
    if (aErrorCode) 
        {
        iObserver->SyncErrorL( aErrorCode );
        }
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::OnSkippedEntityL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnSkippedEntityL( const RString& /* aName */, 
                                     TInt aErrorCode ) 
    {
    if (aErrorCode) 
        {
        iObserver->SyncErrorL( aErrorCode );
        }
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::OnProcessingInstructionL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnProcessingInstructionL( const TDesC8& /* aTarget */, 
                                             const TDesC8& /* aData */, 
                                             TInt aErrorCode ) 
    {
    if (aErrorCode) 
        {
        iObserver->SyncErrorL( aErrorCode );
        }
    }

// --------------------------------------------------------------------------
// void CCdsSyncImpl::OnError() 
// --------------------------------------------------------------------------
void CCdsSyncImpl::OnError( TInt aErrorCode ) 
    {
    if (aErrorCode && iState != ECdsSyncIdle) 
        {
        TRAP_IGNORE( iObserver->SyncErrorL( aErrorCode ) );
        }
        
    
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::GetExtendedInterface() 
// --------------------------------------------------------------------------
TAny* CCdsSyncImpl::GetExtendedInterface( const TInt32 /* aUid */ ) 
    {
    return NULL;    
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::ParseProfileIdL()
// --------------------------------------------------------------------------
void CCdsSyncImpl::ParseProfileIdL( 
    CCmSqlGenericItem& aItem, 
    const CItemResource& aRes ) const
    {
    if( &aRes && &aItem )
        {
        TInt index( aRes.iProtocol->Find( KDlnaPn() ) );
        if( KErrNotFound != index )
            {
            TInt index2( aRes.iProtocol->Find( KSemicolon() ) );
            if( KErrNotFound != index2 )
                {                        
                HBufC8* temp = 
                    aRes.iProtocol->Mid( 
                        index + KDlnaPn().Length(), 
                        index2 - ( index + KDlnaPn().Length() ) ).AllocLC();
                aItem.SetUpnpProfileId( 
                    GetPropertyIdL( KUpnpProfileIndex, *temp ) );
                CleanupStack::PopAndDestroy( temp );
                }
            }
        }     
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::RemoveUnchangedItems()
// --------------------------------------------------------------------------
void CCdsSyncImpl::RemoveUnchangedItems()
    {
    for( TInt i = iItemsInDb.Count() - 1; i >= 0; i-- )
        {
        if( iItemsInDb[i]->SearchId() < iSearchIndex )
            {
            CCmSqlBaseItem* itemToRemove = iItemsInDb[ i ];
            iItemsInDb.Remove( i );
            delete itemToRemove;            
            }
        }    
    }

// --------------------------------------------------------------------------
// CCdsSyncImpl::ParseUri()
// --------------------------------------------------------------------------
void CCdsSyncImpl::ParseUri()
    {
    // Removes ip and port from iCurrentContent
    TInt httpPos = iCurrentContent->Find( _L8("http://") );
    if ( httpPos != KErrNotFound ) 
        {
        TInt ipLength = 
            iCurrentContent->Mid( httpPos + 7 ).Find(_L8("/"));
        if ( ipLength != KErrNotFound ) 
            {
            iCurrentContent->Des().Delete(0, httpPos + 7 + ipLength);
            } 
        }  
    }
    
// End of file