iaupdate/IAD/engine/controller/src/iaupdateloader.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Thu, 07 Jan 2010 12:52:45 +0200
changeset 1 c42dffbd5b4f
parent 0 ba25891c3a9e
child 21 5bddc28da627
permissions -rw-r--r--
Revision: 200951 Kit: 201001

/*
* Copyright (c) 2007-2009 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:   This module contains the implementation of 
*                CIAUpdateLoader class member functions.
*
*/




#include <ncdprovider.h>
#include <ncdnode.h>
#include <ncdnodecontainer.h>
#include <ncdloadnodeoperation.h>
#include <ncdquery.h>
#include <ncdnodechildoftransparent.h>

#include "iaupdateloader.h"
#include "iaupdateloaderobserver.h"
#include "iaupdatectrlconsts.h"
#include "iaupdateversion.h"
#include "iaupdateutils.h"
#include "iaupdatedebug.h"


// Use KMaxTInt value for the child count.
// Then, all the children will be downloaded 
// even if the parent does not have the correct
// information about its child count.
const TInt KChildCountLoadAll( KMaxTInt );


// -----------------------------------------------------------------------------
// CIAUpdateLoader::NewLC
// Two-phased constructor.
// -----------------------------------------------------------------------------
//
CIAUpdateLoader* CIAUpdateLoader::NewLC( 
    MNcdProvider& aProvider, 
    MIAUpdateLoaderObserver& aObserver )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::NewLC() begin");
    
    CIAUpdateLoader* self = 
        new( ELeave ) CIAUpdateLoader( aProvider, aObserver );
    CleanupStack::PushL( self );
    self->ConstructL();

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::NewLC() end");

    return self;
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::NewL
// Two-phased constructor.
// -----------------------------------------------------------------------------
//    
CIAUpdateLoader* CIAUpdateLoader::NewL(
    MNcdProvider& aProvider, 
    MIAUpdateLoaderObserver& aObserver )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::NewL() begin");

    CIAUpdateLoader* self = 
        CIAUpdateLoader::NewLC( aProvider, aObserver );
    CleanupStack::Pop( self );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::NewL() end");

    return self;
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::CIAUpdateLoader
// C++ default constructor can NOT contain any code, that
// might leave.
// -----------------------------------------------------------------------------
//        
CIAUpdateLoader::CIAUpdateLoader(
    MNcdProvider& aProvider, 
    MIAUpdateLoaderObserver& aObserver ) 
: CBase(),
  iProvider( aProvider ), 
  iObserver( aObserver )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::CIAUpdateLoader()");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::ConstructL
// Symbian 2nd phase constructor can leave.
// -----------------------------------------------------------------------------
//
void CIAUpdateLoader::ConstructL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::ConstructL() begin");
    
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::ConstructL() end");
    }
    

