ncdengine/provider/server/src/ncdnodeseeninfo.cpp
changeset 0 ba25891c3a9e
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/ncdengine/provider/server/src/ncdnodeseeninfo.cpp	Thu Dec 17 08:51:10 2009 +0200
@@ -0,0 +1,1073 @@
+/*
+* Copyright (c) 2007-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:  
+*
+*/
+
+
+#include "ncdnodeseeninfo.h"
+
+#include "ncdnodeidentifierutils.h"
+#include "ncdnodeidentifier.h"
+#include "ncdstorage.h"
+#include "ncdstoragemanager.h"
+#include "ncdproviderdefines.h"
+#include "ncddatabasestorage.h"
+#include "ncdstorageitem.h"
+#include "ncdrootnode.h"
+#include "ncdnodeidentifiereditor.h"
+#include "catalogsutils.h"
+#include "ncdchildentity.h"
+#include "ncdchildentitymap.h"
+#include "ncdnodefolderlink.h"
+#include "ncdgeneralmanager.h"
+
+#include "catalogsdebug.h"
+
+CNcdNodeSeenInfo* CNcdNodeSeenInfo::NewL( CNcdGeneralManager& aGeneralManager )
+    {
+    CNcdNodeSeenInfo* self = NewLC( aGeneralManager );
+    CleanupStack::Pop( self );
+    return self;
+    }
+    
+
+CNcdNodeSeenInfo* CNcdNodeSeenInfo::NewLC( CNcdGeneralManager& aGeneralManager )
+    {
+    CNcdNodeSeenInfo* self = new( ELeave ) CNcdNodeSeenInfo( aGeneralManager );
+    CleanupStack::PushL( self );
+    self->ConstructL();
+    return self;
+    }
+
+
+CNcdNodeSeenInfo::CNcdNodeSeenInfo( CNcdGeneralManager& aGeneralManager ) :
+    iGeneralManager( aGeneralManager ),
+    iStorageManager( aGeneralManager.StorageManager() ), 
+    iNodeManager( aGeneralManager.NodeManager() )
+    {	
+    }    
+
+
+void CNcdNodeSeenInfo::ConstructL()
+    {
+    }    
+
+
+CNcdNodeSeenInfo::~CNcdNodeSeenInfo()
+    {
+    DLTRACEIN((""));
+    iClientSeenInfos.ResetAndDestroy();
+    }
+    
+void CNcdNodeSeenInfo::AddNewIdL( const CNcdChildEntity& aChildEntity )
+    {
+    DLTRACEIN((""));
+    AddNewIdL( aChildEntity.Identifier(), ENcdStatusNew, aChildEntity.NodeType() );
+    }
+
+void CNcdNodeSeenInfo::AddNewIdL( const CNcdNodeIdentifier& aNodeIdentifier,
+    TNcdNewStatus aNewStatus, CNcdNodeFactory::TNcdNodeType aNodeType )
+    {
+    DLTRACEIN((""));
+        
+    const TUid& clientUid = aNodeIdentifier.ClientUid();
+            
+    if ( !IsClientInfoLoaded( clientUid ) ) 
+        {
+        DbLoadClientInfoL( clientUid );
+        }
+    
+    CClientSeenInfo* info = ClientInfo( clientUid );
+    DASSERT( info );
+
+#ifdef CATALOGS_BUILD_CONFIG_DEBUG
+    DLINFO(("New status array:"));
+    for( TInt i = 0 ; i < info->iNodeNewStatusArray.Count() ; i++ )
+        {
+        DLINFO((_L("index: %d, id: %S"),i, &info->iNodeNewStatusArray[i]->Identifier().NodeId() ));
+        }
+#endif
+
+    // Latest "new" nodes will be added to the top of the list and older "new" nodes will
+    // drop out when max size is exceeded.
+    TInt index = IndexInNewStatusArray( aNodeIdentifier );
+    if( index == KErrNotFound )
+        {
+        DLTRACE(("Not found, create new."));
+        // Create new status.
+        CNcdNodeNewStatus* nodeNewStatus = CNcdNodeNewStatus::NewLC(
+            aNodeIdentifier,
+            aNewStatus,
+            aNodeType );
+        // Insert to the top of the list.
+        info->iNodeNewStatusArray.InsertL( nodeNewStatus, 0 );
+        CleanupStack::Pop( nodeNewStatus );
+        
+        }
+    else
+        {
+        DLTRACE(("Found, update."));
+        // Node is already in the new list -> move it to the top of the list.
+        CNcdNodeNewStatus* nodeNewStatus = info->iNodeNewStatusArray[index];
+        info->iNodeNewStatusArray.InsertL( nodeNewStatus, 0 );
+        info->iNodeNewStatusArray.Remove( index + 1 );
+        // Remove seen status if any
+        RemoveFromSeenListL( aNodeIdentifier );
+        // Update new status.
+        nodeNewStatus->SetNewStatus( TNcdNewStatus(nodeNewStatus->NewStatus() | aNewStatus) );
+        }
+        
+    // Set parent new as well.
+    if( !NcdNodeIdentifierEditor::IdentifiesSomeRoot( aNodeIdentifier ) )
+        {
+        CNcdNodeIdentifier* parent = NcdNodeIdentifierEditor::ParentOfLC( aNodeIdentifier );
+        AddNewIdL( *parent, ENcdStatusNewNodesInside, CNcdNodeFactory::ENcdNodeFolder );
+        CleanupStack::PopAndDestroy( parent );
+        }
+    
+    // Remove identifiers from the bottom if max size is exceeded.
+    while( info->iNodeNewStatusArray.Count() > KNewListMaxSize )
+        {
+        delete info->iNodeNewStatusArray[info->iNodeNewStatusArray.Count() - 1];
+        info->iNodeNewStatusArray.Remove(
+            info->iNodeNewStatusArray.Count() - 1 );
+        }
+        
+#ifdef CATALOGS_BUILD_CONFIG_DEBUG
+    DLINFO(("New status array:"));
+    for( TInt i = 0 ; i < info->iNodeNewStatusArray.Count() ; i++ )
+        {
+        DLINFO((_L("index: %d, id: %S"),i, &info->iNodeNewStatusArray[i]->Identifier().NodeId() ));
+        }
+#endif
+    }
+    
+
+TBool CNcdNodeSeenInfo::IsSeenL( const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((_L("ns: %S, id: %S"), &aNodeIdentifier.NodeNameSpace(), &aNodeIdentifier.NodeId()));
+
+    const TUid& clientUid = aNodeIdentifier.ClientUid();
+    
+    if ( !IsClientInfoLoaded( clientUid ) )
+        {
+        DbLoadClientInfoL( clientUid );
+        }
+
+    CClientSeenInfo* clientInfo = ClientInfo( aNodeIdentifier.ClientUid() );
+    DASSERT( clientInfo );
+    DLINFO((("clientInfo-ptr: %x"), clientInfo ));    
+    
+    DLINFO(("new count: %d", clientInfo->iNodeNewStatusArray.Count() ));
+    TInt index = IndexInNewStatusArray( aNodeIdentifier );
+    DLINFO(("index in new status array: %d", index ));
+    return index == KErrNotFound;
+    }
+    
+
+TBool CNcdNodeSeenInfo::ChildExistsInArrayL( const CNcdChildEntity& aChildEntity,
+        const RPointerArray<CNcdChildEntity>& aChildArray,
+        TBool& aNodeWithSameIndexFound )
+    {
+    DLTRACEIN((""));
+    TBool childFound = EFalse;
+    aNodeWithSameIndexFound = EFalse;
+    for( TInt i = 0 ; i < aChildArray.Count() ; i++ )
+        {
+        if( aChildArray[i]->Identifier().Equals( aChildEntity.Identifier() ) )
+            {
+            childFound = ETrue;
+            }
+        if( aChildArray[i]->Index() == aChildEntity.Index() )
+            {
+            aNodeWithSameIndexFound = ETrue;
+            }
+        }
+    DLINFO(("childFound %d, aNodeWithSameIndexFound: %d", childFound,
+        aNodeWithSameIndexFound));
+    return childFound;
+    }
+
+void CNcdNodeSeenInfo::RefreshFolderSeenStatusL( 
+    const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((_L("aNodeIdentifier: %S"), &aNodeIdentifier.NodeId() ));
+    const TUid& clientUid = aNodeIdentifier.ClientUid();
+            
+    if ( !IsClientInfoLoaded( clientUid ) ) 
+        {
+        DbLoadClientInfoL( clientUid );
+        }
+    
+    CClientSeenInfo* info = ClientInfo( clientUid );
+    DASSERT( info );
+    
+    TInt index = IndexInNewStatusArray( aNodeIdentifier );
+    if( index == KErrNotFound )
+        {
+        DLTRACE(("Not in new status array"));
+        return;
+        }
+    
+    if( !FolderHasNewChildrenL( aNodeIdentifier ) )
+        {
+        CNcdNodeNewStatus* newStatus = info->iNodeNewStatusArray[index];
+        // Don't touch folders that are really new, only folders that have just
+        // new items inside.
+        if( !( newStatus->NewStatus() & ENcdStatusNew ||
+               newStatus->NewStatus() & ENcdStatusNewButSeen ) )
+            {
+            // Special case: all new children have been removed, immediately remove folder's new status
+            // (better user experience when compared to just setting it seen)
+            RemoveFromNewListL( aNodeIdentifier );
+            RemoveFromSeenListL( aNodeIdentifier );
+            // Check parent as well.
+            if( !NcdNodeIdentifierEditor::IdentifiesSomeRoot( aNodeIdentifier ) )
+                {
+                CNcdNodeIdentifier* parent = NcdNodeIdentifierEditor::ParentOfLC( aNodeIdentifier );
+                RefreshFolderSeenStatusL( *parent );
+                CleanupStack::PopAndDestroy( parent );
+                }
+            }
+        }
+    }
+    
+void CNcdNodeSeenInfo::SetFolderSeenIfNeededL( 
+    const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((""));
+    
+    TInt index = IndexInNewStatusArray( aNodeIdentifier );
+    if( index == KErrNotFound || IsAddedAsSeen( aNodeIdentifier ) )
+        {
+        DLTRACE(("Not in new status array, or already set seen"));
+        return;
+        }
+    
+    if( !FolderHasNewUnseenChildrenL( aNodeIdentifier ) )
+        {
+        // Add to seen list.
+        AddSeenIdL( aNodeIdentifier );
+        // Check parent as well.
+        if( !NcdNodeIdentifierEditor::IdentifiesSomeRoot( aNodeIdentifier ) )
+            {
+            CNcdNodeIdentifier* parent = NcdNodeIdentifierEditor::ParentOfLC( aNodeIdentifier );
+            SetFolderSeenIfNeededL( *parent );
+            CleanupStack::PopAndDestroy( parent );
+            }
+        }
+    }
+    
+TBool CNcdNodeSeenInfo::FolderHasNewUnseenChildrenL(
+    const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((""));    
+    CNcdNodeFolder& folder = iNodeManager.FolderL( aNodeIdentifier );
+    const RPointerArray<CNcdChildEntity>& childArray = folder.ChildArray();
+    for( TInt i = 0 ; i < childArray.Count() ; i++ )
+        {
+        // NOTE: Children that are set seen are not considered new here.
+        CNcdNodeNewStatus* nodeNewStatus = NodeNewStatus( childArray[i]->Identifier() );
+        if( nodeNewStatus && !IsAddedAsSeen( childArray[i]->Identifier() ) )
+            {
+            DLTRACE(("New child found"));
+            return ETrue;
+            }
+        }
+    return EFalse;
+    }
+    
+ TBool CNcdNodeSeenInfo::FolderHasNewChildrenL(
+    const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((""));    
+    CNcdNodeFolder& folder = iNodeManager.FolderL( aNodeIdentifier );
+    const RPointerArray<CNcdChildEntity>& childArray = folder.ChildArray();
+    for( TInt i = 0 ; i < childArray.Count() ; i++ )
+        {
+        // NOTE: Children that are set seen are considered new here.
+        CNcdNodeNewStatus* nodeNewStatus = NodeNewStatus( childArray[i]->Identifier() );
+        if( nodeNewStatus )
+            {
+            DLTRACE(("New child found"));
+            return ETrue;
+            }
+        }
+    return EFalse;
+    }
+
+void CNcdNodeSeenInfo::AddSeenIdL( const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((""));
+    CClientSeenInfo* info = ClientInfo( aNodeIdentifier.ClientUid() );
+    DASSERT( info );
+    // Add to seen list.
+    CNcdNodeIdentifier* copy = CNcdNodeIdentifier::NewLC( aNodeIdentifier );
+    info->iSeenStructureIds.AppendL( copy );
+    CleanupStack::Pop( copy );
+    }
+
+void CNcdNodeSeenInfo::SetSeenL( const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((""));
+        
+    const TUid& clientUid = aNodeIdentifier.ClientUid();
+            
+    if ( !IsClientInfoLoaded( clientUid ) ) 
+        {
+        DbLoadClientInfoL( clientUid );
+        }
+    TInt index = IndexInNewStatusArray( aNodeIdentifier );
+    
+    if( index == KErrNotFound || IsAddedAsSeen( aNodeIdentifier ) )
+        {
+        DLTRACE(("Not in new status array, or already set seen"));
+        return;
+        }
+    
+    CClientSeenInfo* info = ClientInfo( clientUid );
+    DASSERT( info );
+    
+    CNcdNodeNewStatus* nodeNewStatus = info->iNodeNewStatusArray[index];
+    if( nodeNewStatus->NodeType() == CNcdNodeFactory::ENcdNodeItem )
+        {
+        AddSeenIdL( aNodeIdentifier );
+        }
+    else if ( nodeNewStatus->NewStatus() & ENcdStatusNew &&
+        nodeNewStatus->NewStatus() & ENcdStatusNewNodesInside )
+        {
+        nodeNewStatus->SetNewStatus(
+            TNcdNewStatus( ENcdStatusNewButSeen | ENcdStatusNewNodesInside ) );
+        }
+    else if ( nodeNewStatus->NewStatus() & ENcdStatusNew )
+        {
+        AddSeenIdL( aNodeIdentifier );
+        }
+        
+    if( !NcdNodeIdentifierEditor::IdentifiesSomeRoot( aNodeIdentifier ) )
+        {
+        CNcdNodeIdentifier* parent = NcdNodeIdentifierEditor::ParentOfLC( aNodeIdentifier );
+        SetFolderSeenIfNeededL( *parent );
+        CleanupStack::PopAndDestroy( parent );
+        }
+    }
+    
+void CNcdNodeSeenInfo::CheckFolderNewStatusL( CNcdNodeFolder& aParentFolder )
+    {
+    DLTRACEIN((""));
+    // New checking for children can only be done if the previous
+    // child array has been stored. (Not stored e.g. on first time browse and
+    // after restart)
+    if( aParentFolder.PreviousChildCount() != KErrNotFound )
+        {
+        DLTRACE(("Previous children set -> do new comparison"));
+        const RPointerArray<CNcdChildEntity>& previousChildArray =
+            aParentFolder.PreviousChildArray();
+        CheckFolderNewStatusL( aParentFolder, previousChildArray );
+        }
+    }
+        
+void CNcdNodeSeenInfo::CheckFolderNewStatusL( CNcdNodeFolder& aParentFolder,
+    const RPointerArray<CNcdChildEntity>& aPreviousChildArray )
+    {
+    const TUid& clientUid = aParentFolder.Identifier().ClientUid();
+            
+    if ( !IsClientInfoLoaded( clientUid ) ) 
+        {
+        DbLoadClientInfoL( clientUid );
+        }
+        
+    CClientSeenInfo* info = ClientInfo( clientUid );
+    DASSERT( info );
+    
+    // This flag is used to determine whether this folder contains any new items.
+    TBool newChildrenFound = EFalse;
+    // Check new array against the previous array
+    const RPointerArray<CNcdChildEntity>& childArray =
+        aParentFolder.ChildArray();
+    CNcdNodeNewStatus* parentNewStatus = NodeNewStatus( aParentFolder.Identifier() );
+    TBool parentIsNew = parentNewStatus &&
+            ( parentNewStatus->NewStatus() & ENcdStatusNew ||
+              parentNewStatus->NewStatus() & ENcdStatusNewButSeen );
+    for( TInt i = 0 ; i < childArray.Count() ; i++ )
+        {
+        // compare with previous list
+        TBool nodeWithSameIndexFound = EFalse;
+        TBool childFound = ChildExistsInArrayL( *childArray[i],
+            aPreviousChildArray,
+            nodeWithSameIndexFound );
+        
+        if( parentIsNew )
+            {
+            DLTRACE(("Parent folder is new -> all children are new "));
+            AddNewIdL( *childArray[i] );
+            newChildrenFound = ETrue;
+            }
+        // if found -> old
+        else if( childFound )
+            {
+            DLTRACE(("Child found -> old"));
+            }
+        // if not found && index < old child count && no node with this index in previous list
+        //      -> old ( cannot know whether this is new or old so default to old, special case! )
+        else if( !childFound
+            && childArray[i]->Index() < aParentFolder.PreviousChildCount()
+            && !nodeWithSameIndexFound )
+            {
+            DLTRACE(("Child not found and index < previous child count && node with same index not found -> default to old"));
+            }
+        // if not found && index < old child count && node exists with this index in previous list
+        //      -> new ( the "usual" new case )
+        else if( !childFound
+             && childArray[i]->Index() < aParentFolder.PreviousChildCount()
+             && nodeWithSameIndexFound )
+            {
+            DLTRACE(("New"));
+            AddNewIdL( *childArray[i] );
+            newChildrenFound = ETrue;
+            }
+        // if not found && index > old child count -> new 
+        else if( !childFound
+            && childArray[i]->Index() >= aParentFolder.PreviousChildCount() )
+            {
+            DLTRACE(("New"));
+            AddNewIdL( *childArray[i] );
+            newChildrenFound = ETrue;
+            }
+            
+        // If no new children are found before, check that is this node previously new
+        if( !newChildrenFound )
+            {
+            DLTRACE(("Check if already set new"));
+            // Must be in new array and not in seen array to be new
+            // (internally new, i.e. new even after commit).
+            CNcdNodeNewStatus* nodeNewStatus = NodeNewStatus(
+                childArray[i]->Identifier() );
+            if( nodeNewStatus )
+                {
+                newChildrenFound = ETrue;
+                }
+            }
+        }
+        
+    if( !newChildrenFound && parentIsNew )
+        {
+        // Special case: all new items have been removed, immediately remove parent's new status
+        // (better user experience when compared to just setting it seen)
+        RemoveFromNewListL( aParentFolder.Identifier() );
+        }
+    }   
+
+void CNcdNodeSeenInfo::CheckChildNewStatusL( CNcdNodeFolder& aParentFolder,
+    TInt aChildIndex )
+    {
+    DLTRACEIN((""));
+    const TUid& clientUid = aParentFolder.Identifier().ClientUid();
+            
+    if ( !IsClientInfoLoaded( clientUid ) ) 
+        {
+        DbLoadClientInfoL( clientUid );
+        }
+    
+    CNcdNodeNewStatus* parentNewStatus = NodeNewStatus( aParentFolder.Identifier() );
+    TBool parentIsNew = parentNewStatus &&
+            ( parentNewStatus->NewStatus() & ENcdStatusNew ||
+              parentNewStatus->NewStatus() & ENcdStatusNewButSeen );
+    if( parentIsNew )
+        {
+        DLTRACE(("Parent is new, set child new as well"));
+        AddNewIdL( aParentFolder.ChildEntityByServerIndexL( aChildIndex ) );
+        }
+    else if( aParentFolder.PreviousChildCount() != KErrNotFound )
+        {
+        DLTRACE(("Previous child list set, do comparison."));
+        const CNcdChildEntity& child = aParentFolder.ChildEntityByServerIndexL( aChildIndex );
+        // compare with previous list
+        TBool nodeWithSameIndexFound = EFalse;
+        TBool childFound = ChildExistsInArrayL( child,
+            aParentFolder.PreviousChildArray(),
+            nodeWithSameIndexFound );
+
+        // if found -> old
+        if( childFound )
+            {
+            DLTRACE(("Child found -> old"));
+            }
+        // if not found && index < old child count && no node with this index in previous list
+        //      -> old ( cannot know whether this is new or old so default to old, special case! )
+        else if( !childFound
+            && child.Index() < aParentFolder.PreviousChildCount()
+            && !nodeWithSameIndexFound )
+            {
+            DLTRACE(("Child not found and index < previous child count && node with same index not found -> default to old"));
+            }
+        // if not found && index < old child count && node exists with this index in previous list
+        //      -> new ( the "usual" new case )
+        else if( !childFound
+             && child.Index() < aParentFolder.PreviousChildCount()
+             && nodeWithSameIndexFound )
+            {
+            DLTRACE(("New"));
+            AddNewIdL( child );
+            }
+        // if not found && index > old child count -> new 
+        else if( !childFound
+            && child.Index() >= aParentFolder.PreviousChildCount() )
+            {
+            DLTRACE(("New"));
+            AddNewIdL( child );
+            }
+        }
+    }
+    
+void CNcdNodeSeenInfo::ClearInfoL( const TUid& aClientUid )
+    {
+    DLTRACEIN((""));
+    if ( !IsClientInfoLoaded( aClientUid ) ) 
+        {
+        DbLoadClientInfoL( aClientUid );
+        }
+    CClientSeenInfo* info = ClientInfo( aClientUid );
+    DASSERT( info );
+    info->iNodeNewStatusArray.ResetAndDestroy();
+    info->iSeenStructureIds.ResetAndDestroy();
+    }
+
+void CNcdNodeSeenInfo::CommitChangesL( const TUid& aClientUid ) 
+    {
+    DLTRACEIN((""));
+    
+    CClientSeenInfo* info = ClientInfo( aClientUid );
+    if ( !info )
+        {
+        // No changes made, return;
+        return;
+        }
+    
+    for( TInt i = info->iNodeNewStatusArray.Count() - 1 ; i >= 0 ; i-- )
+        {
+        CNcdNodeNewStatus* nodeNewStatus = info->iNodeNewStatusArray[i];
+        if( nodeNewStatus->NewStatus() & ENcdStatusNewButSeen )
+            {
+            nodeNewStatus->SetNewStatus( 
+                TNcdNewStatus(nodeNewStatus->NewStatus() ^ ENcdStatusNewButSeen ) );
+            }
+        }
+    
+    TInt count = info->iSeenStructureIds.Count();
+    // Remove the seen identifiers from the new list.
+    while ( count-- ) 
+        {
+        RemoveFromNewListL( *info->iSeenStructureIds[ count ] );
+        delete info->iSeenStructureIds[ count];
+        // Removing here in case RemoveFromNewListL leaves
+        info->iSeenStructureIds.Remove( count );
+        }
+        
+    DASSERT( info->iSeenStructureIds.Count() == 0 );
+    // Save the changes to db.
+    DbSaveClientInfoL( aClientUid );
+    }
+
+ void CNcdNodeSeenInfo::CreatePreviousListsForChildrenL(
+    CNcdNodeFolder& aParentFolder,
+    RPointerArray<CNcdChildEntityMap>& aChildEntityMaps )
+    {
+    DLTRACEIN((""));
+    for( TInt i = 0 ; i < aParentFolder.ChildArray().Count() ; i++ )
+        {
+        if( aParentFolder.ChildArray()[i]->NodeType() ==
+            CNcdNodeFactory::ENcdNodeFolder )
+            {
+            CNcdNodeFolder* child = NULL;
+            TRAPD(err, child = &iNodeManager.FolderL(
+                aParentFolder.ChildArray()[i]->Identifier() ) ); //TRAPD
+            // Special handling for bundles
+            if( err != KErrNone )
+                {
+                DLTRACE(("Child not found from cache or db."));
+                continue;
+                }
+            TInt childCount = 0;
+            if( CNcdNodeFactory::NodePurposeL( *child ) ==
+                CNcdNodeFactory::ENcdBundleNode && 
+                child->ChildrenPreviouslyLoaded() )
+                {
+                DLTRACE((""))
+                CreatePreviousListsForChildrenL( *child, aChildEntityMaps );
+                childCount = child->ChildCount();
+                }
+            else
+                {
+                childCount = child->FolderLinkL().ExpectedChildrenCount();
+                }
+            // Store only if previously loaded.
+            if( child->ChildrenPreviouslyLoaded() )
+                {
+                DLTRACEIN((_L("Creating previous list for: %S"),
+                    &child->Identifier().NodeId() ));
+                CNcdChildEntityMap* childEntityMap =
+                    CNcdChildEntityMap::NewLC(
+                        child->Identifier(),
+                        child->ChildArray(),
+                        childCount );
+                aChildEntityMaps.AppendL( childEntityMap );
+                CleanupStack::Pop( childEntityMap );
+                }
+            }
+        }
+    }
+
+void CNcdNodeSeenInfo::StorePreviousListsToExistingChildrenL(
+    CNcdNodeFolder& aParentFolder,
+    const RPointerArray<CNcdChildEntityMap>& aChildEntityMaps )
+    {
+    DLTRACEIN((""));
+    const RPointerArray<CNcdChildEntity>& children = aParentFolder.ChildArray();
+    for( TInt i = 0 ; i < children.Count() ; i++ )
+        {
+        CNcdNodeFolder* folder = NULL;
+        TRAPD(err, folder =
+            &iNodeManager.FolderL( children[i]->Identifier() ) ); //TRAPD
+        if( err != KErrNone )
+            {
+            DLTRACE(("Child not found from cache or db."));
+            continue;
+            }
+        if( CNcdNodeFactory::NodePurposeL( 
+            *folder ) == CNcdNodeFactory::ENcdBundleNode )
+            {
+            DLTRACE(("Child is bundle -> store it's children's previous lists."));
+            StorePreviousListsToExistingChildrenL( *folder, aChildEntityMaps );
+            }
+            
+        for( TInt j = 0 ; j < aChildEntityMaps.Count() ; j++ )
+            {
+            // Should remove child entity map when matching child is found.
+            const CNcdChildEntityMap* previousChildEntityMap = aChildEntityMaps[j];
+            if( previousChildEntityMap->ParentIdentifier().Equals( 
+                folder->Identifier() ) )
+                {
+                DLTRACE(("Matching child found, store it's previous list."));
+                folder->StoreChildrenToPreviousListL( 
+                    previousChildEntityMap->ChildArray(),
+                    previousChildEntityMap->ChildCount() );
+                break;
+                }
+            }
+        }
+    }
+
+void CNcdNodeSeenInfo::DoNewCheckForTransparentChildrenL(
+    CNcdNodeFolder& aParentFolder )
+    {
+    DLTRACEIN((""));
+    const RPointerArray<CNcdChildEntity>& children = aParentFolder.ChildArray();
+    for( TInt i = 0 ; i < children.Count() ; i++ )
+        {
+        if( children[i]->NodeType() == CNcdNodeFactory::ENcdNodeFolder
+            && children[i]->IsTransparent() )
+            {
+            CNcdNodeFolder* folder = NULL;
+            TRAPD(err, folder =
+                &iNodeManager.FolderL( children[i]->Identifier() ) ); //TRAPD
+            if( err == KErrNone )
+                {
+                CNcdNodeNewStatus* parentNewStatus = NodeNewStatus( children[i]->Identifier() );
+                TBool parentIsNew = parentNewStatus &&
+                    ( parentNewStatus->NewStatus() & ENcdStatusNew ||
+                      parentNewStatus->NewStatus() & ENcdStatusNewButSeen );
+                if( parentIsNew )
+                    {
+                    SetChildrenNewL( *folder );
+                    }
+                else
+                    {
+                    CheckFolderNewStatusL( *folder );
+                    }
+                }
+            }
+        }
+    }
+
+CNcdNodeSeenInfo::CClientSeenInfo* CNcdNodeSeenInfo::ClientInfo(
+    const TUid& aClientUid ) const 
+    {
+    for ( TInt i = 0; i < iClientSeenInfos.Count(); i++ ) 
+        {
+        if ( iClientSeenInfos[ i ]->iClientUid == aClientUid ) 
+            {
+            return iClientSeenInfos[ i ];
+            }
+        }
+    return NULL;
+    }
+    
+
+TInt CNcdNodeSeenInfo::IndexInSeenArray( const CNcdNodeIdentifier& aNodeIdentifier ) const
+    {
+    DLTRACEIN((""));
+    CClientSeenInfo* info = ClientInfo( aNodeIdentifier.ClientUid() );
+    DASSERT( info );
+    return NcdNodeIdentifierUtils::IdentifierIndex(
+        aNodeIdentifier, info->iSeenStructureIds );
+    }
+    
+TBool CNcdNodeSeenInfo::IsAddedAsSeen( const CNcdNodeIdentifier& aNodeIdentifier ) const
+    {
+    DLTRACEIN((""));
+    return IndexInSeenArray( aNodeIdentifier ) != KErrNotFound;
+    }
+    
+TInt CNcdNodeSeenInfo::IndexInNewStatusArray( const CNcdNodeIdentifier& aNodeIdentifier ) const
+    {
+    DLTRACEIN((""));
+    CClientSeenInfo* info = ClientInfo( aNodeIdentifier.ClientUid() );
+    DASSERT( info );
+    TInt index = KErrNotFound;
+    for( TInt i = 0 ; i < info->iNodeNewStatusArray.Count() ; i++ )
+        {
+        if( info->iNodeNewStatusArray[i]->Identifier().Equals( aNodeIdentifier ) )
+            {
+            index = i;
+            break;
+            }
+        }
+    return index;
+    }
+    
+CNcdNodeSeenInfo::CNcdNodeNewStatus* CNcdNodeSeenInfo::NodeNewStatus( const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((""));
+    CNcdNodeNewStatus* nodeNewStatus = NULL;
+    TInt index = IndexInNewStatusArray( aNodeIdentifier );
+    if( index != KErrNotFound )
+        {
+        CClientSeenInfo* info = ClientInfo( aNodeIdentifier.ClientUid() );
+        DASSERT( info );
+        nodeNewStatus = info->iNodeNewStatusArray[index];
+        }
+    return nodeNewStatus;
+    }
+    
+void CNcdNodeSeenInfo::RemoveFromSeenListL( const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((""));
+    const TUid& clientUid = aNodeIdentifier.ClientUid();
+            
+    if ( !IsClientInfoLoaded( clientUid ) ) 
+        {
+        DbLoadClientInfoL( clientUid );
+        }
+        
+    CClientSeenInfo* info = ClientInfo( clientUid );
+    DASSERT( info );
+    
+    TInt index = NcdNodeIdentifierUtils::IdentifierIndex(
+        aNodeIdentifier, info->iSeenStructureIds );
+    if( index != KErrNotFound )
+        {
+        delete info->iSeenStructureIds[index];
+        info->iSeenStructureIds.Remove( index );
+        }
+    }
+
+void CNcdNodeSeenInfo::RemoveFromNewListL( const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((""));
+    const TUid& clientUid = aNodeIdentifier.ClientUid();
+            
+    if ( !IsClientInfoLoaded( clientUid ) ) 
+        {
+        DbLoadClientInfoL( clientUid );
+        }
+        
+    CClientSeenInfo* info = ClientInfo( clientUid );
+    DASSERT( info );
+    
+    TInt index = IndexInNewStatusArray( aNodeIdentifier );
+    if( index != KErrNotFound )
+        {
+        DLTRACE((_L("Removing from new status array, id: %S"), &aNodeIdentifier.NodeId() ));
+        delete info->iNodeNewStatusArray[index];
+        info->iNodeNewStatusArray.Remove( index );
+        }
+    }
+
+TBool CNcdNodeSeenInfo::IsClientInfoLoaded( const TUid& aClientUid ) const
+    {
+    DLTRACEIN((""));
+    return ClientInfo( aClientUid ) != NULL;
+    }
+    
+
+void CNcdNodeSeenInfo::RemoveClientInfo( const TUid& aClientUid )
+    {
+    DLTRACEIN((""));
+    for ( TInt i = 0; i < iClientSeenInfos.Count(); i++ ) 
+        {
+        if ( iClientSeenInfos[ i ]->iClientUid == aClientUid ) 
+            {
+            delete iClientSeenInfos[ i ];
+            iClientSeenInfos.Remove( i );
+            break;
+            }
+        }
+    }       
+    
+
+void CNcdNodeSeenInfo::DbLoadClientInfoL( const TUid& aClientUid )
+    {
+    DLTRACEIN((""));
+    
+    RemoveClientInfo( aClientUid );
+    
+    MNcdStorage& providerStorage = iStorageManager.ProviderStorageL(
+        iGeneralManager.FamilyName() );
+    MNcdDatabaseStorage& database =
+        providerStorage.DatabaseStorageL( NcdProviderDefines::KDefaultDatabaseUid );
+        
+    if( !database.ItemExistsInStorageL( aClientUid.Name(), 
+        NcdProviderDefines::ENcdNodeSeenInfo ) ) 
+        {
+        // Create empty client info object.
+        CClientSeenInfo* empty = new( ELeave ) CClientSeenInfo( aClientUid );
+        CleanupStack::PushL( empty );
+        iClientSeenInfos.AppendL( empty );
+        CleanupStack::Pop( empty );
+        }
+    else 
+        {
+        // Database contains the data, load it.
+        // Get the storage item from which the data is loaded
+        // Note: database has the ownership of the item
+        MNcdStorageItem* item = database.StorageItemL(
+            aClientUid.Name(), NcdProviderDefines::ENcdNodeSeenInfo );    
+        
+        // Get data from database by using CClientSeenInfo as the target so that 
+        // internalize will be called for it
+        CClientSeenInfo* data = new( ELeave ) CClientSeenInfo( aClientUid );
+        CleanupStack::PushL( data );
+        item->SetDataItem( data );
+    
+        // Read data -> calls CClientSeenInfo::InternalizeL
+        item->ReadDataL();
+        
+        iClientSeenInfos.AppendL( data );
+        CleanupStack::Pop( data );
+        }
+    }
+    
+    
+void CNcdNodeSeenInfo::DbSaveClientInfoL( const TUid& aClientUid )
+    {
+    DLTRACEIN((""));
+    
+    CClientSeenInfo* info = ClientInfo( aClientUid );
+    if ( !info ) 
+        {
+        return;
+        }
+        
+    MNcdStorage& providerStorage = iStorageManager.ProviderStorageL(
+        iGeneralManager.FamilyName() );
+    MNcdDatabaseStorage& database =
+        providerStorage.DatabaseStorageL( NcdProviderDefines::KDefaultDatabaseUid );
+        
+    // Get the storage item to which the client seen info is stored
+    // Note: database has the ownership of the item
+    MNcdStorageItem* item = database.StorageItemL(
+        aClientUid.Name(), NcdProviderDefines::ENcdNodeSeenInfo );
+    item->SetDataItem( info );
+    item->OpenL();
+        
+    // Calls ExternalizeL for this
+    item->WriteDataL();
+    item->SaveL();
+    }
+    
+void CNcdNodeSeenInfo::SetChildrenNewL( CNcdNodeFolder& aParentFolder )
+    {
+    DLTRACEIN((""));
+    const RPointerArray<CNcdChildEntity>& children = aParentFolder.ChildArray();
+    for( TInt i = 0 ; i < children.Count() ; i++ )
+        {
+        AddNewIdL( *children[i] );
+        RemoveFromSeenListL( children[i]->Identifier() );
+        }
+    }
+        
+    
+// ----------------------------------------------------------------------------------------------
+// CNcdNodeSeenInfo::CClientInfo
+// ----------------------------------------------------------------------------------------------
+//
+
+CNcdNodeSeenInfo::CClientSeenInfo::CClientSeenInfo( const TUid& aClientUid ) :
+    iClientUid( aClientUid ) 
+    {
+    }
+    
+CNcdNodeSeenInfo::CClientSeenInfo::~CClientSeenInfo()
+    {
+    iNodeNewStatusArray.ResetAndDestroy();
+    iSeenStructureIds.ResetAndDestroy();
+    }
+    
+
+// From MNcdStorageDataItem
+
+void CNcdNodeSeenInfo::CClientSeenInfo::ExternalizeL( RWriteStream& aStream ) 
+    {
+    DLTRACEIN((""));
+    
+    // Write uid.
+    aStream.WriteInt32L( iClientUid.iUid );
+
+    // Write new structure ids.
+    TInt count = iNodeNewStatusArray.Count();
+    aStream.WriteInt32L( count );
+    for ( TInt i = 0; i < count; i++ ) 
+        {
+        iNodeNewStatusArray[ i ]->ExternalizeL( aStream );
+        }
+    }
+
+
+void CNcdNodeSeenInfo::CClientSeenInfo::InternalizeL( RReadStream& aStream ) 
+    {
+    DLTRACEIN((""));
+    iNodeNewStatusArray.ResetAndDestroy();
+    
+    // Read uid.
+    iClientUid = TUid::Uid( aStream.ReadInt32L() );
+    
+    // Read new structure ids.
+    TInt count = aStream.ReadInt32L();
+    for ( TInt i = 0; i < count; i++ ) 
+        {
+        CNcdNodeNewStatus* identifier = CNcdNodeNewStatus::NewLC( aStream );
+        iNodeNewStatusArray.AppendL( identifier );
+        CleanupStack::Pop( identifier );
+        }
+    }
+
+CNcdNodeSeenInfo::CNcdNodeNewStatus* CNcdNodeSeenInfo::CNcdNodeNewStatus::NewL(
+    const CNcdNodeIdentifier& aNodeIdentifier,
+    TNcdNewStatus aNewStatus,
+    CNcdNodeFactory::TNcdNodeType aNodeType )
+    {
+    DLTRACEIN((""));
+    CNcdNodeNewStatus* self = CNcdNodeNewStatus::NewLC( aNodeIdentifier,
+        aNewStatus,
+        aNodeType );
+    CleanupStack::Pop( self );
+    return self;
+    }
+    
+CNcdNodeSeenInfo::CNcdNodeNewStatus* CNcdNodeSeenInfo::CNcdNodeNewStatus::NewLC(
+    const CNcdNodeIdentifier& aNodeIdentifier,
+    TNcdNewStatus aNewStatus,
+    CNcdNodeFactory::TNcdNodeType aNodeType )
+    {
+    DLTRACEIN((""));
+    CNcdNodeNewStatus* self = new ( ELeave ) CNcdNodeNewStatus( aNewStatus,
+        aNodeType );
+    CleanupStack::PushL( self );
+    self->ConstructL( aNodeIdentifier );
+    return self;
+    }
+    
+CNcdNodeSeenInfo::CNcdNodeNewStatus* CNcdNodeSeenInfo::CNcdNodeNewStatus::NewL(
+    RReadStream& aReadStream )
+    {
+    DLTRACEIN((""));
+    CNcdNodeNewStatus* self = CNcdNodeNewStatus::NewLC( aReadStream );
+    CleanupStack::Pop( self );
+    return self;
+    }
+    
+CNcdNodeSeenInfo::CNcdNodeNewStatus* CNcdNodeSeenInfo::CNcdNodeNewStatus::NewLC(
+    RReadStream& aReadStream )
+    {
+    DLTRACEIN((""));
+    CNcdNodeNewStatus* self = new ( ELeave ) CNcdNodeNewStatus();
+    CleanupStack::PushL( self );
+    self->InternalizeL( aReadStream );
+    return self;
+    }
+    
+CNcdNodeSeenInfo::CNcdNodeNewStatus::~CNcdNodeNewStatus()
+    {
+    DLTRACEIN((""));
+    delete iNodeIdentifier;
+    }
+
+const CNcdNodeIdentifier& CNcdNodeSeenInfo::CNcdNodeNewStatus::Identifier() const
+    {
+    return *iNodeIdentifier;
+    }
+
+TNcdNewStatus CNcdNodeSeenInfo::CNcdNodeNewStatus::NewStatus() const
+    {
+    return iNewStatus;
+    }
+    
+void CNcdNodeSeenInfo::CNcdNodeNewStatus::SetNewStatus( TNcdNewStatus aNewStatus )
+    {
+    DLTRACEIN((""));
+    iNewStatus = aNewStatus;
+    }
+
+CNcdNodeFactory::TNcdNodeType CNcdNodeSeenInfo::CNcdNodeNewStatus::NodeType() const
+    {
+    return iNodeType;
+    }
+
+void CNcdNodeSeenInfo::CNcdNodeNewStatus::ExternalizeL( RWriteStream& aStream )
+    {
+    DLTRACEIN((""));
+    iNodeIdentifier->ExternalizeL( aStream );
+    aStream.WriteInt32L( iNewStatus );
+    aStream.WriteInt32L( iNodeType );
+    }
+
+
+void CNcdNodeSeenInfo::CNcdNodeNewStatus::InternalizeL( RReadStream& aStream ) 
+    {
+    DLTRACEIN((""));
+    CNcdNodeIdentifier* identifier = CNcdNodeIdentifier::NewL( aStream );
+    delete iNodeIdentifier;
+    iNodeIdentifier = identifier;
+    iNewStatus = static_cast<TNcdNewStatus>( aStream.ReadInt32L() );
+    iNodeType = static_cast<CNcdNodeFactory::TNcdNodeType>( aStream.ReadInt32L() );
+    }
+
+CNcdNodeSeenInfo::CNcdNodeNewStatus::CNcdNodeNewStatus( TNcdNewStatus aNewStatus,
+    CNcdNodeFactory::TNcdNodeType aNodeType )
+    : iNewStatus( aNewStatus ), iNodeType( aNodeType )
+    {
+    }
+    
+CNcdNodeSeenInfo::CNcdNodeNewStatus::CNcdNodeNewStatus()
+    {
+    }
+ 
+void CNcdNodeSeenInfo::CNcdNodeNewStatus::ConstructL(
+    const CNcdNodeIdentifier& aNodeIdentifier )
+    {
+    DLTRACEIN((""));
+    iNodeIdentifier = CNcdNodeIdentifier::NewL( aNodeIdentifier );
+    }