ncdengine/provider/server/src/ncdnodemanager.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Tue, 02 Feb 2010 00:20:15 +0200
changeset 6 aba6b8104af3
parent 0 ba25891c3a9e
permissions -rw-r--r--
Revision: 201003 Kit: 201005

/*
* Copyright (c) 2006 Nokia Corporation and/or its subsidiary(-ies).
* All rights reserved.
* This component and the accompanying materials are made available
* under the terms of "Eclipse Public License v1.0"
* which accompanies this distribution, and is available
* at the URL "http://www.eclipse.org/legal/epl-v10.html".
*
* Initial Contributors:
* Nokia Corporation - initial contribution.
*
* Contributors:
*
* Description:   Implements CNcdNodeManager class
*
*/


#include "ncdnodemanager.h"

#include <e32err.h>
#include <s32mem.h>
#include <badesca.h>

#include "ncdnodedbmanager.h"
#include "ncdnodecachecleaner.h"
#include "ncdnodecachecleanermanager.h"
#include "ncdnodeidentifier.h"
#include "ncdnodeidentifiereditor.h"
#include "ncdnodeclassids.h"
#include "ncdnodeimpl.h"
#include "ncdnodeitem.h"
#include "ncdnodefolder.h"
#include "ncdbundlefolder.h"
#include "ncdnodetransparentfolder.h"
#include "ncdrootnode.h"
#include "ncdsearchnodefolder.h"
#include "ncdnodelink.h"
#include "ncdnodemetadataimpl.h"
#include "ncdnodeitemmetadata.h"
#include "ncdnodefoldermetadata.h"
#include "ncdsearchnodeitem.h"
#include "ncdsearchnodefolder.h"
#include "ncdsearchrootnode.h"
#include "ncdexpirednode.h"
#include "ncdchildentity.h"
#include "ncdpurchasehistorydbimpl.h"
#include "ncdnodepreviewimpl.h"
#include "ncdstoragedescriptordataitem.h"
#include "catalogsbasemessage.h"
#include "catalogssession.h"
#include "ncd_pp_folderref.h"
#include "ncd_pp_itemref.h"
#include "ncd_pp_dataentity.h"
#include "ncd_pp_download.h"
#include "ncdpreviewmanager.h"
#include "ncdstoragemanagerimpl.h"
#include "ncdconfigurationmanager.h"
#include "ncdnodefunctionids.h"
#include "ncdproviderdefines.h"
#include "ncdserverdetails.h"
#include "catalogscontext.h"
#include "catalogsutils.h"
#include "catalogsconstants.h"
#include "ncd_pp_expiredcacheddata.h"
#include "ncdproviderutils.h"
#include "ncdutils.h"
#include "ncdsearchnodebundle.h"
#include "ncdfavoritemanagerimpl.h"
#include "ncdnodeseeninfo.h"
#include "ncdnodeiconimpl.h"
#include "ncderrors.h"
#include "ncdsearchablenode.h"
#include "ncdnodeidentifierutils.h"
#include "ncdgeneralmanager.h"

#include "catalogsdebug.h"

// If the values from the provider are given in kilos and other classes
// require them in bytes, this value can be used for conversion.
const TInt KBytesToKilos( 1024 );

const TInt KNodeCacheGranularity = 256;

CNcdNodeManager::CNcdNodeManager( CNcdGeneralManager& aGeneralManager )
: CCatalogsCommunicable(),
  iPurchaseHistory( aGeneralManager.PurchaseHistory() ),  
  iNodeCache( KNodeCacheGranularity ),
  iNodeMetaDataCache( KNodeCacheGranularity ),
  iTempNodeCache( KNodeCacheGranularity ),
  iConfigurationManager( aGeneralManager.ConfigurationManager() ),
  iNodeOrder( CNcdNode::Compare ),
  iGeneralManager( aGeneralManager )
    {
    DLTRACEIN((""));    
    }


void CNcdNodeManager::ConstructL()
    {
    DLTRACEIN((""));
    iGeneralManager.SetNodeManager( *this );
    iDbManager = CNcdNodeDbManager::NewL( iGeneralManager.StorageManager() );
    
    // Make sure that db manager is created before factory is created.
    iNodeFactory = CNcdNodeFactory::NewL( *this );
    
    // Notice that this function uses the objects created above.
    // So, if the creation is moved somewhere else, make sure that
    // NodeDbManager and NodeFactory are also created.
    iNodeCacheCleanerManager = 
        CNcdNodeCacheCleanerManager::NewL( iGeneralManager, 
                                           NodeDbManager(),
                                           NcdProviderDefines::KDbDefaultMaxByteSize,
                                           NodeFactory() );

    iPreviewManager = CNcdPreviewManager::NewL( iGeneralManager, 
                                          NcdProviderDefines::KMaxClientPreviews );
    iPreviewManager->LoadDataL();
    iGeneralManager.SetNodeManager( *this );
    
    iSeenInfo = CNcdNodeSeenInfo::NewL( iGeneralManager );

    iSearchableNode = new ( ELeave ) CNcdSearchableNode( *this );
    DLTRACEOUT((""));
    }


CNcdNodeManager::~CNcdNodeManager()
    {
    DLTRACEIN((""));
    
    // Ensure that all cached objects are closed
    FullCacheCleanup();


    if ( iSearchableNode ) 
        {
        iSearchableNode->Close();        
        }
        
    // Delete member variables in reverse order when comparing
    // to the creation order.

    delete iPreviewManager;
    
    // Delete node cache cleaner.
    delete iNodeCacheCleanerManager;   

    // Delete node factory.
    delete iNodeFactory;

    // Delete the db manager.
    delete iDbManager;
    
    // Delete the seen info.
    delete iSeenInfo;

    iClientDatabaseLocks.Close();
    
    DLTRACEOUT((""));
    }


CNcdNodeManager* CNcdNodeManager::NewL( CNcdGeneralManager& aGeneralManager )
    {
    CNcdNodeManager* self =
        new( ELeave ) CNcdNodeManager( aGeneralManager );
    CleanupClosePushL( *self );
    self->ConstructL();
    CleanupStack::Pop( self );
    return self;
    }


// ---------------------------------------------------------------------------
// General getter functions
// ---------------------------------------------------------------------------

CNcdPurchaseHistoryDb& CNcdNodeManager::PurchaseHistory() const
    {
    return iPurchaseHistory;
    }

CNcdPreviewManager& CNcdNodeManager::PreviewManager()  const
    {
    return *iPreviewManager;
    }



// ---------------------------------------------------------------------------
// Functions to get node objects from caches or from db. 
// And, functions to create node objects.
// ---------------------------------------------------------------------------

CNcdNode& CNcdNodeManager::NodeL( const CNcdNodeIdentifier& aIdentifier )
    {
    DLTRACEIN((""));
    
    CNcdNode* node = NodePtrL( aIdentifier );
    if ( node == NULL )
        {
        User::Leave( KErrNotFound );
        }

    DLTRACEOUT((""));
    return *node;
    }

CNcdNode& CNcdNodeManager::NodeL( const CNcdNodeIdentifier& aParentNodeIdentifier,
                                  const CNcdNodeIdentifier& aNodeMetaDataIdentifier )
    {
    DLTRACEIN((""));
    
    CNcdNodeIdentifier* identifier =
        NcdNodeIdentifierEditor::CreateNodeIdentifierLC( aParentNodeIdentifier, 
                                                         aNodeMetaDataIdentifier );
    CNcdNode& node = NodeL( *identifier );

    CleanupStack::PopAndDestroy( identifier );

    DLTRACEOUT((""));

    return node;
    }


CNcdNode* CNcdNodeManager::NodePtrL( const CNcdNodeIdentifier& aIdentifier )
    {
    DLTRACEIN(( _L("Node id: %S, %S, %d"), 
                &aIdentifier.NodeNameSpace(),
                &aIdentifier.NodeId(),
                aIdentifier.ClientUid().iUid ));

    CNcdNode* node( FindNodeFromCacheL( aIdentifier ) );
    if ( node != NULL )
        {
        DLTRACEOUT(("Found node"));
        return node;
        }

    DLINFO(("Start loading the node from storage"));
    // Check if the node can be found from the db because it was not found
    // from the cache.
    // This function leaves with KErrNotFound if the data was not found from
    // the database. If this is the case then the data should be loaded from
    // internet. But it is not job of this function. Let it leave.
    TRAPD( trapError, node = &DbNodeL( aIdentifier ) );
    if ( trapError == KErrNotFound )
        {
        // Node was not found from db. So, return NULL value.
        DLTRACEOUT(("Node was not found anywhere. Return NULL."));
        return NULL;
        }
    // Leave if some other error occurred.
    User::LeaveIfError( trapError );

    DLTRACEOUT(("Node found from db"));
    return node;
    }


CNcdNode* CNcdNodeManager::NodePtrL( const CNcdNodeIdentifier& aParentNodeIdentifier,
                                     const CNcdNodeIdentifier& aNodeMetaDataIdentifier )
    {
    DLTRACEIN((""));
    
    CNcdNodeIdentifier* identifier =
        NcdNodeIdentifierEditor::CreateNodeIdentifierLC( aParentNodeIdentifier, 
                                                         aNodeMetaDataIdentifier );
    CNcdNode* node = NodePtrL( *identifier );

    CleanupStack::PopAndDestroy( identifier );

    DLTRACEOUT((""));

    return node;
    }


CNcdNodeFolder& CNcdNodeManager::FolderL( const CNcdNodeIdentifier& aIdentifier )
    {
    DLTRACEIN(( _L("Node id: %S, %S, %d"), 
                &aIdentifier.NodeNameSpace(),
                &aIdentifier.NodeId(),
                aIdentifier.ClientUid().iUid ));

    CNcdNode& node( NodeL( aIdentifier ) );
    CNcdNodeFactory::TNcdNodeType nodeType( NodeFactory().NodeTypeL( node ) );
    if ( nodeType != CNcdNodeFactory::ENcdNodeFolder &&
        nodeType != CNcdNodeFactory::ENcdNodeSearchBundle
         && nodeType != CNcdNodeFactory::ENcdNodeRoot )
        {
        // The node was not of the right type
        DLERROR(("Wrong type of node: %d. Should be folder: %d or %d or %d",
                 nodeType, 
                 CNcdNodeFactory::ENcdNodeFolder,
                 CNcdNodeFactory::ENcdNodeSearchBundle,
                 CNcdNodeFactory::ENcdNodeRoot));

        // For debug purposes
        DASSERT( EFalse );
                
        User::Leave( KErrArgument );
        }

    DLTRACEOUT((""));

    return static_cast<CNcdNodeFolder&>( node );
    }


CNcdRootNode& CNcdNodeManager::RootNodeL( const TUid& aClientUid )
    {
    DLTRACEIN((""));

    // All the search roots have the same namespace, but their ids are
    // the client UIDs.
    CNcdNodeIdentifier* identifier = 
        NcdNodeIdentifierEditor::CreateRootIdentifierForClientLC( aClientUid );

    CNcdNode& node( NodeL( *identifier ) );

    CleanupStack::PopAndDestroy( identifier );

    // Check the type to make sure that the casting is ok in the end.
    CNcdNodeFactory::TNcdNodeType nodeType( NodeFactory().NodeTypeL( node ) );
    if ( nodeType != CNcdNodeFactory::ENcdNodeRoot )
        {
        // The node was not right
        DLERROR(("Wrong node type. Should be root"));

        // For debug purposes
        DASSERT( EFalse );
                
        User::Leave( KErrArgument );
        }

    DLTRACEOUT((""));

    return static_cast<CNcdRootNode&>( node );            
    }


CNcdSearchNodeFolder& CNcdNodeManager::SearchFolderL( const CNcdNodeIdentifier& aParentNodeIdentifier,
                                                      const CNcdNodeIdentifier& aNodeMetaDataIdentifier )
    {
    DLTRACEIN((""));

    CNcdNode& node( NodeL( aParentNodeIdentifier, aNodeMetaDataIdentifier ) );
    
    // Check the type and purpose to make sure that the casting is ok in the end.
    CNcdNodeFactory::TNcdNodeType nodeType( NodeFactory().NodeTypeL( node ) );
    CNcdNodeFactory::TNcdNodePurpose nodePurpose( NodeFactory().NodePurposeL( node ) );
    if ( nodeType != CNcdNodeFactory::ENcdNodeFolder
         && nodeType != CNcdNodeFactory::ENcdNodeRoot
         || nodePurpose != CNcdNodeFactory::ENcdSearchNode )
        {
        // The node was not right
        DLERROR(("Wrong node class."));

        DASSERT( EFalse );

        // The node was not of the right type
        User::Leave( KErrArgument );
        }

    DLTRACEOUT((""));

    return static_cast<CNcdSearchNodeFolder&>( node );
    }


CNcdSearchNodeFolder& CNcdNodeManager::SearchFolderL( const CNcdNodeIdentifier& aNodeIdentifier )
    {
    DLTRACEIN((""));

    CNcdNode& node( NodeL( aNodeIdentifier ));
    
    // Check the type and purpose to make sure that the casting is ok in the end.
    CNcdNodeFactory::TNcdNodeType nodeType( NodeFactory().NodeTypeL( node ) );
    CNcdNodeFactory::TNcdNodePurpose nodePurpose( NodeFactory().NodePurposeL( node ) );
    if ( nodeType != CNcdNodeFactory::ENcdNodeFolder
         && nodeType != CNcdNodeFactory::ENcdNodeRoot
         && nodeType != CNcdNodeFactory::ENcdNodeSearchBundle
         || nodePurpose != CNcdNodeFactory::ENcdSearchNode )
        {
        // The node was not right
        DLERROR(("Wrong node class."));

        DASSERT( EFalse );

        // The node was not of the right type
        User::Leave( KErrArgument );
        }

    DLTRACEOUT((""));

    return static_cast<CNcdSearchNodeFolder&>( node );
    }
    
    
CNcdSearchNodeItem& CNcdNodeManager::SearchNodeItemL( const CNcdNodeIdentifier& aNodeIdentifier )
    {
    DLTRACEIN((""));

    CNcdNode& node( NodeL( aNodeIdentifier ));
    
    if ( node.ClassId() != NcdNodeClassIds::ENcdSearchItemNodeClassId )
        {
        // The node was not right
        DLERROR(("Wrong node class."));

        DASSERT( EFalse );

        // The node was not of the right type
        User::Leave( KErrArgument );
        }

    DLTRACEOUT((""));

    return static_cast<CNcdSearchNodeItem&>( node );
    }


CNcdNode& CNcdNodeManager::CreateNodeL( CNcdNodeFactory::TNcdNodeType aNodeType,
                                        CNcdNodeFactory::TNcdNodePurpose aNodePurpose,
                                        const CNcdNodeIdentifier& aNodeIdentifier )
    {
    DLTRACEIN(("Nodetype: %d, nodepurpose: %d", aNodeType, aNodePurpose));
    DLINFO((_L("Identifier ns: %S, id: %S"), 
            &aNodeIdentifier.NodeNameSpace(),
            &aNodeIdentifier.NodeId()));

    // The node identifier can not be empty
    DASSERT( !aNodeIdentifier.ContainsEmptyFields() );

    // Get the class id of the node that has the given purpose.
    // The data type parameter informs if an item or a folder should be created.
    // Set the class id to be some item as a default.
    NcdNodeClassIds::TNcdNodeClassId classId =
            NodeFactory().NodeClassIdL( aNodeType, aNodePurpose );

    DLINFO(("classid of node: %d", classId));

    CNcdNode* node( NodePtrL( aNodeIdentifier ) );
       
    if ( node == NULL )
        {
        DLINFO(("Create node"));
        // Create the node according to the class id
        node = NodeFactory().CreateNodeLC( aNodeIdentifier,
                                           classId );

        DASSERT( node );
        
        // and insert node to the cache.
        AppendNodeToCacheL( node );
        
        // cache takes ownership of the node
        CleanupStack::Pop( node );
        
        // NodePtr inserts the metadata if node was found there.
        // There is no reason to insert the metadata here, because the
        // node has been just created, and it does not contains the
        // link data where the metadata id info is inserted.
        }
    else if ( NodeFactory().NodeTypeL( *node ) != aNodeType
              || NodeFactory().NodePurposeL( *node  ) != aNodePurpose )
        {
        // Node was already in the list. But, the node is not
        // of the expected type or purpose.
        // Notice, that if the requested node purpose was scheme node and the
        // node that already exists is of the different purpose, then we use the
        // original node. So, schemes do not require this checking.
        DLINFO(("classid of real node: %d", node->ClassId() ));
        DLERROR(("Wrong node type or purpose."));
        DLERROR(("Type: %d, should be: %d",
                 NodeFactory().NodeTypeL( *node ), 
                 aNodeType));
        DLERROR(("Purpose: %d, should be: %d",
                 NodeFactory().NodePurposeL( *node ), 
                 aNodePurpose));
        DASSERT( EFalse );
        User::Leave( KErrArgument );
        }

    DLTRACEOUT((""));

    return *node;     
    }


