diff -r 000000000000 -r ba25891c3a9e ncdengine/provider/server/src/ncdnodemanager.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ncdengine/provider/server/src/ncdnodemanager.cpp Thu Dec 17 08:51:10 2009 +0200 @@ -0,0 +1,3738 @@ +/* +* 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 +#include +#include + +#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( 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( 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( 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( 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( 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(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(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(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(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( 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( 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(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(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& 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& 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( aNode ); + const RPointerArray& 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& aFoundNodes ) + { + DLTRACEIN(("")); + + RPointerArray identifiers; + CleanupResetAndDestroyPushL( identifiers ); + CDesCArrayFlat* nameSpaces = new (ELeave) CDesCArrayFlat( KListGranularity ); + CleanupStack::PushL( nameSpaces ); + RArray 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& 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( 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( 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 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( 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& 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& favorites( + iFavoriteManager->FavoriteNodesL( aClientUid ) ); + + if ( !favorites.Count() ) + { + DLTRACEOUT(("No favorites so nothing to do")); + return; + } + + // order by uid & namespace + TLinearOrder sort( + CNcdNodeIdentifier::CompareOrderByUid ); + RPointerArray 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& 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 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& 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( + 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( 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 ); + + // 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& childArray( aFolder.ChildArray() ); + TInt count = childArray.Count(); + + const RPointerArray& 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& childArray( aFolder.ChildArray() ); + TInt count = childArray.Count(); + + if ( !count ) + { + DLTRACEOUT(("No children")); + return; + } + + RPointerArray metaIdArray; + CleanupResetAndDestroyPushL( metaIdArray ); + metaIdArray.ReserveL( count ); + + const RPointerArray& favorites( + iFavoriteManager->FavoriteNodesL( aFolder.Identifier().ClientUid() ) ); + + RPointerArray 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 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& 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& 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() ) ); + } +