// -----------------------------------------------------------------------------
// CIAUpdateLoader::~CIAUpdateLoader
// Destructor
// -----------------------------------------------------------------------------
//     
CIAUpdateLoader::~CIAUpdateLoader()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::~CIAUpdateLoader() begin");

    // Call just to be sure.
    Cancel();

    iOperations.Close();

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::~CIAUpdateLoader() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::RootExpiredL
// 
// -----------------------------------------------------------------------------
//
TBool CIAUpdateLoader::RootExpiredL() const
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::RootExpiredL() begin");
    
    TBool rootExpired( EFalse );

    // Remember to release this node when it is not needed.
    MNcdNode* rootNode( iProvider.RootNodeL() );

    // Get the state of the root.
    MNcdNode::TState rootState( rootNode->State() );

    IAUPDATE_TRACE_1("[IAUPDATE] Root state: %d",
                     rootState);

    // Check if the root is expired or not.
    if ( rootState == MNcdNode::EStateExpired )
        {
        IAUPDATE_TRACE("[IAUPDATE] Root is expired");
        rootExpired = ETrue;
        }

    TInt childCount( 0 );
    MNcdNodeContainer* container( rootNode->QueryInterfaceLC< MNcdNodeContainer >() );
    if ( container )
        {
        childCount = container->ChildCount();
        CleanupStack::PopAndDestroy( container );
        }
       
    IAUPDATE_TRACE_1("[IAUPDATE] count of children: %d", childCount ); 
    
    //2 here means arrow CGW and firmware CGW. If one of them failed last time, 
    //refresh from CDB again
    if ( childCount != 2 && rootState == MNcdNode::EStateInitialized )
        {
        IAUPDATE_TRACE("[IAUPDATE] Root is expired. update from phase 1 to phase 2 or previous CGW load failed");
        rootExpired = ETrue;
        }
    // Release root because it is not needed any more.
    rootNode->Release();
    
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::RootExpiredL() end");

    return rootExpired;
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::LoadNodesL
// 
// -----------------------------------------------------------------------------
//
void CIAUpdateLoader::LoadNodesL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadNodesL() begin");
    
    if ( iOperations.Count() > 0 )
        {
        // 
        IAUPDATE_TRACE("[IAUPDATE] ERROR: Refresh already going on.");
        User::Leave( KErrInUse );
        }

    // Reset this value for new round.
    iErrorCode = KErrNone;
        
    // Root refresh will handle everything.
    LoadRootL();

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadNodesL() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::Cancel
// 
// -----------------------------------------------------------------------------
// 
void CIAUpdateLoader::Cancel()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::Cancel() begin");

    // Cancel asynchronous operations.
    
    // Use this flag to inform that operation complete callback function
    // that cancellation is going on. Cancel related things are handled
    // here.
    iCancelling = ETrue;

    // Operations will use callback functions to inform 
    // when the operation is completed.
    TInt count( iOperations.Count() );
    IAUPDATE_TRACE_1("[IAUPDATE] Cancel array count: %d", count);

    for ( TInt i = count - 1; i >= 0; --i )    
        {
        TIAUpdateOperationInfo info( iOperations[ i ] );
        MNcdOperation* operation( info.iOperation );

        // Notice, that OperationCompleteL callback is called when
        // cancellation finishes
        operation->CancelOperation();
      
        }
        
    // Now, that the array elements were released, reset the array.
    iOperations.Reset();

    // Release the flag now, that all the cancellations have been handled.
    iCancelling = EFalse;

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::CancelOperation() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::SetSkipChildCountRefresh
// 
// -----------------------------------------------------------------------------
// 
void CIAUpdateLoader::SetSkipChildCountRefresh( TBool aSkip )
    {
    IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateLoader::SetSkipChildCountRefresh(): %d",
                     aSkip);
    iSkipChildCountRefresh = aSkip;
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::NodesUpdated
// 
// -----------------------------------------------------------------------------
// 
void CIAUpdateLoader::NodesUpdated( 
    MNcdLoadNodeOperation& /*aOperation*/,
    RCatalogsArray< MNcdNode >& /*aNodes*/ )
    {
    // No need to inform the observer about single updates.
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::QueryReceived
// 
// -----------------------------------------------------------------------------
// 
void CIAUpdateLoader::QueryReceived( 
    MNcdLoadNodeOperation& aOperation,
    MNcdQuery* aQuery )  
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::QueryReceived() begin");

    // Operation query received. Always accept queries.
    // Queries should not be requested from this client.
    TInt trapError( KErrNone );
    if ( aQuery )
        {
        TRAP ( trapError, 
               aQuery->SetResponseL( MNcdQuery::EAccepted );
               aOperation.CompleteQueryL( *aQuery ); );
        // Release needs to be called to the query after it is not used.
        aQuery->Release();        
        }

    if ( ( trapError != KErrNone ) || ( !aQuery ) )
        {
        // Error occurred when query was handled.
        // So, operation can not continue.
        // Cancel operation. Notice, that OperationComplete will be called
        // by the operation when cancel is called.
        aOperation.CancelOperation();
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::QueryReceived() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::OperationComplete
// 
// -----------------------------------------------------------------------------
// 
void CIAUpdateLoader::OperationComplete( 
    MNcdLoadNodeOperation& aOperation,
    TInt aError )
    {
    IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateLoader::OperationComplete() begin: %d",
                     aError);

    MNcdNode* operationNode( NULL );
    TIAUpdateOperationInfo::TOperationType operationType( 
                    TIAUpdateOperationInfo::EIdle );

    // Remember to release the operation, 
    // because its reference count was increased
    // when the operation was inserted into the array.
    for ( TInt i = 0; i < iOperations.Count(); ++i )
        {
        TIAUpdateOperationInfo tmpInfo( iOperations[ i ] );
        MNcdOperation* operation( tmpInfo.iOperation );
        MNcdOperation* paramOperation( &aOperation );
        if ( paramOperation == operation )
            {
            IAUPDATE_TRACE_1("[IAUPDATE] Completed operation was found from the list %d",
                                 i);

            // Get the operation type info. 
            // Then, we can later decide how to continue.
            operationType = tmpInfo.iOperationType;

            // Get the node of the operation.
            // Notice, this needs to be released.
            operationNode = operation->Node();

            // Release the operation here because its reference count
            // was increased before it was added into the array.
            operation->Release();
                
            // Remove completed operation from the array.
            iOperations.Remove( i );

            // No need to continue the for loop anymore.
            break;
            }
        }

    if ( operationNode )
        {
        if ( aError != KErrNone )
            {
            // Get the latest error code, if error has occurred.
            // We handle here only error codes that have been
            // directed to the operations that we recognized from the
            // operation array.
            
            //Continue next round to load the children.
            //This is to make sure when one CGW loading failed, the updates from the other CGW will
            //still be shown in mainview.
            TRAP_IGNORE( NextLoadLoopL( *operationNode, operationType ) );
            IAUPDATE_TRACE_1("[IAUPDATE] New iErrorCode value: %d", aError);
            
            if ( iErrorCode == KErrNone )
                {
                iErrorCode = aError;
                }
            }
        else
            {
            // Continue to the next loop only if an error did not occur 
            // during this round.
            TRAPD( trapError,
                   NextLoadLoopL( *operationNode, operationType ) );
            if ( trapError != KErrNone )
                {
                IAUPDATE_TRACE_1("[IAUPDATE] ERROR: Next round error: %d", trapError);
                // Something went wrong when children load was started.
                // Update error code.
                iErrorCode = trapError;
                }
            }

        // No need for the node anymore.
        operationNode->Release();
        operationNode = NULL;
        }

    if ( iOperations.Count() == 0 )
        {
        IAUPDATE_TRACE("[IAUPDATE] The operation as whole completed");
        // No more operations left. So, inform observer.
        if ( iErrorCode == KErrNone )
            {
            // Because of workaround needed for handling firmware updates.
            // Refresh from network was completed succesfully.
            // Current firmware version is stored to private folder. 
            TRAP_IGNORE( IAUpdateUtils::SaveCurrentFwVersionIfNeededL() );
            }
        iObserver.LoadComplete( iErrorCode );
        }
        
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::OperationComplete() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::LoadRootL
// 
// -----------------------------------------------------------------------------
//
void CIAUpdateLoader::LoadRootL()
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadRootL() begin");
    
    MNcdNode* rootNode( iProvider.RootNodeL() );
    CleanupReleasePushL( *rootNode );

    MNcdNodeContainer* rootContainer(
        rootNode->QueryInterfaceLC< MNcdNodeContainer >() );

    if ( !rootContainer )
        {
        User::Leave( KErrNotFound );            
        }

    MNcdNode::TState rootState( rootNode->State() );
    if ( rootState == MNcdNode::EStateInitialized )
        {
        IAUPDATE_TRACE("[IAUPDATE] Root already initialized.");

        // CDB connections should be avoided if possible. So, if the
        // root node is initialized and not expired, then do not update it
        // from the net. Just continue directly to its children.

        if ( iSkipChildCountRefresh )
            {
            IAUPDATE_TRACE("[IAUPDATE] Root, load all children and skip child count refresh.");
            // Because child count refresh should be skipped. Try to load children
            // of the children directly without requesting the child count first.
            LoadChildrenOfChildrenL( 
                *rootContainer, TIAUpdateOperationInfo::ELoadAllChildren );
            }
        else
            {
            IAUPDATE_TRACE("[IAUPDATE] Root, load child containers.");
            // At the moment, NCD Engine does not load root children nodes 
            // when MNcdContainer::LoadChildrenL is called. So, we have to
            // load the children one by one here.
            LoadChildContainersL( *rootContainer );            
            }
        }
    else
        {
        IAUPDATE_TRACE_1("[IAUPDATE] Refresh root. State: %d", rootState);

        // Start loading root.
        StartLoadOperationL( *rootContainer, TIAUpdateOperationInfo::ELoadRoot );
        }

    // No use for the node objects any more. Release them.
    CleanupStack::PopAndDestroy( rootContainer );
    CleanupStack::PopAndDestroy( rootNode );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadRootL() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::LoadChildContainersL
// 
// -----------------------------------------------------------------------------
//    
void CIAUpdateLoader::LoadChildContainersL( MNcdNodeContainer& aParentContainer )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadChildContainersL() begin");
    
    // LoadL is used to update the children of the parent container here
    // instead of updating them by calling LoadChildrenL function.

    TInt childCount( aParentContainer.ChildCount() );
    IAUPDATE_TRACE_1("[IAUPDATE] Parent container child count: %d", childCount);
 
    for ( TInt i = 0; i < childCount; ++i )
        {
        IAUPDATE_TRACE_1("[IAUPDATE] Container child: %d", i);

        MNcdNode* node( aParentContainer.ChildL( i ) );
        CleanupReleasePushL( *node );

        MNcdNodeContainer* container(
            node->QueryInterfaceLC< MNcdNodeContainer >() );
        if ( container )
            {
            IAUPDATE_TRACE("[IAUPDATE] Container child had container interface");
            LoadContainerL( *container );
            CleanupStack::PopAndDestroy( container );                
            }

        CleanupStack::PopAndDestroy( node );
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadChildContainersL() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::LoadContainerL
// 
// -----------------------------------------------------------------------------
//
void CIAUpdateLoader::LoadContainerL( MNcdNodeContainer& aContainer )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadContainerL() begin");

    StartLoadOperationL( 
        aContainer, TIAUpdateOperationInfo::ELoadContainer );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadContainerL() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::LoadChildrenL
// 
// -----------------------------------------------------------------------------
//    
void CIAUpdateLoader::LoadChildrenL( MNcdNodeContainer& aContainer )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadChildrenL() begin");

    if ( aContainer.ChildCount() > 0 )
        {
        StartLoadOperationL( 
            aContainer, TIAUpdateOperationInfo::ELoadChildren );        
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadChildrenL() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::LoadAllChildrenL
// 
// -----------------------------------------------------------------------------
//    
void CIAUpdateLoader::LoadAllChildrenL( MNcdNodeContainer& aContainer )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadAllChildrenL() begin");

    if ( !iSkipChildCountRefresh )
        {
        // Child count skip flag has been set to EFalse in previous round.
        // Most likely NCD Engine does not support load all children functionality. 
        // So, just load children using normal flow from now on.
        // Notice, that now we need to first load the child count
        // for the child containers. So, even if children were 
        // already updated by LoadChildrenL, a new request for
        // containers needs . Otherwise, the child count 
        // will not be up-to-date. When the child containers are 
        // loaded, the flow will continue to load the children.
        IAUPDATE_TRACE("[IAUPDATE] Child count should not be skipped");
        LoadContainerL( aContainer );
        IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadAllChildrenL() end");
        return;
        }

    TRAPD ( trapError, 
            StartLoadOperationL( 
                aContainer, TIAUpdateOperationInfo::ELoadAllChildren ); );

    IAUPDATE_TRACE_1("[IAUPDATE] trapError: %d", trapError);

    // Only acceptable error is KErrArgument.
    // Then it is possible that an old NCD Engine is used
    // and it does not allow too big page size. So, in that
    // case try one more time below by not skipping child count
    // refresh. Else, leave if error occurred.

    if ( trapError == KErrArgument )
        {
        IAUPDATE_TRACE("[IAUPDATE] Try to change child load method");
        // Notice, that now we need to first load the child count
        // for the child containers. So, even if children were 
        // already updated by LoadChildrenL, a new request for
        // containers needs . Otherwise, the child count 
        // will not be up-to-date. When the child containers are 
        // loaded, the flow will continue to load the children.
        LoadContainerL( aContainer );
        IAUPDATE_TRACE("[IAUPDATE] Child load method changed and container load started");
        // If we come here, it means that support for all children is
        // not available in NCD Engine. So, do not try it after this.
        SetSkipChildCountRefresh( EFalse );
        }
    else if ( trapError != KErrNone )
        {
        IAUPDATE_TRACE_1("[IAUPDATE] Error: %d", trapError);
        User::Leave( trapError );
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadAllChildrenL() end");
    }

// -----------------------------------------------------------------------------
// CIAUpdateLoader::LoadChildrenOfChildrenL
// 
// -----------------------------------------------------------------------------
//
void CIAUpdateLoader::LoadChildrenOfChildrenL( 
    MNcdNodeContainer& aContainer,
    TIAUpdateOperationInfo::TOperationType aPreviousOperationType )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadChildrenOfChildrenL() begin");
    
    TInt childCount( aContainer.ChildCount() );
    IAUPDATE_TRACE_1("[IAUPDATE] Container child count: %d", childCount);
 
    for ( TInt i = 0; i < childCount; ++i )
        {
        MNcdNode* node( aContainer.ChildL( i ) );
        CleanupReleasePushL( *node );

        MNcdNodeContainer* container( 
            node->QueryInterfaceLC< MNcdNodeContainer >() );
        if ( container )
            {
            IAUPDATE_TRACE_1("[IAUPDATE] Container child: %d had container interface", i);

            switch ( aPreviousOperationType )
                {
                case TIAUpdateOperationInfo::ELoadRoot:
                    IAUPDATE_TRACE("[IAUPDATE] Load children of the root child");
                    // When root is loaded, also its children are loaded
                    // and they have now their child count. So, no need to
                    // reload children of the root. Load their children
                    // directly now.
                    LoadChildrenL( *container );
                    break;
                    
                case TIAUpdateOperationInfo::ELoadChildren:
                    IAUPDATE_TRACE("[IAUPDATE] Load children of a container");
                    // Notice, that now we need to first load the child count
                    // for the child containers. So, even if children were 
                    // already updated by LoadChildrenL, a new request for
                    // containers needs . Otherwise, the child count 
                    // will not be up-to-date. When the child containers are 
                    // loaded, the flow will continue to load the children.
                    LoadContainerL( *container );
                    break;

                case TIAUpdateOperationInfo::ELoadAllChildren:
                    IAUPDATE_TRACE("[IAUPDATE] Load all children of a container");
                    // Notice, here we will try to skip the loading of the child
                    // count of the container. So, all the children are tried to
                    // be loaded directly even if the child count may not be
                    // up-to-date.
                    LoadAllChildrenL( *container );
                    break;
                    
                default:
                    IAUPDATE_TRACE("[IAUPDATE] ERROR: Wrong operation type");
                    User::Leave( KErrArgument );
                    break;
                }

            CleanupStack::PopAndDestroy( container );                
            }

        CleanupStack::PopAndDestroy( node );
        }

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::LoadChildrenOfChildrenL() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::StartLoadOperationL
// 
// -----------------------------------------------------------------------------
//
void CIAUpdateLoader::StartLoadOperationL( 
    MNcdNodeContainer& aContainer,
    TIAUpdateOperationInfo::TOperationType aOperationType )
    {
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::StartLoadOperation() begin");

    MNcdOperation* operation( NULL );

    // Create a new operation based on the operation type information.
    switch ( aOperationType )
        {
        case TIAUpdateOperationInfo::ELoadRoot:
        case TIAUpdateOperationInfo::ELoadContainer:
            {
            // Container itself should be loaded.
            IAUPDATE_TRACE("[IAUPDATE] LoadL requested as operation");
            MNcdNode* node( aContainer.QueryInterfaceLC< MNcdNode >() );
            if ( !node )
                {
                IAUPDATE_TRACE("[IAUPDATE] Container did not have node interface.");
                User::Leave( KErrNotFound );
                }
            operation = node->LoadL( *this );
            // No need for the node anymore
            CleanupStack::PopAndDestroy( node );
            }
            break;
 
        case TIAUpdateOperationInfo::ELoadChildren:
            {
            // Children of the container should be loaded.
            IAUPDATE_TRACE("[IAUPDATE] LoadChildrenL requested as operation");
            TInt childCount( aContainer.ChildCount() );
            IAUPDATE_TRACE_1("[IAUPDATE] Child count: %d", childCount);
            operation = 
                aContainer.LoadChildrenL( 
                    0, childCount, ELoadMetadata, *this );
            }
            break;

        case TIAUpdateOperationInfo::ELoadAllChildren:
            {
            // All children of the container should be loaded.
            IAUPDATE_TRACE("[IAUPDATE] LoadAllChildren requested as operation");
            // Because we can not be sure about the container child count,
            // use the really big value. Then we will surely get all the
            // children.            
            operation = 
                aContainer.LoadChildrenL( 
                    0, KChildCountLoadAll, ELoadMetadata, *this );
            }
            break;
        
        default:
            IAUPDATE_TRACE_1("[IAUPDATE] ERROR: Operation type: %d", aOperationType);
            User::Leave( KErrArgument );
            break;
        }

    // Insert the operation to the cleanupstack. So, it will be released
    // if leave occurs.
    CleanupReleasePushL( *operation );

    // Insert the operation into the array first. By doing this before starting
    // the operation, we do not need to Cancel just started operation if leave
    // occurs.
    TIAUpdateOperationInfo info( aOperationType, operation );
    iOperations.AppendL( info );

    TRAPD( trapError, operation->StartOperationL() );
    if ( trapError != KErrNone )
        {
        IAUPDATE_TRACE_1("[IAUPDATE] ERROR: Operation Start failed: %d", trapError);
        // Operation start failed.
        // Remove operation from the array because it was appended
        // above.
        iOperations.Remove( iOperations.Count() - 1 );

        // Now, we can leave.
        // Notice, this leave will pop and destroy the operation. So,
        // it will be released then.
        User::Leave( trapError );
        }

    // Operation was started.
    // Do not release it now but remove only from the stack.
    // Operation will be released when it completes.
    CleanupStack::Pop( operation );           
    
    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::StartLoadOperation() end");
    }


// -----------------------------------------------------------------------------
// CIAUpdateLoader::NextLoadLoopL
// 
// -----------------------------------------------------------------------------
//
void CIAUpdateLoader::NextLoadLoopL( 
    MNcdNode& aNode,
    TIAUpdateOperationInfo::TOperationType aPreviousOperationType )
    {
    IAUPDATE_TRACE_1("[IAUPDATE] CIAUpdateLoader::NextLoadLoopL() begin: %d",
                     aPreviousOperationType);
    
    MNcdNodeContainer* container( 
        aNode.QueryInterfaceLC< MNcdNodeContainer >() );
    if ( !container )
        {
        IAUPDATE_TRACE("[IAUPDATE] Container interface was not found");
        // Container interface should always exist in these cases.
        User::Leave( KErrNotFound );
        }

    // Check if the operation should be continued to its children.
    switch ( aPreviousOperationType )
        {
        case TIAUpdateOperationInfo::ELoadRoot:
            IAUPDATE_TRACE("[IAUPDATE] Previously loaded root");
            // When root is loaded, also its children are loaded
            // and they have now their child count. So, no need to
            // reload children of the root. Load their children
            // directly now.
            LoadChildrenOfChildrenL( 
                *container,
                TIAUpdateOperationInfo::ELoadRoot );
            break;

        case TIAUpdateOperationInfo::ELoadContainer:
            IAUPDATE_TRACE("[IAUPDATE] Previously loaded container");
            // After normal container has been loaded, 
            // its children can be loaded.
            LoadChildrenL( *container );
            break;

        case TIAUpdateOperationInfo::ELoadChildren:
            IAUPDATE_TRACE("[IAUPDATE] Previously loaded children");
            // Because children of the container have been loaded here,
            // maybe we need to start a new loop for their children.
            // If this container does not contain any new containers,
            // then there is nothing to do anymore.
            if ( iSkipChildCountRefresh )
                {
                // We should come here only after root has been loaded
                // and its children of children have been loaded after that.
                // After that, skip child count refreshes in the
                // next levels.
                IAUPDATE_TRACE("[IAUPDATE] Start to load all children");
                LoadChildrenOfChildrenL( 
                    *container,
                    TIAUpdateOperationInfo::ELoadAllChildren );                
                }
            else
                {
                IAUPDATE_TRACE("[IAUPDATE] Continue load children of children");
                LoadChildrenOfChildrenL( 
                    *container,
                    TIAUpdateOperationInfo::ELoadChildren );
                }                
            break;

        case TIAUpdateOperationInfo::ELoadAllChildren:
            IAUPDATE_TRACE("[IAUPDATE] Previously loaded all children");
            // Notice, here we will try to skip the loading of the child
            // count of the container. So, all the children are tried to
            // be loaded directly even if the child count may not be
            // up-to-date.
            LoadChildrenOfChildrenL( 
                *container,
                TIAUpdateOperationInfo::ELoadAllChildren );
            break;
                         
        default:
            IAUPDATE_TRACE("[IAUPDATE] No need for next loop");
            // No need to load anything else anymore.
            break;
        }    

    // No need for the interface anymore. Release it.
    CleanupStack::PopAndDestroy( container );

    IAUPDATE_TRACE("[IAUPDATE] CIAUpdateLoader::NextLoadLoopL() end");
    }