CNcdNode& CNcdNodeManager::CreateNodeL( CNcdNodeFactory::TNcdNodeType aNodeType,
                                        CNcdNodeFactory::TNcdNodePurpose aNodePurpose,
                                        const CNcdNodeIdentifier& aParentNodeIdentifier,
                                        const CNcdNodeIdentifier& aMetaDataIdentifier )
    {
    DLTRACEIN((""));

    // Check the metadata identifier because it has to contain values. These values
    // are used when the node is created. The parent node may contain empty values.
    // because the root does not have parent.
    if ( aMetaDataIdentifier.ContainsEmptyFields() )
        {
        DLERROR(("Cannot create node with empty identifier meta values"));
        
        // For debug purposes
        DASSERT( EFalse );
        
        User::Leave( KErrArgument );
        }

    // Try to find the node before creating it.
    // Use the correct form of identifier by asking it from the factory
    CNcdNodeIdentifier* identifier =
        NcdNodeIdentifierEditor::CreateNodeIdentifierLC( aParentNodeIdentifier, 
                                                         aMetaDataIdentifier );
    CNcdNode& node( CreateNodeL( aNodeType, aNodePurpose, *identifier ) );
    CleanupStack::PopAndDestroy( identifier );

    DLTRACEOUT((""));

    return node; 
    }


CNcdNodeItem& CNcdNodeManager::CreateNodeItemL( CNcdNodeFactory::TNcdNodePurpose aNodePurpose,
                                                const CNcdNodeIdentifier& aNodeIdentifier )
    {
    DLTRACEIN((""));

    CNcdNode& node =
        CreateNodeL( CNcdNodeFactory::ENcdNodeItem, aNodePurpose, 
                     aNodeIdentifier );
    
    // Now it is safe to do casting.

    DLTRACEOUT((""));

    return static_cast<CNcdNodeItem&>(node);    
    }

CNcdNodeItem& CNcdNodeManager::CreateNodeItemL( CNcdNodeFactory::TNcdNodePurpose aNodePurpose,
                                                const CNcdNodeIdentifier& aParentNodeIdentifier,
                                                const CNcdNodeIdentifier& aMetaDataIdentifier )
    {
    DLTRACEIN((""));

    CNcdNode& node =
        CreateNodeL( CNcdNodeFactory::ENcdNodeItem, aNodePurpose, 
                     aParentNodeIdentifier, aMetaDataIdentifier );
    
    // Now it is safe to do casting.

    DLTRACEOUT((""));

    return static_cast<CNcdNodeItem&>(node);   
    }


CNcdNodeFolder& CNcdNodeManager::CreateNodeFolderL( CNcdNodeFactory::TNcdNodePurpose aNodePurpose,
                                                    const CNcdNodeIdentifier& aNodeIdentifier,
                                                    CNcdNodeFactory::TNcdNodeType aFolderType )
    {
    DLTRACEIN((""));

    CNcdNode& node =
        CreateNodeL( aFolderType, aNodePurpose, 
                     aNodeIdentifier );
    
    // Now it is safe to do casting.

    DLTRACEOUT((""));

    return static_cast<CNcdNodeFolder&>(node);    
    }
    
CNcdNodeFolder& CNcdNodeManager::CreateNodeFolderL( CNcdNodeFactory::TNcdNodePurpose aNodePurpose,
                                                    const CNcdNodeIdentifier& aParentNodeIdentifier,
                                                    const CNcdNodeIdentifier& aMetaDataIdentifier,
                                                    CNcdNodeFactory::TNcdNodeType aFolderType )
    {
    DLTRACEIN((""));

    CNcdNode& node =
        CreateNodeL( aFolderType, aNodePurpose, 
                     aParentNodeIdentifier, aMetaDataIdentifier );
    
    // Now it is safe to do casting.

    DLTRACEOUT((""));

    return static_cast<CNcdNodeFolder&>(node);    
    }    


CNcdRootNode& CNcdNodeManager::CreateRootL( const TUid& aClientUid )
    {
    DLTRACEIN((""));
    
    CNcdNodeIdentifier* rootIdentifier( 
        NcdNodeIdentifierEditor::CreateRootIdentifierForClientLC( aClientUid ) );
        
    CNcdNode& node( CreateNodeL( CNcdNodeFactory::ENcdNodeRoot,
                                 CNcdNodeFactory::ENcdNormalNode,
                                 *rootIdentifier ) );

    CleanupStack::PopAndDestroy( rootIdentifier );

    DLTRACEOUT((""));

    return static_cast<CNcdRootNode&>( node );            
    }


CNcdRootNode& CNcdNodeManager::CreateRootL( const MCatalogsContext& aContext )
    {
    DLTRACEIN((""));
    return CreateRootL( aContext.FamilyId() );
    }
    
    
CNcdNodeFolder& CNcdNodeManager::CreateSearchRootL( const MCatalogsContext& aContext )
    {
    DLTRACEIN((""));
    
    CNcdNodeIdentifier* rootIdentifier( 
        NcdNodeIdentifierEditor::CreateSearchRootIdentifierForClientLC( aContext.FamilyId() ) );
        
    CNcdNode& node( CreateNodeL( CNcdNodeFactory::ENcdNodeRoot,
                                 CNcdNodeFactory::ENcdSearchNode,
                                 *rootIdentifier ) );

    CleanupStack::PopAndDestroy( rootIdentifier );

    DLTRACEOUT((""));

    return static_cast<CNcdNodeFolder&>( node );
    }


CNcdNodeFolder& CNcdNodeManager::CreateBundleFolderL( const CNcdNodeIdentifier& aMetaDataIdentifier )
    {
    DLTRACEIN((""));
    // The parent of the bundle is always the root folder
    CNcdNodeIdentifier* rootIdentifier = 
        NcdNodeIdentifierEditor::CreateRootIdentifierForClientLC( aMetaDataIdentifier.ClientUid() );

    CNcdNodeFolder& folder =
        CreateNodeFolderL( CNcdNodeFactory::ENcdBundleNode, 
                           *rootIdentifier, 
                           aMetaDataIdentifier );
                           
    CleanupStack::PopAndDestroy( rootIdentifier );

    DLTRACEOUT((""));
    
    return folder;
    }


CNcdNode& CNcdNodeManager::CreateTemporaryNodeOrSupplierL( const CNcdNodeIdentifier& aMetaDataIdentifier )
    {
    DLTRACEIN((""));

    CNcdNode* node( NULL );

    // First check if the given temporary node already exists in RAM or in databse.
    // We can use NodeL also for this operation.
    CNcdNodeIdentifier* tempNodeIdentifier =
        NcdNodeIdentifierEditor::CreateTemporaryNodeIdentifierLC( aMetaDataIdentifier );
        
    // Note that if the node is found it is also inserted into the node cache
    // but it is not saved into the database.
    TRAP_IGNORE( node = &NodeL( *tempNodeIdentifier ) );

    // If the temporary node did not exist. Then create supplier, which also checks
    // if the corresponding node already exists in RAM or in database.
    // CreateNodeL can handle this.
    // If it does not exist then there is still possibility that the corresponding
    // metadata exists. If metadata exists, then it can be used to create the correct
    // temporary node
    if ( node == NULL )
        {
        // First check the metadata.
        // The ownership of the meta will be in cache.
        CNcdNodeMetaData* meta( NULL );
        TRAP_IGNORE( meta = &NodeMetaDataL( aMetaDataIdentifier ) );
        
        if ( meta != NULL )
            {
            DLINFO(("Meta was found for the temp node"));
            
            // Metadata was found. So, create right type temporary node.
            if ( meta->ClassId() 
                    == NcdNodeClassIds::ENcdFolderNodeMetaDataClassId )
                {
                DLINFO(("Create temp folder"));
                node = &CreateNodeFolderL( CNcdNodeFactory::ENcdNormalNode,
                                           *tempNodeIdentifier );
                }
            else if ( meta->ClassId() 
                        == NcdNodeClassIds::ENcdItemNodeMetaDataClassId )
                {
                DLINFO(("Create temp item"));
                node = &CreateNodeItemL( CNcdNodeFactory::ENcdNormalNode,
                                         *tempNodeIdentifier );                
                }
            else
                {
                DLINFO(("Unknown meta type"));
                DASSERT( EFalse );
                User::Leave( KErrUnknown );
                }
            
            DLINFO(("Setting node metadata"))
            node->SetNodeMetaDataL( *meta );
            }
        else
            {
            DLINFO(("Create node supplier"));
            node = &CreateNodeL( CNcdNodeFactory::ENcdNodeSupplier,
                                 CNcdNodeFactory::ENcdNormalNode,
                                 *tempNodeIdentifier );
            }
        }
    // Notice that if the node creation worked correctly, it will
    // be added to the cache list. So the ownership is in cache, so
    // no need to keep the node in cleanup stack.
    
    // Now that we have the node, make sure that all the necessary information
    // is inserted to its link. This includes metadata identifier and the
    // server uri.
    CNcdNodeLink& link( node->CreateAndSetLinkL() );
    link.SetMetaDataIdentifierL( aMetaDataIdentifier );
    link.SetServerUriL( aMetaDataIdentifier.ServerUri() );
    
    CleanupStack::PopAndDestroy( tempNodeIdentifier );

    // Now that everything is ok, save the node into the db.
    DbSaveNodeL( *node );
    
    DLTRACEOUT((""));
    
    return *node;
    }
    

CNcdNode* CNcdNodeManager::CreateTemporaryNodeIfMetadataExistsL(
    const CNcdNodeIdentifier& aMetadataIdentifier ) 
    {
    DLTRACEIN((""));
    
    // Check if the metadata exists.
    CNcdNodeMetaData* metadata( NULL );
    TRAPD( err, metadata = &NodeMetaDataL( aMetadataIdentifier ) );
    if ( err == KErrNotFound ) 
        {
        // Metadata not found, cannot do anything.
        return NULL;
        }
    
    User::LeaveIfError( err );
    DASSERT( metadata );
    
    // Metadata exists, create temporary node of correct type.   

    // First check if the given temporary node already exists in RAM or in databse.
    // We can use NodeL also for this operation.
    CNcdNodeIdentifier* tempNodeIdentifier =
        NcdNodeIdentifierEditor::CreateTemporaryNodeIdentifierLC( aMetadataIdentifier );

    CNcdNode* node( NULL );
    TRAP( err, node = &NodeL( *tempNodeIdentifier ) );
    LeaveIfNotErrorL( err, KErrNotFound );
    
    if ( node == NULL )
        {
        // If the temporary node did not exist. Create one according to the type of
        // the metadata.            
        if ( metadata->ClassId() == NcdNodeClassIds::ENcdFolderNodeMetaDataClassId )
            {
            DLINFO(("Create temp folder"));
            node = &CreateNodeFolderL( CNcdNodeFactory::ENcdNormalNode,
                                       *tempNodeIdentifier );
            }
        else if ( metadata->ClassId() 
                    == NcdNodeClassIds::ENcdItemNodeMetaDataClassId )
            {
            DLINFO(("Create temp item"));
            node = &CreateNodeItemL( CNcdNodeFactory::ENcdNormalNode,
                                     *tempNodeIdentifier );                
            }
        else
            {
            DLINFO(("Unknown meta type"));
            DASSERT( EFalse );
            User::Leave( KErrUnknown );
            }
        
        DLINFO(("Setting node metadata"))
        node->SetNodeMetaDataL( *metadata );
        }
    else
        {
        // Temporary node exists. Check that it has the metadata.
        CNcdNodeMetaData* existingMeta = node->NodeMetaData();
        if ( existingMeta )
            {
            DASSERT( existingMeta == metadata );
            }
        else 
            {
            // Temporary node did not have metadata, install it.
            node->SetNodeMetaDataL( *metadata );
            }
        }
        
    DASSERT( node );
    DASSERT( node->NodeMetaData() );
    
    // Notice that if the node creation worked correctly, it will
    // be added to the cache list. So the ownership is in cache, so
    // no need to keep the node in cleanup stack.
    
    // Now that we have the node, make sure that all the necessary information
    // is inserted to its link. This includes metadata identifier and the
    // server uri.
    CNcdNodeLink& link( node->CreateAndSetLinkL() );
    link.SetMetaDataIdentifierL( aMetadataIdentifier );
    link.SetServerUriL( aMetadataIdentifier.ServerUri() );
    
    CleanupStack::PopAndDestroy( tempNodeIdentifier );

    // Now that everything is ok, save the node into the db.
    DbSaveNodeL( *node );
    
    DLTRACEOUT((""));
    
    return node;
    }
    
    
CNcdSearchNodeBundle& CNcdNodeManager::CreateSearchBundleL( const CNcdNodeIdentifier& aMetaDataIdentifier,
    const CNcdNodeIdentifier& aParentIdentifier )
    {
    DLTRACEIN((""));
    

    CNcdNodeFolder& folder =
        CreateNodeFolderL( CNcdNodeFactory::ENcdSearchNode, 
                           aParentIdentifier, 
                           aMetaDataIdentifier,
                           CNcdNodeFactory::ENcdNodeSearchBundle );
                           
    // Now it is safe to do casting.

    DLTRACEOUT((""));

    return static_cast<CNcdSearchNodeBundle&>(folder);
    }

CNcdSearchNodeFolder& CNcdNodeManager::CreateSearchFolderL( const CNcdNodeIdentifier& aMetaDataIdentifier,
    const CNcdNodeIdentifier& aParentIdentifier )
    {
    DLTRACEIN((""));
    

    CNcdNodeFolder& folder =
        CreateNodeFolderL( CNcdNodeFactory::ENcdSearchNode, 
                           aParentIdentifier, 
                           aMetaDataIdentifier,
                           CNcdNodeFactory::ENcdNodeFolder );
                           
    // Now it is safe to do casting.

    DLTRACEOUT((""));

    return static_cast<CNcdSearchNodeBundle&>(folder);
    }


CNcdNodeMetaData& CNcdNodeManager::NodeMetaDataL(  
    const CNcdNodeIdentifier& aIdentifier )
    {
    DLTRACEIN(( _L("Metadata id: %S, %S, %d"), 
                &aIdentifier.NodeNameSpace(),
                &aIdentifier.NodeId(),
                aIdentifier.ClientUid().iUid ));

    CNcdNodeMetaData* metaData( 
        FindNodeMetaDataFromCache( aIdentifier ) );

    if ( metaData == NULL )
        {
        // Check if the node can be found from the db because it was not found
        // from the cache.
        // This function leaves with KErrNotFound if the data was not found from
        // the database. If this is the case then the data should be loaded from
        // internet. But it is not job of this function. Let it leave.
        metaData = &DbMetaDataL( aIdentifier );        
        }

    DLTRACEOUT((""));
    
    return *metaData;
    }


CNcdNodeMetaData& CNcdNodeManager::CreateNodeMetaDataL(
    const CNcdNodeIdentifier& aMetaDataIdentifier,
    CNcdNodeFactory::TNcdNodeType aMetaType ) 
    {
    
    DLTRACEIN(( _L("Metadata id: %S, %S, %d"), 
                &aMetaDataIdentifier.NodeNameSpace(),
                &aMetaDataIdentifier.NodeId(),
                aMetaDataIdentifier.ClientUid().iUid ));
    
    CNcdNodeMetaData* metaData( NULL );
    TRAPD( err, metaData = &NodeMetaDataL( aMetaDataIdentifier ) );
    if ( err != KErrNone ) 
        {
        if ( err == KErrNotFound ) 
            {
            metaData = NodeFactory().CreateMetaDataLC( aMetaDataIdentifier, aMetaType );
            iNodeMetaDataCache.AppendL( metaData );
            // Cache takes ownership of the metadata
            CleanupStack::Pop( metaData );
            }
        else 
            {
            User::Leave( err );
            }
        }
    
    return *metaData;
    }



// ---------------------------------------------------------------------------
// Functions that are used to insert data information
// gotten from the data parsers into the correct node
// objects. These functions are provided for operations.
// The updated information may also be inserted from the 
// purchase history.
// ---------------------------------------------------------------------------

