mpx/collectionframework/collectionengine/src/mpxcollectioncache.cpp
changeset 0 a2952bb97e68
child 3 b425f12f328d
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mpx/collectionframework/collectionengine/src/mpxcollectioncache.cpp	Thu Dec 17 08:55:47 2009 +0200
@@ -0,0 +1,1271 @@
+/*
+* Copyright (c) 2007 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 the collection cache
+*
+*/
+
+
+// INCLUDES
+#include <e32base.h>
+#include <mpxitemid.h>
+#include <mpxcmn.h>
+#include <mpxmedia.h>
+#include <mpxmediaarray.h>
+#include <mpxcollectionpath.h>
+#include <mpxmediageneraldefs.h>
+#include <mpxmediacontainerdefs.h>
+#include <mpxcollectionopenlresultdef.h>
+#include <mpxlog.h>
+#include "mpxcollectioncache.h"
+
+// CONSTANTS
+const TInt KMPXRootNodeId = 0;
+
+// ---------------------------------------------------------------------------
+// static function to compare two datablock items
+// used for sorting datablocks
+// ---------------------------------------------------------------------------
+//
+static TInt CompareOpenDataBlock( const TMPXOpenDataBlock& aFirst,
+                                  const TMPXOpenDataBlock& aSecond )
+    {
+    return aFirst.iOffset > aSecond.iOffset;
+    }
+
+
+/**
+* Encapsulates a node in the cache tree.
+*/
+NONSHARABLE_CLASS(CMPXCollectionCacheNode) :
+    public CBase
+    {
+public:
+    /**
+    * Two phase constructor.
+    */
+    static CMPXCollectionCacheNode* NewL( const TMPXItemId aId );
+
+    /**
+    * Destructor.
+    */
+    virtual ~CMPXCollectionCacheNode();
+
+    /**
+    * Adds a child to the current node.
+    * Ownership of the node is transferred
+    */
+    void AddChildL(CMPXCollectionCacheNode* aChild);
+
+    /**
+    * Removes a child from the current node.
+    * Ownership of the node is not transferred
+    */
+    void RemoveChild(CMPXCollectionCacheNode& aChild);
+
+    /**
+    * Retrieves a child by ID. Returns NULL if not found.
+    * Ownership of the node is not transferred.
+    */
+    CMPXCollectionCacheNode* GetChild(TMPXItemId aId);
+
+    /**
+    * Sets the results and attribute set for the current node.
+    * @param aAttrs list of open attributes
+    * @param aResults results to set in
+    * @param aMediaFromOpen is the media from common
+    */
+    void SetResultsL(
+        const TArray<TMPXAttribute>& aAttrs,
+        CMPXMedia& aResults,
+        TBool aMediaFromOpen );
+
+    /**
+    * Checks if the specified attribute set exists (is included) in the current
+    * node attribute set.
+    */
+    TBool AttributesExist(const TArray<TMPXAttribute>& aAttrs);
+
+    /**
+    * Checks if the specified attribute set exists (is included) in the current
+    * node attribute set.
+    */
+    TBool ContainerAttributesExist(const TArray<TMPXAttribute>& aAttrs);
+    
+    /**
+    * Checks if the specified attribute exists (is included) in the current
+    * node attribute set.
+    * @param aAttribute attribute to look for
+    * @param aArray array to check
+    * @return ETrue if found, EFalse if not found
+    */
+    TBool AttributeExists(const TMPXAttribute& aAttribute, 
+                          RArray<TMPXAttribute>& aArray );
+    
+    /**
+    * Checks this node and all child nodes for invalidated data
+    * @param aChangeItemId ID of the item modified
+    * @param aArray array to store the list of affected nodes
+    */
+    void HandleChangeL( TMPXItemId aChangeItemId, 
+                        RPointerArray<CMPXCollectionCacheNode>& aArray );
+                        
+#ifdef _DEBUG
+    /**
+    * Prints the node.
+    */
+    void PrintNode(TBool aPrintChildren, TBool aPrintAtts);
+#endif
+
+private:
+    /**
+    * C++ constructor
+    */
+    CMPXCollectionCacheNode(TMPXItemId aId,
+        CMPXCollectionCache::TCachePriority aPriority);
+
+    /**
+    * Second phase constructor.
+    */
+    void ConstructL();
+
+    /**
+    * Returns the child index for the specified ID or KErrNotFound if not found.
+    */
+    TInt IndexOfId(TMPXItemId aId);
+
+    /**
+    * Compares two media instances. If the instances refer to the same collection
+    * item (they have the same general ID) returns ETrue, otherwise EFalse.
+    */
+    TBool CompareMediaItemsL(
+        const CMPXMedia& aMedia1, 
+        const CMPXMedia& aMedia2 );
+
+    /**
+    *  Comparison method for node IDs. Used when searching child nodes by ID.
+    *  Ownership of nodes is not transferred.
+    *  This method is used with the Find method of RPointerArray and 
+    *  must be declared as follow:
+    *  TBool *(aFunction)(const K *k, const T &t);
+    */
+    static TBool CompareIDs(
+        const CMPXCollectionCacheNode* aNode1,
+        const CMPXCollectionCacheNode& aNode2 );
+
+    /**
+    * Merge the data from two arrays
+    * @param aExistingArray Old array with current data
+    * @param aNewArray New arrary with data to merge
+    */
+    TBool DoMergeMediaArraysL( const CMPXMediaArray& aExistingArray, 
+                               const CMPXMediaArray& aNewArray );
+    
+    /**
+    * Merge the data from two arrays
+    * @param aExistingArray Old array with current data
+    * @param aNewArray New arrary with data to insert
+    */
+    void DoInsertMediaArraysL( CMPXMediaArray& aExistingArray, 
+                               const CMPXMediaArray& aNewArray, 
+                               const TMPXOpenDataBlock& aBlock );
+
+    /**
+    * Compact a list of open datablocks into larger chunks
+    * @param aArray Array to compact
+    */
+    void DoMergeArrayL( RArray<TMPXOpenDataBlock>& aArray );
+
+public:
+    TMPXItemId iId;                                 // node ID (same as path node ID)
+    RArray<TMPXAttribute> iAttrs;                   // attribute set corresponding to the results
+    RArray<TMPXAttribute> iContainerAttrs;          // attribute set corresponding to the container 
+                                                    // of a result set
+    CMPXMedia* iResults;                            // results
+    CMPXCollectionCache::TCachePriority iPriority;  // node priority
+    TTime iTime;                                    // last time the node was updated
+    TInt iLevel;                                    // level of the node in the original path
+
+    CMPXCollectionCacheNode* iParent;               // parent of the node (NULL for root)
+    RPointerArray<CMPXCollectionCacheNode> iChildren;   // node children
+    TBool iMediaFromOpenL;                          // whether or not this media was from an openl
+    };
+
+// ============================ MEMBER FUNCTIONS ==============================
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::NewL
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionCacheNode* CMPXCollectionCacheNode::NewL(
+    const TMPXItemId aId)
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::NewL");
+
+    CMPXCollectionCacheNode* self = new(ELeave)CMPXCollectionCacheNode(aId,
+        CMPXCollectionCache::EPriorityNormal);
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::~CMPXCollectionCacheNode
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionCacheNode::~CMPXCollectionCacheNode()
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::~CMPXCollectionCacheNode");
+
+    iAttrs.Close();
+    iContainerAttrs.Close();
+    delete iResults;
+
+    // delete all children
+    iChildren.ResetAndDestroy();
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::CMPXCollectionCacheNode
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionCacheNode::CMPXCollectionCacheNode(
+    TMPXItemId aId,
+    CMPXCollectionCache::TCachePriority aPriority) :
+    iId(aId),
+    iPriority(aPriority)
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::CMPXCollectionCacheNode");
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::ConstructL
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCacheNode::ConstructL()
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::ConstructL");
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::AddChildL
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCacheNode::AddChildL(
+    CMPXCollectionCacheNode* aChild)
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::AddChildL");
+    aChild->iParent = this;
+    iChildren.AppendL(aChild);
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::RemoveChild
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCacheNode::RemoveChild(
+    CMPXCollectionCacheNode& aChild)
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::RemoveChild");
+
+    TInt index = IndexOfId(aChild.iId);
+    if (index != KErrNotFound)
+        {
+        // remove the child
+        iChildren.Remove(index);
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::GetChild
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionCacheNode* CMPXCollectionCacheNode::GetChild(
+    TMPXItemId aId)
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::GetChild");
+
+    CMPXCollectionCacheNode* child(NULL);
+
+    TInt index = IndexOfId(aId);
+    if (index != KErrNotFound)
+        {
+        child = iChildren[index];
+        }
+
+    return child;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::SetResultsL
+// If results are already stored in the node try to merge the new results,
+// otherwise overwrite them.
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCacheNode::SetResultsL(
+    const TArray<TMPXAttribute>& aAttrs,
+    CMPXMedia& aResults,
+    TBool aMediaFromOpen )
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::SetResultsL");
+
+    // Problem if we did not have results from before, but
+    // want to merge in new data. Plugins returning all results will
+    // not contain KMPXCollectionOpenLResultRange in the media
+    // therefore we do manual merging. If it does contain the attribute
+    // That means it is from a subset, with a particular offset and block size
+    //
+    TMPXOpenDataBlock block;
+    block.iOffset = KErrNotFound;
+    if( aResults.IsSupported( KMPXCollectionOpenLResultRange ) )
+        {
+        block = aResults.ValueTObjectL<TMPXOpenDataBlock>( 
+                                            KMPXCollectionOpenLResultRange );
+        
+        // If the results have not been cached before, 
+        // we have nothing to merge
+        //
+        if( !iResults && block.iOffset != KErrNotFound )
+            {
+            User::Leave( KErrArgument );
+            }
+        }
+            
+    TBool overwrite(ETrue);
+    
+    // Merge
+    if ( iResults )
+        {
+        // extract the arrays from the two media instances
+        CMPXMediaArray* existingArray(NULL);
+        if ( iResults->IsSupported(KMPXMediaArrayContents) )
+            {
+            existingArray = 
+                        iResults->Value<CMPXMediaArray>(KMPXMediaArrayContents);
+            User::LeaveIfNull(existingArray);
+            }
+
+        const CMPXMediaArray* newArray(NULL);
+        if ( aResults.IsSupported( KMPXMediaArrayContents ))
+            {
+            newArray = aResults.Value<CMPXMediaArray>(KMPXMediaArrayContents);
+            User::LeaveIfNull(const_cast<CMPXMediaArray*>(newArray));
+            }
+            
+        // Only need to merge the two arrays if they exist, else it will just overwrite
+        if ( existingArray && newArray && existingArray->Count())
+            {
+            if( block.iOffset == KErrNotFound )
+                {
+                overwrite = DoMergeMediaArraysL( *existingArray, 
+                                                 *newArray );
+                }
+            else // aOffset != KErrNotFound 
+                {
+                DoInsertMediaArraysL( *existingArray, 
+                                      *newArray, 
+                                      block );
+                
+                // These 2 attributes already merged
+                //
+                aResults.Delete( KMPXMediaArrayContents );
+                aResults.Delete( KMPXMediaArrayCount );
+                
+                // Merge container media
+                //
+                iResults->MergeMediaL( aResults );
+                
+                // Merge the root media
+                //
+                overwrite = EFalse;
+                }
+            }
+        else
+            {
+            iResults->MergeMediaL( aResults );
+            overwrite = EFalse;
+            }
+        }
+
+    if (overwrite)
+        {
+        // List of supported attributes is 
+        // seperated to media w/ container from OpenL()
+        // and media only from MediaL()
+        //
+        if( aMediaFromOpen )
+            {
+            iAttrs.Reset();
+            ::CopyArrayL(aAttrs, iAttrs);
+            }
+        else
+            {
+            iContainerAttrs.Reset();
+            ::CopyArrayL(aAttrs, iContainerAttrs);
+            }
+                
+        delete iResults;
+        iResults = NULL;
+        iResults = CMPXMedia::NewL( aResults );
+        }
+    else
+        {
+        // Update either the array list or the root media list
+        //
+        if( aMediaFromOpen )
+            {
+            // Add the attributes into the list
+            for ( TInt attrIndex = 0; attrIndex < aAttrs.Count(); attrIndex++ )
+                {
+                const TMPXAttribute& attr = aAttrs[attrIndex];
+                if ( !AttributeExists( attr, iAttrs ))
+                    {
+                    iAttrs.AppendL( attr );
+                    }
+                }    
+            }
+        else
+            {
+            // Add the attributes into the container list
+            for ( TInt attrIndex = 0; attrIndex < aAttrs.Count(); attrIndex++ )
+                {
+                const TMPXAttribute& attr = aAttrs[attrIndex];
+                if ( !AttributeExists( attr, iContainerAttrs ))
+                    {
+                    iContainerAttrs.AppendL( attr );
+                    }
+                }
+            }
+        }
+#ifdef _DEBUG
+    MPX_DEBUG1("CMPXCollectionCacheNode::SetResultsL(): Attributes cached");
+    for ( TInt k = 0; k < iAttrs.Count(); k++ )
+        {
+        MPX_DEBUG4("CMPXCollectionCacheNode::SetResultsL(): attr[%d] = {0x%x,0x%x}", 
+                   k, iAttrs[k].ContentId(), iAttrs[k].AttributeId());
+        }
+#endif
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::AttributesExist
+// ----------------------------------------------------------------------------
+//
+TBool CMPXCollectionCacheNode::AttributesExist(
+    const TArray<TMPXAttribute>& aAttrs)
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::AttributesExist");
+
+    TBool exist(ETrue);
+    if (0 == aAttrs.Count())
+        {
+        // no attributes specified, always exist
+        exist = ETrue;
+        }
+    else if (0 == iAttrs.Count())
+        {
+        // no existing attributes, always do not exist
+        exist = EFalse;
+        }
+    else
+        {
+        // both have valid number of attributes
+        TInt count(aAttrs.Count());
+        for (TInt index = 0; index < count; ++index)
+            {
+            if (!AttributeExists(aAttrs[index], iAttrs))
+                {
+                exist = EFalse;
+                break;
+                }
+            }
+        }
+
+    return exist;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::AttributesExist
+// ----------------------------------------------------------------------------
+//
+TBool CMPXCollectionCacheNode::ContainerAttributesExist(
+    const TArray<TMPXAttribute>& aAttrs)
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::ContainerAttributesExist");
+
+    TBool exist(ETrue);
+    if (0 == aAttrs.Count())
+        {
+        // no attributes specified, always exist
+        exist = ETrue;
+        }
+    else if (0 == iContainerAttrs.Count())
+        {
+        // no existing attributes, always do not exist
+        exist = EFalse;
+        }
+    else
+        {
+        // both have valid number of attributes
+        TInt count(aAttrs.Count());
+        for (TInt index = 0; index < count; ++index)
+            {
+            if (!AttributeExists(aAttrs[index], 
+                                 iContainerAttrs))
+                {
+                exist = EFalse;
+                break;
+                }
+            }
+        }
+
+    return exist;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::AttributeExists
+// ----------------------------------------------------------------------------
+//
+TBool CMPXCollectionCacheNode::AttributeExists(
+    const TMPXAttribute& aAttribute, 
+    RArray<TMPXAttribute>& aArray)
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::AttributeExists");
+
+    TBool ret(ETrue);
+
+    TUint content(aAttribute.ContentId());
+    TUint attribute(aAttribute.AttributeId());
+    TInt count(aArray.Count());
+    
+    // Have to do an exhaustive search of every bit
+    // This is because clients can compress the content IDs
+    //
+    TUint32 mask(1); // 0x00000001
+    while( mask != 0 )
+        {
+        TUint32 val(attribute&mask);
+        if( val )
+            {
+            TInt i;
+            for (i = 0; i < count; ++i)
+                {
+                if (aArray[i].ContentId() == content &&
+                    (aArray[i].AttributeId() & val) )
+                    {
+                    // break out of for loop
+                    break;
+                    }
+                }
+            if( i==count )
+                {
+                // break out of while loop if
+                // the particular bit is not found
+                ret = EFalse;
+                break; 
+                }
+            }
+        mask = mask<<1; // Check next bit
+        }
+    
+    return ret;
+    }
+
+// ----------------------------------------------------------------------------
+// Handles any changes to the collection cache
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCacheNode::HandleChangeL( TMPXItemId aChangeItemId, 
+                                RPointerArray<CMPXCollectionCacheNode>& aArray )
+    {
+    // This node is affected
+    // All child nodes are invalid!
+    //
+    TBool aOpen(ETrue);
+    if( iId == aChangeItemId )
+        {
+        aArray.AppendL( this );
+        aOpen=EFalse; // wiping out this node already
+        }
+    else if( iChildren.Count() )
+        {
+        TInt c( iChildren.Count() );
+        for( TInt i=0; i<c; ++i )
+            {
+            // Next level maybe all MediaL() data 
+            // in which case we do want to check this level's array
+            //
+            iChildren[i]->HandleChangeL( aChangeItemId, aArray );
+            }
+        }
+        
+    // Check the contents as well
+    //
+    if( aOpen )
+        {
+        // This is a leaf node, so we check the array
+        //
+        if( iResults && 
+            iResults->IsSupported(KMPXMediaArrayContents) )
+            {
+            // Array not owned
+            const CMPXMediaArray& ary =
+                          *iResults->Value<CMPXMediaArray>(KMPXMediaArrayContents);
+            User::LeaveIfNull(const_cast<CMPXMediaArray*>(&ary));
+            TInt c(ary.Count() );
+            
+            // Check every media item, this *MAYBE* slow. May want to consider
+            // caching all item IDs within each node for performance
+            for( TInt i=0; i<c; ++i )
+                {
+                TMPXItemId id=KMPXInvalidItemId;
+                const CMPXMedia& m = *(ary.AtL(i));
+                
+                // Just to be safe
+                if( m.IsSupported( KMPXMediaGeneralId ) )
+                    {
+                    id=m.ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId);
+                    }
+                
+                // If we match, add this as affected and return
+                if( id == aChangeItemId )
+                    {
+                    aArray.AppendL( this );
+                    break;
+                    }
+                } // for
+            } // if iResults
+        } // if no children
+    }
+    
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::IndexOfId
+// ----------------------------------------------------------------------------
+//
+TInt CMPXCollectionCacheNode::IndexOfId(
+    TMPXItemId aId)
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::IndexOfId");
+    TInt index( KErrNotFound );
+
+    CMPXCollectionCacheNode* node( NULL );
+    MPX_TRAPD(err, node = CMPXCollectionCacheNode::NewL( aId ));
+    if ( KErrNone == err )
+        {
+        index = iChildren.Find(*node, CompareIDs);
+        delete node;
+        }
+
+    return index;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::CompareMediaItems
+// ----------------------------------------------------------------------------
+//
+TBool CMPXCollectionCacheNode::CompareMediaItemsL(
+    const CMPXMedia& aItem1,
+    const CMPXMedia& aItem2)
+    {
+    return aItem1.IsSupported(KMPXMediaGeneralId) &&
+        aItem2.IsSupported(KMPXMediaGeneralId) &&
+        (aItem1.ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId) ==
+         aItem2.ValueTObjectL<TMPXItemId>(KMPXMediaGeneralId));
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::CompareIDs
+// ----------------------------------------------------------------------------
+//
+TBool CMPXCollectionCacheNode::CompareIDs(
+    const CMPXCollectionCacheNode* aNode1,
+    const CMPXCollectionCacheNode& aNode2)
+    {
+    MPX_FUNC("CMPXCollectionCacheNode::CompareIDs");
+    return aNode1->iId == aNode2.iId;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::DoMergeMediaArraysL
+// ----------------------------------------------------------------------------
+//
+TBool CMPXCollectionCacheNode::DoMergeMediaArraysL( const CMPXMediaArray& aExistingArray,
+                                                    const CMPXMediaArray& aNewArray )
+    {
+    TBool overwrite(ETrue);
+    TInt newCount( aNewArray.Count() );
+    for ( TInt arrayIndex = 0; arrayIndex < newCount; arrayIndex++ )
+        {
+        const CMPXMedia* newItem( (aNewArray).AtL(arrayIndex) );
+        CMPXMedia* existingItem( NULL );
+        TInt existingCount( aExistingArray.Count() );
+        if ( arrayIndex < existingCount )
+            {
+            existingItem = aExistingArray.AtL(arrayIndex);
+            }
+        else
+            {
+            existingItem = aExistingArray.AtL(0);
+            }
+
+        MPX_ASSERT(newItem->IsSupported(KMPXMediaGeneralId));
+        MPX_ASSERT(existingItem->IsSupported(KMPXMediaGeneralId));
+
+        // try to match the items at the same index
+        if ( CompareMediaItemsL( *newItem, *existingItem ))
+            {
+            existingItem->MergeMediaL( *newItem );
+            overwrite = EFalse;
+            }
+        else
+            {
+            // items do not match, try linear search
+            TBool found(EFalse);
+            for ( TInt i = 0; i < existingCount && !found; ++i )
+                {
+                CMPXMedia* item( aExistingArray.AtL(i) );
+                // Already compared existing item so skip it
+                if ( item != existingItem )
+                    {
+                    if ( CompareMediaItemsL( *newItem, *item ))
+                        {
+                        // found the item
+                        item->MergeMediaL( *newItem );
+                        overwrite = EFalse;
+                        found = ETrue;
+                        }
+                    }
+                }
+            }
+        }
+    return overwrite;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::DoInsertMediaArraysL
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCacheNode::DoInsertMediaArraysL( CMPXMediaArray& aExistingArray, 
+                                                    const CMPXMediaArray& aNewArray, 
+                                                    const TMPXOpenDataBlock& aBlock )
+    {
+    // Overflow checking. Make sure array indexes are correct
+    //
+    TInt newCount( aNewArray.Count() );
+    if( aBlock.iOffset + newCount > aExistingArray.Count() )
+        {
+        User::Leave( KErrArgument );
+        }
+    
+    // Merge all results by inserting into the existing array
+    //
+    for( TInt i=0; i<newCount; ++i )
+        {
+        aExistingArray.Set(*aNewArray.AtL(i),aBlock.iOffset+i);
+        }
+
+
+    // Update the container information about what has been read
+    RArray<TMPXOpenDataBlock> datablocks;
+    CleanupClosePushL( datablocks );
+    if( iResults && 
+        iResults->IsSupported(KMPXCollectionOpenLAllResultRange) )
+        {
+        // De-serialize from global data, would be good to have global arrays
+        //
+        const TDesC& buf = iResults->ValueText(KMPXCollectionOpenLAllResultRange);
+        CBufBase* buffer(NULL);
+        MPXUser::CreateBufferL( buf, buffer );
+        CleanupStack::PushL( buffer );
+        ::CreateFromBufferL( *buffer, datablocks );
+        CleanupStack::PopAndDestroy( buffer );
+        }
+    
+    // Append, sort and merge the array for more effient access
+    //
+    datablocks.AppendL( aBlock );
+    
+    // Sort and merge the data array
+    //
+    datablocks.Sort( CompareOpenDataBlock );
+    DoMergeArrayL( datablocks );
+    
+    // Package it and store in media
+    // Store the block list
+    //
+    CBufBase* buffer(NULL);
+    ::CreateBufferL( datablocks.Array(), buffer );
+    CleanupStack::PushL( buffer );
+    TPtr8 ptr8 = buffer->Ptr(0);
+    TPtrC ptr = MPXUser::Ptr( ptr8 );
+    iResults->SetTextValueL( KMPXCollectionOpenLAllResultRange, ptr );
+    CleanupStack::PopAndDestroy( buffer );
+    
+    CleanupStack::PopAndDestroy( &datablocks );
+    }
+
+// ----------------------------------------------------------------------------
+// Compact the datablocks
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCacheNode::DoMergeArrayL( RArray<TMPXOpenDataBlock>& aArray )
+    {
+    // O(n^2) complexity here is OK, Merging keeps the number of items 
+    // very small. Less than 3 items are alive at a time
+    //
+    RArray<TMPXOpenDataBlock> newArray;
+    CleanupClosePushL( newArray );
+    
+    TInt c1 = aArray.Count();
+    for( TInt i=0; i<c1; ++i )
+        {
+        TBool updated(EFalse);
+        TMPXOpenDataBlock& curBlock = aArray[i];
+        
+        TInt c2( newArray.Count() );
+        for( TInt j=0; j<c2; ++j )
+            {
+            TMPXOpenDataBlock& mergedBlock = newArray[j];
+            if( mergedBlock.iOffset == curBlock.iOffset + curBlock.iSize )
+                {
+                mergedBlock.iOffset = curBlock.iOffset;
+                mergedBlock.iSize += curBlock.iSize;
+                updated = ETrue;
+                break;
+                }
+            else if( mergedBlock.iOffset + mergedBlock.iSize == curBlock.iOffset )
+                {
+                mergedBlock.iSize += curBlock.iSize;
+                updated = ETrue;
+                break;
+                }
+            }
+        
+        if( !updated )
+            {
+            newArray.AppendL( curBlock );
+            }
+        }
+        
+    aArray.Reset();
+    TInt c3( newArray.Count() );
+    for( TInt i=0; i<c3; ++i )
+        {
+        aArray.AppendL( newArray[i] );
+        MPX_DEBUG3("Order: %i %i", newArray[i].iOffset, newArray[i].iSize );
+        }
+    CleanupStack::PopAndDestroy( &newArray );
+    }
+                                 
+#ifdef _DEBUG
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCacheNode::PrintNode
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCacheNode::PrintNode(
+    TBool aPrintChildren,
+    TBool aPrintAtts )
+    {
+    TBuf<30> buf;
+    _LIT(KFormatString, "%-B%:0%J%:1%T%:2%S%:3%+B");
+    TRAP_IGNORE( iTime.FormatL( buf, KFormatString ));
+    // use RDebug directly since there is no MPX_DEBUG with 6 parameters.
+    RDebug::Print(_L("+Id: %08X, Level: %d, Priority: %d, Time: %S"),
+        iId.iId1, iLevel, iPriority, &buf);
+    MPX_DEBUG2("CMPXCollectionCacheNode::PrintNode(): iMediaFromOpenL = %d", 
+                iMediaFromOpenL);
+    
+    if ( aPrintAtts )
+        {
+        for ( TInt j = 0; j < iAttrs.Count(); j++ )
+            {
+            TMPXAttribute att( iAttrs[j] );
+            MPX_DEBUG4("CMPXCollectionCacheNode::PrintNode(): attr[%d] = {0x%x,0x%x}", 
+                                    j, att.ContentId(), att.AttributeId());
+            TMPXAttributeType type( iResults->Type( att ));
+            if ( EMPXTypeText == type )
+                {
+                MPX_DEBUG2("CMPXCollectionCacheNode::PrintNode(): Text = %S", 
+                                                    &(iResults->ValueText( att )));
+                }
+            else if ( EMPXTypeTInt == type )
+                {
+                TInt val(0);
+                TRAP_IGNORE(val = (iResults->ValueTObjectL<TInt>( att )));
+                MPX_DEBUG2("CMPXCollectionCacheNode::PrintNode(): TInt = %d", 
+                           val);
+                }
+            }
+        }
+
+    if ( aPrintChildren )
+        {
+        TInt children( iChildren.Count() );
+        for ( TInt index = 0; index < children; ++index )
+            {
+            iChildren[index]->PrintNode( ETrue, aPrintAtts );
+            }
+        }
+    }
+
+#endif
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::NewL
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionCache* CMPXCollectionCache::NewL(
+    TInt aMaximumSizeRatio /* = -1 */)
+    {
+    MPX_FUNC("CMPXCollectionCache::NewL");
+
+    CMPXCollectionCache* self = new(ELeave) CMPXCollectionCache( aMaximumSizeRatio );
+    CleanupStack::PushL(self);
+    self->ConstructL();
+    CleanupStack::Pop(self);
+    return self;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::~CMPXCollectionCache
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionCache::~CMPXCollectionCache()
+    {
+    MPX_FUNC("CMPXCollectionCache::~CMPXCollectionCache");
+
+    // delete the entire cache
+    delete iRoot;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::Add
+// ----------------------------------------------------------------------------
+//
+CMPXMedia* CMPXCollectionCache::AddL(
+    const CMPXCollectionPath& aPath,
+    const TArray<TMPXAttribute>& aAttrs,
+    CMPXMedia& aResults,
+    TBool aMediaFromOpenL, 
+    TCachePriority aPriority)
+    {
+    MPX_FUNC("CMPXCollectionCache::DoAddL");
+    MPX_DEBUG_PATH(aPath);
+    
+    CMPXMedia* ret( &aResults );
+
+    //if OOM then clear current cache and don't add to cache
+    TBool toCache = ManageCacheSizeL();
+    
+    //!media in array should support KMPXMediaGeneralId otherwise we don't cache it
+    if(toCache && aResults.IsSupported(KMPXMediaArrayContents))
+        {
+        const CMPXMediaArray* resultsArray(
+                    aResults.Value<CMPXMediaArray>(KMPXMediaArrayContents));
+        User::LeaveIfNull(const_cast<CMPXMediaArray*>(resultsArray));
+        TInt arrCount(resultsArray->Count());
+        for(TInt arrInd = 0; arrInd < arrCount; ++arrInd)
+            {
+            const CMPXMedia* curItem(resultsArray->AtL(arrInd));
+            if(!curItem->IsSupported(KMPXMediaGeneralId))
+                {
+                MPX_DEBUG1("CMPXCollectionCache::AddL: Missing MediaGeneralId - caching skipped");
+                toCache = EFalse;
+                break;
+                }
+            }
+        }
+    
+    if (toCache && aResults.IsSupported(KMPXMediaGeneralNonPermissibleActions ) )
+        {
+        // check for auto playlist, it should not be cached
+        TMPXGeneralNonPermissibleActions attr(
+                aResults.ValueTObjectL<TMPXGeneralNonPermissibleActions>( 
+                KMPXMediaGeneralNonPermissibleActions ) );
+        if ( attr & EMPXCache )
+            {
+            toCache = EFalse;
+            }
+        }                                        
+
+    if(toCache)
+        {
+        // get the corresponding node, create if not available
+        CMPXCollectionCacheNode* node = NodeFromPathL(aPath, ETrue);
+
+        // update the node
+        node->SetResultsL( aAttrs, aResults, aMediaFromOpenL );
+        node->iMediaFromOpenL = node->iMediaFromOpenL || aMediaFromOpenL;
+
+        // set the priority and the "last updated" timestamp
+        node->iPriority = aPriority;
+        node->iLevel = aPath.Levels();
+        node->iTime.HomeTime();
+
+        ret = node->iResults;
+        }
+#ifdef _DEBUG
+    PrintTree( ETrue );
+#endif
+    return ret;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::GetL
+// ----------------------------------------------------------------------------
+//
+CMPXMedia* CMPXCollectionCache::GetL(
+    const CMPXCollectionPath& aPath,
+    const TArray<TMPXAttribute>& aAttrs,
+    TBool aMediaFromOpenL /* = EFalse */ )
+    {
+    MPX_FUNC("CMPXCollectionCache::GetL");
+    MPX_DEBUG_PATH(aPath);
+
+    CMPXMedia* results(NULL);
+
+    // get the corresponding node, do not create if not available
+    CMPXCollectionCacheNode* node = NodeFromPathL(aPath, EFalse);
+
+    TInt selCount = aPath.Selection().Count();
+
+    // Check if node exists and attributes match
+    // If we are calling from openL() or mediaL() on multiple items, we check the array
+    // attributes. If we are calling from MediaL() with a single item
+    // we check the container attributes
+    //
+    if ( (aMediaFromOpenL || selCount )
+          && node && node->AttributesExist(aAttrs))
+        {
+        // Check if it needs to be a media from openl
+        // If not just return the results.  But if it needs to be a media from
+        // OpenL, then need to check if the results are actually from an OpenL
+        if ( (aMediaFromOpenL && node->iMediaFromOpenL) ||
+             !aMediaFromOpenL )
+            {
+            results = node->iResults;
+            }
+        }
+    else if( node && node->ContainerAttributesExist(aAttrs) )
+        {
+        // Here we end from a MediaL call, we need to check if there are items selected
+        // in this case NodeFromPathL result may be ivalid as it using only focused item
+        // so we will pass that call to plugin and ignore found node from cache in that case
+        if(selCount == 0)
+            {
+            results = node->iResults;
+            }
+        }
+
+    // note that NULL is returned for nodes without results
+    // (created empty to link to other nodes)
+    return results;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::RemoveL
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCache::RemoveL(
+    const CMPXCollectionPath& aPath)
+    {
+    MPX_FUNC("CMPXCollectionCache::RemoveL(CMPXCollectionPath)");
+    MPX_DEBUG_PATH(aPath);
+
+    // get the corresponding node, do not create if not available
+    CMPXCollectionCacheNode* node( NodeFromPathL( aPath, EFalse ));
+
+    // if node exists
+    if ( node )
+        {
+        // delete it and its children
+        RemoveNode( node );
+#ifdef _DEBUG
+        PrintTree( ETrue );
+#endif
+        }
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::Reset
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCache::Reset()
+    {
+    MPX_FUNC("CMPXCollectionCache::ClearCache");
+    iRoot->iChildren.ResetAndDestroy();
+    iRoot->iAttrs.Reset();
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::HandleChangeL()
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCache::HandleChangeL( TUid aCollectionId, TMPXItemId aChangeItemId )
+    {
+    // First get the cache node associated with the collection
+    // If the node exists, then we recursively get every node to handle the change
+    //
+    TMPXItemId root( aCollectionId.iUid );
+    CMPXCollectionCacheNode* rootNode = iRoot->GetChild( root );
+    
+    // Array does not own any of the objects
+    RPointerArray<CMPXCollectionCacheNode> affectedNodes; 
+    CleanupClosePushL( affectedNodes );
+    if( rootNode )
+        {
+        // Iteratively check every node
+        //
+        rootNode->HandleChangeL( aChangeItemId, 
+                                 affectedNodes );
+        }
+        
+    // Remove all affected nodes
+    TInt c( affectedNodes.Count() ); 
+    if( c )
+        {
+        for(TInt i=0; i<c; ++i )
+            {
+            // FYI:
+            // Child nodes are always inserted before their parents
+            // Otherwise this will ASSERT
+            RemoveNode(affectedNodes[i]);
+            }
+        }
+    
+    CleanupStack::PopAndDestroy( &affectedNodes );
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::CMPXCollectionCache
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionCache::CMPXCollectionCache(
+    TInt aMaximumSizeRatio ) :
+    iMaximumSizeRatio( aMaximumSizeRatio )
+    {
+    MPX_FUNC("CMPXCollectionCache::CMPXCollectionCache");
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::ConstructL
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCache::ConstructL()
+    {
+    MPX_FUNC("CMPXCollectionCache::ConstructL");
+    iRoot = CMPXCollectionCacheNode::NewL(KMPXRootNodeId);
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::NodeFromPathL
+// ----------------------------------------------------------------------------
+//
+CMPXCollectionCacheNode* CMPXCollectionCache::NodeFromPathL(
+    const CMPXCollectionPath& aPath,
+    TBool aCreateNodes /* = EFalse */)
+    {
+    MPX_FUNC("CMPXCollectionCache::NodeFromPathL");
+
+    CMPXCollectionCacheNode* crtNode(iRoot);
+
+    TInt count(aPath.Levels());
+    for (TInt level = 0; level < count; ++level)
+        {
+        TMPXItemId id = aPath.Id(level);
+        CMPXCollectionCacheNode* childNode = crtNode->GetChild(id);
+        if (!childNode)
+            {
+            if (aCreateNodes)
+                {
+                // create the child node and add it
+                childNode = CMPXCollectionCacheNode::NewL(id);
+                CleanupStack::PushL(childNode);
+                crtNode->AddChildL(childNode);	// ownership transferred
+                CleanupStack::Pop(childNode);
+                }
+            else
+                {
+                // exit and return NULL
+                crtNode = NULL;
+                break;
+                }
+            }
+
+        // move to the next level
+        crtNode = childNode;
+        }
+
+    return crtNode;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::RemoveNode
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCache::RemoveNode(
+    CMPXCollectionCacheNode* aNode)
+    {
+    MPX_FUNC("CMPXCollectionCache::RemoveNode");
+    MPX_ASSERT( aNode );
+
+
+    // remove from parent node
+    aNode->iParent->RemoveChild(*aNode);
+
+    // delete the node and its children
+    delete aNode;
+    }
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::ManageCacheSizeL
+// ----------------------------------------------------------------------------
+//
+TBool CMPXCollectionCache::ManageCacheSizeL()
+    {
+    MPX_FUNC("CMPXCollectionCache::ManageCacheSizeL");
+    
+    TBool addToCache(ETrue);
+    TInt totalMemory(0);
+    TInt usedMemory(0);
+    CMPXMedia::HeapMemoryInfoL( totalMemory, usedMemory );
+    TInt ratio( (usedMemory * 100) / totalMemory );
+    MPX_DEBUG4("ManageCacheSizeL : ratio=%d, total=%d, used=%d",ratio,totalMemory,usedMemory);
+    if(ratio > iMaximumSizeRatio)
+        {
+        // clear cache to free all memory we can
+        Reset();
+        addToCache = EFalse;
+        }
+        
+    return addToCache;
+    }
+
+
+#ifdef _DEBUG
+
+// ----------------------------------------------------------------------------
+// CMPXCollectionCache::PrintTree
+// ----------------------------------------------------------------------------
+//
+void CMPXCollectionCache::PrintTree( TBool aPrintAtts /*= EFalse */ )
+    {
+    MPX_FUNC("CMPXCollectionCache::PrintTree");
+    iRoot->PrintNode( ETrue, aPrintAtts);
+    }
+
+#endif
+
+// End of file