CNcdNode& CNcdNodeManager::RefHandlerL( 
    const CNcdNodeIdentifier& aParentNodeIdentifier,
    MNcdPreminetProtocolEntityRef& aData,
    const TUid& aClientUid,
    TNcdRefHandleMode aMode,
    TInt aIndex,
    CNcdNodeFactory::TNcdNodeType aParentNodeType,
    CNcdNodeFactory::TNcdNodePurpose aParentNodePurpose,
    CNcdNodeFactory::TNcdNodePurpose aNodePurpose,
    TBool aCreateParent )
    {
    DLTRACEIN(("aData: %X, nodepurpose: %d, aCreateParent: %d", &aData, aNodePurpose, aCreateParent));
    
    DLINFO((_L("parent node ns: %S, id: %S"), 

            &aParentNodeIdentifier.NodeNameSpace(), &aParentNodeIdentifier.NodeId()));
    DLINFO((_L("parent node from entity ns: %S, id: %S"), 
            &aData.ParentNamespace(), &aData.ParentId()));

    DLINFO((_L("data ns: %S, id: %S"), 
            &aData.Namespace(), &aData.Id()));
            
    DLINFO((_L("insert index: %d, mode: %d"), aIndex, aMode ));
    DPROFILING_BEGIN( x );

    // Get the type of the node -- folder or item.
    CNcdNodeFactory::TNcdNodeType nodeType = CNcdNodeFactory::ENcdNodeItem;
    if ( aData.Type() == MNcdPreminetProtocolEntityRef::EFolderRef )
        {
        nodeType = CNcdNodeFactory::ENcdNodeFolder;
        }
    else if ( aData.Type() != MNcdPreminetProtocolEntityRef::EItemRef )
        {                
        // Wrong type
        DLINFO(( "Wrong node data type: %d", aData.Type() ));
        DASSERT( EFalse );
        User::Leave( KErrArgument );
        }   

    // Notice that the aClientUid is used here instead of the parent identifier uid,
    // because the parent identifier may be empty in some cases.
    CNcdNodeIdentifier* metaIdentifier = 
        CNcdNodeIdentifier::NewLC( aData.Namespace(), 
                                   aData.Id(),
                                   aData.ServerUri(), 
                                   aClientUid );

        
    // Check if the node already exists in RAM cache or in the db.
    // Also, check the node type. This function replaces old node
    // in RAM cache (not in db) if the node type and purpose do
    // not match with the ones gotten from the parser.
    CNcdNode& node( CheckAndCreateNodeL( nodeType, 
                                         aNodePurpose,
                                         aParentNodeIdentifier,
                                         *metaIdentifier ) );



    if ( aCreateParent ||
         !aParentNodeIdentifier.ContainsEmptyFields() &&
         NodePtrL( aParentNodeIdentifier ) ) 
        {        
        // Insert the node to the parent if it does not already exist there.
        // Note that here we insert it into the actual parent. Not into the parent
        // that is set for the proxy parent.
        // Note that the node link is internalized after the node has been inserted into the
        // parent. This way, the link will finally contain the correct info, for example
        // the correct parent info.
        // Make sure that the parent node type is correct in case root is used
        AddToParentL( aParentNodeIdentifier, 
                      *metaIdentifier, 
                      aParentNodeType, 
                      aParentNodePurpose, 
                      aNodePurpose, 
                      aMode, 
                      aIndex );
        DLINFO(("AddToParentL done, aData: %X", &aData));
        }

    // Metaid is not needed anymore. So, delete it.
    CleanupStack::PopAndDestroy( metaIdentifier );
    metaIdentifier = NULL;

    // Internalize data to the link of the node
    DLINFO(("Internalize link data"));
    
    // Note that the parent identifier should be checked if the parent is transparent in
    // other words if this node should be child of transparent.
    // Then the actual parent for the proxy transmission should be set to be the
    // grand parent node.
    if ( aNodePurpose == CNcdNodeFactory::ENcdChildOfTransparentNode )
        {
        DLINFO(("Transparent child. Get parent of parent"));
        // Parse the correct grandparent identifier because the proxy side will get
        // the grandparent identifier for the parent identifier info in case of transparent
        // nodes.
        CNcdNodeIdentifier* grandParentIdentifier( 
            NcdNodeIdentifierEditor::ParentOfLC( aParentNodeIdentifier ) );
        node.InternalizeLinkL( 
            aData, aParentNodeIdentifier, *grandParentIdentifier, aClientUid );
        CleanupStack::PopAndDestroy( grandParentIdentifier );
        }
    else 
        {
        node.InternalizeLinkL( 
            aData, aParentNodeIdentifier, aParentNodeIdentifier, aClientUid );
        }

    // Metadata of the node may exists in cache at this point already, but it is not
    // installed to the node, because otherwise the state of the node would be
    // "initialized" and UI would not reload the metadata. This is a problem if
    // user wants to force refresh level3 view.
    
    // Save the node now after it has been inserted into its parent.
    // Because the node has been updated and contains at least new
    // link data, we should save the new data to the database.
    DLINFO(("Save node"));

    DbSaveNodeL( node );
    DPROFILING_END( x );
    DLTRACEOUT((""));
    
    return node;
    }


CNcdNode& CNcdNodeManager::DataHandlerL( 
    const CNcdNodeIdentifier& aParentNodeIdentifier,
    MNcdPreminetProtocolDataEntity& aData,
    const TUid& aClientUid )
    {
    DLTRACEIN((_L("parent ns: %S, id: %S, data ns: %S, id: %S, name: %S"), 
                &aParentNodeIdentifier.NodeNameSpace(), &aParentNodeIdentifier.NodeId(), 
                &aData.Namespace(), &aData.Id(),&aData.Name()));
    DPROFILING_BEGIN( x );
    // Create the metadata according to the entity information
    
    // Notice:
    // The data entity namespace information is conditional accroding to the protocol. 
    // But the parser should always set the correct namespace. The parent node namespace
    // can not be used here because the parent may be in different namespace than
    // the child. At least if the root is used.

    // Notice that if the parent is for example root,
    // then the parent server uri is KNullDesC which is not the right server.
    // Use the serveruri that is gotten from the aData interface object.
    // Also, notice that aClientUid is used here. Because in some special cases,
    // the parent node identifier may be empty.
    CNcdNodeIdentifier* metaId = 
        CNcdNodeIdentifier::NewLC( aData.Namespace(), 
                                   aData.Id(), 
                                   aData.ServerUri(),
                                   aClientUid );

    // The node should always exist in the database if metadata exist.
    CNcdNode& node( NodeL( aParentNodeIdentifier, *metaId ) );


    DLINFO(("Create metadata"));
    CNcdNodeFactory::TNcdNodeType metaType( CNcdNodeFactory::ENcdNodeItem );
    if ( aData.Type() == EFolderEntity )
        {
        metaType = CNcdNodeFactory::ENcdNodeFolder;
        }
    else if ( aData.Type() != EItemEntity )
        {
        DLERROR(("Wrong entity type"));
        DASSERT( EFalse );
        User::Leave( KErrArgument );
        }
    
    // Checks that the old metadata is of the right type if it already
    // exists. If the types do not match, then the old metadata in 
    // RAM cache is replaced by the new metadata object that is of the
    // correct type. Notice, that the db is not updated by this function call.    
    CNcdNodeMetaData& metaData( 
        CheckAndCreateMetaDataL( *metaId, metaType ) );    
    
    CleanupStack::PopAndDestroy( metaId );

    DLINFO(("Internalize meta"));

    // Set all the data from the entity to the metadata itself
    metaData.InternalizeL( aData );

    DLINFO(( "Set meta for the node"));

    // Insert the metadata information to the node. Metadata's timestamp
    // is also updated to link in here
    node.SetNodeMetaDataL( metaData );
    
    DLINFO(("Save meta to db"));        
    
    // Do not save the node here, because it already contains uptodate info
    // or if it was just created, then nothing worth saving.
    // Just save the metadata to the database,
    // because it contains new info.
    DbSaveNodeMetaDataL( metaData );
    DPROFILING_END( x );
    DLTRACEOUT((""));
    
    return node;
    }


void CNcdNodeManager::PreviewHandlerL( const CNcdNodeIdentifier& aNodeIdentifier,
                                       const TDesC& aFileName,
                                       TInt aIndex,
                                       const TDesC& aMimeType )
    {
    DLTRACEIN(("Check old meta from the cache"));
    
    CNcdNode& node( NodeL( aNodeIdentifier ) );    
    CNcdNodeMetaData& metadata( node.NodeMetaDataL() );
    CNcdNodePreview& preview( metadata.PreviewL() );

    iPreviewManager->AddPreviewL( metadata.Identifier(), 
                                  preview.Uri( aIndex ), 
                                  aFileName,
                                  aMimeType );

    // Updates aMimeType to CNcdNodePreview if the MIME type
    // was not received in protocol responses
    preview.UpdateMimesFromPreviewManagerL();
    
    // Notice that the metadata does not need to be saved after this
    // because the preview manager handles all the necessary info.
    
    DLTRACEOUT((""));
    }


void CNcdNodeManager::PurchaseHandlerL( const CNcdNodeIdentifier& aNodeIdentifier )
    {
    DLTRACEIN(("Update installation information to the node"));
    
    CNcdNode& node( NodeL( aNodeIdentifier ) );
    CNcdNodeMetaData& metadata( node.NodeMetaDataL() );

    CNcdPurchaseDetails* details = metadata.PurchaseDetailsLC();

    // Try to internalize URI content from purchase history
    metadata.InternalizeUriContentL( *details );                    
     
    // Try to internalize node download from purchase history.
    metadata.InternalizeDownloadL( *details );
    
    metadata.InternalizeInstallFromContentInfoL();
    
    // Try to internalize node install from purchase history
    metadata.InternalizeInstallL( *details );    
    
    CleanupStack::PopAndDestroy( details );


    // Notice that the metadata does not need to be saved after this
    // because the purchase history contains all the necessary info.
            
    DLTRACEOUT((""));
    }


void CNcdNodeManager::DownloadDataHandlerL( const CNcdNodeIdentifier& aNodeIdentifier )
    {
    DLTRACEIN((""));

    CNcdNode& node( NodeL( aNodeIdentifier ) );
    CNcdNodeMetaData& metadata( node.NodeMetaDataL() );


    CNcdPurchaseDetails* details = metadata.PurchaseDetailsLC();
        
    metadata.InternalizeDependencyL( *details );
     
    // Try to internalize node download from purchase history.
    metadata.InternalizeDownloadL( *details );
    
    metadata.InternalizeInstallFromContentInfoL();
    
    // Try to internalize node install from purchase history
    metadata.InternalizeInstallL( *details );    
    
    CleanupStack::PopAndDestroy( details );

    // Notice that the metadata does not need to be saved after this
    // because the purchase history contains all the necessary info.
            
    DLTRACEOUT((""));
    }


void CNcdNodeManager::InstallHandlerL( const CNcdNodeIdentifier& aNodeIdentifier )
    {
    DLTRACEIN(("Update installation information to the node"));
    
    CNcdNode& node( NodeL( aNodeIdentifier ) );
    CNcdNodeMetaData& metadata( node.NodeMetaDataL() );
        
    CNcdPurchaseDetails* details = metadata.PurchaseDetailsLC();
        
    metadata.InternalizeDependencyL( *details );
    
    // Try to internalize node download from purchase history.
    metadata.InternalizeDownloadL( *details );
    
    metadata.InternalizeInstallFromContentInfoL();
    
    // Try to internalize node install from purchase history
    metadata.InternalizeInstallL( *details );    
    
    metadata.HandleContentUpgradeL();
    
    CleanupStack::PopAndDestroy( details );

    // Notice that the metadata does not need to be saved after this
    // because the purchase history contains all the necessary info.
            
    DLTRACEOUT((""));
    }


void CNcdNodeManager::SetNodeExpiredL(
    const CNcdNodeIdentifier& aNodeIdentifier,
    TBool aRecursive,
    TBool aForceUpdate,
    RPointerArray<CNcdExpiredNode>& aFoundNodes )
    {
    DLTRACEIN((""));

    CNcdNode* node( NULL );
    TRAPD( err, node = &NodeL( aNodeIdentifier ) );
    if ( err == KErrNotFound )
        {
        // No need to do anything if node was not found.
        return;
        }
    else if ( err != KErrNone )
        {
        User::Leave( err );
        }
        
    SetNodeExpiredL( *node, aRecursive, aForceUpdate, aFoundNodes );
    DLTRACEOUT((""));
    }

void CNcdNodeManager::SetNodeExpiredL(
    CNcdNode& aNode,
    TBool aRecursive,
    TBool aForceUpdate,
    RPointerArray<CNcdExpiredNode>& aFoundNodes )
    {
    DLTRACEIN((""));
    CNcdNodeLink& nodeLink = aNode.CreateAndSetLinkL();
    nodeLink.SetValidUntilDelta( 0 );
    DASSERT( nodeLink.IsExpired() );
    DbSaveNodeL( aNode );
    aFoundNodes.AppendL( CNcdExpiredNode::NewL( aNode.Identifier(), aForceUpdate ) );
    
    if ( aRecursive 
         && ( aNode.ClassId() == NcdNodeClassIds::ENcdFolderNodeClassId
              || aNode.ClassId() == NcdNodeClassIds::ENcdSearchFolderNodeClassId
              || aNode.ClassId() == NcdNodeClassIds::ENcdRootNodeClassId
              || aNode.ClassId() == NcdNodeClassIds::ENcdBundleFolderNodeClassId 
              || aNode.ClassId() == NcdNodeClassIds::ENcdTransparentFolderNodeClassId 
              || aNode.ClassId() == NcdNodeClassIds::ENcdSearchBundleNodeClassId ) ) 
        {
        CNcdNodeFolder& folder = static_cast<CNcdNodeFolder&>( aNode );
        const RPointerArray<CNcdChildEntity>& children = folder.ChildArray();
        for ( TInt i = 0; i < children.Count(); i++ )
            {
            SetNodeExpiredL( children[i]->Identifier(), ETrue, aForceUpdate, aFoundNodes );
            }
        }
    DLTRACEOUT((""));
    }

void CNcdNodeManager::SetNodesExpiredByMetadataL(
    const MNcdPreminetProtocolExpiredCachedData& aData,
    const TUid& aClientUid,
    const TDesC& aNameSpace,
    RPointerArray<CNcdExpiredNode>& aFoundNodes )
    {
    DLTRACEIN((""));
    
    RPointerArray<CNcdNodeIdentifier> identifiers;
    CleanupResetAndDestroyPushL( identifiers );
    CDesCArrayFlat* nameSpaces = new (ELeave) CDesCArrayFlat( KListGranularity );
    CleanupStack::PushL( nameSpaces );
    RArray<NcdNodeClassIds::TNcdNodeClassType> types;
    CleanupClosePushL( types );
    types.AppendL( NcdNodeClassIds::ENcdNode );
    iDbManager->GetAllClientItemIdentifiersL( identifiers, aClientUid,
        *nameSpaces, types );
        
    for ( TInt i = 0 ; i < identifiers.Count() ; i++ )
        {
        for ( TInt j = 0 ; j < aData.ExpiredEntityCount() ; j++ )
            {
            if ( NcdNodeIdentifierEditor::DoesMetaDataIdentifierMatchL(
                *identifiers[i],
                aData.ExpiredEntityL( j ).EntityId(),
                aNameSpace,
                aClientUid ) )
                {
                // need to create a temp identifier because identifiers
                // from db have encoded namespace
                CNcdNodeIdentifier* realIdentifier = CNcdNodeIdentifier::NewLC(
                    aNameSpace,
                    identifiers[i]->NodeId(),
                    identifiers[i]->ClientUid() );
                SetNodeExpiredL( *realIdentifier,
                    aData.ExpiredEntityL( j ).Recursive(),
                    aData.ExpiredEntityL( j ).ForceUpdate(),
                    aFoundNodes );
                CleanupStack::PopAndDestroy( realIdentifier );
                break;
                }
            }
        }
        
    CleanupStack::PopAndDestroy( &types );
    CleanupStack::PopAndDestroy( nameSpaces );
    CleanupStack::PopAndDestroy( &identifiers );
    }


void CNcdNodeManager::RemoveNodeL( const CNcdNodeIdentifier& aParentIdentifier,
                                   const CNcdNodeIdentifier& aNodeMetaDataIdentifier ) 
    {
    DLTRACEIN((""));
    CNcdNodeIdentifier* identifier =
        NcdNodeIdentifierEditor::CreateNodeIdentifierLC( aParentIdentifier,
                                                         aNodeMetaDataIdentifier );
    RemoveNodeL( *identifier );
    CleanupStack::PopAndDestroy( identifier );
    }

void CNcdNodeManager::RemoveNodeL( const CNcdNodeIdentifier& aIdentifier ) 
    {
    DLTRACEIN((""));
    CNcdNode* node = NodePtrL( aIdentifier );
    if ( node == NULL ) 
        {
        DLINFO(("Node did not exist"));
        return;
        }
            
    CNcdNodeLink& nodeLink = node->CreateAndSetLinkL();
    const CNcdNodeIdentifier& parentId = nodeLink.ParentIdentifier();
    
    CNcdNodeFolder* parentFolder = NULL;
    // Parent node doesn't necessarily exists, eg. "floating" favorite nodes
    // If the parent node identifier is empty, then FolderL will leave
    // with KErrArgument
    TRAPD( err, parentFolder = &FolderL( parentId ) );
    LeaveIfNotErrorL( err, KErrNotFound, KErrArgument );
    
    NodeCacheCleanerManager().
        CacheCleanerL( 
            aIdentifier.ClientUid() ).
                AddCleanupIdentifierL( aIdentifier );
    
    if ( parentFolder ) 
        {
        DLTRACE(("Removing from parent"));
        // NOTE: After next function aIdentifier may not be valid anymore as
        // node owning it has been deleted.
        parentFolder->RemoveChild( aIdentifier );
        DbSaveNodeL( *parentFolder );
        }
    }
    

void CNcdNodeManager::ClearSearchResultsL( const MCatalogsContext& aContext )
    {
    DLTRACEIN((""));
    
    CNcdNodeFolder& searchRoot = CreateSearchRootL( aContext );
    
    // Maybe this should be recursive?
    const RPointerArray<CNcdChildEntity>& children = searchRoot.ChildArray();
    for (TInt i = 0 ; i < children.Count() ; i++ )
        {
        const CNcdNodeIdentifier& child = children[i]->Identifier();
        CNcdNodeFolder* childFolder = NULL;
        TRAPD(err, childFolder = &FolderL( child ) );
        if( err == KErrNone )
            {
            childFolder->RemoveChildrenL();            
            }
        }
    searchRoot.RemoveChildrenL();  
         
    }
    
    
void CNcdNodeManager::BackupAndClearCacheL(
    const CNcdNodeIdentifier& aRootNode )
    {
    DLTRACEIN((_L("aRootNode ns: %S, id: %S"), &aRootNode.NodeNameSpace(), &aRootNode.NodeId()));

    CNcdNode* root = NodePtrL( aRootNode );
   
    // Remove children from the list of root node.
    if ( root ) 
        {
        CNcdNodeFolder* rootFolder = static_cast<CNcdNodeFolder*>( root );
        // Note that RemoveChildren() doesn't remove nodes from the database like
        // RemoveChildrenL()        
        rootFolder->RemoveChildren();
        }    
    
    
    // Move all the children of the root node ( recursively ).
    for ( TInt i = iNodeCache.Count() - 1; i >= 0; i-- ) 
        {
        CNcdNode* node = iNodeCache[i];
        if ( NcdNodeIdentifierEditor::ParentOf( aRootNode, node->Identifier() ) ) 
            {

            if ( CNcdNodeFactory::NodeTypeL( *node ) == CNcdNodeFactory::ENcdNodeFolder )
                {
                DLTRACE(("Clearing child list"));
                
                CNcdNodeFolder* folder = static_cast<CNcdNodeFolder*>( node );
                // Do not use RemoveChildrenL method, since it saves the parent node to
                // db --> original child list is lost, reverting is impossible
                folder->RemoveChildren();
                }
                
            User::LeaveIfError( InsertNodeInOrder( node, iTempNodeCache ) );
            DLINFO((_L("Moved node: %S, %S"), &node->Identifier().NodeNameSpace(), &node->Identifier().NodeId() ));                
            iNodeCache.Remove( i );
            }
        }          
        
    }

    
void CNcdNodeManager::RevertNodeCacheL(
    const CNcdNodeIdentifier& aRootNode ) 
    {
    DLTRACEIN((""));
    
    RPointerArray<CNcdNode> schemeNodes;
    
    // Remove all the children of the given root from the main cache.
    for ( TInt i = iNodeCache.Count() - 1; i >= 0; i-- ) 
        {
        CNcdNode* node = iNodeCache[i];
        if ( NcdNodeIdentifierEditor::ParentOf( aRootNode, node->Identifier() ) ) 
            {
            node->Close();
            iNodeCache.Remove( i );
            }
        }
        
    // Move the nodes from temp cache to main RAM cache
    for ( TInt i = iTempNodeCache.Count() - 1; i >= 0; i-- ) 
        {
        CNcdNode* node = iTempNodeCache[i];
        if ( NcdNodeIdentifierEditor::ParentOf( aRootNode, node->Identifier() ) ) 
            {
            User::LeaveIfError( InsertNodeInOrder( node, iNodeCache ) );
            iTempNodeCache.Remove( i );
            }
        }
        
    // Internalize the root and its children from database to get correct child lists.
    for ( TInt i = 0; i < iNodeCache.Count(); i++ ) 
        {
        CNcdNode* node = iNodeCache[ i ];
        if ( NcdNodeIdentifierEditor::ParentOf( aRootNode, node->Identifier() ) ||
             node->Identifier().Equals( aRootNode ) ) 
            {
            HBufC8* data( 
                NodeDbManager().ReadDataFromDatabaseLC(
                    node->Identifier(), NcdNodeClassIds::ENcdNode ) );
            if ( data != NULL && data->Length() > 0 ) 
                {
                NodeFactory().InternalizeNodeL( *node, *data );
                }
            else 
                {
                // Data was not in database, clear the child list.
                CNcdNodeFactory::TNcdNodeType nodeType = CNcdNodeFactory::NodeTypeL( *node );
                if ( nodeType == CNcdNodeFactory::ENcdNodeFolder || 
                     nodeType == CNcdNodeFactory::ENcdNodeRoot ) 
                    {
                    CNcdNodeFolder* folder = static_cast<CNcdNodeFolder*>( node );
                    folder->RemoveChildrenL();
                    }                                
                }
            CleanupStack::PopAndDestroy( data );
            }
        }
    }        

void CNcdNodeManager::RevertNodeFromTempCacheL( const CNcdNodeIdentifier& aNodeIdentifier ) 
    {
    DLTRACEIN((""));
    // Find the node from temp cache
    TInt index = FindNodeFromArray( aNodeIdentifier, iTempNodeCache );
    
    if ( index != KErrNotFound ) 
        {
        CNcdNode* nodeFromMainCache = FindNodeFromMainCache( aNodeIdentifier );
        if ( nodeFromMainCache ) 
            {
            DASSERT( nodeFromMainCache == iTempNodeCache[ index ] );
            DLINFO(("No need to revert, the node was in main cache already"));            
            iTempNodeCache[ index ]->Close();
            }
        else 
            {            
            User::LeaveIfError( InsertNodeInOrder( iTempNodeCache[ index ], iNodeCache ) );            
            }
            
        iTempNodeCache.Remove( index );
        }
    }
    
void CNcdNodeManager::ClearTempCacheL( const CNcdNodeIdentifier& aRootNode ) 
    {
    DLTRACEIN((""));
    for ( TInt i = iTempNodeCache.Count() - 1; i >= 0; i-- ) 
        {
        CNcdNode* node = iTempNodeCache[i];
        if ( NcdNodeIdentifierEditor::ParentOf( aRootNode, node->Identifier() ) ) 
            {
            
            // Also remove from db if not present in main cache.
            if ( FindNodeFromMainCache( node->Identifier() ) == NULL ) 
                {
                RemoveNodeL( node->Identifier() );
                }
                
            iTempNodeCache.Remove( i );
            node->Close();
            }
        }
    }

// ---------------------------------------------------------------------------
// Cache cleanup functions.
// These functions are called by the node objects when
// they are released by the corresponding proxy object.
// ---------------------------------------------------------------------------

void CNcdNodeManager::NodeReleased( CNcdNode& aNode )
    {
    if ( aNode.AccessCount() == 1 )
        {
        // We need to release the node immediately if its metadata needs to be
        // released as soon as possible
        CNcdNodeMetaData* metadata = aNode.NodeMetaData();
        if ( metadata && 
             metadata->DeleteSoon() )
            {
            DLTRACE(( _L("Remove node from cache: %S"), 
                &aNode.Identifier().NodeId() ));
            
            if ( iClientDatabaseLocks.Find(
                aNode.Identifier().ClientUid().iUid ) == KErrNotFound )
                {                
                // Because this node will not be in the cache anymore, we can remove
                // it from the block list of the cleaner if it exists there. 
                TRAP_IGNORE(
                    NodeCacheCleanerManager().
                        CacheCleanerL( aNode.Identifier().ClientUid() ).
                            RemoveDoNotRemoveIdentifierL( aNode.Identifier() ) );        
                }
                
            TInt index = iNodeCache.Find( &aNode );
            if ( index != KErrNotFound ) 
                {                
                // Remove unreferenced node from the cache and destroy it.
                iNodeCache.Remove( index );
                }
            
            aNode.Close();
            
            // Ensure that metadata is released if possible
            MetaDataReleased( *metadata );
            }
            
        // Node is ready for closing.
        NodeCacheCleanup();
        }
    }
    
    
void CNcdNodeManager::MetaDataReleased( CNcdNodeMetaData& aMetaData )
    {
    DLTRACEIN((""));
    if ( aMetaData.AccessCount() == 1 )
        {
        if ( aMetaData.DeleteSoon() && !IsMetadataUsed( &aMetaData ) ) 
            {            
            DLTRACE(("Metadata wants to be released as soon as possible"));
            TInt index = iNodeMetaDataCache.Find( &aMetaData );
            DASSERT( index != KErrNotFound );
            aMetaData.Close();            

            iNodeMetaDataCache.Remove( index );            
            }
            
        // Metadata is ready for closing.
        MetaDataCacheCleanup();
        }
    }


void CNcdNodeManager::ClearClientCacheL( 
    const MCatalogsContext& aContext,
    TBool aClearDownloads )
    {    
    DLTRACEIN(("Clearing previews"));        
    
    TRAP_IGNORE( iPreviewManager->RemoveAllPreviewsL() );
        
    DLTRACE(("Close cached objects"));
    FullCacheCleanup();
        
    DLTRACE(("Clearing nodes and icons"));
    
    // Clear namespaces that contain favorites so that favorites are
    // left intact. Also checks that favorite nodes can be loaded from db
    TRAPD( err, ClearNamespacesWithFavoritesL( aContext.FamilyId() ) );
            
    // Now clear rest of the namespaces (except subscriptions)
    // Favorites are deleted if they are corrupted
    ClearClientNamespacesL( 
        aContext.FamilyId(), 
        err != KErrNone, 
        aClearDownloads );
    
    // Clear seen info.
    iSeenInfo->ClearInfoL( aContext.FamilyId() );
    
    // Unset previously loaded flag to prevent all children from being interpreted
    // as new on next refresh.
    CreateRootL( aContext.FamilyId() ).SetChildrenPreviouslyLoaded( EFalse );
    
    DLTRACEOUT(("All cleared"));
    }


void CNcdNodeManager::ClearClientNamespacesL( 
    const TUid& aClientUid, 
    TBool aClearFavorites,
    TBool aClearDownloads )
    {
    DLTRACEIN((""));
    // Insert the subscription namespace into the skip array. Because
    // subscriptions should not be deleted from the db.
    CPtrCArray* doNotCleanNameSpaces = 
        new(ELeave) CPtrCArray( KListGranularity );
    CleanupStack::PushL( doNotCleanNameSpaces );
    
    doNotCleanNameSpaces->AppendL( 
        NcdProviderDefines::KSubscriptionNamespace() );
    
    doNotCleanNameSpaces->AppendL(
        NcdProviderDefines::KProviderStorageNamespace() );
    
    if ( !aClearDownloads )
        {
        DLTRACE(("Do not delete downloads"));
        doNotCleanNameSpaces->AppendL( 
            NcdProviderDefines::KDownloadNamespace() );
        
        doNotCleanNameSpaces->AppendL( 
            NcdProviderDefines::KDataNamespace() );
        }
    
    if ( !aClearFavorites ) 
        {        
        // Insert the namespaces from which nodes are in 
        // favorites list to skip list.
        const RPointerArray<CNcdNodeIdentifier>& favorites = 
            iFavoriteManager->FavoriteNodesL( aClientUid );
        TInt favoriteCount = favorites.Count();
        TInt count = 0;
        for ( TInt i = 0; i < favoriteCount; i++ ) 
            {
            const TDesC& nameSpace = favorites[ i ]->NodeNameSpace();
            
            count = doNotCleanNameSpaces->MdcaCount();
            while( count-- ) 
                {
                if ( doNotCleanNameSpaces->MdcaPoint( count ) != nameSpace ) 
                    {
                    doNotCleanNameSpaces->AppendL( nameSpace );
                    break;
                    }
                }
            }        
        }
    else // favorites need to be removed from the favorite manager too
        {        
        iFavoriteManager->RemoveFavoritesL( aClientUid );
        }

    NodeDbManager().ClearClientL( aClientUid, *doNotCleanNameSpaces );
    CleanupStack::PopAndDestroy( doNotCleanNameSpaces );    
    }


void CNcdNodeManager::ClearNamespacesWithFavoritesL( const TUid& aClientUid ) 
    {
    DLTRACEIN((""));
    const RPointerArray<CNcdNodeIdentifier>& favorites( 
        iFavoriteManager->FavoriteNodesL( aClientUid ) );
    
    if ( !favorites.Count() ) 
        {
        DLTRACEOUT(("No favorites so nothing to do"));
        return;
        }
        
    // order by uid & namespace
    TLinearOrder<CNcdNodeIdentifier> sort( 
        CNcdNodeIdentifier::CompareOrderByUid );
    RPointerArray<CNcdNodeIdentifier> sortedArray;
    
    // array won't own the identifiers so just close
    CleanupClosePushL( sortedArray );
    sortedArray.ReserveL( favorites.Count() );
        
    TInt count = favorites.Count();
    DLTRACE(("Sorting %d favorites", count));
    while ( count-- ) 
        {
        sortedArray.InsertInOrderL( favorites[ count ], sort );
        }
    
    count = sortedArray.Count();
    TInt index = 0;
    // iterate through favorites one namespace at a time
    while ( index < count )
        {        
        index = ClearNamespaceL( sortedArray, index );
        }    
    
    CleanupStack::PopAndDestroy( &sortedArray );    
    
    DLTRACEOUT((""));
    }
    

TInt CNcdNodeManager::ClearNamespaceL( 
    const RPointerArray<CNcdNodeIdentifier>& aSortedArray, 
    TInt aIndex )
    {
    DLTRACEIN((""));
    TPtrC currentNamespace( aSortedArray[ aIndex ]->NodeNameSpace() );

    // Array for metadata id's that must not be removed
    CDesCArrayFlat* doNotRemoveMetadataArray = 
        new( ELeave ) CDesCArrayFlat( KListGranularity );
        
    // owns the array
    RNcdDatabaseItems unremovableMetadata(         
        KErrNotFound, // ignores type so all types that match the id are left
        doNotRemoveMetadataArray );
    CleanupClosePushL( unremovableMetadata );

    // Array for node id's that must not be removed
    CPtrCArray* doNotRemoveArray = 
        new( ELeave ) CPtrCArray( KListGranularity );

    // owns the array
    RNcdDatabaseItems unremovableItem( 
        NcdNodeClassIds::ENcdNode,
        doNotRemoveArray );
    
    CleanupClosePushL( unremovableItem );

    // Array for icon id's that must not be removed
    CDesCArrayFlat* doNotRemoveIconArray = 
        new( ELeave ) CDesCArrayFlat( KListGranularity );

    // owns the array
    RNcdDatabaseItems unremovableIcon( 
        NcdNodeClassIds::ENcdIconData,
        doNotRemoveIconArray );
    
    CleanupClosePushL( unremovableIcon );

    TInt pos = 0; // needed for CDesCArray::Find
    TInt err = KErrNone;            
    
    TInt index = aIndex; 
    TInt count = aSortedArray.Count();
    // Generate array of id's that must not be removed from current namespace    
    while( index < count && 
           aSortedArray[ index ]->NodeNameSpace() == currentNamespace )          
        {
        doNotRemoveArray->AppendL( aSortedArray[ index ]->NodeId() );                              

        CNcdNodeIdentifier* metaId = 
            NcdNodeIdentifierEditor::CreateMetaDataIdentifierLC( 
                *aSortedArray[ index ] );
    
        if ( doNotRemoveMetadataArray->Find( 
                metaId->NodeId(), pos, ECmpNormal ) )
            {            
            doNotRemoveMetadataArray->AppendL( metaId->NodeId() );
            
            const CNcdNodeMetaData* metadata( NULL );
            TRAP( err, metadata = &NodeMetaDataL( *metaId ) );
            LeaveIfNotErrorL( err, KErrNotFound );
        
            if ( metadata ) 
                {                                                                       
                // Icons are checked only when new metadata is added
                // IconL leaves only with KErrNotFound
                CNcdNodeIcon* icon( NULL );
                TRAP_IGNORE( icon = &metadata->IconL() );
                
                if ( icon && 
                     doNotRemoveIconArray->Find( 
                        icon->IconId(), pos, ECmpNormal ) ) 
                    {
                    DLTRACE(("Adding icon id to do not remove list"));
                    doNotRemoveIconArray->AppendL( icon->IconId() );
                    }

                // close the opened metadata    
                CloseMetadatas();
                }
            }
        CleanupStack::PopAndDestroy( metaId );          
        ++index;  
        }

    RArray<RNcdDatabaseItems> items;
    CleanupClosePushL( items );
    
    // note: ownership is not transferred
    items.AppendL( unremovableItem );
    items.AppendL( unremovableMetadata );
    items.AppendL( unremovableIcon );
    
    DLTRACE(("Removing from database"));
    // commits and compacts data     
    NodeDbManager().RemoveDataFromDatabaseL( 
        *aSortedArray[ index - 1 ], items );
            
    CleanupStack::PopAndDestroy( &items );
    CleanupStack::PopAndDestroy( &unremovableIcon );
    CleanupStack::PopAndDestroy( &unremovableItem );
    CleanupStack::PopAndDestroy( &unremovableMetadata );    
    
    CheckNodesL( aSortedArray, aIndex, index );
    return index;
    }


void CNcdNodeManager::CheckNodesL( 
    const RPointerArray<CNcdNodeIdentifier>& aNodeIds,
    TInt aStart,
    TInt aEnd )
    {
    DLTRACEIN(("aStart: %d, aEnd: %d", aStart, aEnd ));
    for ( ; aStart < aEnd; ++aStart ) 
        {        
        CNcdNode& node = NodeL( *aNodeIds[ aStart ] );
        node.NodeMetaDataL();
        FullCacheCleanup();
        }
    }

// ---------------------------------------------------------------------------
// Db functions that are needed from other class objects.
// The db functions are provided here instead of directly providing
// db manager because node manager may want to do some additional checking
// before db actions are allowed.
// ---------------------------------------------------------------------------


void CNcdNodeManager::DbRemoveNodeL( const CNcdNodeIdentifier& aIdentifier )
    {
    if ( iClientDatabaseLocks.Find( aIdentifier.ClientUid().iUid ) == KErrNotFound ) 
        {        
        NodeDbManager().RemoveDataFromDatabaseL( aIdentifier,
                                                 NcdNodeClassIds::ENcdNode );
        }
    }


void CNcdNodeManager::DbSaveUserDataL( const CNcdNodeIdentifier& aUserDataIdentifier,
                                       MNcdStorageDataItem& aDataItem )
    {
    NodeDbManager().SaveDataIntoDatabaseL( aUserDataIdentifier,
                                           aDataItem,
                                           NcdNodeClassIds::ENcdNodeUserData );    
    }

    
void CNcdNodeManager::DbRemoveUserDataL( const CNcdNodeIdentifier& aUserDataIdentifier )
    {
    NodeDbManager().RemoveDataFromDatabaseL( aUserDataIdentifier,
                                             NcdNodeClassIds::ENcdNodeUserData );     
    }

    
void CNcdNodeManager::DbLoadUserDataL( const CNcdNodeIdentifier& aUserDataIdentifier,
                                       MNcdStorageDataItem& aDataItem )
    {
    NodeDbManager().StartStorageLoadActionL( aUserDataIdentifier,
                                             aDataItem,
                                             NcdNodeClassIds::ENcdNodeUserData );
    }


HBufC8* CNcdNodeManager::DbScreenshotDataLC( const CNcdNodeIdentifier& aScreenshotIdentifier )
    {
     return NodeDbManager().ReadDataFromDatabaseLC( aScreenshotIdentifier,
                                                    NcdNodeClassIds::ENcdScreenshotData );     
    }


HBufC8* CNcdNodeManager::DbIconDataLC( const CNcdNodeIdentifier& aIconIdentifier )
    {
    DLTRACEIN((""));
     return NodeDbManager().ReadDataFromDatabaseLC( aIconIdentifier,
                                                    NcdNodeClassIds::ENcdIconData );     
    }


void CNcdNodeManager::DbSaveIconDataL( const CNcdNodeIdentifier& aIconIdentifier, 
                                       const TDesC8& aIconData )
    {
    DLTRACEIN((""));
    CNcdStorageDescriptorDataItem* iconDataItem = 
        CNcdStorageDescriptorDataItem::NewLC( aIconData );
    NodeDbManager().SaveDataIntoDatabaseL( aIconIdentifier, 
                                           *iconDataItem,
                                           NcdNodeClassIds::ENcdIconData );
    CleanupStack::PopAndDestroy( iconDataItem );

    // Because node data was saved into the db. Check if the max size has
    // been exceeded.
    // This is propably good place to do checking because icons are one of
    // the biggest datablobs that are saved into the db.
    NodeCacheCleanerManager().
        CacheCleanerL( aIconIdentifier.ClientUid() ).CheckDbSizeL();
    DLTRACEOUT(("Data saved successfully"));
    }



// ---------------------------------------------------------------------------
// General tool functions
// ---------------------------------------------------------------------------

void CNcdNodeManager::AddToParentL( const CNcdNodeIdentifier& aParentNodeIdentifier,
                                    const CNcdNodeIdentifier& aChildNodeMetaDataIdentifier,
                                    CNcdNodeFactory::TNcdNodeType aParentNodeType,
                                    CNcdNodeFactory::TNcdNodePurpose aParentNodePurpose,
                                    CNcdNodeFactory::TNcdNodePurpose aNodePurpose,
                                    TNcdRefHandleMode aMode,
                                    TInt aIndex,
                                    TBool aTransparent )
    {
    DLTRACEIN(( _L("Child: %S::%S Parent %S::%S"),
              &aChildNodeMetaDataIdentifier.NodeNameSpace(), 
              &aChildNodeMetaDataIdentifier.NodeId(), 
              &aParentNodeIdentifier.NodeNameSpace(), 
              &aParentNodeIdentifier.NodeId() ));
    DLINFO(("Node purpose: %d", aNodePurpose));
    DPROFILING_BEGIN( x );    
    
    if ( aParentNodeIdentifier.ContainsEmptyFields() )
        {
        // In some situations the parent may be empty. For example when temporary
        // nodes are used. Because we do not create nodes with empty identifiers,
        // we do not try to add the child to one either here. 
        DLINFO(("Empty parent. Do not add child."));
        return;
        }

    if ( aParentNodeType != CNcdNodeFactory::ENcdNodeRoot
         && aParentNodeType != CNcdNodeFactory::ENcdNodeFolder
         && aParentNodeType != CNcdNodeFactory::ENcdNodeSearchBundle )
        {
        DLERROR(("Only root or folder can be a parent!"));
        DASSERT( EFalse );
        User::Leave( KErrArgument );
        }
        
    DLINFO(("Parent node purpose: %d", aParentNodePurpose));
    CNcdNodeFolder& folder( 
        static_cast<CNcdNodeFolder&>( 
            CheckAndCreateNodeL( aParentNodeType, 
                                 aParentNodePurpose,
                                 aParentNodeIdentifier ) ) );

    TBool childAdded = EFalse;
    
    CNcdNodeIdentifier* childIdentifier =
        NcdNodeIdentifierEditor::CreateNodeIdentifierLC( aParentNodeIdentifier,
                                                         aChildNodeMetaDataIdentifier );

    DASSERT( childIdentifier != NULL );    
    
    // Ensure that transparent nodes are set as transparent
    aTransparent = aTransparent || 
                 ( aNodePurpose == CNcdNodeFactory::ENcdTransparentNode ); 
                 
    CNcdNode* child = NodePtrL( *childIdentifier );          
    DASSERT( child != NULL );
    CNcdNodeFactory::TNcdNodeType nodeType = CNcdNodeFactory::NodeTypeL( *child );
    
    if( aMode == EInsert )
        {
        childAdded = folder.InsertChildL( *childIdentifier, aIndex,
            aTransparent, nodeType );
        }
    else if ( aMode == EReplace )
        {
        childAdded = folder.ReplaceChildL( *childIdentifier, aIndex,
            aTransparent, nodeType );
        }
    else if ( aMode == EAppend )
        {
        childAdded = folder.AppendChildL( *childIdentifier, aTransparent,
            nodeType );
        }
    else 
        {
        DASSERT( aMode == EUpdate );
        // Notice that here we do not set the childAdded value.
        // So, the parent node will not be saved below.
        // For example, when scheme nodes are loaded, they will be loaded by using the
        // EUpdate value, which means that the root node will not be saved into the
        // database and the scheme information is contained in root node only while
        // the root node is in RAM. 
        }

    if( childAdded )
        {
        DLINFO(( _L("Updating child: %S::%S to parent %S::%S in db"),
                  &childIdentifier->NodeNameSpace(), 
                  &childIdentifier->NodeId(), 
                  &aParentNodeIdentifier.NodeNameSpace(), 
                  &aParentNodeIdentifier.NodeId() ));       

        // Notice, that the RefHandler should Internalize the link after this operation
        // because the transparent folder children should have the different parent set for
        // requests than just the real parent.
        child->CreateAndSetLinkL().SetParentIdentifierL( aParentNodeIdentifier );
        
        
        // Check new status.
        iSeenInfo->CheckChildNewStatusL( folder, aIndex );
        
        // The child was added to the list because it did not exist there before.
        // This is reason enought to save the node the db. So, it is upto date.
        DbSaveNodeL( folder );
        DLINFO(("Parent updated"));
        }

    CleanupStack::PopAndDestroy( childIdentifier );
    DPROFILING_END( x );        
    DLTRACEOUT((""));
    }



// ---------------------------------------------------------------------------
// CCatalogsCommunicable
// ---------------------------------------------------------------------------

void CNcdNodeManager::ReceiveMessage( MCatalogsBaseMessage* aMessage,
                                      TInt aFunctionNumber )
    {
    DLTRACEIN((""));

    DASSERT( aMessage );
    
    // Now, we can be sure that rest of the time iMessage exists.
    // This member variable is set for the CounterPartLost function.
    iMessage = aMessage;
        
    TInt trapError( KErrNone );
        
    switch( aFunctionNumber )
        {           
        case NcdNodeFunctionIds::ENcdRootNodeHandle:
            DLINFO(("Getting root node"));
            TRAP( trapError, RootNodeRequestL( *aMessage ) );
            break;
            
        case NcdNodeFunctionIds::ENcdSearchRootNodeHandle:
            DLINFO(("Getting search root node"));
            TRAP( trapError, SearchRootNodeRequestL( *aMessage ) );
            break;
            
        case NcdNodeFunctionIds::ENcdNodeHandle:
            DLINFO(("Getting node"));
            TRAP( trapError, NodeRequestL( *aMessage ) );
            break;

        case NcdNodeFunctionIds::ENcdTemporaryNodeFolderHandle:
            DLINFO(("Getting node"));
            TRAP( trapError, TemporaryNodeRequestL( *aMessage, ENcdTemporaryNodeFolder, EFalse ) );
            break;

        case NcdNodeFunctionIds::ENcdTemporaryNodeFolderWithMetaDataHandle:
            DLINFO(("Getting node"));
            TRAP( trapError, TemporaryNodeRequestL( *aMessage, ENcdTemporaryNodeFolder, ETrue ) );
            break;

        case NcdNodeFunctionIds::ENcdTemporaryNodeItemHandle:
            DLINFO(("Getting node"));
            TRAP( trapError, TemporaryNodeRequestL( *aMessage, ENcdTemporaryNodeItem, EFalse ) );
            break;
            
        case NcdNodeFunctionIds::ENcdTemporaryNodeItemWithMetaDataHandle:
            DLINFO(("Getting node"));
            TRAP( trapError, TemporaryNodeRequestL( *aMessage, ENcdTemporaryNodeItem, ETrue ) );
            break;

        case NcdNodeFunctionIds::ENcdTemporaryBundleFolderHandle:
            DLINFO(("Getting bundle folder"));
            TRAP( trapError, TemporaryNodeRequestL( *aMessage, ENcdTemporaryBundleFolder, EFalse ) );
            break;
            
        case NcdNodeFunctionIds::ENcdTemporaryBundleFolderWithMetaDataHandle:
            DLINFO(("Getting bundle folder"));
            TRAP( trapError, TemporaryNodeRequestL( *aMessage, ENcdTemporaryBundleFolder, ETrue ) );
            break;

        case NcdNodeFunctionIds::ENcdCreateTemporaryOrSupplierNode:
            DLINFO(("Getting temporary or supplier node"));
            TRAP( trapError, TemporaryOrSupplierNodeRequestL( *aMessage ) );
            break;
            
        case NcdNodeFunctionIds::ENcdCreateTemporaryNodeIfMetadataExists:
            DLINFO(("Getting temporary node if metadata exists"));
            TRAP( trapError, TemporaryNodeIfMetadataExistsRequestL( *aMessage ) );
            break;

        case NcdNodeFunctionIds::ENcdRelease:
            ReleaseRequest( *aMessage );
            break;
            
        case NcdNodeFunctionIds::ENcdClearSearchResults:
            TRAP( trapError, ClearSearchResultsRequestL( *aMessage ) );
            break;
            
        case NcdNodeFunctionIds::ENcdIsCapabilitySupported:
            TRAP( trapError, IsCapabilitySupportedRequestL( *aMessage ));
            break;
            
        default:
            break;
        }

    if ( trapError != KErrNone )
        {
        // Because something went wrong the complete has not been
        // yet called for the message.
        // So, inform the client about the error.
        DLINFO(("ERROR, Complete and release %d", trapError));
        
        aMessage->CompleteAndRelease( trapError );
        }

    // Because the message should not be used after this, set it NULL.
    // So, CounterPartLost function will know that no messages are
    // waiting the response at the moment.
    iMessage = NULL;        
            
    DLTRACEOUT((""));
    }


void CNcdNodeManager::CounterPartLost( const MCatalogsSession& aSession )
    {
    DLTRACEIN((""));    
    CommitSeenChanges( const_cast<MCatalogsSession&>( aSession ).Context() );
    
    // This function may be called whenever -- when the message is waiting
    // response or when the message does not exist.
    // iMessage may be NULL here, because in the end of the
    // ReceiveMessage it is set to NULL. The life time of the message
    // ends shortly after CompleteAndRelease is called.
    if ( iMessage != NULL )
        {
        iMessage->CounterPartLost( aSession );
        }    

    DLTRACEOUT((""));    
    }


// ---------------------------------------------------------------------------
// Public:
// General getter and setter functions.
// ---------------------------------------------------------------------------

CNcdNodeDbManager& CNcdNodeManager::NodeDbManager() const
    {
    return *iDbManager;
    }
    
CNcdNodeSeenInfo& CNcdNodeManager::SeenInfo() const
    {
    return *iSeenInfo;
    }

CNcdNodeFactory& CNcdNodeManager::NodeFactory() const
    {
    return *iNodeFactory;
    }    

CNcdNodeCacheCleanerManager& CNcdNodeManager::NodeCacheCleanerManager() const
    {
    return *iNodeCacheCleanerManager;
    }    

void CNcdNodeManager::SetFavoriteManager( CNcdFavoriteManager& aManager ) 
    {
    iFavoriteManager = &aManager;
    }

void CNcdNodeManager::SetNodeMetaDataL( CNcdNode& aNode )
    {
    DLTRACEIN(("Check metadata for node"));
    CNcdNodeMetaData* metadata( NULL );
    CNcdNodeLink* link = aNode.NodeLink();    
    if ( link )
        {
        const CNcdNodeIdentifier& metaDataIdentifier( link->MetaDataIdentifier() );
        if ( !metaDataIdentifier.ContainsEmptyFields() )
            {
            TRAP_IGNORE(
                   metadata = &NodeMetaDataL( metaDataIdentifier ) );        
            if ( metadata != NULL )
                {
                DLINFO(("Setting metadata for the created node"));
                aNode.SetNodeMetaDataL( *metadata );            
                }                    
            }
        }    
    DLTRACEOUT((""));
    }


// ---------------------------------------------------------------------------
// Protected:
// Db functions
// ---------------------------------------------------------------------------

CNcdNode& CNcdNodeManager::DbNodeL( const CNcdNodeIdentifier& aIdentifier )
    {
    DLTRACEIN(( _L("Node id: %S, %S, %d"), 
                &aIdentifier.NodeNameSpace(),
                &aIdentifier.NodeId(),
                aIdentifier.ClientUid().iUid ));
    DPROFILING_BEGIN( x );
    // If the database is locked, leave immediately.
    if ( iClientDatabaseLocks.Find( aIdentifier.ClientUid().iUid ) != KErrNotFound ) 
        {
        DLINFO(("Database locked -> leave."));
        User::Leave( KErrNotFound );
        }

    HBufC8* data( 
        NodeDbManager().
            ReadDataFromDatabaseLC( aIdentifier,
                                    NcdNodeClassIds::ENcdNode ) );

    if ( data == NULL
         || data->Length() == 0 )
        {
        // Node was not found from db. So, leave.
        DLINFO(("Node was not found from db."));
        User::Leave( KErrNotFound );
        }

    // Create and internalize node according to the data gotten from the
    // database.
    CNcdNode* node( 
        NodeFactory().
            CreateNodeLC( aIdentifier,
                          *data ) );   
 
     // Insert node to the cache.
    AppendNodeToCacheL( node );        

    // Cache took ownership of the node.
    CleanupStack::Pop( node );

    // Delete the node data because new node has been created. 
    CleanupStack::PopAndDestroy( data );    

    // Now that the node has been created from the db, the metadata should
    // be also set if it can be found. If it can, it should be set when the
    // metadata is gotten from the internet and handled with DataHandlerL
    SetNodeMetaDataL( *node );    
    DPROFILING_END( x );
    DLTRACEOUT((""));    
    return *node;
    }
    
    
CNcdNodeMetaData& CNcdNodeManager::DbMetaDataL( const CNcdNodeIdentifier& aIdentifier )
    {
    DLTRACEIN(( _L("Metadata id: %S, %S, %d"), 
                &aIdentifier.NodeNameSpace(),
                &aIdentifier.NodeId(),
                aIdentifier.ClientUid().iUid ));

    HBufC8* data( 
        NodeDbManager().
        ReadDataFromDatabaseLC( aIdentifier,
                                NcdNodeClassIds::ENcdMetaData ) );

    if ( data == NULL )
        {
        // Node was not found from db. So, leave.
        DLINFO(("Node metadata was not found from db"));
        User::Leave( KErrNotFound );
        }
    else if ( data->Length() == 0 )
        {
        // Node was not found from db. So, leave.
        DLINFO(("Node metadata from db was empty"));
        User::Leave( KErrNotFound );        
        }

    // Use factory to create and to initialize the correct node from the data.
    CNcdNodeMetaData* metaData( NodeFactory().CreateMetaDataLC( aIdentifier, *data ) );

    // Insert node to the cache.
    iNodeMetaDataCache.AppendL( metaData );
    
    // Cache took ownership of the node.
    CleanupStack::Pop( metaData );

    CleanupStack::PopAndDestroy( data );
        
    DLTRACEOUT((""));    
    return *metaData;    
    }


void CNcdNodeManager::DbSaveNodeL( CNcdNode& aNode )
    {
    DLTRACEIN((_L("Node id: %S, %S, %d"), 
               &aNode.Identifier().NodeNameSpace(),
               &aNode.Identifier().NodeId(),
               aNode.Identifier().ClientUid().iUid));
    DPROFILING_BEGIN( x );
    // If the database is locked, do not do anything.
    if ( iClientDatabaseLocks.Find( aNode.Identifier().ClientUid().iUid ) == 
         KErrNotFound ) 
        {
        // Save the node information to the database
        // The node implements MNcdStorageDataItem interface.
        // So, the externalize function will insert the data to the stream
        // and the database handler will save the stream to the database.
        NodeDbManager().SaveDataIntoDatabaseL( aNode.Identifier(), 
                                               aNode,
                                               NcdNodeClassIds::ENcdNode );
        }
        

    // Inform cache cleaner about the node saving.
    // Because the node has been updated into the db. Make sure
    // that it will not be in the cleanup list anymore.
    NodeCacheCleanerManager().
        CacheCleanerL( aNode.Identifier().ClientUid() ).
            RemoveCleanupIdentifier( aNode.Identifier() );
    DPROFILING_END( x );
    DLTRACEOUT((""));
    }
    
    
void CNcdNodeManager::DbSaveNodesL( const CNcdNodeIdentifier& aRootNode ) 
    {
    DLTRACEIN((""));
    
    for ( TInt i = 0; i < iNodeCache.Count(); i++ ) 
        {
        CNcdNode* node = iNodeCache[i];
        if ( NcdNodeIdentifierEditor::ParentOf( aRootNode, node->Identifier() ) ||
             node->Identifier().Equals( aRootNode ) )
            {
            DLINFO((_L("Saving node ns: %S, id: %S"), &node->Identifier().NodeNameSpace(), &node->Identifier().NodeId() ));
            DbSaveNodeL( *node );
            }
        }
    }


void CNcdNodeManager::DbSaveNodeMetaDataL( CNcdNodeMetaData& aMetaData )
    {
    DLTRACEIN(( _L("Metadata id: %S, %S, %d"), 
                &aMetaData.Identifier().NodeNameSpace(),
                &aMetaData.Identifier().NodeId(),
                aMetaData.Identifier().ClientUid().iUid ));
    DPROFILING_BEGIN( x );
    // Save the node information to the database
    // The node implements MNcdStorageDataItem interface.
    // So, the externalize function will insert the data to the stream
    // and the database handler will save the stream to the database.
    NodeDbManager().SaveDataIntoDatabaseL( aMetaData.Identifier(), 
                                           aMetaData,
                                           NcdNodeClassIds::ENcdMetaData );

    // Because node data was saved into the db. Check if the max size has
    // been exceeded.
    // This is propably good enough place to do checking because most of the
    // data is gotten from the metadata.
    NodeCacheCleanerManager().
        CacheCleanerL( aMetaData.Identifier().ClientUid() ).CheckDbSizeL();
    
    DPROFILING_END( x );    
    DLTRACEOUT((""));
    }


void CNcdNodeManager::DbSetMaxSizeL( const TUid& aClientUid, 
                                    const TInt aMaxDbKiloByteSize )
    {
    DLTRACEIN(("Setting max db size: %d for client: %d", 
               aMaxDbKiloByteSize, aClientUid));
    
    // The cache cleaner uses this information so forward the info for it.
    NodeCacheCleanerManager().
        CacheCleanerL(aClientUid).
            SetDbMaxSize( aMaxDbKiloByteSize * KBytesToKilos );

    DLTRACEOUT((""));
    }
    
void CNcdNodeManager::LockNodeDbL( TUid aClientUid ) 
    {
    DLTRACEIN((""));
    iClientDatabaseLocks.AppendL( aClientUid.iUid );
    }
    
void CNcdNodeManager::UnlockNodeDb( TUid aClientUid ) 
    {
    DLTRACEIN((""));
    TInt index = iClientDatabaseLocks.Find( aClientUid.iUid );
    if ( index != KErrNotFound ) 
        {
        iClientDatabaseLocks.Remove( index );
        }
    }

// ---------------------------------------------------------------------------
// Protected:
// Functions that are called from the ReceiveMessageL, 
// which is meant to be used by the client side.
// ---------------------------------------------------------------------------

void CNcdNodeManager::RootNodeRequestL( MCatalogsBaseMessage& aMessage )
    {
    DLTRACEIN((""));

    // Get the session that will contain the handle of the node
    MCatalogsSession& requestSession( aMessage.Session() );

    // Creates the root if does not exist yet.
    CNcdNodeFolder& root = CreateRootL( requestSession.Context() );

    // Add the node to the session and get the handle.
    // If the node already existed in the session we will still
    // get a new handle to the same object.
    TInt32 rootHandle( requestSession.AddObjectL( &root ) );

    DLINFO(("Root handle: %d", rootHandle ));

    // Send the information to the client side
    // If this leaves, ReceiveMessage will complete the message.
    aMessage.CompleteAndReleaseL( rootHandle, KErrNone );

    DLTRACEOUT((""));
    }

void CNcdNodeManager::SearchRootNodeRequestL( MCatalogsBaseMessage& aMessage )
    {
    DLTRACEIN((""));

    // Get the session that will contain the handle of the node
    MCatalogsSession& requestSession( aMessage.Session() );

    // Creates the search root if does not exist yet.
    CNcdNodeFolder& searchRoot = CreateSearchRootL( requestSession.Context() );

    // Add the node to the session and get the handle.
    // If the node already existed in the session we will still
    // get a new handle to the same object.
    TInt32 searchRootHandle( requestSession.AddObjectL( &searchRoot ) );

    DLINFO(("Search root handle: %d", searchRootHandle ));

    // Send the information to the client side
    // If this leaves, ReceiveMessage will complete the message.
    aMessage.CompleteAndReleaseL( searchRootHandle, KErrNone );

    DLTRACEOUT((""));
    }


void CNcdNodeManager::NodeRequestL( MCatalogsBaseMessage& aMessage )
    {
    DLTRACEIN((""));

    CNcdNodeIdentifier* nodeIdentifier( RequestNodeIdentifierLC( aMessage ) );

    // Get the node from the cache or from the db.
    // Notice that new node is not created if it is not found from the
    // cache or from the db.
    CNcdNode& node( NodeL( *nodeIdentifier ) );

    CleanupStack::PopAndDestroy( nodeIdentifier );
    
    DLINFO(("Node found"));

    // Add the node to the session and get the handle.
    // If the node already existed in the session we will still
    // get a new handle to the same object.
    TInt32 nodeHandle( aMessage.Session().AddObjectL( &node ) );

    DLINFO(("Node handle: %d", nodeHandle ));

    // Send the information to the client side
    // If this leaves, ReceiveMessage will complete the message.
    aMessage.CompleteAndReleaseL( nodeHandle, KErrNone );

    DLTRACEOUT((""));
    }
    

void CNcdNodeManager::TemporaryNodeRequestL(
    MCatalogsBaseMessage& aMessage,
    TNcdTemporaryNodeType aType,
    TBool aCreateMetaData )
    {
    DLTRACEIN((""));
    
    CNcdNodeIdentifier* nodeIdentifier( RequestNodeIdentifierLC( aMessage ) );

    CNcdNode* node = &CreateTemporaryNodeL( 
        *nodeIdentifier, 
        aType, 
        aCreateMetaData );
    
    CleanupStack::PopAndDestroy( nodeIdentifier );
    
    // Add the node to the session and get the handle.
    // If the node already existed in the session we will still
    // get a new handle to the same object.
    TInt32 nodeHandle( aMessage.Session().AddObjectL( node ) );

    DLINFO(("Node handle: %d", nodeHandle ));

    // Send the information to the client side
    // If this leaves, ReceiveMessage will complete the message.
    aMessage.CompleteAndReleaseL( nodeHandle, KErrNone );

    DLTRACEOUT((""));
    }


CNcdNode& CNcdNodeManager::CreateTemporaryNodeL(
    CNcdNodeIdentifier& aTempNodeIdentifier,
    TNcdTemporaryNodeType aType,
    TBool aCreateMetaData )
    {
    DLTRACEIN((""));
    // Check if the metadata exists already, and is of correct type.
    CNcdNodeIdentifier* metaIdentifier =
        NcdNodeIdentifierEditor::CreateMetaDataIdentifierLC( 
                aTempNodeIdentifier );
    
    CNcdNodeMetaData* meta( NULL );
    TRAPD( err, meta = &NodeMetaDataL( *metaIdentifier ) );
    if ( err == KErrNone ) 
        {
        // Metadata was found, check the type.
        NcdNodeClassIds::TNcdNodeClassId acceptedMetaClassId = 
            NcdNodeClassIds::ENcdNullObjectClassId;
            
        switch ( aType ) 
            {
            case ENcdTemporaryBundleFolder:
            case ENcdTemporaryNodeFolder:
                acceptedMetaClassId = NcdNodeClassIds::ENcdFolderNodeMetaDataClassId;
                break;
            
            case ENcdTemporaryNodeItem:
                acceptedMetaClassId = NcdNodeClassIds::ENcdItemNodeMetaDataClassId;
                break;
            
            default:
                DASSERT( EFalse );
                break;
            }
                
        if ( meta->ClassId() != acceptedMetaClassId ) 
            {
            DLINFO(("Wrong metadata type, leave"));
            User::Leave( KErrArgument );
            }
        }

            
    // Get the node from the cache or from the db.
    // Notice that new node is not created if it is not found from the
    // cache or from the db.
    CNcdNode* node( NULL );
    switch( aType )
        {
        case ENcdTemporaryNodeFolder:        
            node = &CreateNodeFolderL(
                CNcdNodeFactory::ENcdNormalNode, aTempNodeIdentifier );
            break;
            
        case ENcdTemporaryNodeItem:
            node = &CreateNodeItemL(
                CNcdNodeFactory::ENcdNormalNode, aTempNodeIdentifier );
            break;
        
        case ENcdTemporaryBundleFolder:
            node = &CreateNodeFolderL(
                CNcdNodeFactory::ENcdBundleNode, aTempNodeIdentifier );
            break;
        
        default:
            DASSERT( EFalse );
            break;
        }

    DASSERT( node );
    if (!node )
        {
        User::Leave( KErrGeneral );
        }
            
    // Because this is temporary node, we will create or get the metadata for the node.
    // This way the metadata will be at least initialized with the purchase history data.
    if ( !meta && aCreateMetaData ) 
        {
        switch( aType )
            {
            case ENcdTemporaryBundleFolder:
            case ENcdTemporaryNodeFolder:
                meta = &CreateNodeMetaDataL(
                    *metaIdentifier, CNcdNodeFactory::ENcdNodeFolder );
                break;
                
            case ENcdTemporaryNodeItem:
                meta = &CreateNodeMetaDataL(
                    *metaIdentifier, CNcdNodeFactory::ENcdNodeItem );
                break;

            default:
                DASSERT( EFalse );
                break;
            }
        }


    // Also to be sure that the node will be in initialized mode, we set the
    // link for the node here.

    // Set the server and metadata information for the link.
    // These are required, so the temp node may also be loaded from web.
    node->CreateAndSetLinkL().SetServerUriL( node->Identifier().ServerUri() );
    node->CreateAndSetLinkL().SetMetaDataIdentifierL( *metaIdentifier );

    CleanupStack::PopAndDestroy( metaIdentifier );    

    if ( meta )
        {
        // Set the metadata for the node.
        node->SetNodeMetaDataL( *meta );
        DLINFO(("Node and meta created"));
        }

    // Also, save the node now that it has been updated
    DbSaveNodeL( *node );
    return *node;
    }

    
void CNcdNodeManager::TemporaryOrSupplierNodeRequestL( MCatalogsBaseMessage& aMessage )
    {
    DLTRACEIN((""));
    
    // This will use the helper function to get the actual node identifier that is included
    // into the message data.
    CNcdNodeIdentifier* nodeIdentifier( RequestNodeIdentifierLC( aMessage ) );

    // The creator function requires metadata identifier in this case. 
    // So, create correct identifier here.    
    CNcdNodeIdentifier* metadataIdentifier( 
        NcdNodeIdentifierEditor::CreateMetaDataIdentifierLC( *nodeIdentifier ) );
    CNcdNode& node = CreateTemporaryNodeOrSupplierL( *metadataIdentifier );
    CleanupStack::PopAndDestroy( metadataIdentifier );

    CleanupStack::PopAndDestroy( nodeIdentifier );

    // NOTICE: We do not get the handle here, but the proxy side has to request it
    // separately by using the normal node request. This just created the node.    

    // Send the information to the client side
    // If this leaves, ReceiveMessage will complete the message.
    // Notice that we just return zero here, because we do not have anything 
    // special to give back.
    aMessage.CompleteAndReleaseL( 0, KErrNone );

    DLTRACEOUT((""));
    }
    

void CNcdNodeManager::TemporaryNodeIfMetadataExistsRequestL(
    MCatalogsBaseMessage& aMessage )
    {
    DLTRACEIN((""));
    
    // This will use the helper function to get the actual node identifier that is included
    // into the message data.
    CNcdNodeIdentifier* nodeIdentifier( RequestNodeIdentifierLC( aMessage ) );

    // The creator function requires metadata identifier in this case. 
    // So, create correct identifier here.    
    CNcdNodeIdentifier* metadataIdentifier( 
        NcdNodeIdentifierEditor::CreateMetaDataIdentifierLC( *nodeIdentifier ) );
        
    CNcdNode* node = CreateTemporaryNodeIfMetadataExistsL( *metadataIdentifier );
    CleanupStack::PopAndDestroy( metadataIdentifier );
    CleanupStack::PopAndDestroy( nodeIdentifier );
    
    // NOTICE: We do not get the handle here, but the proxy side has to request it
    // separately by using the normal node request. This just created the node.    

    // Send the information to the client side
    // If this leaves, ReceiveMessage will complete the message.
    // Return the information whether the node was created or not.
    TBool retValue = node != NULL;
    aMessage.CompleteAndReleaseL( retValue, KErrNone );
    }


void CNcdNodeManager::ReleaseRequest( MCatalogsBaseMessage& aMessage )
    {
    DLTRACEIN((""));

    // Commit the changes in seen info.
    // Unable to handle the possible error.
    CommitSeenChanges( aMessage.Session().Context() );

    // Decrease the reference count for this object.
    // When the reference count reaches zero, this object will be destroyed
    // and removed from the session.
    MCatalogsSession& requestSession( aMessage.Session() );
    TInt handle( aMessage.Handle() );
    aMessage.CompleteAndRelease( KErrNone );
    requestSession.RemoveObject( handle );
            
    DLTRACEOUT((""));
    }


void CNcdNodeManager::ClearSearchResultsRequestL( MCatalogsBaseMessage& aMessage )
    {
    DLTRACEIN((""));
    ClearSearchResultsL( aMessage.Session().Context() );
    aMessage.CompleteAndReleaseL( KErrNone, KErrNone );
    }

void CNcdNodeManager::IsCapabilitySupportedRequestL(
        MCatalogsBaseMessage& aMessage )
    {
    DLTRACEIN((""));        
      
    HBufC8* des = HBufC8::NewLC( aMessage.InputLength() );
    TPtr8 ptr = des->Des();
    aMessage.ReadInput( ptr );
    RDesReadStream stream( *des );
    CleanupReleasePushL( stream );
    
    CNcdNodeIdentifier* nodeId = CNcdNodeIdentifier::NewLC( stream );
    CNcdNode& node = NodeL( *nodeId );
    CleanupStack::PopAndDestroy( nodeId );
    const TDesC* serverUri = NULL;
    if( node.NodeLinkL().RemoteUri() != KNullDesC )
        {
        DLTRACE(("Using Remote URI as ServerURI"));
        serverUri = &node.NodeLinkL().RemoteUri();
        }
    else
        {
        DLTRACE(("Using Server URI as ServerURI"));
        serverUri = &node.NodeLinkL().ServerUri();
        }
    
    HBufC* capability = NULL;
    InternalizeDesL( capability, stream );
    CleanupStack::PushL( capability );
    
    MNcdServerDetails& serverDetails =
        iConfigurationManager.ServerDetailsL(
            aMessage.Session().Context(),
            *serverUri,
            node.NodeLinkL().MetaDataIdentifier().NodeNameSpace() );
            
    TBool isCapabilitySupported = 
        serverDetails.IsCapabilitySupported( *capability );
        
    DLINFO(( _L("server uri: %S, namespace: %S, capability: %S, is supported: %d"),
        serverUri, &node.NodeLinkL().MetaDataIdentifier().NodeNameSpace(),
        capability, isCapabilitySupported ));
        
    CleanupStack::PopAndDestroy( capability );
    CleanupStack::PopAndDestroy( &stream );
    CleanupStack::PopAndDestroy( des );
    
    aMessage.CompleteAndReleaseL( isCapabilitySupported, KErrNone );
    }


TBool CNcdNodeManager::IsCapabilitySupportedL( const CNcdNodeIdentifier& aNodeIdentifier,
    const TDesC& aCapability, MCatalogsContext& aContext )
    {
    DLTRACEIN((""));
    
    CNcdNode& node = NodeL( aNodeIdentifier );
    const TDesC* serverUri = NULL;
    if( node.NodeLinkL().RemoteUri() != KNullDesC )
        {
        DLTRACE(("Using Remote URI as ServerURI"));
        serverUri = &node.NodeLinkL().RemoteUri();
        }
    else
        {
        DLTRACE(("Using Server URI as ServerURI"));
        serverUri = &node.NodeLinkL().ServerUri();
        }
    
    MNcdServerDetails& serverDetails =
        iConfigurationManager.ServerDetailsL(
            aContext,
            *serverUri,
            node.NodeLinkL().MetaDataIdentifier().NodeNameSpace() );
            
    TBool isCapabilitySupported = 
        serverDetails.IsCapabilitySupported( aCapability );
        
    DLINFO(( _L("server uri: %S, namespace: %S, capability: %S, is supported: %d"),
        serverUri, &node.NodeLinkL().MetaDataIdentifier().NodeNameSpace(),
        &aCapability, isCapabilitySupported ));
    return isCapabilitySupported;
    }


CNcdNodeIdentifier* CNcdNodeManager::GetOriginIdentifierL( const CNcdNodeIdentifier& aNodeIdentifier )
    {
    DLTRACEIN((""));
    CNcdNode& node = NodeL( aNodeIdentifier );
    
    CNcdNodeMetaData* nodeMeta( NULL );
    DLTRACE(("Try to get metadata for node."));
    TRAPD( err, nodeMeta = &node.NodeMetaDataL() );
    if ( err == KErrNotFound ) 
        {
        DLTRACE(("Metadata not found, return NULL"));
        return NULL;
        }
    User::LeaveIfError( err );
    DASSERT( nodeMeta );
    
    CNcdPurchaseDetails* purchaseDetails = NULL;
    DLTRACE(("Try to get purchase details for node."));
    TRAP( err,
        purchaseDetails = nodeMeta->PurchaseDetailsLC();
        CleanupStack::Pop( purchaseDetails );
        );
    if ( err == KNcdErrorNoPurchaseInformation )
        {
        DLTRACE(("Purchase details not found for node, return NULL."))
        return NULL;
        }
    else if ( err != KErrNone )
        {
        User::Leave( err );
        }
    // Create origin identifier
    CleanupStack::PushL( purchaseDetails );
    CNcdNodeIdentifier* originIdentifier = CNcdNodeIdentifier::NewL(
        aNodeIdentifier.NodeNameSpace(), purchaseDetails->OriginNodeId(),
        aNodeIdentifier.ClientUid() );
    CleanupStack::PopAndDestroy( purchaseDetails );
    DLTRACE(( _L("Purchase details found, origin node id: %S"), &originIdentifier->NodeId() ));
    return originIdentifier;
    }


void CNcdNodeManager::RemoveChildrenL( CNcdNodeFolder& aFolder )
    {
    DLTRACEIN((""));

    const RPointerArray<CNcdChildEntity>& childArray( aFolder.ChildArray() );
    TInt count = childArray.Count();

    const RPointerArray<CNcdNodeIdentifier>& favorites( 
        iFavoriteManager->FavoriteNodesL( aFolder.Identifier().ClientUid() ) );

    while ( count-- ) 
        {        
        RemoveNodeFromRamCache( childArray[ count ]->Identifier() );

        if ( !NcdNodeIdentifierUtils::ContainsIdentifier( 
            childArray[ count ]->Identifier(),
            favorites )  )
            {
            DbRemoveNodeL( childArray[ count ]->Identifier() );
            }
        }
        
    // Empties folder's child array
    aFolder.RemoveChildren();
        
    DbSaveNodeL( aFolder );
    }


void CNcdNodeManager::RemoveChildrenMetadataL( CNcdNodeFolder& aFolder )
    {
    DLTRACEIN((""));
    const RPointerArray<CNcdChildEntity>& childArray( aFolder.ChildArray() );
    TInt count = childArray.Count();
    
    if ( !count ) 
        {
        DLTRACEOUT(("No children"));
        return;
        }
    
    RPointerArray<CNcdNodeIdentifier> metaIdArray;
    CleanupResetAndDestroyPushL( metaIdArray );
    metaIdArray.ReserveL( count );    
    
    const RPointerArray<CNcdNodeIdentifier>& favorites( 
        iFavoriteManager->FavoriteNodesL( aFolder.Identifier().ClientUid() ) );
    
    RPointerArray<CNcdNodeIdentifier> favoriteMetas;
    CleanupResetAndDestroyPushL( favoriteMetas );
            
    // We need to convert node ids of favorite nodes to metadata ids so that
    // we can be absolutely sure that we don't delete anything we are not
    // supposed to delete
    TInt favoriteCount = favorites.Count();
    favoriteMetas.ReserveL( favoriteCount );
    while( favoriteCount-- )
        {        
        CNcdNodeIdentifier* metaId = 
            NcdNodeIdentifierEditor::CreateMetaDataIdentifierL(
                *favorites[ favoriteCount ] );
        favoriteMetas.Append( metaId );
        }
    
    while( count-- ) 
        {
        CNcdNodeIdentifier* metaId = 
            NcdNodeIdentifierEditor::CreateMetaDataIdentifierL(
                childArray[ count ]->Identifier() );
        
        // Ensure that we don't remove favorite nodes
        if ( !NcdNodeIdentifierUtils::ContainsIdentifier( 
                *metaId, 
                favoriteMetas ) )
            {
            DLTRACE(("Meta not favorite"));
            CNcdNodeMetaData* metadata = FindNodeMetaDataFromCache( *metaId );
            if ( metadata ) 
                {
                DLTRACE(("Setting metadata to be deleted as soon as possible from cache"));
                metadata->SetDeleteSoon( ETrue );
                }            
            
            // We don't have to worry about running out of memory because the
            // array has already enough space reserved
            metaIdArray.Append( metaId );
            }
        else 
            {
            delete metaId;
            }
        }
    
    CleanupStack::PopAndDestroy( &favoriteMetas );
    
    RArray<NcdNodeClassIds::TNcdNodeClassType> classTypes;
    CleanupClosePushL( classTypes );   
    classTypes.AppendL( NcdNodeClassIds::ENcdMetaData );        
    
    if ( iClientDatabaseLocks.Find( 
            aFolder.Identifier().ClientUid().iUid ) == KErrNotFound ) 
        {        
        DLTRACE(("Removing %d metadatas from disk", metaIdArray.Count() ));
        // Delete from database but don't compact since it's veeeery slow
        NodeDbManager().RemoveDataFromDatabaseL( 
            metaIdArray, classTypes, EFalse );
        }
        
    CleanupStack::PopAndDestroy( 2, &metaIdArray ); // classTypes, metaIdArray
    }



// ---------------------------------------------------------------------------
// Protected:
// Functions that are called from functions that handle received messages.
// ---------------------------------------------------------------------------

CNcdNodeIdentifier* CNcdNodeManager::RequestNodeIdentifierLC( MCatalogsBaseMessage& aMessage ) const
    {
    DLTRACEIN((""));
    
    // Get the session that will contain the handle of the node
    MCatalogsSession& requestSession( aMessage.Session() );

    DLINFO(("Message, length: %X", aMessage.InputLength() ));

    // Get the node object
    RBuf8 nodeIdentifierData;
    nodeIdentifierData.CreateL( aMessage.InputLength() );
    CleanupClosePushL( nodeIdentifierData );
    User::LeaveIfError( aMessage.ReadInput( nodeIdentifierData ) );

    // Get the node information from the stream
    CNcdNodeIdentifier* nodeIdentifier = 
        CNcdNodeIdentifier::NewLC( nodeIdentifierData );
        
    // Check if we should update the nodeidentifier with the correct uid info
    if ( nodeIdentifier->ClientUid() == TUid::Null() )
        {        
        // Nodeidentifier was created in the proxy side without knowing
        // the uid value of the application.
        // Create a new nodeidentifier here that will contain the actual UID
        CNcdNodeIdentifier* uidNodeIdentifier =
            CNcdNodeIdentifier::NewL( nodeIdentifier->NodeNameSpace(),
                                      nodeIdentifier->NodeId(),
                                      nodeIdentifier->ServerUri(),
                                      requestSession.Context().FamilyId() );
        CleanupStack::PopAndDestroy( nodeIdentifier );
        nodeIdentifier = uidNodeIdentifier;
        CleanupStack::PushL( nodeIdentifier );

        DLINFO(( ("Null uid. New uid: %d"), nodeIdentifier->ClientUid().iUid ));
        }

    DLINFO((_L("Node namespace: %S, node id: %S, server uri: %S, node uid: %d"), 
            &nodeIdentifier->NodeNameSpace(), 
            &nodeIdentifier->NodeId(), 
            &nodeIdentifier->ServerUri(),
            nodeIdentifier->ClientUid()));

    CleanupStack::Pop( nodeIdentifier );
    CleanupStack::PopAndDestroy( &nodeIdentifierData );
    CleanupStack::PushL( nodeIdentifier );
    
    DLTRACEOUT((""));
    
    return nodeIdentifier;    
    }
        


// ---------------------------------------------------------------------------
// Private:
// Cache functions
// ---------------------------------------------------------------------------

CNcdNode* CNcdNodeManager::FindNodeFromCacheL(
    const CNcdNodeIdentifier& aIdentifier )
    {
    DLTRACEIN(( _L("Node id: %S, %S, %d"), 
                &aIdentifier.NodeNameSpace(),
                &aIdentifier.NodeId(),
                aIdentifier.ClientUid().iUid ));
    
    if ( aIdentifier.ContainsEmptyFields() ) 
        {
        DLERROR(("Identifier contains empty fields, leaving with KErrArgument (%d)", 
            KErrArgument ));
        User::Leave( KErrArgument );
        }
        
    // Check from main cache at first.
    CNcdNode* node = FindNodeFromMainCache( aIdentifier );
    if ( node ) 
        {
        return node;
        }
 
        
    // Check from temp cache too. If node is found there, copy it to the main cache to
    // keep the main cache up to date.
    TInt index = FindNodeFromArray( aIdentifier, iTempNodeCache );
    if ( index != KErrNotFound ) 
        {
        User::LeaveIfError( InsertNodeInOrder( 
            iTempNodeCache[ index ], iNodeCache ) );
        iTempNodeCache[ index ]->Open();
        return iTempNodeCache[ index ];
        }

    DLTRACEOUT((""));
        
    return NULL;    
    }


// ---------------------------------------------------------------------------
// Private:
// Cache functions
// ---------------------------------------------------------------------------

CNcdNode* CNcdNodeManager::FindNodeFromMainCache(
    const CNcdNodeIdentifier& aIdentifier )
    {
    DLTRACEIN((""));
    // Check if the node already exists in the cache.
    // If it does, do not create it.
    TInt index = FindNodeFromArray( aIdentifier, iNodeCache );
    if ( index != KErrNotFound ) 
        {
        return iNodeCache[ index ];
        }
    return NULL;
    
    }


TInt CNcdNodeManager::FindNodeFromArray( 
    const CNcdNodeIdentifier& aIdentifier,
    const RPointerArray<CNcdNode>& aArray ) const
    {            
    
    iSearchableNode->SetIdentifier( aIdentifier );
    return aArray.FindInOrder( 
        iSearchableNode, 
        iNodeOrder );
    
    /*
    for ( TInt i = 0; i < aArray.Count(); ++i )
        {
        if ( aArray[ i ]->Identifier().Equals( aIdentifier ) )
            {
            // The node has already been created.
            // Return the old node.
            return i;
            }
        }
    return KErrNotFound; 
     */
    }


TInt CNcdNodeManager::InsertNodeInOrder( 
    CNcdNode* aNode,
    RPointerArray<CNcdNode>& aArray )
    {    
    return aArray.InsertInOrder( aNode, iNodeOrder );
    //return aArray.Append( aNode );
    }


CNcdNodeMetaData* CNcdNodeManager::FindNodeMetaDataFromCache( 
    const CNcdNodeIdentifier& aIdentifier )
    {
    DLTRACEIN(( _L("Metadata id: %S, %S, %d"), 
                &aIdentifier.NodeNameSpace(),
                &aIdentifier.NodeId(),
                aIdentifier.ClientUid().iUid ));
    
    // Check if the metadata already exists in the cache.
    // If it does, do not create it.
    for ( TInt i = 0; i < iNodeMetaDataCache.Count(); ++i )
        {
        if ( iNodeMetaDataCache[ i ]->Identifier().Equals( aIdentifier ) )
            {
            // The node has already been created.
            // Return the old node.
            return iNodeMetaDataCache[ i ];
            }
        }

    DLTRACEOUT((""));
        
    return NULL;    
    }


void CNcdNodeManager::NodeCacheCleanup()
    {
    DLTRACEIN((""));

    if ( iNodeCache.Count() < NcdProviderDefines::KNodeRamCacheMaxCount )
        {
        // Node cache has not reached the maximum size yet.
        // So, no need to do cleanup yet.
        return;
        }

    CNcdNode* node( NULL );
    TBool nodeRemoved( EFalse );    
        
    // Because the unreferenced item has the access count 1,
    // we remove them from the cache and call Close, which will
    // delete the item itself.
    // The cache does not need to be totally cleaned. So, if the
    // delimiter value is reached then the cleanup can be stopped.
    for( TInt i = 0; 
         i < iNodeCache.Count() 
            && iNodeCache.Count() >= NcdProviderDefines::KNodeRamCacheDelimiterCount; 
         ++i )
        {
        node = iNodeCache[ i ];
        if( node->AccessCount() == 1 &&
            iClientDatabaseLocks.Find(
                node->Identifier().ClientUid().iUid ) == KErrNotFound )
            {
            DLINFO(( _L("Remove node from cache: %S"), 
                &node->Identifier().NodeId() ));
                
            // Because this node will not be in the cache anymore, we can remove
            // it from the block list of the cleaner if it exists there. 
            TRAP_IGNORE(
                NodeCacheCleanerManager().
                    CacheCleanerL( node->Identifier().ClientUid() ).
                        RemoveDoNotRemoveIdentifierL( node->Identifier() ) );        

            // Remove unreferenced node from the cache and destroy it.
            iNodeCache.Remove( i );
            i--;
            node->Close();
            node = NULL;

            nodeRemoved = ETrue;
            
            DLINFO(("node removed from the cache"));
            }
        }

    if ( nodeRemoved )
        {
        // Notice that we will come here only if the RAM cache max count has been
        // reached. Otherwise the beginning of this function will return immediately.
        // So, we will not come here every time some node is released. Therefore,
        // this db check can be done here. It is run only once in a while.
        DLINFO(("Nodes were removed from RAM cache. So, check also database."));
        TRAP_IGNORE( NodeCacheCleanerManager().CheckAllL() );
        }

    DLTRACEOUT((""));
    }
    
    
void CNcdNodeManager::MetaDataCacheCleanup()
    {
    DLTRACEIN(("Metadata cache count: %d", iNodeMetaDataCache.Count() ));
    
    if ( iNodeMetaDataCache.Count() < NcdProviderDefines::KNodeRamCacheMaxCount )
        {
        // Node cache has not reached the maximum size yet.
        // So, no need to do cleanup yet.
        DLTRACEOUT(("No need for cleanup"));
        return;
        }            
    
    // Check if metadatas are used in some node
    // Go through all the metadata info

    for ( TInt i = 0; 
          i < iNodeMetaDataCache.Count()
            && iNodeMetaDataCache.Count() >= NcdProviderDefines::KNodeRamCacheDelimiterCount; 
          ++i )
        {        
        DLTRACEIN(("Going through %d nodes for metadata in index: %i", 
            iNodeCache.Count(), i ));            
                    
        if( !IsMetadataUsed( iNodeMetaDataCache[ i ] ) )
            {
            DLTRACE(("Removing metadata"));
            // Because none of the nodes needed this metadata,
            // we may close the metadata and remove it from the cache.
            CNcdNodeMetaData* metaData( iNodeMetaDataCache[ i ] );
            iNodeMetaDataCache.Remove( i );
            metaData->Close();
            metaData = NULL;
            // Because one item was removed also update the index
            // for the next round.
            --i;
            DLINFO(("metadata removed from the cache"));
            }
        }        

    DLTRACEOUT((""));
    }


TBool CNcdNodeManager::IsMetadataUsed( const CNcdNodeMetaData* aMetadata ) const
    {        
    DASSERT( aMetadata );
    
    // Compare the metadata against the metadata info that
    // nodes contain.
    for ( TInt j = 0; j < iNodeCache.Count(); ++j )
        {
        // Use non-leaving metadata getter            
        if ( iNodeCache[ j ]->NodeMetaData() == aMetadata )
            {                
            DLTRACE(("Metadata in use, index: %d", j));
            // Metadata was used in some node
            return ETrue;            
            }
        }

    for ( TInt j = 0; j < iTempNodeCache.Count(); ++j )
        {
        // Use non-leaving metadata getter            
        if ( iTempNodeCache[ j ]->NodeMetaData() == aMetadata )
            {                
            DLTRACE(("Metadata in use, index: %d", j));
            // Metadata was used in some node            
            return ETrue;
            }
        }
    
    return EFalse;
    }
    

// Closes all nodes and metadata objects     
void CNcdNodeManager::FullCacheCleanup()
    {
    DLTRACEIN((""));
    // Here we call the Close-function of the nodes which are CObjects.
    // When the access count of CObject is decreased to zero, it will
    // be destroyed. Because, the initial access number of the node is one,
    // this manager has to call the Close method, so the node will be 
    // deleted after nobody is using it.
    DLINFO(("Closing node-objects"));
    for ( TInt i = 0; i < iNodeCache.Count(); ++i )
        {
        // The element should always be deleted here because
        // its access count should reach zero after this close.
        // So, the access count should always print 1 to the debug log here.
        DLINFO(("Close node %d access count: %d", 
                i, iNodeCache[ i ]->AccessCount()));

        // Because this node will not be in the cache anymore, we can remove
        // it from the block list of the cleaner if it exists there.
        TRAP_IGNORE(
            NodeCacheCleanerManager().
                CacheCleanerL( iNodeCache[ i ]->Identifier().ClientUid() ).
                    RemoveDoNotRemoveIdentifierL( 
                        iNodeCache[ i ]->Identifier() ) );
        
        // Note that the element is most likely deleted after this close
        // call. But, it does not matter here. Because the array is also
        // reset after all the elements here have been closed. So, it does
        // not matter that pointers to the deleted elements are left to the
        // array.             
        iNodeCache[ i ]->Close();
        }
    // Also, close the cache array.
    iNodeCache.Reset();
    
    // Close the objects of temp cache too.
    for ( TInt i = 0; i < iTempNodeCache.Count(); i++ ) 
        {
        // Because this node will not be in the cache anymore, we can remove
        // it from the block list of the cleaner if it exists there.
        TRAP_IGNORE(
            NodeCacheCleanerManager().
                CacheCleanerL( iTempNodeCache[ i ]->Identifier().ClientUid() ).
                    RemoveDoNotRemoveIdentifierL( 
                        iTempNodeCache[ i ]->Identifier() ) );
        iTempNodeCache[i]->Close();
        }
    iTempNodeCache.Reset();        
    
    DLINFO(("Closing nodemetadata-objects"));
    CloseMetadatas();
    }


void CNcdNodeManager::CloseMetadatas() 
    {
    DLTRACEIN((""));
    for ( TInt i = 0; i < iNodeMetaDataCache.Count(); ++i )
        {
        DLINFO(("Close metadata %d access count: %d", 
                i, iNodeMetaDataCache[ i ]->AccessCount()));                
        iNodeMetaDataCache[i]->Close();
        }
    iNodeMetaDataCache.Reset();

    DLTRACEOUT(("Nodemetadata-objects closed"));    
    }
    

void CNcdNodeManager::AppendNodeToCacheL( CNcdNode* aNode )
    {
    DLTRACEIN((""));
    
    DASSERT( aNode );

    User::LeaveIfError( InsertNodeInOrder( aNode, iNodeCache ) );

    // Because this node was appended to the list, inform the cleaner that
    // this node or its parents should not be removed from the database
    NodeCacheCleanerManager().
        CacheCleanerL( aNode->Identifier().ClientUid() ).
            AddDoNotRemoveIdentifierL( aNode->Identifier() );
        
    DLTRACEOUT(("Nodes in cache: %d", iNodeCache.Count() ));
    }


CNcdNode& CNcdNodeManager::CheckAndCreateNodeL( CNcdNodeFactory::TNcdNodeType aNodeType,
                                                CNcdNodeFactory::TNcdNodePurpose aNodePurpose,
                                                const CNcdNodeIdentifier& aParentNodeIdentifier,
                                                const CNcdNodeIdentifier& aMetaIdentifier )
    {
    DLTRACEIN((""));
    
    CNcdNodeIdentifier* nodeIdentifier( 
        NcdNodeIdentifierEditor::CreateNodeIdentifierLC( aParentNodeIdentifier,
                                                         aMetaIdentifier ) );
    CNcdNode& node( CheckAndCreateNodeL( aNodeType, aNodePurpose,
                                         *nodeIdentifier ) );
    CleanupStack::PopAndDestroy( nodeIdentifier );
    
    DLTRACEOUT((""));
    
    return node;
    }


CNcdNode& CNcdNodeManager::CheckAndCreateNodeL( CNcdNodeFactory::TNcdNodeType aNodeType,
                                                CNcdNodeFactory::TNcdNodePurpose aNodePurpose,
                                                const CNcdNodeIdentifier& aNodeIdentifier )
    {
    DLTRACEIN((""));

    // Check if the node can be found from the RAM cache or from the db.
    CNcdNode* node( NodePtrL( aNodeIdentifier) );

    // Get the class id of the node that has the given purpose.
    // The data type parameter informs if an item or a folder should be created.
    // Set the class id to be some item as a default.
    NcdNodeClassIds::TNcdNodeClassId classId =
            NodeFactory().NodeClassIdL( aNodeType, aNodePurpose );

    if ( node != NULL )
        {
        DLINFO(("Node was found"));
        DLINFO(("Class comparison. Old: %d, new: %d",
                node->ClassId(), classId));

        // Check if we should replace the old node by new one because its type or purpose has changed.
        if ( node->ClassId() != classId )
            {
            DLWARNING(("Be sure that the server has changed the type and there is no bug in the code!"));
            // Because the original type is wrong type,
            // remove the node from the RAM cache.
            for ( TInt i = 0; i < iNodeCache.Count(); ++i )
                {
                if ( iNodeCache[ i ] == node )
                    {
                    DLINFO(("Remove node from db"));
                    // Because node is removed from cache.
                    // Call Close, so the access count will be correct
                    // for the hanging node. Most likely it will be
                    // deleted soon.                
                    iNodeCache.Remove( i );
                    // Because the node is removed from the cache
                    // also inform cleaner that the node may be cleaned
                    // from the database if wanted.
                    NodeCacheCleanerManager().
                        CacheCleanerL( node->Identifier().ClientUid() ).
                                       RemoveDoNotRemoveIdentifierL( 
                                        node->Identifier() );
                    // Finally close the node. Because we do not own it
                    // anymore.
                    node->Close();
                    node = NULL;
                    break;
                    }
                }
                
            // Because the node was removed from the cache and we do not
            // want to use the node that is saved into the db,
            // we have to create the node directly here by using
            // node factory. This way we get the new uninitialized node
            // that is of the correct type.

            DLINFO(("Create node"));
            // Create the node according to the class id
            node = NodeFactory().CreateNodeLC( aNodeIdentifier,
                                               classId );

            DASSERT( node );
            
            // Insert node to the cache.
            // This will also insert the node back into do not remove list of
            // the cleaner.
            AppendNodeToCacheL( node );
            
            // Cache takes ownership of the node
            CleanupStack::Pop( node );
            }
        }
    else
        {
        DLINFO(("Node has to be created because it was not found"));
        // Create the node according to the class id
        node = NodeFactory().CreateNodeLC( aNodeIdentifier,
                                           classId );

        
        DASSERT( node );
        
        // Insert node to the cache.
        // This will also insert the node back into do not remove list of
        // the cleaner.
        AppendNodeToCacheL( node );
        
        // Cache takes ownership of the node
        CleanupStack::Pop( node );
        }
    
    DLTRACEOUT((""));
    
    return *node;
    }


CNcdNodeMetaData& CNcdNodeManager::CheckAndCreateMetaDataL( 
    const CNcdNodeIdentifier& aMetaIdentifier,
    CNcdNodeFactory::TNcdNodeType aMetaType )
    {
    DLTRACEIN((""));

    // Check if the metadata can be found from the RAM cache or from the db.
    CNcdNodeMetaData* metaData( NULL );
    TRAPD( metaDataError, metaData = &NodeMetaDataL( aMetaIdentifier ) );

    // Accept leave with KEreNotFound because next we can create it
    if( metaDataError != KErrNone && metaDataError != KErrNotFound )
        {
        DLERROR(( "metaDataError: %d", metaDataError ));
        User::Leave( metaDataError );
        }
    DLINFO((""));

    // Get the class id of the node that has the given purpose.
    // The data type parameter informs if an item or a folder should be created.
    // Set the class id to be some item as a default.
    NcdNodeClassIds::TNcdNodeClassId classId =
            NodeFactory().MetaDataClassId( aMetaType );

    if ( metaData != NULL )
        {
        DLINFO(("Metadata was found"));
        DLINFO(("Class comparison. Old: %d, new: %d",
                metaData->ClassId(), classId));

        if ( metaData->ClassId() != classId )
            {
            DLWARNING(("Be sure that the server has changed the type and there is no bug in the code!"))
            // Because the original type is wrong type,
            // remove the node from the RAM cache.
            for ( TInt i = 0; i < iNodeMetaDataCache.Count(); ++i )
                {
                if ( iNodeMetaDataCache[ i ] == metaData )
                    {
                    DLINFO(("Remove meta from db"));
                    // Because metadata is removed from cache.
                    // Call Close, so the access count will be correct
                    // for the hanging metadata. Most likely it will be
                    // deleted soon.                
                    iNodeMetaDataCache.Remove( i );
                    // Finally close the metadata. Because we do not own it
                    // anymore.
                    metaData->Close();
                    metaData = NULL;
                    break;
                    }
                }
                
            // Because the metadata was removed from the cache and we do not
            // want to use the metadata that is saved into the db,
            // we have to create the metadata directly here by using
            // node factory. This way we get the new uninitialized node
            // that is of the correct type.

            DLINFO(("Create metadata"));
            // Create the node according to the class id
            metaData = NodeFactory().CreateMetaDataLC( aMetaIdentifier,
                                                       classId );

            if ( metaData == NULL )
                {
                // Node was not be created.
                DLERROR(("Metadata was not created."));
                DASSERT( EFalse );
                User::Leave( KErrNotFound );
                }
            
            // Insert node to the cache.
            // This will also insert the node back into do not remove list of
            // the cleaner.
            iNodeMetaDataCache.AppendL( metaData );
            
            // Cache takes ownership of the node
            CleanupStack::Pop( metaData );
            }
        }
    else
        {
        DLINFO(("Metadata has to be created because it was not found"));
        // Create the node according to the class id
        metaData = NodeFactory().CreateMetaDataLC( aMetaIdentifier,
                                                   classId );

        if ( metaData == NULL )
            {
            // Node was not be created.
            DLERROR(("Node was not created."));
            DASSERT( EFalse );
            User::Leave( KErrNotFound );
            }
        
        // Insert node to the cache.
        // This will also insert the node back into do not remove list of
        // the cleaner.
        iNodeMetaDataCache.AppendL( metaData );
        
        // Cache takes ownership of the node
        CleanupStack::Pop( metaData );
        }
    
    DLTRACEOUT((""));
    
    return *metaData;
    }
    

MNcdConfigurationManager& CNcdNodeManager::ConfigurationManager()  const
    {
    return iConfigurationManager;
    }
    

void CNcdNodeManager::RemoveNodeFromRamCache( 
    const CNcdNodeIdentifier& aIdentifier )
    {
    DLTRACEIN((""));
    TInt index = FindNodeFromArray( aIdentifier, iNodeCache );
    if ( index != KErrNotFound )
        {
        DLTRACE(("Removing node from RAM cache"));
        iNodeCache[ index ]->Close();
        iNodeCache.Remove( index );        
        }
    }


void CNcdNodeManager::CommitSeenChanges( const MCatalogsContext& aContext )
    {
    TRAP_IGNORE( iSeenInfo->CommitChangesL( aContext.FamilyId() ) );
    }